@neetru/sdk 3.0.2 → 3.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.
- package/CHANGELOG.md +554 -530
- package/dist/auth.cjs +219 -54
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +1 -2
- package/dist/auth.d.ts +1 -2
- package/dist/auth.mjs +219 -54
- package/dist/auth.mjs.map +1 -1
- package/dist/catalog.cjs +50 -2
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.d.cts +1 -2
- package/dist/catalog.d.ts +1 -2
- package/dist/catalog.mjs +50 -2
- package/dist/catalog.mjs.map +1 -1
- package/dist/checkout.cjs +52 -3
- package/dist/checkout.cjs.map +1 -1
- package/dist/checkout.d.cts +1 -2
- package/dist/checkout.d.ts +1 -2
- package/dist/checkout.mjs +52 -3
- package/dist/checkout.mjs.map +1 -1
- package/dist/db-react.cjs +18 -6
- package/dist/db-react.cjs.map +1 -1
- package/dist/db-react.d.cts +35 -13
- package/dist/db-react.d.ts +35 -13
- package/dist/db-react.mjs +18 -6
- package/dist/db-react.mjs.map +1 -1
- package/dist/db.cjs +164 -56
- package/dist/db.cjs.map +1 -1
- package/dist/db.d.cts +1 -2
- package/dist/db.d.ts +1 -2
- package/dist/db.mjs +164 -56
- package/dist/db.mjs.map +1 -1
- package/dist/entitlements.cjs +50 -2
- package/dist/entitlements.cjs.map +1 -1
- package/dist/entitlements.d.cts +1 -2
- package/dist/entitlements.d.ts +1 -2
- package/dist/entitlements.mjs +50 -2
- package/dist/entitlements.mjs.map +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +3 -1
- package/dist/errors.d.ts +3 -1
- package/dist/errors.mjs.map +1 -1
- package/dist/firestore-compat.cjs +411 -0
- package/dist/firestore-compat.cjs.map +1 -0
- package/dist/firestore-compat.d.cts +319 -0
- package/dist/firestore-compat.d.ts +319 -0
- package/dist/firestore-compat.mjs +383 -0
- package/dist/firestore-compat.mjs.map +1 -0
- package/dist/index.cjs +266 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.mjs +265 -67
- package/dist/index.mjs.map +1 -1
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.cts +1 -2
- package/dist/mocks.d.ts +1 -2
- package/dist/mocks.mjs.map +1 -1
- package/dist/notifications.cjs +50 -2
- package/dist/notifications.cjs.map +1 -1
- package/dist/notifications.d.cts +1 -2
- package/dist/notifications.d.ts +1 -2
- package/dist/notifications.mjs +50 -2
- package/dist/notifications.mjs.map +1 -1
- package/dist/react.cjs +268 -2
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +135 -3
- package/dist/react.d.ts +135 -3
- package/dist/react.mjs +266 -3
- package/dist/react.mjs.map +1 -1
- package/dist/support.cjs +50 -2
- package/dist/support.cjs.map +1 -1
- package/dist/support.d.cts +1 -2
- package/dist/support.d.ts +1 -2
- package/dist/support.mjs +50 -2
- package/dist/support.mjs.map +1 -1
- package/dist/telemetry.cjs +65 -4
- package/dist/telemetry.cjs.map +1 -1
- package/dist/telemetry.d.cts +1 -2
- package/dist/telemetry.d.ts +1 -2
- package/dist/telemetry.mjs +65 -4
- package/dist/telemetry.mjs.map +1 -1
- package/dist/{types-D7zVkO3n.d.ts → types-B3XFHD4A.d.ts} +640 -2
- package/dist/{types-Dc4bjrrt.d.cts → types-DePdSU3P.d.cts} +640 -2
- package/dist/usage.cjs +50 -2
- package/dist/usage.cjs.map +1 -1
- package/dist/usage.d.cts +1 -2
- package/dist/usage.d.ts +1 -2
- package/dist/usage.mjs +50 -2
- package/dist/usage.mjs.map +1 -1
- package/dist/webhooks.cjs +83 -2
- package/dist/webhooks.cjs.map +1 -1
- package/dist/webhooks.d.cts +1 -2
- package/dist/webhooks.d.ts +1 -2
- package/dist/webhooks.mjs +83 -3
- package/dist/webhooks.mjs.map +1 -1
- package/package.json +156 -151
- package/dist/collection-ref-BDdfD87k.d.cts +0 -581
- package/dist/collection-ref-BDdfD87k.d.ts +0 -581
package/CHANGELOG.md
CHANGED
|
@@ -1,530 +1,554 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to `@neetru/sdk` will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## [3.0
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
- `
|
|
76
|
-
- `
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
- `
|
|
95
|
-
- `
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
- `
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
- **
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
- `
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
- `
|
|
138
|
-
-
|
|
139
|
-
- `
|
|
140
|
-
- `
|
|
141
|
-
- `
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
await client.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
await client.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
Fix
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
###
|
|
324
|
-
|
|
325
|
-
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
###
|
|
361
|
-
|
|
362
|
-
-
|
|
363
|
-
`transport
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
-
|
|
525
|
-
-
|
|
526
|
-
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
- **`
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@neetru/sdk` will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [3.1.0] - 2026-06-05 — **feat: subpath `firestore-compat` + hooks de dados React (frente-B P0)**
|
|
9
|
+
|
|
10
|
+
Release **minor** — só adições, nenhum export removido nem assinatura alterada (a suite de snapshot SDK-QW-5 confirma que a superfície pública 3.0 segue intacta). Upgrade direto de qualquer 3.0.x.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Subpath novo `@neetru/sdk/firestore-compat`** — camada de compatibilidade (migração) que espelha a superfície modular do Firebase Firestore que os produtos já usavam: `getFirestore`, `collection`, `doc`, `query`, `where`, `orderBy`, `limit`, `getDoc`, `getDocs`, `setDoc`, `updateDoc`, `deleteDoc`, `addDoc`, `onSnapshot`, `writeBatch`, `runTransaction`, `serverTimestamp`, `Timestamp`, `FieldValue` (`arrayUnion`/`arrayRemove`/`increment`/`delete`), `deleteField`. `DocumentSnapshot`/`QuerySnapshot` emulam `.exists()`/`.data()`/`.docs[]`/`.forEach()` com `id` em `.id` (nunca duplicado dentro de `data()`).
|
|
15
|
+
|
|
16
|
+
- Backing 100% por `client.db` (`NeetruDb`). Aceita `NeetruClient`, `client.db` ou o handle `Firestore` opaco. `serverTimestamp()`/`increment()` delegam pros sentinels server-side do SDK (não mais ISO spoofável do cliente).
|
|
17
|
+
- Mata os ~600–1000 LOC de shims caseiros que cada SaaS reescrevia (desenhado a partir dos shims reais de `neetru-gestovendas` e `neetru-pdvagiliza`). Marcado **claramente como aid de migração** — não compete com a API nativa `NeetruDb`.
|
|
18
|
+
|
|
19
|
+
- **Hooks de dados React em `@neetru/sdk/react`** — `useCollection`, `useDoc`, `useUser` → `{ data, isLoading, error, isStale, lastUpdated }`.
|
|
20
|
+
|
|
21
|
+
- Consomem refs do `firestore-compat`; realtime nativo via `client.db.onSnapshot` com fallback de polling visibility-aware (pausa em `document.hidden`). `id` embutido em cada item sem duplicar dentro de `data`.
|
|
22
|
+
- `useUser`: seed síncrono do estado + timeout de seed (8s) anti-spinner-infinito; aceita `NeetruClient`, `{ auth }` ou `AuthNamespace` direto. Safe em StrictMode.
|
|
23
|
+
- **Não colidem** com os hooks offline-first de `@neetru/sdk/db/react` (assinaturas e retornos distintos). O bundle `@neetru/sdk/react` segue leve (~11KB, sem offline stack).
|
|
24
|
+
|
|
25
|
+
- **`useSyncState(client)`** (`@neetru/sdk/db/react`) — agora aceita o `NeetruClient` inteiro OU a `SyncStateSource` direta (resolve `client.db` automaticamente, forma oficial do `02-sdk.md §3.6`). Retrocompat 100%: passar `client.db`/namespace offline cru segue funcionando. Novo type export `UseSyncStateInput` (adição não-breaking).
|
|
26
|
+
|
|
27
|
+
### Tests / CI
|
|
28
|
+
|
|
29
|
+
- **VERSION drift guard + snapshot da superfície pública (SDK-QW-5)** — `src/__tests__/version-and-export-surface.test.ts` trava `VERSION` (`src/index.ts`) ≡ `package.json` version e falha o build se qualquer export público de valor sumir (remoção = breaking → exige major bump consciente; adição é permitida).
|
|
30
|
+
- Suite cresceu de 771 → **827 tests** (+56 de `firestore-compat` + `react-data-hooks`, +4 de `useSyncState` wiring, + guards SDK-QW-5).
|
|
31
|
+
|
|
32
|
+
## [3.0.2] - 2026-06-02 — **fix: sentinels de escrita server-side (serverTimestamp + increment) — bug_05aedb8b**
|
|
33
|
+
|
|
34
|
+
### Fixed / Added
|
|
35
|
+
|
|
36
|
+
Fecha 3 lacunas do DB de documentos (`client.db.collection()`):
|
|
37
|
+
|
|
38
|
+
- **`serverTimestamp()`** — sentinel de timestamp resolvido pelo **servidor**. Antes, `createdAt`/`updatedAt` de produto usavam o relógio do CLIENTE (spoofável + clock-skew). Agora:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { serverTimestamp } from '@neetru/sdk/db';
|
|
42
|
+
await orders.add({ total: 99, createdAt: serverTimestamp() });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
O marcador atravessa o transporte como `{ "__neetru__": "serverTimestamp" }` e o Core o resolve em `FieldValue.serverTimestamp()` no momento da escrita.
|
|
46
|
+
|
|
47
|
+
- **`increment(n)`** — incremento **atômico** de campo numérico (antes `update()` era só merge/replace, sujeito a lost-update sob concorrência):
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { increment } from '@neetru/sdk/db';
|
|
51
|
+
await counters.update('global', { hits: increment(1) });
|
|
52
|
+
await wallet.update(uid, { balance: increment(-50) });
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Resolvido server-side via `FieldValue.increment(n)`. Aplicação otimista local: `serverTimestamp()` → `Date.now()` aproximado; `increment(n)` → soma local sobre o valor atual (valor canônico vem do servidor no sync).
|
|
56
|
+
|
|
57
|
+
- **Tipos alinhados ao runtime** — `add`/`set`/`update`/`batch` e `DbDocRef.set/update` agora aceitam `WithFieldSentinels<T>`: cada campo pode receber seu tipo OU um sentinel, sem erro de tipo. O `.d.ts` é gerado da mesma fonte do runtime (tsup), eliminando divergência declaração↔runtime.
|
|
58
|
+
|
|
59
|
+
### Novos exports
|
|
60
|
+
|
|
61
|
+
- De `@neetru/sdk` e `@neetru/sdk/db`: `serverTimestamp`, `increment`, `isFieldSentinel`, `isServerTimestampSentinel`, `isIncrementSentinel`, `NEETRU_SENTINEL_KEY`.
|
|
62
|
+
- Tipos: `FieldSentinel`, `ServerTimestampSentinel`, `IncrementSentinel`, `WithFieldSentinels`.
|
|
63
|
+
|
|
64
|
+
### Segurança
|
|
65
|
+
|
|
66
|
+
- Sentinels só são resolvidos em campos **top-level** (espelha o Core). Marcador desconhecido, `operand` não-numérico, ou sentinel aninhado → o Core rejeita com `400 validation_failed` (fail-closed) e **nunca grava** o objeto cru.
|
|
67
|
+
|
|
68
|
+
> Requer Core com o resolver de sentinels (`src/lib/datastore-sentinels.ts`) nos endpoints `/api/sdk/v1/datastore/*`. Ambos os repos mudam neste fix.
|
|
69
|
+
|
|
70
|
+
## [3.0.1] - 2026-05-29 — **fix: sincronizar VERSION + docstrings + contract-staging códigos de erro**
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
|
|
74
|
+
- `src/index.ts` — `VERSION` constante sincronizada com `package.json` (`'3.0.0'` → `'3.0.1'`). Consumers que fazem `import { VERSION } from '@neetru/sdk'` recebiam `'3.0.0'` enquanto `npm list` mostrava `3.0.1` (LOW-NEW-SDK-01).
|
|
75
|
+
- `src/telemetry.ts` — JSDoc na linha 6 corrigido: path legacy `/api/v1/sdk/telemetry/event` → canônico `/api/sdk/v1/telemetry/event`. O código sempre usou o path correto; apenas a documentação estava stale (LOW-NEW-SDK-02).
|
|
76
|
+
- `src/__tests__/telemetry.test.ts` — comentário de cabeçalho atualizado para não dizer "(legado)" (LOW-NEW-SDK-02).
|
|
77
|
+
- `src/__tests__/contract-staging.test.ts` — `'http_error'` removido de todos os arrays `toContain`. `NeetruErrorCode` não inclui `'http_error'`; respostas HTTP não mapeáveis caem em `'unknown'`. Substituído por `'unknown'` em todos os 7 sites (LOW-NEW-SDK-03).
|
|
78
|
+
|
|
79
|
+
### Nota
|
|
80
|
+
|
|
81
|
+
Este patch corresponde ao commit `4c40f01` (inline realtime-protocol types) que não atualizou `VERSION` nem o CHANGELOG.
|
|
82
|
+
|
|
83
|
+
## [3.0.0] - 2026-05-27 — **MAJOR: contract hardening + Universal runtime + paths canonical**
|
|
84
|
+
|
|
85
|
+
> Major rewrite resolvendo 5 CRITICAL + 6 HIGH do review `CHEFE_SDK_REVIEW_2026-05-27`.
|
|
86
|
+
> SDK 2.3.6 estava com **4 dos 11 namespaces broken** contra Core LIVE (`usage`, `webhooks`, `notifications`, `support`) — 13 endpoints batendo 404/400. Tests passavam validando contrato errado (mock fetch aceita qualquer URL).
|
|
87
|
+
>
|
|
88
|
+
> **Justificativa do MAJOR**: paths/body shapes mudaram em 4 namespaces; 2 métodos removidos (`usage.track`, `usage.getQuota`); `verifyWebhookSignature` virou async. Toda CLI release/SaaS inaugural depende disso primeiro.
|
|
89
|
+
|
|
90
|
+
### BREAKING CHANGES
|
|
91
|
+
|
|
92
|
+
#### 1. `usage` namespace 100% rewrite (CRITICAL-SDK-01)
|
|
93
|
+
|
|
94
|
+
- **REMOVIDO** `usage.track(event, props)` — Core não aceitava esse shape; era vaporware testado contra mock. Use `usage.report(resource, qty, opts)`.
|
|
95
|
+
- **REMOVIDO** `usage.getQuota(metric)` — endpoint `/usage/quota` nunca existiu no Core. `usage.report()` devolve `{value, limit, remaining}` inline em todo POST.
|
|
96
|
+
- Paths corrigidos:
|
|
97
|
+
- `/sdk/v1/usage/record` → `/api/sdk/v1/usage/record` (canonical)
|
|
98
|
+
- `/sdk/v1/entitlements` → `/api/sdk/v1/entitlements` (canonical)
|
|
99
|
+
- `usage.report()` agora envia `Idempotency-Key: <uuid v4>` header automaticamente.
|
|
100
|
+
- `usage.check()` retorna novo field `behavior: "readonly" | null` (inferido de `reason === "limit_exceeded"`).
|
|
101
|
+
|
|
102
|
+
#### 2. `webhooks` namespace — `productId` obrigatório (CRITICAL-SDK-03)
|
|
103
|
+
|
|
104
|
+
- `RegisterWebhookInput.productId` adicionado. Default resolve do `config.productId`; lança `validation_failed` se ambos ausentes.
|
|
105
|
+
- `list(options?)`, `unregister(id, options?)`, `test(id, options?)` agora aceitam `{ productId }` (Core exige query param em todos os verbos).
|
|
106
|
+
- `webhooks.test()` envia `Idempotency-Key` header.
|
|
107
|
+
|
|
108
|
+
#### 3. `notifications` namespace — `productId` obrigatório (CRITICAL-SDK-04)
|
|
109
|
+
|
|
110
|
+
- `SendNotificationInput.productId` adicionado (Core valida via Zod `SendNotificationSchema`).
|
|
111
|
+
- `ListNotificationsOptions.productId` adicionado.
|
|
112
|
+
- `NotificationScopeOptions` novo type para `markRead`/`dismiss`.
|
|
113
|
+
- `notifications.send()` envia `Idempotency-Key` header.
|
|
114
|
+
|
|
115
|
+
#### 4. `support` namespace — endpoint canonical (CRITICAL-SDK-02)
|
|
116
|
+
|
|
117
|
+
- Endpoint mudou de `/api/v1/products/{slug}/tickets` (vaporware — **nunca existiu no Core**) para `/api/sdk/v1/support/tickets` (canonical SDK path).
|
|
118
|
+
- `productId` obrigatório (default `config.productId`).
|
|
119
|
+
- `createTicket()` envia `Idempotency-Key` header.
|
|
120
|
+
- `listMyTickets(options?)` aceita `{ productSlug }` override.
|
|
121
|
+
- **Pré-requisito Core**: endpoint `/api/sdk/v1/support/tickets` precisa ser construído (vide `docs/CORE_CHANGES_REQUIRED.md` §1).
|
|
122
|
+
|
|
123
|
+
#### 5. `verifyWebhookSignature` é async + Universal (HIGH-SDK-03)
|
|
124
|
+
|
|
125
|
+
- **Antes (2.x)**: `(payload, sig, secret) => boolean` — usava `require('node:crypto')` que quebrava em bundle browser/Edge ESM puro. README anunciava "Universal" mas mentia.
|
|
126
|
+
- **Agora (3.0)**: `(payload, sig, secret) => Promise<boolean>` via `WebCrypto.subtle.sign` HMAC-SHA-256 + constant-time XOR compare.
|
|
127
|
+
- Funciona em Node ≥20, browsers modernos, Vercel Edge Functions, Cloudflare Workers.
|
|
128
|
+
- Lança `NeetruError('runtime_unsupported')` se WebCrypto ausente (distingue "ambiente não suporta" de "assinatura inválida").
|
|
129
|
+
|
|
130
|
+
#### 6. `telemetry.event` / `telemetry.track` path canonical (CRITICAL-SDK-05)
|
|
131
|
+
|
|
132
|
+
- `POST /api/v1/sdk/telemetry/event` → `POST /api/sdk/v1/telemetry/event` (canonical SDK pattern).
|
|
133
|
+
- Pré-requisito Core: alias/handler para o canonical path (vide `docs/CORE_CHANGES_REQUIRED.md` §3).
|
|
134
|
+
|
|
135
|
+
#### 7. `baseUrl` allowlist fail-closed (HIGH-SDK-05)
|
|
136
|
+
|
|
137
|
+
- `createNeetruClient` agora chama `validateBaseUrl` antes de aceitar `config.baseUrl`.
|
|
138
|
+
- Allowlist padrão: HTTPS `*.neetru.com` + `localhost`/`127.0.0.1`/`*.test`/`*.localhost`.
|
|
139
|
+
- `NeetruClientConfig.allowedHosts: string[]` novo — adicione hosts custom (suffix match).
|
|
140
|
+
- Bypass: `NEETRU_ALLOW_INSECURE_BASEURL=1` (env) ou `globalThis.NEETRU_ALLOW_INSECURE_BASEURL='1'`. Warning no console quando ativo.
|
|
141
|
+
- Cenário ataque fechado: dev configura `NEETRU_BASE_URL=http://attacker.example/` no `.env`, agora lança `invalid_config` em vez de vazar Bearer.
|
|
142
|
+
|
|
143
|
+
#### 8. Retries default por método (HIGH-SDK-01)
|
|
144
|
+
|
|
145
|
+
- **GET/HEAD**: 2 retries default (= 3 tentativas) — idempotente, seguro.
|
|
146
|
+
- **POST/PUT/PATCH/DELETE**: **0 retries default** — mutations podem duplicar side effects (ex: `FieldValue.increment` no `usage/record`).
|
|
147
|
+
- Caller pode override: `httpRequest(config, { method: 'POST', ..., retries: 2 })`.
|
|
148
|
+
- Combinado com `Idempotency-Key` automático em mutations: dup-write rate → zero.
|
|
149
|
+
|
|
150
|
+
#### 9. Cleanup de stubs deprecated
|
|
151
|
+
|
|
152
|
+
- `initNeetru` (deprecated desde v0.2, no-op em v2.0) **definitivamente removido**.
|
|
153
|
+
- `NeetruConfig` type alias **removido** (use `NeetruClientConfig`).
|
|
154
|
+
|
|
155
|
+
#### 10. `NeetruErrorCode` ganhou `runtime_unsupported`
|
|
156
|
+
|
|
157
|
+
- Lançado por `verifyWebhookSignature` quando WebCrypto ausente. Não é breaking se você usa só `instanceof NeetruError`; é breaking se você fez `switch` no union type (TS error em case missing).
|
|
158
|
+
|
|
159
|
+
### ADDED
|
|
160
|
+
|
|
161
|
+
- `src/idempotency.ts` — `generateIdempotencyKey()` via `crypto.randomUUID()` (Node ≥20, browsers, Edge).
|
|
162
|
+
- `src/base-url-allowlist.ts` — `validateBaseUrl(url, allowedHosts)` fail-closed.
|
|
163
|
+
- `HttpRequestOptions.idempotencyKey: boolean | string` — injeta header automaticamente em todo POST de mutation.
|
|
164
|
+
- `client.usage.check().behavior: 'readonly' | null` — sinal para `<EntitlementGate mode='readonly'>` decidir UI.
|
|
165
|
+
- `NeetruClientConfig.allowedHosts` — extension point para hosts custom (staging/proxies).
|
|
166
|
+
- `MockUsage.__getReports()` substitui `__getRecords()` (legacy alias mantido).
|
|
167
|
+
- Test suite: `src/__tests__/base-url-allowlist.test.ts` (16 tests), `src/__tests__/idempotency.test.ts` (9 tests).
|
|
168
|
+
- `docs/CORE_CHANGES_REQUIRED.md` — catálogo de 5 endpoints Core a criar/modificar para 3.0 funcionar end-to-end.
|
|
169
|
+
|
|
170
|
+
### FIXED
|
|
171
|
+
|
|
172
|
+
- `<EntitlementGate>` v1.1 não morre mais em silent `allowed:false` — paths corrigidos no `usage.check` (era dependência transitiva, fixed via CRITICAL-SDK-01).
|
|
173
|
+
- README v2.x prometia "Universal: browser/Node/Edge" mas `verifyWebhookSignature` só funcionava em Node. 3.0 cumpre.
|
|
174
|
+
|
|
175
|
+
### Migration guide v2.x → v3.0
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
// ❌ Antes (v2.x)
|
|
179
|
+
await client.usage.track('api_call', { route: '/x' });
|
|
180
|
+
const quota = await client.usage.getQuota('api_calls');
|
|
181
|
+
|
|
182
|
+
await client.webhooks.register({
|
|
183
|
+
url: 'https://x.com/h',
|
|
184
|
+
events: ['subscription.activated'],
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await client.notifications.send({
|
|
188
|
+
userId: 'u1',
|
|
189
|
+
kind: 'order.received',
|
|
190
|
+
title: 'Pedido',
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
await client.support.createTicket({
|
|
194
|
+
subject: 'help',
|
|
195
|
+
message: 'x',
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// ✅ Depois (v3.0)
|
|
199
|
+
const client = createNeetruClient({
|
|
200
|
+
apiKey: 'nrt_...',
|
|
201
|
+
productId: 'gestovendas', // 🆕 default p/ todos os namespaces
|
|
202
|
+
tenantId: 't-acme', // 🆕 default p/ usage
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const result = await client.usage.report('api_call', 1);
|
|
206
|
+
// result.value, result.limit, result.remaining inline — não precisa getQuota separado.
|
|
207
|
+
|
|
208
|
+
const allowed = await client.usage.check('api_call');
|
|
209
|
+
// allowed.behavior === 'readonly' quando over limit (use em <EntitlementGate>)
|
|
210
|
+
|
|
211
|
+
await client.webhooks.register({
|
|
212
|
+
// productId default config.productId, ou explícito:
|
|
213
|
+
url: 'https://x.com/h',
|
|
214
|
+
events: ['subscription.activated'],
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
await client.notifications.send({
|
|
218
|
+
userId: 'u1',
|
|
219
|
+
kind: 'order.received',
|
|
220
|
+
title: 'Pedido',
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
await client.support.createTicket({
|
|
224
|
+
subject: 'help',
|
|
225
|
+
message: 'x',
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Verificação de assinatura de webhook — agora async
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
// ❌ Antes (v2.x) — sync, quebrava em Edge
|
|
233
|
+
import { verifyWebhookSignature } from '@neetru/sdk/webhooks';
|
|
234
|
+
const ok = verifyWebhookSignature(rawPayload, sigHeader, secret);
|
|
235
|
+
|
|
236
|
+
// ✅ Depois (v3.0) — async, Universal
|
|
237
|
+
const ok = await verifyWebhookSignature(rawPayload, sigHeader, secret);
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Endpoints Core que precisam ser criados/modificados antes do publish 3.0
|
|
241
|
+
|
|
242
|
+
Vide `docs/CORE_CHANGES_REQUIRED.md` completo:
|
|
243
|
+
|
|
244
|
+
1. **CRITICAL** — `POST/GET /api/sdk/v1/support/tickets` (customer-facing) — 3-4h.
|
|
245
|
+
2. **HIGH** — Idempotency-Key dedup em endpoints de mutation — 4-6h.
|
|
246
|
+
3. **HIGH** — `POST /api/sdk/v1/telemetry/event` (canonical) — 1-2h ou alias middleware.
|
|
247
|
+
4. **LOW** — `behavior` field em `/api/sdk/v1/entitlements` response — 30min.
|
|
248
|
+
5. **LOW** — Verificar envelope `/api/sdk/v1/datastore/{collection}` — 30min check.
|
|
249
|
+
|
|
250
|
+
### Tests
|
|
251
|
+
|
|
252
|
+
- **719 tests passing** (era 668 em 2.3.6). Tsc clean. Build clean.
|
|
253
|
+
- Contract tests reescritos para validar paths canonical + body shapes + headers (Idempotency-Key UUID v4 regex match).
|
|
254
|
+
|
|
255
|
+
## [2.3.6] - 2026-05-26 — **fix(bundle): webpackIgnore em TODOS os dynamic imports node-only — bug_168368ae [CRITICAL]**
|
|
256
|
+
|
|
257
|
+
### Corrigido
|
|
258
|
+
|
|
259
|
+
- **bug_168368aedb664edba1ca1f1945b568ac (CRITICAL) — fix 2.3.5 foi parcial: `webpackIgnore` aplicado só em `createSqlClientFromConfig`, mas `createPgPool` + `createDrizzleHandle` (helpers separados no mesmo arquivo) + os 3 dynamic imports em `MockDb.sql()` (`pg-mem`, `drizzle-orm/node-postgres`, `@electric-sql/pglite`, `drizzle-orm/pglite`) ainda estavam sem o magic comment.**
|
|
260
|
+
Resultado: webpack do consumer Next.js seguia o module graph daqueles paths e ainda tentava bundle `pg` → `Module not found: tls/fs` no client.
|
|
261
|
+
Fix 2.3.6: `/* webpackIgnore: true */` em **todos** os 7 spots de `await import('pg' | 'drizzle-orm/node-postgres' | 'pg-mem' | '@electric-sql/pglite' | 'drizzle-orm/pglite')` em `sql-client.ts` + `mocks.ts`.
|
|
262
|
+
|
|
263
|
+
### Testes (antirregressão atualizado)
|
|
264
|
+
|
|
265
|
+
- **`no-node-deps-in-browser-bundle.regression.test.ts`** expandido: agora pega `import("X")` dynamic SEM `webpackIgnore` (regex negative lookahead). Antes só cobria `from "X"` static. Adicionado 4 patterns: pg, drizzle-orm/node-postgres, pg-mem, @electric-sql/pglite.
|
|
266
|
+
|
|
267
|
+
## [2.3.5] - 2026-05-26 — **fix(bundle): node deps fora do browser entry — bug_0f836014 [CRITICAL]**
|
|
268
|
+
|
|
269
|
+
### Corrigido
|
|
270
|
+
|
|
271
|
+
- **bug_0f836014055747858adfa8ab0e8cbb47 (CRITICAL) — SDK 2.3.4 publicou com `require('pg')` + `require('drizzle-orm/node-postgres')` literais no `dist/index.mjs`.**
|
|
272
|
+
Webpack do consumer Next.js seguia o module graph e tentava bundle pg → puxa fs → `Module not found: Can't resolve 'fs'` no client bundle. Bloqueava `npm run build` de qualquer projeto Next.js que importa `createNeetruClient`.
|
|
273
|
+
Fix: dynamic import com `/* webpackIgnore: true */` em `createSqlClientFromConfig`. Instrui webpack a NÃO processar — runtime Node resolve. `sql()` é server-only por contrato.
|
|
274
|
+
Resultado: `dist/index.mjs` zero imports literais de pg/drizzle/fs/node:*. Verified via teste antirregressão novo.
|
|
275
|
+
|
|
276
|
+
### Testes (antirregressão)
|
|
277
|
+
|
|
278
|
+
- **`src/__tests__/regressions/no-node-deps-in-browser-bundle.regression.test.ts`** (novo) — varre 13 entry bundles e falha se encontrar require/import estático de pg/drizzle-orm/node-postgres/pg-mem/@electric-sql/pglite/fs/node:*. Roda APÓS `npm run build`. Pega regressão dessa classe.
|
|
279
|
+
|
|
280
|
+
## [2.3.4] - 2026-05-26 — **fix(mocks): MockDb.sql() migra pra pglite + pg-mem fallback — bug_6bae8674 [HIGH]**
|
|
281
|
+
|
|
282
|
+
### Corrigido
|
|
283
|
+
|
|
284
|
+
- **bug_6bae8674461f4600809e2f9de13c6043 (HIGH) — fix anterior (2.3.3) teve efeito INVERSO em `MockDb.sql()`.**
|
|
285
|
+
O fix de 2.3.3 injetava `getTypeParser` em `pool.types` para resolver o erro "Not supported: getTypeParser is not supported".
|
|
286
|
+
Porém `pg-mem` tem um guard interno: `if (query.types?.getTypeParser) throw NotSupported`.
|
|
287
|
+
`drizzle-orm/node-postgres/session.js:113` passa `rawQuery.types = pool.types` em TODA query — ao adicionar
|
|
288
|
+
`getTypeParser` ao pool, drizzle passou-o para o pg-mem que o rejeitou imediatamente.
|
|
289
|
+
Efeito líquido: qualquer `INSERT`/`SELECT`/`UPDATE` continuava quebrando com o mesmo erro.
|
|
290
|
+
|
|
291
|
+
**Fix 2.3.4 — duas soluções em cascata:**
|
|
292
|
+
|
|
293
|
+
1. **Solução A (preferida): `@electric-sql/pglite` + `drizzle-orm/pglite`** — WASM-Postgres nativo
|
|
294
|
+
sem adapter pg-mem. Sem conflito `getTypeParser`, sem monkey-patch. `PGlite.create()` retorna
|
|
295
|
+
instância pronta, DDL via `pglite.query(ddl)`, ORM via `drizzle(pglite, { schema })`. Cast
|
|
296
|
+
`PgliteDatabase → NodePgDatabase` via `unknown` (interfaces Drizzle compatíveis em superfície
|
|
297
|
+
mas com tipos de resultado internos distintos).
|
|
298
|
+
|
|
299
|
+
2. **Solução B (fallback pg-mem)**: strip interceptor — intercepta `pool.query` e deleta
|
|
300
|
+
`types.getTypeParser` do objeto de query ANTES do pg-mem ver. Guard não dispara, query passa
|
|
301
|
+
limpa. Compatível com consumers que só têm pg-mem nas devDeps.
|
|
302
|
+
|
|
303
|
+
**`@electric-sql/pglite` adicionado como `peerDependency` optional** (`^0.4.0`) + `devDependency`
|
|
304
|
+
interno (`^0.4.6`).
|
|
305
|
+
|
|
306
|
+
**8 novos testes** em `src/__tests__/mocks-sql-pglite.test.ts`: criação sem erro, INSERT sem throw,
|
|
307
|
+
SELECT após INSERT, UPDATE, múltiplos INSERTs, sem initSql, close idempotente, transaction().
|
|
308
|
+
Todos passando.
|
|
309
|
+
|
|
310
|
+
**655 testes totais** (era 647 em 2.3.3). `tsc --noEmit` clean. Build clean.
|
|
311
|
+
|
|
312
|
+
## [2.3.3] - 2026-05-26 — **fix(mocks): pool.types.getTypeParser identity parser — bug_44bcc99f [HIGH]**
|
|
313
|
+
|
|
314
|
+
### Corrigido
|
|
315
|
+
|
|
316
|
+
- **bug_44bcc99f9d3140d78abd8f7810d1afa4 (HIGH) — `MockDb.sql()` queries Drizzle falhavam em runtime.**
|
|
317
|
+
Followup do `bug_a7f2ae25` (SDK 2.3.2): `sql()` retornava handle Drizzle, mas qualquer query (`INSERT`, `SELECT`, etc) jogava `Error: Not supported: getTypeParser is not supported`. `pg-mem` não implementa `pool.types.getTypeParser(oid)` que `drizzle-orm/node-postgres/session.js` chama em TODA query.
|
|
318
|
+
Fix: monkey-patch identity parser no Pool retornado por `pg-mem.adapters.createPg()` antes de passar pro Drizzle. Types retornam o valor sem transformação (suficiente pra tests in-memory; produção usa `pg` real com parsers nativos).
|
|
319
|
+
3 linhas. Non-breaking. Tests E2E Drizzle in-memory destravados.
|
|
320
|
+
|
|
321
|
+
## [2.3.2] - 2026-05-26 — **fix(mocks): MockDb.sql() implementado in-memory via pg-mem — bug_a7f2ae25 [HIGH]**
|
|
322
|
+
|
|
323
|
+
### Corrigido
|
|
324
|
+
|
|
325
|
+
- **bug_a7f2ae25fda54129b52b6dd55aaa6050 (HIGH) — `MockDb.sql()` lançava `db_unavailable` em vez de implementar Drizzle in-memory.**
|
|
326
|
+
Bloqueava vitest server-side dos consumers (pdv-agiliza Fase 4 migration). Política do projeto proíbe workaround instalar `pg-mem` no consumer.
|
|
327
|
+
Fix: `MockDb.sql(schema, options)` agora carrega `pg-mem` lazy + cria `NodePgDatabase<TSchema>` Drizzle apontando pra DB in-memory. Suporta `options.initSql: string | string[]` pra rodar DDL no setup (CREATE TABLE etc).
|
|
328
|
+
`pg-mem` adicionado como `peerDependency optional` (com `peerDependenciesMeta.optional=true`) + `devDependency` interno. Consumers que querem mock SQL: `npm install -D pg-mem drizzle-orm pg`.
|
|
329
|
+
**Não-breaking:** signature `sql(schema, options?)` mantida. SqlOptions ganhou `initSql?` opcional (ignorado por lease real em produção). Comportamento default — `sql()` sem `initSql` — funciona em ambos modos.
|
|
330
|
+
|
|
331
|
+
## [2.3.1] - 2026-05-26 — **fix(deps): drizzle-orm + pg como peerDependencies — bug_42ae9f80 [CRITICAL]**
|
|
332
|
+
|
|
333
|
+
### Corrigido
|
|
334
|
+
|
|
335
|
+
- **bug_42ae9f80b2884bd583c5246771f7730c (CRITICAL) — `drizzle-orm` em `dependencies` causava type collision com versão do consumer.**
|
|
336
|
+
Quando o projeto consumer instalava `drizzle-orm` direto e tentava `client.db.sql(schema)`, o TypeScript via 2 cópias diferentes (uma nested em `node_modules/@neetru/sdk/node_modules/drizzle-orm`, outra no consumer) e reclamava: `'Property config is protected but type Column<...> is not a class derived from Column<...>'` + `'shouldInlineParams private'`. Bloqueava Fases 2-8 da migração pdv-agiliza pra `vm-postgres-single`.
|
|
337
|
+
Fix: movidas `drizzle-orm` e `pg` de `dependencies` pra `peerDependencies` (com `peerDependenciesMeta.optional: true`). Consumer instala 1 versão, todos os tipos convergem. Devkit mantém `drizzle-orm` + `pg` em `devDependencies` pra build/test internos.
|
|
338
|
+
**Breaking expected on consumer side:** se o projeto consumer NÃO tinha `drizzle-orm` em deps mas usava `client.db.sql()`, agora precisa `npm install drizzle-orm pg`. Consumers que só usam transport REST/Firestore não são afetados (peerDeps optional).
|
|
339
|
+
|
|
340
|
+
## [2.3.0] - 2026-05-25 — **feat(db-config): renomeia engine→transport com deprecation — bug_ed9da7fd [HIGH]**
|
|
341
|
+
|
|
342
|
+
Fix do bug `bug_ed9da7fd11194eac87f0eb9c75e5fad9` (HIGH) — colisão de terminologia: `engine` no SDK
|
|
343
|
+
configurava o transporte SDK↔Core (REST/Firestore/WebSocket), mas `engine` no admin database (CLI
|
|
344
|
+
`neetru admin database create --engine=...`) configura o storage engine (firestore-instance,
|
|
345
|
+
cloud-sql-postgres, vm-postgres-single…). Devs colocavam `firestore-instance` no SDK e quebravam.
|
|
346
|
+
|
|
347
|
+
### Added
|
|
348
|
+
|
|
349
|
+
- **`NeetruDbTransport`** — novo tipo público canônico para o campo de transporte SDK↔Core.
|
|
350
|
+
Valores: `'rest' | 'firestore' | 'nosql-vm'` (idênticos aos de `NeetruDbEngine`).
|
|
351
|
+
|
|
352
|
+
- **`NeetruDbOptions.transport`** — novo campo canônico que substitui `engine`.
|
|
353
|
+
```ts
|
|
354
|
+
// v2.3+ (preferido):
|
|
355
|
+
createNeetruClient({ db: { transport: 'rest' } });
|
|
356
|
+
createNeetruClient({ db: { transport: 'firestore' } });
|
|
357
|
+
createNeetruClient({ db: { transport: 'nosql-vm', realtimeGatewayUrl: '...' } });
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Deprecated
|
|
361
|
+
|
|
362
|
+
- **`NeetruDbOptions.engine`** — depreciado em v2.3, será removido em v3.0.
|
|
363
|
+
Continua funcionando: se `transport` estiver ausente e `engine` presente, o valor de `engine`
|
|
364
|
+
é usado como transport com `console.warn` de deprecação.
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
// v2.x (depreciado — emite console.warn):
|
|
368
|
+
createNeetruClient({ db: { engine: 'firestore' } });
|
|
369
|
+
// ⚠ [neetru/sdk] NeetruDbOptions.engine está depreciado...
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
- **`NeetruDbEngine`** — depreciado em v2.3, será removido em v3.0.
|
|
373
|
+
Agora é alias de `NeetruDbTransport`. Use `NeetruDbTransport` em código novo.
|
|
374
|
+
|
|
375
|
+
### Behavior
|
|
376
|
+
|
|
377
|
+
- `transport` e `engine` podem coexistir: `transport` sempre prevalece, sem warn.
|
|
378
|
+
- Sem `transport` nem `engine`: default `'rest'`, sem warn (comportamento idêntico ao v2.x).
|
|
379
|
+
- A relação 1:N entre storage engines e transports é documentada no JSDoc de `NeetruDbTransport`:
|
|
380
|
+
qualquer storage engine (cloud-sql-postgres, vm-postgres-single…) pode ser acessado com
|
|
381
|
+
`transport: 'rest'`; `transport: 'firestore'` e `transport: 'nosql-vm'` são para bancos
|
|
382
|
+
documentais com realtime nativo.
|
|
383
|
+
|
|
384
|
+
### Tests
|
|
385
|
+
|
|
386
|
+
- 5 testes novos em `src/__tests__/db-real.test.ts`:
|
|
387
|
+
`transport=firestore`, `transport=nosql-vm`, `transport=rest`, colisão `transport+engine`
|
|
388
|
+
(transport prevalece, sem warn), default sem campos (sem warn).
|
|
389
|
+
- Testes de regressão: `engine=firestore` e `engine=nosql-vm` legados ainda passam + emitem warn.
|
|
390
|
+
|
|
391
|
+
## [2.2.1] - 2026-05-25 — **fix(offline-docs): fallback MemoryStore em Node — bug_ba287a CRITICAL**
|
|
392
|
+
|
|
393
|
+
Fix do bug `bug_ba287aefdf6c4327b026d588a239ad06` (CRITICAL) — `client.db.collection().set/add/update`
|
|
394
|
+
lançava `ReferenceError: indexedDB is not defined` em Node.js (reportado por pdv-agiliza prod).
|
|
395
|
+
|
|
396
|
+
### Fixed
|
|
397
|
+
|
|
398
|
+
- **`createOfflineDocumentsNamespace` — auto-detect Node runtime e usa `MemoryStore`**.
|
|
399
|
+
A função agora verifica `typeof indexedDB === 'undefined' || typeof window === 'undefined'`
|
|
400
|
+
antes de instanciar `LocalStore` (que depende de `idb` / IndexedDB). Em Node/Bun/Edge Workers
|
|
401
|
+
sem IDB, cai automaticamente em `MemoryStore` — implementação in-process com a mesma API,
|
|
402
|
+
sem nenhuma flag explícita necessária. Ops de documentos (`set/add/update/remove/get/list`,
|
|
403
|
+
`batch`, `onSnapshot`, `onDoc`) funcionam normalmente em Node; dados não persistem entre
|
|
404
|
+
reinicializações do processo (comportamento esperado em SSR/server-side).
|
|
405
|
+
|
|
406
|
+
### Added
|
|
407
|
+
|
|
408
|
+
- **`src/db/offline/memory-store.ts`** — `MemoryStore`: implementação in-memory do contrato
|
|
409
|
+
público de `LocalStore`. Cobre todas as 5 stores (documents, mutations, query\_cache,
|
|
410
|
+
sync\_meta, conflict\_log) com Maps/Arrays em memória.
|
|
411
|
+
|
|
412
|
+
### Tests
|
|
413
|
+
|
|
414
|
+
- **`src/__tests__/offline-docs-node-compat.test.ts`** — 5 casos de regressão:
|
|
415
|
+
verifica que a stack offline não lança `ReferenceError` em ambiente sem IDB,
|
|
416
|
+
e que `set/add/update` + `get` roundtrips funcionam in-memory.
|
|
417
|
+
|
|
418
|
+
## [2.2.0] - 2026-05-25 — **auth.verifyToken — validação server-side de id_token via JWKS [feature]**
|
|
419
|
+
|
|
420
|
+
Fix do bug `bug_a7ef1702680044e3994b74bdccecb2ab` — resolve P0-1 audit security pdv-agiliza.
|
|
421
|
+
|
|
422
|
+
### Added
|
|
423
|
+
|
|
424
|
+
- **`client.auth.verifyToken(token, options?): Promise<NeetruUser>`** — valida um id_token JWT
|
|
425
|
+
emitido por `auth.neetru.com` server-side, sem round-trip de rede por request. Usa JWKS local
|
|
426
|
+
cacheado (default TTL 1h, configurável via `options.jwksCacheTtlMs`).
|
|
427
|
+
|
|
428
|
+
Erros tipados novos (`NeetruErrorCode`):
|
|
429
|
+
- `token_expired` — `exp` no passado
|
|
430
|
+
- `token_invalid_signature` — assinatura não verificada pela JWKS
|
|
431
|
+
- `token_invalid_audience` — `aud` não bate com o `clientId` extraído do `apiKey`
|
|
432
|
+
- `token_invalid_issuer` — `iss` não é `https://auth.neetru.com`
|
|
433
|
+
|
|
434
|
+
Dependência nova: `jose` (Vercel/Cloudflare-friendly, sem Node nativo).
|
|
435
|
+
|
|
436
|
+
- **`MockAuth.verifyToken(token)`** — decodifica JWT sem verificar assinatura (útil em testes).
|
|
437
|
+
Aceita `__mockVerifyToken(fn)` pra injetar falhas determinísticas.
|
|
438
|
+
|
|
439
|
+
- **`VerifyTokenOptions`** — exportado em `index.ts` pra consumers tiparem as options.
|
|
440
|
+
|
|
441
|
+
### Usage
|
|
442
|
+
|
|
443
|
+
```ts
|
|
444
|
+
// Next.js route handler
|
|
445
|
+
import { createNeetruClient } from '@neetru/sdk';
|
|
446
|
+
const client = createNeetruClient({ apiKey: 'nrt_a1b2c3_...', env: 'prod' });
|
|
447
|
+
|
|
448
|
+
export async function POST(req: Request) {
|
|
449
|
+
const token = req.cookies.get('neetru-session')?.value ?? '';
|
|
450
|
+
try {
|
|
451
|
+
const user = await client.auth.verifyToken(token);
|
|
452
|
+
// user.uid, user.email, user.isStaff, user.tenantId disponíveis
|
|
453
|
+
} catch (err) {
|
|
454
|
+
return new Response('Unauthorized', { status: 401 });
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
## [Unreleased]
|
|
460
|
+
|
|
461
|
+
### Planned (post-2.2)
|
|
462
|
+
- size-limit budget no CI (<12KB gz core com db namespace).
|
|
463
|
+
- CDN distribution (`cdn.neetru.com/sdk/v2/`).
|
|
464
|
+
- Endpoint batch `/api/v1/sdk/telemetry/batch` (substitui drenagem N×1 do `track()` por 1 request).
|
|
465
|
+
|
|
466
|
+
## [2.1.1] - 2026-05-25 — **DbDocRef identity + DbBatchOp discriminated union [patch]**
|
|
467
|
+
|
|
468
|
+
Dois fixes LOW reportados por pdv-agiliza (tickets `bug_97b3f0488d1c49cf8c5f94e5d544289b` + `bug_6aab9adc182640218652a65cdcb71b63`).
|
|
469
|
+
Sem breaking changes — todos os usos de `doc()` existentes continuam funcionando.
|
|
470
|
+
|
|
471
|
+
### Fixed
|
|
472
|
+
|
|
473
|
+
- **`DbDocRef.id / .path / .collection`** (fix #13, ticket `bug_97b3f0488d1c49cf8c5f94e5d544289b`):
|
|
474
|
+
`DbDocRef` agora expõe três propriedades readonly de identidade:
|
|
475
|
+
- `id: string` — ID do documento dentro da coleção.
|
|
476
|
+
- `path: string` — Caminho completo `"colecao/docId"`.
|
|
477
|
+
- `collection: string` — Nome da coleção parent.
|
|
478
|
+
Análogo ao `DocumentReference.id/.path` do Firestore Web SDK. Elimina a necessidade
|
|
479
|
+
de adaptadores externos que reattachavam `collection + id` manualmente.
|
|
480
|
+
Implementado em `DbCollectionRefImpl.doc()` (offline), `createLazyCollectionRef.doc()`
|
|
481
|
+
(client-db) e `MockDb.doc()` (mocks).
|
|
482
|
+
|
|
483
|
+
- **`DbBatchOp` discriminated union** (fix #14, ticket `bug_6aab9adc182640218652a65cdcb71b63`):
|
|
484
|
+
`DbBatchOp` foi convertido de `interface` opaca (campo `op: 'add'|'set'|'update'|'remove'`
|
|
485
|
+
com `id?`/`data?` opcionais) para **discriminated union** pelo campo `kind`:
|
|
486
|
+
```ts
|
|
487
|
+
type DbBatchOp<T = Record<string, unknown>> =
|
|
488
|
+
| { kind: 'add'; data: Omit<T, 'id'> }
|
|
489
|
+
| { kind: 'set'; id: string; data: Omit<T, 'id'> }
|
|
490
|
+
| { kind: 'update'; id: string; data: Partial<Omit<T, 'id'>> }
|
|
491
|
+
| { kind: 'remove'; id: string };
|
|
492
|
+
```
|
|
493
|
+
O `.d.ts` público agora expõe claramente quais campos são obrigatórios por variante.
|
|
494
|
+
O campo `collection` foi removido da operação (sempre opera na coleção do `DbCollectionRef`).
|
|
495
|
+
|
|
496
|
+
### Migration (2.0.0 → 2.1.1)
|
|
497
|
+
|
|
498
|
+
Callers que usavam `batch()` devem trocar `op` por `kind` e remover o campo `collection`:
|
|
499
|
+
|
|
500
|
+
```ts
|
|
501
|
+
// antes (2.0.0)
|
|
502
|
+
col.batch([
|
|
503
|
+
{ op: 'add', collection: 'orders', data: { total: 10 } },
|
|
504
|
+
{ op: 'set', collection: 'orders', id: 'x', data: { total: 20 } },
|
|
505
|
+
{ op: 'update', collection: 'orders', id: 'y', data: { total: 30 } },
|
|
506
|
+
{ op: 'remove', collection: 'orders', id: 'z' },
|
|
507
|
+
]);
|
|
508
|
+
|
|
509
|
+
// depois (2.1.1)
|
|
510
|
+
col.batch([
|
|
511
|
+
{ kind: 'add', data: { total: 10 } },
|
|
512
|
+
{ kind: 'set', id: 'x', data: { total: 20 } },
|
|
513
|
+
{ kind: 'update', id: 'y', data: { total: 30 } },
|
|
514
|
+
{ kind: 'remove', id: 'z' },
|
|
515
|
+
]);
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
## [2.0.0] - 2026-05-22 — **client.db NeetruDb (Documentos engine-aware + SQL) [M2]**
|
|
519
|
+
|
|
520
|
+
Major bump — breaking changes no namespace `db`. Liga todos os building blocks M2 na superfície pública.
|
|
521
|
+
|
|
522
|
+
### Breaking Changes
|
|
523
|
+
|
|
524
|
+
- **`client.db`** troca de `DbNamespace` (v0.3 REST simples) para `NeetruDb` (v2.0 offline-first engine-aware).
|
|
525
|
+
- `collection().list()` agora retorna `DbListResult<T>` (com `docs`, `nextCursor`, `fromCache`, `stale`, `hasPendingWrites`, `changes`) em vez de `T[]`.
|
|
526
|
+
- `collection().get(id)` retorna `DbGetResult<T> | null` em vez de `T | null`.
|
|
527
|
+
- `db_unavailable` não significa mais "sem rede" — só ciclo de vida do banco. Offline é transparente.
|
|
528
|
+
- Novos métodos obrigatórios na superfície: `sql()`, `syncState`, `flush()`, `clearCache()`, `getConflicts()`, `onSyncStateChanged()`, `batch()`, `onDoc()`, `onSnapshot()`, `doc()`.
|
|
529
|
+
- **`db.sql(schema)` lança `NeetruDbError('db_unavailable')` em `NEETRU_ENV=dev`** — sem Postgres local, sem acesso SQL. Use `neetru dev` para subir o container.
|
|
530
|
+
- **`initNeetru`** — mantido como stub funcional mas não mapeia mais `apiUrl → baseUrl`. Remover em v3.0.
|
|
531
|
+
- **`MockDb`** foi atualizado para implementar `NeetruDb` (v2.0) em vez de `DbNamespace` (v0.3).
|
|
532
|
+
|
|
533
|
+
### Added
|
|
534
|
+
|
|
535
|
+
- **`client.db.collection<T>(name)`** — retorna `DbCollectionRef<T>` offline-first, engine-aware. Funciona sem rede. Sync automático quando conectado.
|
|
536
|
+
- CRUD: `add`, `set`, `update`, `remove`, `get`, `list` (com query, cursor, orderBy, where, limit).
|
|
537
|
+
- Batch: `batch(ops)` — operações atômicas (client-side).
|
|
538
|
+
- Realtime: `onSnapshot(query, cb)`, `onDoc(id, cb)` — subscriptions permanentes, delivery imediato do cache.
|
|
539
|
+
- Doc ref: `doc(id).get()`, `.set()`, `.update()`, `.remove()`, `.onSnapshot()`.
|
|
540
|
+
- **`client.db.sql<TSchema>(schema)`** — lease SQL. Retorna `NeetruSqlClient<TSchema>` com `orm` (Drizzle `NodePgDatabase`) + `transaction()` + `close()`.
|
|
541
|
+
- **`client.db.syncState`** — estado de sincronização observável (`status`, `pendingWrites`, `lastSyncedAt`, `isLeaderTab`).
|
|
542
|
+
- **`client.db.onSyncStateChanged(cb)`** — subscrição de mudanças de sync state.
|
|
543
|
+
- **`client.db.flush()`** — força ciclo de sync imediato.
|
|
544
|
+
- **`client.db.clearCache(collections?)`** — limpa o cache local (IndexedDB).
|
|
545
|
+
- **`client.db.getConflicts(collection?)`** — lista conflitos LWW pendentes.
|
|
546
|
+
- **Engine detection** via `NeetruDbOptions.engine`: `'firestore'` | `'nosql-vm'` | `'rest'` (default MVP).
|
|
547
|
+
- **Subpath `@neetru/sdk/db/react`** — hooks React: `useCollection`, `useDoc` (exporta via `dist/db-react.{mjs,cjs}`).
|
|
548
|
+
- **Novos tipos públicos exportados**: `NeetruDb`, `NeetruDbEngine`, `NeetruDbOptions`, `NeetruSqlClient`, `DbSqlLease`, `DbCollectionRef`, `DbDocRef`, `DbQuery`, `DbDoc`, `DbListResult`, `DbGetResult`, `DbBatchOp`, `DbChangeType`, `NeetruDbError`, `NeetruDbErrorCode`.
|
|
549
|
+
- **`createNeetruDb(config, opts)`** — factory async (para uso fora do `createNeetruClient`).
|
|
550
|
+
- **`createNeetruDbSync(config, opts)`** — factory sync com lazy init IndexedDB (usada internamente em `createNeetruClient`).
|
|
551
|
+
|
|
552
|
+
### Changed
|
|
553
|
+
|
|
554
|
+
- **`NeetruClientConfig.db`** — aceita `NeetruDbOptions` (engine, dbId, collections, dbName, etc.).
|