@neetru/sdk 3.1.6 → 3.1.9

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 (77) hide show
  1. package/dist/auth.cjs +65 -22
  2. package/dist/auth.cjs.map +1 -1
  3. package/dist/auth.d.cts +1 -1
  4. package/dist/auth.d.ts +1 -1
  5. package/dist/auth.mjs +65 -22
  6. package/dist/auth.mjs.map +1 -1
  7. package/dist/catalog.cjs.map +1 -1
  8. package/dist/catalog.d.cts +1 -1
  9. package/dist/catalog.d.ts +1 -1
  10. package/dist/catalog.mjs.map +1 -1
  11. package/dist/checkout.cjs.map +1 -1
  12. package/dist/checkout.d.cts +1 -1
  13. package/dist/checkout.d.ts +1 -1
  14. package/dist/checkout.mjs.map +1 -1
  15. package/dist/db-react.d.cts +1 -1
  16. package/dist/db-react.d.ts +1 -1
  17. package/dist/db.cjs +55 -16
  18. package/dist/db.cjs.map +1 -1
  19. package/dist/db.d.cts +1 -1
  20. package/dist/db.d.ts +1 -1
  21. package/dist/db.mjs +55 -16
  22. package/dist/db.mjs.map +1 -1
  23. package/dist/entitlements.cjs.map +1 -1
  24. package/dist/entitlements.d.cts +1 -1
  25. package/dist/entitlements.d.ts +1 -1
  26. package/dist/entitlements.mjs.map +1 -1
  27. package/dist/errors.cjs.map +1 -1
  28. package/dist/errors.d.cts +8 -8
  29. package/dist/errors.d.ts +8 -8
  30. package/dist/errors.mjs.map +1 -1
  31. package/dist/firestore-compat.cjs +4 -9
  32. package/dist/firestore-compat.cjs.map +1 -1
  33. package/dist/firestore-compat.d.cts +2 -2
  34. package/dist/firestore-compat.d.ts +2 -2
  35. package/dist/firestore-compat.mjs +4 -9
  36. package/dist/firestore-compat.mjs.map +1 -1
  37. package/dist/index.cjs +66 -23
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.d.cts +2 -2
  40. package/dist/index.d.ts +2 -2
  41. package/dist/index.mjs +66 -23
  42. package/dist/index.mjs.map +1 -1
  43. package/dist/mocks.cjs.map +1 -1
  44. package/dist/mocks.d.cts +1 -1
  45. package/dist/mocks.d.ts +1 -1
  46. package/dist/mocks.mjs.map +1 -1
  47. package/dist/notifications.cjs.map +1 -1
  48. package/dist/notifications.d.cts +1 -1
  49. package/dist/notifications.d.ts +1 -1
  50. package/dist/notifications.mjs.map +1 -1
  51. package/dist/react.cjs.map +1 -1
  52. package/dist/react.d.cts +1 -1
  53. package/dist/react.d.ts +1 -1
  54. package/dist/react.mjs.map +1 -1
  55. package/dist/support.cjs.map +1 -1
  56. package/dist/support.d.cts +1 -1
  57. package/dist/support.d.ts +1 -1
  58. package/dist/support.mjs.map +1 -1
  59. package/dist/telemetry.cjs +5 -1
  60. package/dist/telemetry.cjs.map +1 -1
  61. package/dist/telemetry.d.cts +1 -1
  62. package/dist/telemetry.d.ts +1 -1
  63. package/dist/telemetry.mjs +5 -1
  64. package/dist/telemetry.mjs.map +1 -1
  65. package/dist/{types-BXvGHppn.d.cts → types-DDiVzgCT.d.cts} +19 -2
  66. package/dist/{types-CQVak2a3.d.ts → types-HWDOUmlC.d.ts} +19 -2
  67. package/dist/usage.cjs +2 -2
  68. package/dist/usage.cjs.map +1 -1
  69. package/dist/usage.d.cts +1 -1
  70. package/dist/usage.d.ts +1 -1
  71. package/dist/usage.mjs +2 -2
  72. package/dist/usage.mjs.map +1 -1
  73. package/dist/webhooks.cjs.map +1 -1
  74. package/dist/webhooks.d.cts +1 -1
  75. package/dist/webhooks.d.ts +1 -1
  76. package/dist/webhooks.mjs.map +1 -1
  77. package/package.json +156 -156
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/db/field-value.ts","../src/firestore-compat.ts"],"names":["serverTimestamp","increment"],"mappings":";AA2EO,SAAS,eAAA,GAA2C;AACzD,EAAA,OAAO,EAAE,YAAY,iBAAA,EAAkB;AACzC;AAeO,SAAS,UAAU,CAAA,EAA8B;AACtD,EAAA,IAAI,OAAO,CAAA,KAAM,QAAA,IAAY,CAAC,MAAA,CAAO,QAAA,CAAS,CAAC,CAAA,EAAG;AAChD,IAAA,MAAM,IAAI,SAAA;AAAA,MACR,CAAA,wDAAA,EAAwD,MAAA,CAAO,CAAC,CAAC,CAAA,EAAA;AAAA,KACnE;AAAA,EACF;AACA,EAAA,OAAO,EAAE,UAAA,EAAY,WAAA,EAAa,OAAA,EAAS,CAAA,EAAE;AAC/C;;;AC/BA,SAAS,gBAAgB,CAAA,EAAqD;AAC5E,EAAA,OACE,OAAO,CAAA,KAAM,QAAA,IACb,CAAA,KAAM,IAAA,IACN,IAAA,IAAQ,CAAA,IACR,OAAQ,CAAA,CAAuB,EAAA,KAAO,QAAA,IACrC,CAAA,CAAwC,IAAI,UAAA,KAAe,MAAA;AAEhE;AAEA,SAAS,UAAU,MAAA,EAA6C;AAC9D,EAAA,IAAK,MAAA,CAAqB,iBAAA,EAAmB,OAAQ,MAAA,CAAqB,EAAA;AAC1E,EAAA,IAAI,eAAA,CAAgB,MAAM,CAAA,EAAG,OAAO,MAAA,CAAO,EAAA;AAE3C,EAAA,OAAO,MAAA;AACT;AAYO,SAAS,aAAa,MAAA,EAAkC;AAC7D,EAAA,OAAO,EAAE,iBAAA,EAAmB,IAAA,EAAM,EAAA,EAAI,SAAA,CAAU,MAAM,CAAA,EAAE;AAC1D;AAgDO,SAAS,UAAA,CACd,IACA,IAAA,EACwB;AACxB,EAAA,MAAM,QAAA,GAAW,UAAU,EAAE,CAAA;AAC7B,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,YAAA;AAAA,IACN,MAAA,EAAQ,YAAA;AAAA,IACR,EAAA,EAAI,IAAA;AAAA,IACJ,IAAA,EAAM,IAAA;AAAA,IACN,EAAA,EAAI,QAAA;AAAA,IACJ,GAAA,EAAK,QAAA,CAAS,UAAA,CAAc,IAAI;AAAA,GAClC;AACF;AAWO,SAAS,GAAA,CACd,mBACG,IAAA,EACmB;AAEtB,EAAA,IACE,OAAO,cAAA,KAAmB,QAAA,IAC1B,mBAAmB,IAAA,IAClB,cAAA,CAA0C,WAAW,YAAA,EACtD;AACA,IAAA,MAAM,GAAA,GAAM,cAAA;AACZ,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,CAAC,CAAA,IAAK,UAAA,EAAW;AACjC,IAAA,OAAO,UAAA,CAAc,GAAA,CAAI,EAAA,EAAI,GAAA,CAAI,IAAI,EAAE,CAAA;AAAA,EACzC;AAEA,EAAA,MAAM,QAAA,GAAW,UAAU,cAAuB,CAAA;AAElD,EAAA,IAAI,IAAA,CAAK,UAAU,CAAA,EAAG;AACpB,IAAA,OAAO,UAAA,CAAc,QAAA,EAAU,IAAA,CAAK,CAAC,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,CAAE,IAAA,CAAK,GAAG,CAAC,CAAA;AAAA,EACjE;AACA,EAAA,IAAI,IAAA,CAAK,CAAC,CAAA,EAAG,QAAA,CAAS,GAAG,CAAA,EAAG;AAC1B,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,CAAC,CAAA,CAAE,MAAM,GAAG,CAAA;AAC/B,IAAA,OAAO,UAAA,CAAc,QAAA,EAAU,KAAA,CAAM,CAAC,CAAA,EAAG,KAAA,CAAM,KAAA,CAAM,CAAC,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA,IAAK,YAAY,CAAA;AAAA,EACnF;AAEA,EAAA,OAAO,WAAc,QAAA,EAAU,IAAA,CAAK,CAAC,CAAA,IAAK,EAAA,EAAI,YAAY,CAAA;AAC5D;AAEA,SAAS,UAAA,CAAc,EAAA,EAAc,cAAA,EAAwB,EAAA,EAAkC;AAC7F,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,UAAA;AAAA,IACN,MAAA,EAAQ,UAAA;AAAA,IACR,EAAA;AAAA,IACA,UAAA,EAAY,cAAA;AAAA,IACZ,IAAA,EAAM,CAAA,EAAG,cAAc,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA;AAAA,IAC7B;AAAA,GACF;AACF;AAaA,SAAS,UAAA,GAAqB;AAC5B,EAAA,MAAM,KAAA,GAAQ,gEAAA;AACd,EAAA,MAAM,YAAa,UAAA,CAAmC,MAAA;AACtD,EAAA,IAAI,CAAC,WAAW,eAAA,EAAiB;AAC/B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AACA,EAAA,IAAI,GAAA,GAAM,EAAA;AACV,EAAA,OAAO,GAAA,CAAI,SAAS,EAAA,EAAI;AACtB,IAAA,MAAM,GAAA,GAAM,IAAI,UAAA,CAAW,EAAA,GAAK,IAAI,MAAM,CAAA;AAC1C,IAAA,SAAA,CAAU,gBAAgB,GAAG,CAAA;AAC7B,IAAA,KAAA,MAAW,QAAQ,GAAA,EAAK;AACtB,MAAA,IAAI,OAAO,GAAA,EAAK;AACd,QAAA,GAAA,IAAO,KAAA,CAAM,OAAO,EAAE,CAAA;AACtB,QAAA,IAAI,GAAA,CAAI,WAAW,EAAA,EAAI;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AA4CO,SAAS,KAAA,CACd,WACG,WAAA,EACO;AACV,EAAA,MAAM,IAAA,GACJ,MAAA,CAAO,MAAA,KAAW,OAAA,GACd,MAAA,GACA;AAAA,IACE,IAAA,EAAM,OAAA;AAAA,IACN,MAAA,EAAQ,OAAA;AAAA,IACR,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,YAAY,MAAA,CAAO,EAAA;AAAA,IACnB,IAAI,MAAA,CAAO,EAAA;AAAA,IACX,KAAK,MAAA,CAAO,GAAA;AAAA,IACZ,aAAa;AAAC,GAChB;AACN,EAAA,OAAO,EAAE,GAAG,IAAA,EAAM,WAAA,EAAa,CAAC,GAAG,IAAA,CAAK,WAAA,EAAa,GAAG,WAAW,CAAA,EAAE;AACvE;AAEO,SAAS,KAAA,CAAM,KAAA,EAAe,EAAA,EAAmB,KAAA,EAAiC;AACvF,EAAA,OAAO,EAAE,YAAA,EAAc,OAAA,EAAS,KAAA,EAAO,IAAI,KAAA,EAAM;AACnD;AAEO,SAAS,OAAA,CAAQ,KAAA,EAAe,SAAA,GAA4B,KAAA,EAA0B;AAC3F,EAAA,OAAO,EAAE,YAAA,EAAc,SAAA,EAAW,KAAA,EAAO,SAAA,EAAU;AACrD;AAEO,SAAS,MAAM,KAAA,EAAgC;AACpD,EAAA,OAAO,EAAE,YAAA,EAAc,OAAA,EAAS,KAAA,EAAM;AACxC;AAQO,SAAS,UAAU,WAAA,EAAyC;AACjE,EAAA,MAAM,eAAgC,EAAC;AACvC,EAAA,IAAI,aAAA;AACJ,EAAA,IAAI,UAAA;AAEJ,EAAA,KAAA,MAAW,KAAK,WAAA,EAAa;AAC3B,IAAA,IAAI,CAAA,CAAE,iBAAiB,OAAA,EAAS;AAC9B,MAAA,MAAM,CAAA,GAAI,CAAA;AACV,MAAA,YAAA,CAAa,IAAA,CAAK,EAAE,KAAA,EAAO,CAAA,CAAE,KAAA,EAAO,EAAA,EAAI,WAAA,CAAY,CAAA,CAAE,EAAE,CAAA,EAAG,KAAA,EAAO,CAAA,CAAE,OAAO,CAAA;AAAA,IAC7E,CAAA,MAAA,IAAW,CAAA,CAAE,YAAA,KAAiB,SAAA,EAAW;AACvC,MAAA,MAAM,CAAA,GAAI,CAAA;AACV,MAAA,aAAA,GAAgB,EAAE,KAAA,EAAO,CAAA,CAAE,KAAA,EAAO,SAAA,EAAW,EAAE,SAAA,EAAU;AAAA,IAC3D,CAAA,MAAA,IAAW,CAAA,CAAE,YAAA,KAAiB,OAAA,EAAS;AACrC,MAAA,UAAA,GAAc,CAAA,CAAsB,KAAA;AAAA,IACtC;AAAA,EACF;AAEA,EAAA,MAAM,MAAe,EAAC;AACtB,EAAA,IAAI,YAAA,CAAa,MAAA,GAAS,CAAA,EAAG,GAAA,CAAI,KAAA,GAAQ,YAAA;AACzC,EAAA,IAAI,aAAA,MAAmB,OAAA,GAAU,aAAA;AACjC,EAAA,IAAI,UAAA,KAAe,MAAA,EAAW,GAAA,CAAI,KAAA,GAAQ,UAAA;AAC1C,EAAA,OAAO,GAAA;AACT;AAWA,SAAS,YAAY,EAAA,EAAwC;AAE3D,EAAA,OAAO,EAAA;AACT;AAOA,SAAS,QAAW,GAAA,EAAiC;AACnD,EAAA,IAAI,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA,IAAY,QAAQ,GAAA,EAAK;AACjD,IAAA,MAAM,EAAE,EAAA,EAAI,KAAA,EAAO,GAAG,MAAK,GAAI,GAAA;AAC/B,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,GAAA;AACT;AAEO,IAAM,mBAAN,MAAoD;AAAA,EACzD,WAAA,CACkB,EAAA,EACC,KAAA,EACD,GAAA,EAChB;AAHgB,IAAA,IAAA,CAAA,EAAA,GAAA,EAAA;AACC,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AACD,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AAAA,EACf;AAAA,EAHe,EAAA;AAAA,EACC,KAAA;AAAA,EACD,GAAA;AAAA,EAGlB,MAAA,GAAkB;AAChB,IAAA,OAAO,KAAK,KAAA,IAAS,IAAA;AAAA,EACvB;AAAA,EAEA,IAAA,GAAsB;AACpB,IAAA,OAAO,KAAK,KAAA,IAAS,MAAA;AAAA,EACvB;AAAA;AAAA,EAGA,IAAI,KAAA,EAAwB;AAC1B,IAAA,OAAO,IAAA,CAAK,KAAA,GAAS,IAAA,CAAK,KAAA,CAAkC,KAAK,CAAA,GAAI,MAAA;AAAA,EACvE;AACF;AAUO,IAAM,gBAAN,MAAiD;AAAA,EACtC,IAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EAEhB,WAAA,CACE,IAAA,EACA,EAAA,EACA,cAAA,EACA;AACA,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,EAAE,EAAA,EAAI,MAAK,KAAM;AACrC,MAAA,MAAM,QAAA,GAAW,QAAW,IAAI,CAAA;AAChC,MAAA,MAAM,MACJ,EAAA,IAAM,cAAA,GAAiB,WAAc,EAAA,EAAI,cAAA,EAAgB,EAAE,CAAA,GAAI,MAAA;AACjE,MAAA,OAAO;AAAA,QACL,EAAA;AAAA,QACA,MAAM,MAAM,QAAA;AAAA,QACZ,QAAQ,MAAM,IAAA;AAAA,QACd,GAAA,EAAK,CAAC,KAAA,KAAmB,QAAA,GAAuC,KAAK,CAAA;AAAA;AAAA,QAErE;AAAA,OACF;AAAA,IACF,CAAC,CAAA;AACD,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAA,CAAK,IAAA,CAAK,MAAA,KAAW,CAAA;AAClC,IAAA,IAAA,CAAK,IAAA,GAAO,KAAK,IAAA,CAAK,MAAA;AAAA,EACxB;AAAA,EAEA,QAAQ,EAAA,EAAmD;AACzD,IAAA,IAAA,CAAK,IAAA,CAAK,QAAQ,EAAE,CAAA;AAAA,EACtB;AACF;AAOA,SAAS,aAAa,MAAA,EAGpB;AACA,EAAA,MAAM,KAAA,GAAQ,MAAA,EAAQ,IAAA,CAAK,CAAC,CAAA;AAC5B,EAAA,OAAO,EAAE,MAAM,KAAA,GAAS,KAAA,CAAM,OAAmC,IAAA,EAAM,EAAA,EAAI,KAAA,EAAO,EAAA,IAAM,IAAA,EAAK;AAC/F;AAEA,eAAsB,OACpB,GAAA,EAC8B;AAC9B,EAAA,MAAM,MAAA,GAAU,MAAM,GAAA,CAAI,EAAA,CAAG,UAAA,CAAW,IAAI,UAAU,CAAA,CAAE,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA;AAGlE,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,YAAA,CAAa,MAAM,CAAA;AACpC,EAAA,OAAO,IAAI,gBAAA;AAAA,IACT,GAAA,CAAI,EAAA;AAAA,IACJ,IAAA,GAAQ,QAAW,EAAE,EAAA,EAAI,IAAI,EAAA,EAAI,GAAG,IAAA,EAAM,CAAA,GAAK,IAAA;AAAA,IAC/C;AAAA,GACF;AACF;AAGA,SAAS,iBACP,MAAA,EACsD;AACtD,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,OAAO,EAAE,EAAA,EAAI,CAAA,CAAE,EAAA,EAAI,MAAM,EAAE,GAAI,CAAA,CAAE,IAAA,IAAmC,CAAE,CAAA;AAChG;AAEA,eAAsB,QACpB,MAAA,EAC2B;AAC3B,EAAA,MAAM,UAAU,MAAA,CAAO,MAAA,KAAW,UAAU,SAAA,CAAU,MAAA,CAAO,WAAW,CAAA,GAAI,MAAA;AAC5E,EAAA,MAAM,iBAAiB,MAAA,CAAO,MAAA,KAAW,OAAA,GAAU,MAAA,CAAO,aAAa,MAAA,CAAO,EAAA;AAC9E,EAAA,MAAM,MAAA,GAAU,MAAM,MAAA,CAAO,EAAA,CAC1B,WAAW,cAAc,CAAA,CACzB,KAAK,OAAO,CAAA;AACf,EAAA,OAAO,IAAI,aAAA,CAAiB,gBAAA,CAAiB,MAAM,CAAA,EAAG,MAAA,CAAO,IAAI,cAAc,CAAA;AACjF;AAWA,eAAsB,MAAA,CACpB,GAAA,EACA,IAAA,EACA,OAAA,GAAsB,EAAC,EACR;AACf,EAAA,MAAM,GAAA,GAAM,GAAA,CAAI,EAAA,CAAG,UAAA,CAAW,IAAI,UAAU,CAAA;AAC5C,EAAA,MAAM,OAAA,GAAU,IAAA;AAChB,EAAA,IAAI,QAAQ,KAAA,IAAU,OAAA,CAAQ,eAAe,OAAA,CAAQ,WAAA,CAAY,SAAS,CAAA,EAAI;AAI5E,IAAA,MAAM,QAAA,GAAW,MAAM,GAAA,CAAI,GAAA,CAAI,IAAI,EAAE,CAAA;AACrC,IAAA,MAAM,SAAA,GAAY,KAAA,CAAM,OAAA,CAAS,QAAA,EAA0C,IAAI,IACzE,QAAA,CAAiC,IAAA,CAAK,MAAA,GAAS,CAAA,GACjD,QAAA,IAAY,IAAA;AAChB,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,MAAM,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,EAAA,EAAI,OAAO,CAAA;AAAA,IAClC,CAAA,MAAO;AACL,MAAA,MAAM,GAAA,CAAI,GAAA,CAAI,GAAA,CAAI,EAAA,EAAI,OAAO,CAAA;AAAA,IAC/B;AAAA,EACF,CAAA,MAAO;AACL,IAAA,MAAM,GAAA,CAAI,GAAA,CAAI,GAAA,CAAI,EAAA,EAAI,OAAO,CAAA;AAAA,EAC/B;AACF;AAEA,eAAsB,MAAA,CACpB,QACA,IAAA,EAC+B;AAC/B,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,EAAA,CAAG,WAAW,MAAA,CAAO,EAAE,CAAA,CAAE,GAAA,CAAI,IAA+B,CAAA;AACxF,EAAA,OAAO,WAAc,MAAA,CAAO,EAAA,EAAI,MAAA,CAAO,EAAA,EAAI,OAAO,EAAE,CAAA;AACtD;AAEA,eAAsB,SAAA,CACpB,KACA,IAAA,EACe;AACf,EAAA,MAAM,GAAA,CAAI,GAAG,UAAA,CAAW,GAAA,CAAI,UAAU,CAAA,CAAE,MAAA,CAAO,GAAA,CAAI,EAAA,EAAI,IAA+B,CAAA;AACxF;AAEA,eAAsB,UAAU,GAAA,EAAuC;AACrE,EAAA,MAAM,GAAA,CAAI,GAAG,UAAA,CAAW,GAAA,CAAI,UAAU,CAAA,CAAE,MAAA,CAAO,IAAI,EAAE,CAAA;AACvD;AAcO,IAAM,aAAN,MAAiB;AAAA,EAGtB,YAA6B,EAAA,EAAc;AAAd,IAAA,IAAA,CAAA,EAAA,GAAA,EAAA;AAAA,EAAe;AAAA,EAAf,EAAA;AAAA,EAFrB,MAAwB,EAAC;AAAA,EAIjC,GAAA,CACE,GAAA,EACA,IAAA,EACA,QAAA,EACM;AACN,IAAA,IAAA,CAAK,IAAI,IAAA,CAAK;AAAA,MACZ,YAAY,GAAA,CAAI,UAAA;AAAA,MAChB,IAAI,EAAE,IAAA,EAAM,OAAO,EAAA,EAAI,GAAA,CAAI,IAAI,IAAA;AAAsC,KACtE,CAAA;AACD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAA,CAAU,KAA2B,IAAA,EAAkD;AACrF,IAAA,IAAA,CAAK,IAAI,IAAA,CAAK;AAAA,MACZ,YAAY,GAAA,CAAI,UAAA;AAAA,MAChB,IAAI,EAAE,IAAA,EAAM,UAAU,EAAA,EAAI,GAAA,CAAI,IAAI,IAAA;AAAsC,KACzE,CAAA;AACD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,OAAO,GAAA,EAA8B;AACnC,IAAA,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,EAAE,UAAA,EAAY,IAAI,UAAA,EAAY,EAAA,EAAI,EAAE,IAAA,EAAM,QAAA,EAAU,EAAA,EAAI,GAAA,CAAI,EAAA,IAAM,CAAA;AAChF,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAM,MAAA,GAAwB;AAE5B,IAAA,MAAM,KAAA,uBAAY,GAAA,EAAoC;AACtD,IAAA,KAAA,MAAW,EAAE,UAAA,EAAY,IAAA,EAAM,EAAA,EAAG,IAAK,KAAK,GAAA,EAAK;AAC/C,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,GAAA,CAAI,IAAI,KAAK,EAAC;AACjC,MAAA,IAAA,CAAK,KAAK,EAAE,CAAA;AACZ,MAAA,KAAA,CAAM,GAAA,CAAI,MAAM,IAAI,CAAA;AAAA,IACtB;AACA,IAAA,KAAA,MAAW,CAAC,IAAA,EAAM,GAAG,CAAA,IAAK,KAAA,CAAM,SAAQ,EAAG;AAEzC,MAAA,MAAM,KAAK,EAAA,CAAG,UAAA,CAAW,IAAI,CAAA,CAAE,MAAM,GAAY,CAAA;AAAA,IACnD;AACA,IAAA,IAAA,CAAK,MAAM,EAAC;AAAA,EACd;AACF;AAEO,SAAS,WAAW,EAAA,EAAuB;AAChD,EAAA,OAAO,IAAI,UAAA,CAAW,SAAA,CAAU,EAAE,CAAC,CAAA;AACrC;AA8CA,IAAI,0BAAA,GAA6B,KAAA;AAEjC,eAAsB,cAAA,CACpB,IACA,cAAA,EACY;AAEZ,EAAA,IAAI,CAAC,0BAAA,EAA4B;AAC/B,IAAA,0BAAA,GAA6B,IAAA;AAC7B,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN;AAAA,KAKF;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAAW,UAAU,EAAE,CAAA;AAC7B,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,QAAQ,CAAA;AAErC,EAAA,MAAM,EAAA,GAAkB;AAAA,IACtB,MAAM,IAAO,GAAA,EAAyD;AACpE,MAAA,OAAO,OAAO,GAAG,CAAA;AAAA,IACnB,CAAA;AAAA,IACA,GAAA,CAAI,GAAA,EAAK,IAAA,EAAM,OAAA,EAAS;AACtB,MAAA,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,IAAA,EAAM,OAAO,CAAA;AAC5B,MAAA,OAAO,EAAA;AAAA,IACT,CAAA;AAAA,IACA,MAAA,CAAO,KAAK,IAAA,EAAM;AAChB,MAAA,KAAA,CAAM,MAAA,CAAO,KAAK,IAAI,CAAA;AACtB,MAAA,OAAO,EAAA;AAAA,IACT,CAAA;AAAA,IACA,OAAO,GAAA,EAAK;AACV,MAAA,KAAA,CAAM,OAAO,GAAG,CAAA;AAChB,MAAA,OAAO,EAAA;AAAA,IACT;AAAA,GACF;AAEA,EAAA,MAAM,MAAA,GAAS,MAAM,cAAA,CAAe,EAAE,CAAA;AACtC,EAAA,MAAM,MAAM,MAAA,EAAO;AACnB,EAAA,OAAO,MAAA;AACT;AAsBO,SAAS,UAAA,CACd,MAAA,EACA,MAAA,EACA,OAAA,EACa;AAEb,EAAA,IAAI,MAAA,CAAO,WAAW,UAAA,EAAY;AAChC,IAAA,MAAM,GAAA,GAAM,MAAA;AACZ,IAAA,MAAM,MAAA,GAAS,IAAI,EAAA,CAAG,UAAA,CAAW,IAAI,UAAU,CAAA,CAAE,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA;AAC3D,IAAA,IAAI;AACF,MAAA,OAAO,MAAA,CAAO,UAAA,CAAW,CAAC,IAAA,KAAS;AACjC,QAAA,MAAM,IAAA,GAAO,IAAA,EAAM,IAAA,CAAK,CAAC,CAAA,EAAG,IAAA;AAC5B,QAAC,MAAA;AAAA,UACC,IAAI,gBAAA;AAAA,YACF,GAAA,CAAI,EAAA;AAAA,YACJ,IAAA,GAAO,QAAW,EAAE,EAAA,EAAI,IAAI,EAAA,EAAI,GAAG,IAAA,EAAM,CAAA,GAAI,IAAA;AAAA,YAC7C;AAAA;AACF,SACF;AAAA,MACF,CAAC,CAAA;AAAA,IACH,SAAS,GAAA,EAAK;AACZ,MAAA,OAAA,GAAU,GAAA,YAAe,QAAQ,GAAA,GAAM,IAAI,MAAM,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAC7D,MAAA,OAAO,MAAM;AAAA,MAAC,CAAA;AAAA,IAChB;AAAA,EACF;AAGA,EAAA,MAAM,iBAAiB,MAAA,CAAO,MAAA,KAAW,OAAA,GAAU,MAAA,CAAO,aAAc,MAAA,CAAkC,EAAA;AAC1G,EAAA,MAAM,UAAU,MAAA,CAAO,MAAA,KAAW,UAAU,SAAA,CAAU,MAAA,CAAO,WAAW,CAAA,GAAI,MAAA;AAC5E,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,EAAA,CAAG,UAAA,CAAW,cAAc,CAAA;AAC/C,EAAA,IAAI;AACF,IAAA,OAAO,GAAA,CAAI,UAAA,CAAW,OAAA,EAAS,CAAC,IAAA,KAAgD;AAC9E,MAAC,MAAA;AAAA,QACC,IAAI,aAAA,CAAiB,gBAAA,CAAiB,IAAI,CAAA,EAAG,MAAA,CAAO,IAAI,cAAc;AAAA,OACxE;AAAA,IACF,CAAC,CAAA;AAAA,EACH,SAAS,GAAA,EAAK;AACZ,IAAA,OAAA,GAAU,GAAA,YAAe,QAAQ,GAAA,GAAM,IAAI,MAAM,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAC7D,IAAA,OAAO,MAAM;AAAA,IAAC,CAAA;AAAA,EAChB;AACF;AAWO,IAAM,SAAA,GAAN,MAAM,UAAA,CAAU;AAAA,EACrB,WAAA,CACkB,SACA,WAAA,EAChB;AAFgB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AACA,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AAAA,EACf;AAAA,EAFe,OAAA;AAAA,EACA,WAAA;AAAA,EAGlB,OAAO,GAAA,GAAiB;AACtB,IAAA,OAAO,UAAA,CAAU,UAAA,CAAW,IAAA,CAAK,GAAA,EAAK,CAAA;AAAA,EACxC;AAAA,EAEA,OAAO,SAAS,IAAA,EAAuB;AACrC,IAAA,OAAO,UAAA,CAAU,UAAA,CAAW,IAAA,CAAK,OAAA,EAAS,CAAA;AAAA,EAC5C;AAAA,EAEA,OAAO,WAAW,MAAA,EAA2B;AAC3C,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,MAAA,GAAS,GAAI,CAAA;AACxC,IAAA,MAAM,WAAA,GAAA,CAAe,MAAA,GAAS,OAAA,GAAU,GAAA,IAAQ,GAAA;AAChD,IAAA,OAAO,IAAI,UAAA,CAAU,OAAA,EAAS,WAAW,CAAA;AAAA,EAC3C;AAAA,EAEA,MAAA,GAAe;AACb,IAAA,OAAO,IAAI,IAAA,CAAK,IAAA,CAAK,QAAA,EAAU,CAAA;AAAA,EACjC;AAAA,EAEA,QAAA,GAAmB;AACjB,IAAA,OAAO,KAAK,OAAA,GAAU,GAAA,GAAO,KAAK,KAAA,CAAM,IAAA,CAAK,cAAc,GAAG,CAAA;AAAA,EAChE;AAAA,EAEA,QAAQ,KAAA,EAA2B;AACjC,IAAA,OAAO,KAAK,OAAA,KAAY,KAAA,CAAM,OAAA,IAAW,IAAA,CAAK,gBAAgB,KAAA,CAAM,WAAA;AAAA,EACtE;AAAA,EAEA,OAAA,GAAkB;AAEhB,IAAA,OAAO,GAAG,IAAA,CAAK,OAAA,CAAQ,QAAA,EAAS,CAAE,SAAS,EAAA,EAAI,GAAG,CAAC,CAAA,CAAA,EAAI,KAAK,WAAA,CACzD,QAAA,GACA,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA;AAAA,EACrB;AAAA,EAEA,MAAA,GAAmD;AACjD,IAAA,OAAO,EAAE,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,WAAA,EAAa,KAAK,WAAA,EAAY;AAAA,EAChE;AAAA,EAEA,QAAA,GAAmB;AACjB,IAAA,OAAO,CAAA,kBAAA,EAAqB,IAAA,CAAK,OAAO,CAAA,cAAA,EAAiB,KAAK,WAAW,CAAA,CAAA,CAAA;AAAA,EAC3E;AACF;AAgCO,SAASA,gBAAAA,GAAiC;AAC/C,EAAA,OAAO,eAAA,EAAmB;AAC5B;AAKO,SAASC,WAAU,CAAA,EAA0B;AAClD,EAAA,OAAO,UAAa,CAAC,CAAA;AACvB;AAGO,SAAS,cAAc,QAAA,EAAyC;AACrE,EAAA,OAAO,EAAE,UAAA,EAAY,YAAA,EAAc,QAAA,EAAS;AAC9C;AAGO,SAAS,eAAe,QAAA,EAA0C;AACvE,EAAA,OAAO,EAAE,UAAA,EAAY,aAAA,EAAe,QAAA,EAAS;AAC/C;AAGO,SAAS,WAAA,GAAmC;AACjD,EAAA,OAAO,EAAE,YAAY,aAAA,EAAc;AACrC;AAgBO,IAAM,UAAA,GAAa;AAAA,EACxB,eAAA,EAAAD,gBAAAA;AAAA,EACA,SAAA,EAAAC,UAAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,MAAA,EAAQ;AACV","file":"firestore-compat.mjs","sourcesContent":["/**\r\n * field-value.ts — Sentinels de escrita server-side para o Mundo Documentos.\r\n *\r\n * Resolve duas lacunas confirmadas (bug_05aedb8b) da superfície `client.db`:\r\n *\r\n * 1. **Timestamp resolvido no servidor** — antes deste módulo, o produto que\r\n * quisesse `createdAt`/`updatedAt` confiáveis tinha que usar `Date.now()`\r\n * no CLIENTE, que é spoofável e sofre clock-skew. Agora:\r\n *\r\n * await orders.add({ total: 99, createdAt: serverTimestamp() });\r\n *\r\n * O marcador atravessa o transporte REST como um objeto JSON namespaceado\r\n * e o Core o substitui por `FieldValue.serverTimestamp()` no momento da\r\n * escrita — relógio do SERVIDOR, não do cliente.\r\n *\r\n * 2. **Increment atômico** — antes, `update(id, { n: x+1 })` era read-modify-write\r\n * no cliente, sujeito a lost-update sob concorrência. Agora:\r\n *\r\n * await counters.update('global', { hits: increment(1) });\r\n *\r\n * O Core aplica `FieldValue.increment(1)` atomicamente no documento.\r\n *\r\n * ## Contrato de fio (wire format)\r\n *\r\n * Os sentinels são objetos JSON simples — sobrevivem `JSON.stringify` no\r\n * transporte REST e ao `IndexedDB structured clone` da camada offline:\r\n *\r\n * serverTimestamp() → `{ \"__neetru__\": \"serverTimestamp\" }`\r\n * increment(n) → `{ \"__neetru__\": \"increment\", \"operand\": n }`\r\n *\r\n * A chave-âncora `__neetru__` é deliberadamente improvável de colidir com\r\n * dados de produto. O Core valida e resolve esses marcadores; qualquer objeto\r\n * com `__neetru__` desconhecido é rejeitado server-side (fail-closed).\r\n *\r\n * ## Aplicação otimista (offline-first)\r\n *\r\n * A camada offline aplica uma APROXIMAÇÃO local imediata (para a UI não piscar):\r\n * - `serverTimestamp()` → `Date.now()` local (substituído pelo valor real no sync).\r\n * - `increment(n)` → soma `n` ao valor numérico atual do campo (0 se ausente).\r\n * O valor canônico vem sempre do servidor no pull/confirm subsequente.\r\n *\r\n * @module\r\n */\r\n\r\n/** Chave-âncora dos marcadores de sentinel no wire format. */\r\nexport const NEETRU_SENTINEL_KEY = '__neetru__' as const;\r\n\r\n/** Marcador de timestamp resolvido pelo servidor. */\r\nexport interface ServerTimestampSentinel {\r\n readonly __neetru__: 'serverTimestamp';\r\n}\r\n\r\n/** Marcador de incremento atômico aplicado pelo servidor. */\r\nexport interface IncrementSentinel {\r\n readonly __neetru__: 'increment';\r\n /** Delta a somar (pode ser negativo para decremento). */\r\n readonly operand: number;\r\n}\r\n\r\n/**\r\n * União de todos os sentinels de campo. Use `serverTimestamp()` / `increment()`\r\n * para construir — nunca monte o objeto à mão.\r\n */\r\nexport type FieldSentinel = ServerTimestampSentinel | IncrementSentinel;\r\n\r\n/**\r\n * Sentinel que instrui o servidor a gravar o timestamp do MOMENTO da escrita\r\n * usando o relógio do servidor (não do cliente).\r\n *\r\n * @example\r\n * ```ts\r\n * import { serverTimestamp } from '@neetru/sdk/db';\r\n * await orders.add({ total: 99, createdAt: serverTimestamp() });\r\n * ```\r\n */\r\nexport function serverTimestamp(): ServerTimestampSentinel {\r\n return { __neetru__: 'serverTimestamp' };\r\n}\r\n\r\n/**\r\n * Sentinel que instrui o servidor a incrementar atomicamente o campo numérico\r\n * por `n` (negativo decrementa). Se o campo não existir/não for numérico, o\r\n * servidor o trata como `0` antes de somar (semântica `FieldValue.increment`).\r\n *\r\n * @param n — delta inteiro ou fracionário a somar.\r\n * @example\r\n * ```ts\r\n * import { increment } from '@neetru/sdk/db';\r\n * await counters.update('global', { hits: increment(1) });\r\n * await wallet.update(uid, { balance: increment(-50) });\r\n * ```\r\n */\r\nexport function increment(n: number): IncrementSentinel {\r\n if (typeof n !== 'number' || !Number.isFinite(n)) {\r\n throw new TypeError(\r\n `increment(n): n deve ser um número finito (recebido: ${String(n)}).`,\r\n );\r\n }\r\n return { __neetru__: 'increment', operand: n };\r\n}\r\n\r\n// ─── Type guards ────────────────────────────────────────────────────────────\r\n\r\n/** `true` se `v` é qualquer sentinel de campo Neetru. */\r\nexport function isFieldSentinel(v: unknown): v is FieldSentinel {\r\n return (\r\n typeof v === 'object' &&\r\n v !== null &&\r\n NEETRU_SENTINEL_KEY in v &&\r\n typeof (v as Record<string, unknown>)[NEETRU_SENTINEL_KEY] === 'string'\r\n );\r\n}\r\n\r\n/** `true` se `v` é o sentinel `serverTimestamp()`. */\r\nexport function isServerTimestampSentinel(v: unknown): v is ServerTimestampSentinel {\r\n return isFieldSentinel(v) && v.__neetru__ === 'serverTimestamp';\r\n}\r\n\r\n/** `true` se `v` é o sentinel `increment(n)` com `operand` numérico válido. */\r\nexport function isIncrementSentinel(v: unknown): v is IncrementSentinel {\r\n return (\r\n isFieldSentinel(v) &&\r\n v.__neetru__ === 'increment' &&\r\n typeof (v as IncrementSentinel).operand === 'number' &&\r\n Number.isFinite((v as IncrementSentinel).operand)\r\n );\r\n}\r\n\r\n// ─── Aplicação otimista local (offline-first) ────────────────────────────────\r\n\r\n/**\r\n * Resolve sentinels para valores LOCAIS aproximados, para aplicação otimista\r\n * no cache offline. O valor canônico vem sempre do servidor no sync seguinte.\r\n *\r\n * - `serverTimestamp()` → `Date.now()` (epoch ms local).\r\n * - `increment(n)` → `baseValue` numérico atual (ou 0) + n.\r\n * - valores normais → retornados inalterados.\r\n *\r\n * @param value — o valor do campo (pode ser sentinel ou valor literal).\r\n * @param baseValue — valor atual do campo no doc existente (para increment).\r\n */\r\nexport function resolveSentinelLocally(value: unknown, baseValue: unknown): unknown {\r\n if (isServerTimestampSentinel(value)) {\r\n return Date.now();\r\n }\r\n if (isIncrementSentinel(value)) {\r\n const base = typeof baseValue === 'number' && Number.isFinite(baseValue) ? baseValue : 0;\r\n return base + value.operand;\r\n }\r\n return value;\r\n}\r\n\r\n/**\r\n * Aplica `resolveSentinelLocally` a cada campo top-level de `data`, usando\r\n * `existing` como base para increments. Não recursa em objetos aninhados\r\n * (sentinels só são suportados em campos top-level, espelhando a resolução\r\n * top-level do Core).\r\n *\r\n * @param data — payload da mutação (pode conter sentinels).\r\n * @param existing — dados atuais do documento (para base de increment).\r\n */\r\nexport function resolveSentinelsLocally(\r\n data: Record<string, unknown>,\r\n existing: Record<string, unknown> | undefined,\r\n): Record<string, unknown> {\r\n let touched = false;\r\n const out: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(data)) {\r\n if (isFieldSentinel(v)) {\r\n touched = true;\r\n out[k] = resolveSentinelLocally(v, existing?.[k]);\r\n } else {\r\n out[k] = v;\r\n }\r\n }\r\n // Micro-opt: se nenhum sentinel, devolve o objeto original (evita realocar).\r\n return touched ? out : data;\r\n}\r\n\r\n/** `true` se algum campo top-level de `data` é um sentinel. */\r\nexport function hasFieldSentinel(data: Record<string, unknown>): boolean {\r\n for (const v of Object.values(data)) {\r\n if (isFieldSentinel(v)) return true;\r\n }\r\n return false;\r\n}\r\n","/**\r\n * @neetru/sdk/firestore-compat — camada de COMPATIBILIDADE (transicional).\r\n *\r\n * ⚠️ **STATUS: compat / transitional.** Este módulo espelha a superfície\r\n * modular do Firebase Firestore (`doc`, `collection`, `query`, `where`,\r\n * `getDocs`, `setDoc`, `onSnapshot`, `writeBatch`, `runTransaction`,\r\n * `serverTimestamp`, `Timestamp`, `FieldValue`, …) para que produtos que\r\n * migram do Firebase **não precisem reescrever as chamadas à mão**.\r\n *\r\n * Ele NÃO é a API nativa do SDK e NÃO compete com `client.db` (NeetruDb).\r\n * É um auxílio de migração: troque os imports de `firebase/firestore` por\r\n * `@neetru/sdk/firestore-compat`, valide, e depois migre incrementalmente\r\n * para a superfície nativa `client.db.collection(...)` quando fizer sentido.\r\n *\r\n * Esta implementação foi desenhada a partir dos shims reais que os produtos\r\n * `neetru-gestovendas` (`src/neetru/firestore.ts`) e `neetru-pdvagiliza`\r\n * (`src/lib/neetru/firestore-compat.ts`) escreveram à mão — espelha\r\n * exatamente a superfície que eles precisam para poder **deletar os shims**.\r\n *\r\n * ### Backing\r\n * Tudo flui por `client.db` (`NeetruDb`). As funções recebem ou um `NeetruDb`\r\n * (o `client.db`) ou o `NeetruClient` inteiro como primeiro argumento — ambos\r\n * são aceitos por `getFirestore(...)`/`collection(...)`/`doc(...)`.\r\n *\r\n * ### Diferenças deliberadas vs Firebase\r\n * - `serverTimestamp()` e `increment()`/`arrayUnion()`/`arrayRemove()` retornam\r\n * sentinels do SDK (`@neetru/sdk` `serverTimestamp`/`increment`) — resolvidos\r\n * SERVER-SIDE pelo Core (não pelo relógio spoofável do cliente).\r\n * - `Timestamp` é uma classe leve (sem proto de Firebase) com `toDate()`,\r\n * `toMillis()`, `seconds`, `nanoseconds` e `Timestamp.now()/fromDate()/fromMillis()`.\r\n * - `onSnapshot` usa o realtime nativo do `client.db` (`onSnapshot`/`doc().onSnapshot`)\r\n * quando o transporte oferece; caso contrário entrega o snapshot inicial e\r\n * reage às mudanças locais (offline-first do SDK).\r\n *\r\n * @module\r\n */\r\n\r\nimport type {\r\n NeetruDb,\r\n DbCollectionRef,\r\n DbQuery,\r\n DbWhereFilter,\r\n DbListResult,\r\n DbGetResult,\r\n} from './db/client-db';\r\nimport {\r\n serverTimestamp as sdkServerTimestamp,\r\n increment as sdkIncrement,\r\n type FieldSentinel,\r\n} from './db/field-value';\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Resolução do handle de banco\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Aceita tanto o `NeetruDb` (`client.db`) quanto o `NeetruClient` inteiro.\r\n * Produtos legados chamam `getFirestore(client)` ou `getFirestore(client.db)`;\r\n * ambos resolvem para o `NeetruDb` subjacente.\r\n */\r\nexport type FirestoreLike = NeetruDb | { db: NeetruDb };\r\n\r\n/** Marker brand reconhecível em runtime — `getFirestore()` devolve isto. */\r\nexport interface Firestore {\r\n readonly __neetruFirestore: true;\r\n readonly db: NeetruDb;\r\n}\r\n\r\nfunction isClientWrapper(v: FirestoreLike | Firestore): v is { db: NeetruDb } {\r\n return (\r\n typeof v === 'object' &&\r\n v !== null &&\r\n 'db' in v &&\r\n typeof (v as { db?: unknown }).db === 'object' &&\r\n (v as { db?: { collection?: unknown } }).db?.collection !== undefined\r\n );\r\n}\r\n\r\nfunction resolveDb(handle: FirestoreLike | Firestore): NeetruDb {\r\n if ((handle as Firestore).__neetruFirestore) return (handle as Firestore).db;\r\n if (isClientWrapper(handle)) return handle.db;\r\n // É o próprio NeetruDb (tem `.collection`).\r\n return handle as NeetruDb;\r\n}\r\n\r\n/**\r\n * Equivalente compat de `getFirestore(app)`. Aceita o `NeetruClient` ou o\r\n * `client.db` (`NeetruDb`) e devolve um handle `Firestore` opaco.\r\n *\r\n * @example\r\n * ```ts\r\n * import { getFirestore } from '@neetru/sdk/firestore-compat';\r\n * const db = getFirestore(client); // ou getFirestore(client.db)\r\n * ```\r\n */\r\nexport function getFirestore(handle: FirestoreLike): Firestore {\r\n return { __neetruFirestore: true, db: resolveDb(handle) };\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Refs — Collection & Document\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Operadores `where` suportados (subset do Firestore + ops do SDK). */\r\nexport type WhereFilterOp =\r\n | '=='\r\n | '!='\r\n | '<'\r\n | '<='\r\n | '>'\r\n | '>='\r\n | 'in'\r\n | 'not-in'\r\n | 'array-contains'\r\n | 'array-contains-any';\r\n\r\nexport interface CollectionReference<T = Record<string, unknown>> {\r\n readonly type: 'collection';\r\n readonly __kind: 'collection';\r\n readonly id: string;\r\n readonly path: string;\r\n readonly db: NeetruDb;\r\n /** Handle nativo do SDK — atalho para a superfície `client.db`. */\r\n readonly ref: DbCollectionRef<T>;\r\n}\r\n\r\nexport interface DocumentReference<T = Record<string, unknown>> {\r\n readonly type: 'document';\r\n readonly __kind: 'document';\r\n readonly id: string;\r\n readonly path: string;\r\n readonly collection: string;\r\n readonly db: NeetruDb;\r\n /** Marcador de tipo (phantom) — preserva `T` para inferência do caller. */\r\n readonly __doc?: T;\r\n}\r\n\r\n/** Aceita tanto `Firestore` quanto `NeetruDb`/`NeetruClient` direto. */\r\ntype DbArg = Firestore | FirestoreLike;\r\n\r\n/**\r\n * `collection(db, name)` — referência a uma coleção.\r\n *\r\n * Aceita `db` como `Firestore` (de `getFirestore`), `NeetruClient` ou `client.db`.\r\n */\r\nexport function collection<T = Record<string, unknown>>(\r\n db: DbArg,\r\n name: string,\r\n): CollectionReference<T> {\r\n const resolved = resolveDb(db);\r\n return {\r\n type: 'collection',\r\n __kind: 'collection',\r\n id: name,\r\n path: name,\r\n db: resolved,\r\n ref: resolved.collection<T>(name),\r\n };\r\n}\r\n\r\n/**\r\n * `doc(...)` — referência a um documento. Suporta as 3 assinaturas legadas:\r\n * doc(db, 'collection', 'id') → coleção + id\r\n * doc(collectionRef, 'id') → id dentro de uma CollectionReference\r\n * doc(db, 'collection/id') → path com barra (id auto-gerado se id ausente)\r\n *\r\n * Quando o `id` é omitido, gera um id v4 (espelha `doc(collectionRef)` do Firebase\r\n * que cria uma ref com id automático antes do `setDoc`).\r\n */\r\nexport function doc<T = Record<string, unknown>>(\r\n dbOrCollection: DbArg | CollectionReference<T>,\r\n ...path: string[]\r\n): DocumentReference<T> {\r\n // Forma doc(collectionRef, id?)\r\n if (\r\n typeof dbOrCollection === 'object' &&\r\n dbOrCollection !== null &&\r\n (dbOrCollection as CollectionReference<T>).__kind === 'collection'\r\n ) {\r\n const col = dbOrCollection as CollectionReference<T>;\r\n const id = path[0] ?? generateId();\r\n return makeDocRef<T>(col.db, col.id, id);\r\n }\r\n\r\n const resolved = resolveDb(dbOrCollection as DbArg);\r\n\r\n if (path.length >= 2) {\r\n return makeDocRef<T>(resolved, path[0], path.slice(1).join('/'));\r\n }\r\n if (path[0]?.includes('/')) {\r\n const parts = path[0].split('/');\r\n return makeDocRef<T>(resolved, parts[0], parts.slice(1).join('/') || generateId());\r\n }\r\n // doc(db, 'collection') — id auto-gerado\r\n return makeDocRef<T>(resolved, path[0] ?? '', generateId());\r\n}\r\n\r\nfunction makeDocRef<T>(db: NeetruDb, collectionName: string, id: string): DocumentReference<T> {\r\n return {\r\n type: 'document',\r\n __kind: 'document',\r\n id,\r\n collection: collectionName,\r\n path: `${collectionName}/${id}`,\r\n db,\r\n };\r\n}\r\n\r\n/**\r\n * Gera um id estilo Firestore (20 chars alfanuméricos) com aleatoriedade\r\n * CRIPTOGRÁFICA.\r\n *\r\n * Antes usava `Math.random()` — previsível e colisível (PRNG não-criptográfico,\r\n * estado recuperável). IDs de documento previsíveis permitem enumeração/forja.\r\n * Agora usa `globalThis.crypto.getRandomValues` (WebCrypto), disponível tanto em\r\n * Node ≥20 quanto em browsers — sem importar `node:crypto` (que quebraria o\r\n * bundle browser). Rejeita bytes ≥ 248 (4×62) para eliminar o viés de módulo\r\n * sobre o alfabeto de 62 caracteres.\r\n */\r\nfunction generateId(): string {\r\n const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; // 62\r\n const cryptoObj = (globalThis as { crypto?: Crypto }).crypto;\r\n if (!cryptoObj?.getRandomValues) {\r\n throw new Error(\r\n '[Neetru SDK / firestore-compat] WebCrypto indisponível: generateId() exige ' +\r\n 'globalThis.crypto.getRandomValues (Node ≥20 ou ambiente browser).',\r\n );\r\n }\r\n let out = '';\r\n while (out.length < 20) {\r\n const buf = new Uint8Array(20 - out.length);\r\n cryptoObj.getRandomValues(buf);\r\n for (const byte of buf) {\r\n if (byte < 248) {\r\n out += CHARS[byte % 62];\r\n if (out.length === 20) break;\r\n }\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// QUERY — acumula constraints e gera DbQuery quando consumido\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type QueryConstraintType = 'where' | 'orderBy' | 'limit';\r\n\r\nexport interface QueryConstraint {\r\n readonly __constraint: QueryConstraintType;\r\n}\r\n\r\ninterface WhereConstraint extends QueryConstraint {\r\n readonly __constraint: 'where';\r\n readonly field: string;\r\n readonly op: WhereFilterOp;\r\n readonly value: unknown;\r\n}\r\n\r\ninterface OrderByConstraint extends QueryConstraint {\r\n readonly __constraint: 'orderBy';\r\n readonly field: string;\r\n readonly direction: 'asc' | 'desc';\r\n}\r\n\r\ninterface LimitConstraint extends QueryConstraint {\r\n readonly __constraint: 'limit';\r\n readonly value: number;\r\n}\r\n\r\nexport interface Query<T = Record<string, unknown>> {\r\n readonly type: 'query';\r\n readonly __kind: 'query';\r\n readonly path: string;\r\n readonly collection: string;\r\n readonly db: NeetruDb;\r\n readonly ref: DbCollectionRef<T>;\r\n readonly constraints: QueryConstraint[];\r\n}\r\n\r\n/**\r\n * `query(collectionRef, ...constraints)` — combina `where`/`orderBy`/`limit`.\r\n * Aceita também uma `Query` como fonte (encadeamento de constraints).\r\n */\r\nexport function query<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T>,\r\n ...constraints: QueryConstraint[]\r\n): Query<T> {\r\n const base: Query<T> =\r\n source.__kind === 'query'\r\n ? source\r\n : {\r\n type: 'query',\r\n __kind: 'query',\r\n path: source.path,\r\n collection: source.id,\r\n db: source.db,\r\n ref: source.ref,\r\n constraints: [],\r\n };\r\n return { ...base, constraints: [...base.constraints, ...constraints] };\r\n}\r\n\r\nexport function where(field: string, op: WhereFilterOp, value: unknown): WhereConstraint {\r\n return { __constraint: 'where', field, op, value };\r\n}\r\n\r\nexport function orderBy(field: string, direction: 'asc' | 'desc' = 'asc'): OrderByConstraint {\r\n return { __constraint: 'orderBy', field, direction };\r\n}\r\n\r\nexport function limit(value: number): LimitConstraint {\r\n return { __constraint: 'limit', value };\r\n}\r\n\r\n/**\r\n * Converte os constraints de uma `Query` compat para o `DbQuery` nativo do SDK.\r\n *\r\n * Exportado para que os hooks React (`@neetru/sdk/react`) possam reusar a mesma\r\n * tradução — fonte única de verdade da conversão de constraints.\r\n */\r\nexport function toDbQuery(constraints: QueryConstraint[]): DbQuery {\r\n const whereFilters: DbWhereFilter[] = [];\r\n let orderByClause: DbQuery['orderBy'];\r\n let limitValue: number | undefined;\r\n\r\n for (const c of constraints) {\r\n if (c.__constraint === 'where') {\r\n const w = c as WhereConstraint;\r\n whereFilters.push({ field: w.field, op: normalizeOp(w.op), value: w.value });\r\n } else if (c.__constraint === 'orderBy') {\r\n const o = c as OrderByConstraint;\r\n orderByClause = { field: o.field, direction: o.direction };\r\n } else if (c.__constraint === 'limit') {\r\n limitValue = (c as LimitConstraint).value;\r\n }\r\n }\r\n\r\n const out: DbQuery = {};\r\n if (whereFilters.length > 0) out.where = whereFilters;\r\n if (orderByClause) out.orderBy = orderByClause;\r\n if (limitValue !== undefined) out.limit = limitValue;\r\n return out;\r\n}\r\n\r\n/**\r\n * O `DbWhereFilter` do SDK não declara `array-contains-any`/`not-in` no union.\r\n * Passamos o op como-está; o Core valida server-side e retorna erro explícito\r\n * se não suportado.\r\n *\r\n * Bug corrigido: remapeava 'array-contains-any' → 'array-contains' silenciosamente,\r\n * o que tem semântica completamente diferente (checa literal do array, não interseção)\r\n * e nunca retornava resultados para queries reais.\r\n */\r\nfunction normalizeOp(op: WhereFilterOp): DbWhereFilter['op'] {\r\n // Todas as ops passam direto; o Core valida server-side.\r\n return op as DbWhereFilter['op'];\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// SNAPSHOTS — emulam a API antiga do Firebase\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Remove o campo `id` dos dados (o Firestore expõe id via `.id`, não em `data()`). */\r\nfunction stripId<T>(raw: Record<string, unknown>): T {\r\n if (raw && typeof raw === 'object' && 'id' in raw) {\r\n const { id: _omit, ...rest } = raw;\r\n return rest as T;\r\n }\r\n return raw as T;\r\n}\r\n\r\nexport class DocumentSnapshot<T = Record<string, unknown>> {\r\n constructor(\r\n public readonly id: string,\r\n private readonly _data: T | null,\r\n public readonly ref?: DocumentReference<T>,\r\n ) {}\r\n\r\n exists(): boolean {\r\n return this._data != null;\r\n }\r\n\r\n data(): T | undefined {\r\n return this._data ?? undefined;\r\n }\r\n\r\n /** Compat: `snap.get('field')` lê um campo top-level. */\r\n get(field: string): unknown {\r\n return this._data ? (this._data as Record<string, unknown>)[field] : undefined;\r\n }\r\n}\r\n\r\nexport interface QueryDocumentSnapshot<T = Record<string, unknown>> {\r\n readonly id: string;\r\n data(): T;\r\n exists(): boolean;\r\n get(field: string): unknown;\r\n readonly ref: DocumentReference<T>;\r\n}\r\n\r\nexport class QuerySnapshot<T = Record<string, unknown>> {\r\n public readonly docs: Array<QueryDocumentSnapshot<T>>;\r\n public readonly empty: boolean;\r\n public readonly size: number;\r\n\r\n constructor(\r\n rows: Array<{ id: string; data: Record<string, unknown> }>,\r\n db?: NeetruDb,\r\n collectionName?: string,\r\n ) {\r\n this.docs = rows.map(({ id, data }) => {\r\n const stripped = stripId<T>(data);\r\n const ref: DocumentReference<T> | undefined =\r\n db && collectionName ? makeDocRef<T>(db, collectionName, id) : undefined;\r\n return {\r\n id,\r\n data: () => stripped,\r\n exists: () => true,\r\n get: (field: string) => (stripped as Record<string, unknown>)?.[field],\r\n // ref sempre presente quando db+collection conhecidos (caso comum).\r\n ref: ref as DocumentReference<T>,\r\n };\r\n });\r\n this.empty = this.docs.length === 0;\r\n this.size = this.docs.length;\r\n }\r\n\r\n forEach(cb: (doc: QueryDocumentSnapshot<T>) => void): void {\r\n this.docs.forEach(cb);\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// READS\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Extrai os dados crus do primeiro doc de um `DbGetResult`. */\r\nfunction firstDocData(result: DbGetResult<Record<string, unknown>> | null): {\r\n data: Record<string, unknown> | null;\r\n id: string | null;\r\n} {\r\n const first = result?.docs[0];\r\n return { data: first ? (first.data as Record<string, unknown>) : null, id: first?.id ?? null };\r\n}\r\n\r\nexport async function getDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n): Promise<DocumentSnapshot<T>> {\r\n const result = (await ref.db.collection(ref.collection).get(ref.id)) as DbGetResult<\r\n Record<string, unknown>\r\n > | null;\r\n const { data } = firstDocData(result);\r\n return new DocumentSnapshot<T>(\r\n ref.id,\r\n data ? (stripId<T>({ id: ref.id, ...data })) : null,\r\n ref,\r\n );\r\n}\r\n\r\n/** Mapeia um `DbListResult` para linhas `{ id, data }` (id garantido). */\r\nfunction listResultToRows(\r\n result: DbListResult<Record<string, unknown>>,\r\n): Array<{ id: string; data: Record<string, unknown> }> {\r\n return result.docs.map((d) => ({ id: d.id, data: { ...(d.data as Record<string, unknown>) } }));\r\n}\r\n\r\nexport async function getDocs<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T>,\r\n): Promise<QuerySnapshot<T>> {\r\n const dbQuery = source.__kind === 'query' ? toDbQuery(source.constraints) : undefined;\r\n const collectionName = source.__kind === 'query' ? source.collection : source.id;\r\n const result = (await source.db\r\n .collection(collectionName)\r\n .list(dbQuery)) as DbListResult<Record<string, unknown>>;\r\n return new QuerySnapshot<T>(listResultToRows(result), source.db, collectionName);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// WRITES\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface SetOptions {\r\n merge?: boolean;\r\n mergeFields?: string[];\r\n}\r\n\r\nexport async function setDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n options: SetOptions = {},\r\n): Promise<void> {\r\n const col = ref.db.collection(ref.collection);\r\n const payload = data as Record<string, unknown>;\r\n if (options.merge || (options.mergeFields && options.mergeFields.length > 0)) {\r\n // Bug corrigido: `update()` retorna 404 no Core quando o doc não existe.\r\n // Verificamos existência antes: se existir → update (merge); se não → set (criar).\r\n // Isso espelha a semântica do Firebase setDoc({merge:true}) (upsert real).\r\n const existing = await col.get(ref.id);\r\n const docExists = Array.isArray((existing as { docs?: unknown[] } | null)?.docs)\r\n ? ((existing as { docs: unknown[] }).docs.length > 0)\r\n : existing != null;\r\n if (docExists) {\r\n await col.update(ref.id, payload);\r\n } else {\r\n await col.set(ref.id, payload);\r\n }\r\n } else {\r\n await col.set(ref.id, payload);\r\n }\r\n}\r\n\r\nexport async function addDoc<T = Record<string, unknown>>(\r\n source: CollectionReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n): Promise<DocumentReference<T>> {\r\n const result = await source.db.collection(source.id).add(data as Record<string, unknown>);\r\n return makeDocRef<T>(source.db, source.id, result.id);\r\n}\r\n\r\nexport async function updateDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n): Promise<void> {\r\n await ref.db.collection(ref.collection).update(ref.id, data as Record<string, unknown>);\r\n}\r\n\r\nexport async function deleteDoc(ref: DocumentReference): Promise<void> {\r\n await ref.db.collection(ref.collection).remove(ref.id);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// WRITE BATCH\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\ninterface PendingBatchOp {\r\n collection: string;\r\n op:\r\n | { kind: 'set'; id: string; data: Record<string, unknown> }\r\n | { kind: 'update'; id: string; data: Record<string, unknown> }\r\n | { kind: 'remove'; id: string };\r\n}\r\n\r\nexport class WriteBatch {\r\n private ops: PendingBatchOp[] = [];\r\n\r\n constructor(private readonly db: NeetruDb) {}\r\n\r\n set<T>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n _options?: SetOptions,\r\n ): this {\r\n this.ops.push({\r\n collection: ref.collection,\r\n op: { kind: 'set', id: ref.id, data: data as Record<string, unknown> },\r\n });\r\n return this;\r\n }\r\n\r\n update<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>): this {\r\n this.ops.push({\r\n collection: ref.collection,\r\n op: { kind: 'update', id: ref.id, data: data as Record<string, unknown> },\r\n });\r\n return this;\r\n }\r\n\r\n delete(ref: DocumentReference): this {\r\n this.ops.push({ collection: ref.collection, op: { kind: 'remove', id: ref.id } });\r\n return this;\r\n }\r\n\r\n async commit(): Promise<void> {\r\n // Agrupa por coleção — o `DbCollectionRef.batch` opera numa coleção por vez.\r\n const byCol = new Map<string, PendingBatchOp['op'][]>();\r\n for (const { collection: name, op } of this.ops) {\r\n const list = byCol.get(name) ?? [];\r\n list.push(op);\r\n byCol.set(name, list);\r\n }\r\n for (const [name, ops] of byCol.entries()) {\r\n // O union nativo `DbBatchOp` casa 1:1 com nossas variantes (set/update/remove).\r\n await this.db.collection(name).batch(ops as never);\r\n }\r\n this.ops = [];\r\n }\r\n}\r\n\r\nexport function writeBatch(db: DbArg): WriteBatch {\r\n return new WriteBatch(resolveDb(db));\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// runTransaction — emulação otimista (read → write) sobre client.db\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Handle de transação compat. O SDK não expõe transações multi-doc atômicas\r\n * com lock server-side, então emulamos a forma do Firebase: `tx.get` lê do\r\n * `client.db`, e `tx.set/update/delete` enfileiram escritas que são aplicadas\r\n * no fim do callback.\r\n *\r\n * ⚠️ **LIMITAÇÃO CONHECIDA — sem isolamento server-side (lost-update silencioso):**\r\n *\r\n * Diferente do Firestore nativo (que usa serializable snapshot isolation com\r\n * contention detection), este shim executa reads e writes em rounds separados\r\n * sem garantia de atomicidade:\r\n *\r\n * 1. `tx.get(ref)` faz um read imediato via `client.db`.\r\n * 2. As escritas (`tx.set/update/delete`) são **enfileiradas** — não bloqueiam\r\n * outros writers.\r\n * 3. No fim do callback, `batch.commit()` aplica as escritas. Se outro processo\r\n * tiver modificado o mesmo documento entre o read e o commit, a escrita\r\n * será um **lost-update silencioso** (sem erro, sem retry automático).\r\n *\r\n * **Quando isso importa:** counters, saldos, estoques — qualquer dado\r\n * que precise de leitura-modificação-escrita atômica.\r\n *\r\n * **Alternativas seguras:**\r\n * - Use **Server Actions no Core** (`src/lib/actions/`) que rodam dentro\r\n * de transações Firestore Admin SDK reais (com locks server-side).\r\n * - Use `increment()` sentinel para contadores numéricos — server-side atomic.\r\n * - Use `arrayUnion()`/`arrayRemove()` para sets (também server-side atomic).\r\n *\r\n * Esta emulação é aceitável para migrações transitórias e para operações\r\n * onde a probabilidade de concorrência é baixa (ex: dados de um único usuário\r\n * sem write concorrente). **Não use para dados financeiros ou de inventário.**\r\n */\r\nexport interface Transaction {\r\n get<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>>;\r\n set<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>, options?: SetOptions): Transaction;\r\n update<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>): Transaction;\r\n delete(ref: DocumentReference): Transaction;\r\n}\r\n\r\n/** Flag para emitir o aviso de limitação uma só vez por processo (não spammar). */\r\nlet _runTransactionWarnEmitted = false;\r\n\r\nexport async function runTransaction<R>(\r\n db: DbArg,\r\n updateFunction: (transaction: Transaction) => Promise<R>,\r\n): Promise<R> {\r\n // Aviso em desenvolvimento/staging: sem isolamento server-side.\r\n if (!_runTransactionWarnEmitted) {\r\n _runTransactionWarnEmitted = true;\r\n console.warn(\r\n '[Neetru SDK / firestore-compat] runTransaction(): esta emulação NÃO oferece' +\r\n ' isolamento server-side. Reads e writes são executados em rounds separados;' +\r\n ' um write concorrente entre o get() e o commit() resultará em lost-update' +\r\n ' silencioso. Para atomicidade real, use Server Actions no Core ou sentinels' +\r\n ' increment()/arrayUnion(). Veja a doc em src/firestore-compat.ts § runTransaction.',\r\n );\r\n }\r\n\r\n const resolved = resolveDb(db);\r\n const batch = new WriteBatch(resolved);\r\n\r\n const tx: Transaction = {\r\n async get<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>> {\r\n return getDoc(ref);\r\n },\r\n set(ref, data, options) {\r\n batch.set(ref, data, options);\r\n return tx;\r\n },\r\n update(ref, data) {\r\n batch.update(ref, data);\r\n return tx;\r\n },\r\n delete(ref) {\r\n batch.delete(ref);\r\n return tx;\r\n },\r\n };\r\n\r\n const result = await updateFunction(tx);\r\n await batch.commit();\r\n return result;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// onSnapshot — realtime via client.db (com fallback inicial)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type Unsubscribe = () => void;\r\n\r\ntype DocOnNext<T> = (snap: DocumentSnapshot<T>) => void;\r\ntype QueryOnNext<T> = (snap: QuerySnapshot<T>) => void;\r\ntype OnError = (err: Error) => void;\r\n\r\nexport function onSnapshot<T>(\r\n ref: DocumentReference<T>,\r\n onNext: DocOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe;\r\nexport function onSnapshot<T>(\r\n source: Query<T> | CollectionReference<T>,\r\n onNext: QueryOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe;\r\nexport function onSnapshot<T>(\r\n source: DocumentReference<T> | Query<T> | CollectionReference<T>,\r\n onNext: DocOnNext<T> | QueryOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe {\r\n // Documento único\r\n if (source.__kind === 'document') {\r\n const ref = source as DocumentReference<T>;\r\n const docRef = ref.db.collection(ref.collection).doc(ref.id);\r\n try {\r\n return docRef.onSnapshot((snap) => {\r\n const data = snap?.docs[0]?.data as Record<string, unknown> | undefined;\r\n (onNext as DocOnNext<T>)(\r\n new DocumentSnapshot<T>(\r\n ref.id,\r\n data ? stripId<T>({ id: ref.id, ...data }) : null,\r\n ref,\r\n ),\r\n );\r\n });\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n return () => {};\r\n }\r\n }\r\n\r\n // Coleção / Query\r\n const collectionName = source.__kind === 'query' ? source.collection : (source as CollectionReference<T>).id;\r\n const dbQuery = source.__kind === 'query' ? toDbQuery(source.constraints) : undefined;\r\n const col = source.db.collection(collectionName);\r\n try {\r\n return col.onSnapshot(dbQuery, (snap: DbListResult<Record<string, unknown>>) => {\r\n (onNext as QueryOnNext<T>)(\r\n new QuerySnapshot<T>(listResultToRows(snap), source.db, collectionName),\r\n );\r\n });\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n return () => {};\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Timestamp — classe leve compat\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Reimplementação compat de `Timestamp` do Firestore. Sem proto do Firebase.\r\n * Cobre `seconds`, `nanoseconds`, `toDate()`, `toMillis()`, `isEqual()`,\r\n * `valueOf()`, `toJSON()` + estáticos `now()`, `fromDate()`, `fromMillis()`.\r\n */\r\nexport class Timestamp {\r\n constructor(\r\n public readonly seconds: number,\r\n public readonly nanoseconds: number,\r\n ) {}\r\n\r\n static now(): Timestamp {\r\n return Timestamp.fromMillis(Date.now());\r\n }\r\n\r\n static fromDate(date: Date): Timestamp {\r\n return Timestamp.fromMillis(date.getTime());\r\n }\r\n\r\n static fromMillis(millis: number): Timestamp {\r\n const seconds = Math.floor(millis / 1000);\r\n const nanoseconds = (millis - seconds * 1000) * 1e6;\r\n return new Timestamp(seconds, nanoseconds);\r\n }\r\n\r\n toDate(): Date {\r\n return new Date(this.toMillis());\r\n }\r\n\r\n toMillis(): number {\r\n return this.seconds * 1000 + Math.floor(this.nanoseconds / 1e6);\r\n }\r\n\r\n isEqual(other: Timestamp): boolean {\r\n return this.seconds === other.seconds && this.nanoseconds === other.nanoseconds;\r\n }\r\n\r\n valueOf(): string {\r\n // Estável para ordenação por string (espelha Firebase).\r\n return `${this.seconds.toString().padStart(12, '0')}.${this.nanoseconds\r\n .toString()\r\n .padStart(9, '0')}`;\r\n }\r\n\r\n toJSON(): { seconds: number; nanoseconds: number } {\r\n return { seconds: this.seconds, nanoseconds: this.nanoseconds };\r\n }\r\n\r\n toString(): string {\r\n return `Timestamp(seconds=${this.seconds}, nanoseconds=${this.nanoseconds})`;\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// FieldValue + sentinels\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Sentinel de `arrayUnion`/`arrayRemove`. O SDK ainda não tem sentinel de array\r\n * server-side, então emitimos um marker namespaceado que o Core reconhece\r\n * (mesma família de `__neetru__`). Se o backing não suportar, o produto deve\r\n * preferir read-modify-write via Server Action. Documentado como compat.\r\n */\r\nexport interface ArrayUnionSentinel {\r\n readonly __neetru__: 'arrayUnion';\r\n readonly elements: unknown[];\r\n}\r\nexport interface ArrayRemoveSentinel {\r\n readonly __neetru__: 'arrayRemove';\r\n readonly elements: unknown[];\r\n}\r\n\r\n/**\r\n * Sentinel de campo a deletar (compat `deleteField()`).\r\n */\r\nexport interface DeleteFieldSentinel {\r\n readonly __neetru__: 'deleteField';\r\n}\r\n\r\n/**\r\n * `serverTimestamp()` — delega ao sentinel do SDK (resolvido SERVER-SIDE).\r\n * NÃO retorna mais ISO/Date do cliente (que era spoofável).\r\n */\r\nexport function serverTimestamp(): FieldSentinel {\r\n return sdkServerTimestamp();\r\n}\r\n\r\n/**\r\n * `increment(n)` — incremento atômico server-side (delega ao sentinel do SDK).\r\n */\r\nexport function increment(n: number): FieldSentinel {\r\n return sdkIncrement(n);\r\n}\r\n\r\n/** `arrayUnion(...elements)` — sentinel compat (resolvido pelo Core). */\r\nexport function arrayUnion(...elements: unknown[]): ArrayUnionSentinel {\r\n return { __neetru__: 'arrayUnion', elements };\r\n}\r\n\r\n/** `arrayRemove(...elements)` — sentinel compat (resolvido pelo Core). */\r\nexport function arrayRemove(...elements: unknown[]): ArrayRemoveSentinel {\r\n return { __neetru__: 'arrayRemove', elements };\r\n}\r\n\r\n/** `deleteField()` — sentinel compat para remover um campo. */\r\nexport function deleteField(): DeleteFieldSentinel {\r\n return { __neetru__: 'deleteField' };\r\n}\r\n\r\n/**\r\n * Namespace estático `FieldValue` — espelha `firebase/firestore`'s `FieldValue`.\r\n * Os métodos delegam às funções modulares acima.\r\n *\r\n * @example\r\n * ```ts\r\n * import { FieldValue } from '@neetru/sdk/firestore-compat';\r\n * await updateDoc(ref, {\r\n * tags: FieldValue.arrayUnion('novo'),\r\n * counter: FieldValue.increment(1),\r\n * updatedAt: FieldValue.serverTimestamp(),\r\n * });\r\n * ```\r\n */\r\nexport const FieldValue = {\r\n serverTimestamp,\r\n increment,\r\n arrayUnion,\r\n arrayRemove,\r\n delete: deleteField,\r\n} as const;\r\n"]}
1
+ {"version":3,"sources":["../src/db/field-value.ts","../src/firestore-compat.ts"],"names":["serverTimestamp","increment"],"mappings":"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","file":"firestore-compat.mjs","sourcesContent":["/**\r\n * field-value.ts — Sentinels de escrita server-side para o Mundo Documentos.\r\n *\r\n * Resolve duas lacunas confirmadas (bug_05aedb8b) da superfície `client.db`:\r\n *\r\n * 1. **Timestamp resolvido no servidor** — antes deste módulo, o produto que\r\n * quisesse `createdAt`/`updatedAt` confiáveis tinha que usar `Date.now()`\r\n * no CLIENTE, que é spoofável e sofre clock-skew. Agora:\r\n *\r\n * await orders.add({ total: 99, createdAt: serverTimestamp() });\r\n *\r\n * O marcador atravessa o transporte REST como um objeto JSON namespaceado\r\n * e o Core o substitui por `FieldValue.serverTimestamp()` no momento da\r\n * escrita — relógio do SERVIDOR, não do cliente.\r\n *\r\n * 2. **Increment atômico** — antes, `update(id, { n: x+1 })` era read-modify-write\r\n * no cliente, sujeito a lost-update sob concorrência. Agora:\r\n *\r\n * await counters.update('global', { hits: increment(1) });\r\n *\r\n * O Core aplica `FieldValue.increment(1)` atomicamente no documento.\r\n *\r\n * ## Contrato de fio (wire format)\r\n *\r\n * Os sentinels são objetos JSON simples — sobrevivem `JSON.stringify` no\r\n * transporte REST e ao `IndexedDB structured clone` da camada offline:\r\n *\r\n * serverTimestamp() → `{ \"__neetru__\": \"serverTimestamp\" }`\r\n * increment(n) → `{ \"__neetru__\": \"increment\", \"operand\": n }`\r\n *\r\n * A chave-âncora `__neetru__` é deliberadamente improvável de colidir com\r\n * dados de produto. O Core valida e resolve esses marcadores; qualquer objeto\r\n * com `__neetru__` desconhecido é rejeitado server-side (fail-closed).\r\n *\r\n * ## Aplicação otimista (offline-first)\r\n *\r\n * A camada offline aplica uma APROXIMAÇÃO local imediata (para a UI não piscar):\r\n * - `serverTimestamp()` → `Date.now()` local (substituído pelo valor real no sync).\r\n * - `increment(n)` → soma `n` ao valor numérico atual do campo (0 se ausente).\r\n * O valor canônico vem sempre do servidor no pull/confirm subsequente.\r\n *\r\n * @module\r\n */\r\n\r\n/** Chave-âncora dos marcadores de sentinel no wire format. */\r\nexport const NEETRU_SENTINEL_KEY = '__neetru__' as const;\r\n\r\n/** Marcador de timestamp resolvido pelo servidor. */\r\nexport interface ServerTimestampSentinel {\r\n readonly __neetru__: 'serverTimestamp';\r\n}\r\n\r\n/** Marcador de incremento atômico aplicado pelo servidor. */\r\nexport interface IncrementSentinel {\r\n readonly __neetru__: 'increment';\r\n /** Delta a somar (pode ser negativo para decremento). */\r\n readonly operand: number;\r\n}\r\n\r\n/**\r\n * União de todos os sentinels de campo. Use `serverTimestamp()` / `increment()`\r\n * para construir — nunca monte o objeto à mão.\r\n */\r\nexport type FieldSentinel = ServerTimestampSentinel | IncrementSentinel;\r\n\r\n/**\r\n * Sentinel que instrui o servidor a gravar o timestamp do MOMENTO da escrita\r\n * usando o relógio do servidor (não do cliente).\r\n *\r\n * @example\r\n * ```ts\r\n * import { serverTimestamp } from '@neetru/sdk/db';\r\n * await orders.add({ total: 99, createdAt: serverTimestamp() });\r\n * ```\r\n */\r\nexport function serverTimestamp(): ServerTimestampSentinel {\r\n return { __neetru__: 'serverTimestamp' };\r\n}\r\n\r\n/**\r\n * Sentinel que instrui o servidor a incrementar atomicamente o campo numérico\r\n * por `n` (negativo decrementa). Se o campo não existir/não for numérico, o\r\n * servidor o trata como `0` antes de somar (semântica `FieldValue.increment`).\r\n *\r\n * @param n — delta inteiro ou fracionário a somar.\r\n * @example\r\n * ```ts\r\n * import { increment } from '@neetru/sdk/db';\r\n * await counters.update('global', { hits: increment(1) });\r\n * await wallet.update(uid, { balance: increment(-50) });\r\n * ```\r\n */\r\nexport function increment(n: number): IncrementSentinel {\r\n if (typeof n !== 'number' || !Number.isFinite(n)) {\r\n throw new TypeError(\r\n `increment(n): n deve ser um número finito (recebido: ${String(n)}).`,\r\n );\r\n }\r\n return { __neetru__: 'increment', operand: n };\r\n}\r\n\r\n// ─── Type guards ────────────────────────────────────────────────────────────\r\n\r\n/** `true` se `v` é qualquer sentinel de campo Neetru. */\r\nexport function isFieldSentinel(v: unknown): v is FieldSentinel {\r\n return (\r\n typeof v === 'object' &&\r\n v !== null &&\r\n NEETRU_SENTINEL_KEY in v &&\r\n typeof (v as Record<string, unknown>)[NEETRU_SENTINEL_KEY] === 'string'\r\n );\r\n}\r\n\r\n/** `true` se `v` é o sentinel `serverTimestamp()`. */\r\nexport function isServerTimestampSentinel(v: unknown): v is ServerTimestampSentinel {\r\n return isFieldSentinel(v) && v.__neetru__ === 'serverTimestamp';\r\n}\r\n\r\n/** `true` se `v` é o sentinel `increment(n)` com `operand` numérico válido. */\r\nexport function isIncrementSentinel(v: unknown): v is IncrementSentinel {\r\n return (\r\n isFieldSentinel(v) &&\r\n v.__neetru__ === 'increment' &&\r\n typeof (v as IncrementSentinel).operand === 'number' &&\r\n Number.isFinite((v as IncrementSentinel).operand)\r\n );\r\n}\r\n\r\n// ─── Aplicação otimista local (offline-first) ────────────────────────────────\r\n\r\n/**\r\n * Resolve sentinels para valores LOCAIS aproximados, para aplicação otimista\r\n * no cache offline. O valor canônico vem sempre do servidor no sync seguinte.\r\n *\r\n * - `serverTimestamp()` → `Date.now()` (epoch ms local).\r\n * - `increment(n)` → `baseValue` numérico atual (ou 0) + n.\r\n * - valores normais → retornados inalterados.\r\n *\r\n * @param value — o valor do campo (pode ser sentinel ou valor literal).\r\n * @param baseValue — valor atual do campo no doc existente (para increment).\r\n */\r\nexport function resolveSentinelLocally(value: unknown, baseValue: unknown): unknown {\r\n if (isServerTimestampSentinel(value)) {\r\n return Date.now();\r\n }\r\n if (isIncrementSentinel(value)) {\r\n const base = typeof baseValue === 'number' && Number.isFinite(baseValue) ? baseValue : 0;\r\n return base + value.operand;\r\n }\r\n return value;\r\n}\r\n\r\n/**\r\n * Aplica `resolveSentinelLocally` a cada campo top-level de `data`, usando\r\n * `existing` como base para increments. Não recursa em objetos aninhados\r\n * (sentinels só são suportados em campos top-level, espelhando a resolução\r\n * top-level do Core).\r\n *\r\n * @param data — payload da mutação (pode conter sentinels).\r\n * @param existing — dados atuais do documento (para base de increment).\r\n */\r\nexport function resolveSentinelsLocally(\r\n data: Record<string, unknown>,\r\n existing: Record<string, unknown> | undefined,\r\n): Record<string, unknown> {\r\n let touched = false;\r\n const out: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(data)) {\r\n if (isFieldSentinel(v)) {\r\n touched = true;\r\n out[k] = resolveSentinelLocally(v, existing?.[k]);\r\n } else {\r\n out[k] = v;\r\n }\r\n }\r\n // Micro-opt: se nenhum sentinel, devolve o objeto original (evita realocar).\r\n return touched ? out : data;\r\n}\r\n\r\n/** `true` se algum campo top-level de `data` é um sentinel. */\r\nexport function hasFieldSentinel(data: Record<string, unknown>): boolean {\r\n for (const v of Object.values(data)) {\r\n if (isFieldSentinel(v)) return true;\r\n }\r\n return false;\r\n}\r\n","/**\r\n * @neetru/sdk/firestore-compat — camada de COMPATIBILIDADE (transicional).\r\n *\r\n * ⚠️ **STATUS: compat / transitional.** Este módulo espelha a superfície\r\n * modular do Firebase Firestore (`doc`, `collection`, `query`, `where`,\r\n * `getDocs`, `setDoc`, `onSnapshot`, `writeBatch`, `runTransaction`,\r\n * `serverTimestamp`, `Timestamp`, `FieldValue`, …) para que produtos que\r\n * migram do Firebase **não precisem reescrever as chamadas à mão**.\r\n *\r\n * Ele NÃO é a API nativa do SDK e NÃO compete com `client.db` (NeetruDb).\r\n * É um auxílio de migração: troque os imports de `firebase/firestore` por\r\n * `@neetru/sdk/firestore-compat`, valide, e depois migre incrementalmente\r\n * para a superfície nativa `client.db.collection(...)` quando fizer sentido.\r\n *\r\n * Esta implementação foi desenhada a partir dos shims reais que os produtos\r\n * `neetru-gestovendas` (`src/neetru/firestore.ts`) e `neetru-pdvagiliza`\r\n * (`src/lib/neetru/firestore-compat.ts`) escreveram à mão — espelha\r\n * exatamente a superfície que eles precisam para poder **deletar os shims**.\r\n *\r\n * ### Backing\r\n * Tudo flui por `client.db` (`NeetruDb`). As funções recebem ou um `NeetruDb`\r\n * (o `client.db`) ou o `NeetruClient` inteiro como primeiro argumento — ambos\r\n * são aceitos por `getFirestore(...)`/`collection(...)`/`doc(...)`.\r\n *\r\n * ### Diferenças deliberadas vs Firebase\r\n * - `serverTimestamp()` e `increment()`/`arrayUnion()`/`arrayRemove()` retornam\r\n * sentinels do SDK (`@neetru/sdk` `serverTimestamp`/`increment`) — resolvidos\r\n * SERVER-SIDE pelo Core (não pelo relógio spoofável do cliente).\r\n * - `Timestamp` é uma classe leve (sem proto de Firebase) com `toDate()`,\r\n * `toMillis()`, `seconds`, `nanoseconds` e `Timestamp.now()/fromDate()/fromMillis()`.\r\n * - `onSnapshot` usa o realtime nativo do `client.db` (`onSnapshot`/`doc().onSnapshot`)\r\n * quando o transporte oferece; caso contrário entrega o snapshot inicial e\r\n * reage às mudanças locais (offline-first do SDK).\r\n *\r\n * @module\r\n */\r\n\r\nimport type {\r\n NeetruDb,\r\n DbCollectionRef,\r\n DbQuery,\r\n DbWhereFilter,\r\n DbListResult,\r\n DbGetResult,\r\n} from './db/client-db';\r\nimport {\r\n serverTimestamp as sdkServerTimestamp,\r\n increment as sdkIncrement,\r\n type FieldSentinel,\r\n} from './db/field-value';\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Resolução do handle de banco\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Aceita tanto o `NeetruDb` (`client.db`) quanto o `NeetruClient` inteiro.\r\n * Produtos legados chamam `getFirestore(client)` ou `getFirestore(client.db)`;\r\n * ambos resolvem para o `NeetruDb` subjacente.\r\n */\r\nexport type FirestoreLike = NeetruDb | { db: NeetruDb };\r\n\r\n/** Marker brand reconhecível em runtime — `getFirestore()` devolve isto. */\r\nexport interface Firestore {\r\n readonly __neetruFirestore: true;\r\n readonly db: NeetruDb;\r\n}\r\n\r\nfunction isClientWrapper(v: FirestoreLike | Firestore): v is { db: NeetruDb } {\r\n return (\r\n typeof v === 'object' &&\r\n v !== null &&\r\n 'db' in v &&\r\n typeof (v as { db?: unknown }).db === 'object' &&\r\n (v as { db?: { collection?: unknown } }).db?.collection !== undefined\r\n );\r\n}\r\n\r\nfunction resolveDb(handle: FirestoreLike | Firestore): NeetruDb {\r\n if ((handle as Firestore).__neetruFirestore) return (handle as Firestore).db;\r\n if (isClientWrapper(handle)) return handle.db;\r\n // É o próprio NeetruDb (tem `.collection`).\r\n return handle as NeetruDb;\r\n}\r\n\r\n/**\r\n * Equivalente compat de `getFirestore(app)`. Aceita o `NeetruClient` ou o\r\n * `client.db` (`NeetruDb`) e devolve um handle `Firestore` opaco.\r\n *\r\n * @example\r\n * ```ts\r\n * import { getFirestore } from '@neetru/sdk/firestore-compat';\r\n * const db = getFirestore(client); // ou getFirestore(client.db)\r\n * ```\r\n */\r\nexport function getFirestore(handle: FirestoreLike): Firestore {\r\n return { __neetruFirestore: true, db: resolveDb(handle) };\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Refs — Collection & Document\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Operadores `where` suportados (subset do Firestore + ops do SDK). */\r\nexport type WhereFilterOp =\r\n | '=='\r\n | '!='\r\n | '<'\r\n | '<='\r\n | '>'\r\n | '>='\r\n | 'in'\r\n | 'not-in'\r\n | 'array-contains'\r\n | 'array-contains-any';\r\n\r\nexport interface CollectionReference<T = Record<string, unknown>> {\r\n readonly type: 'collection';\r\n readonly __kind: 'collection';\r\n readonly id: string;\r\n readonly path: string;\r\n readonly db: NeetruDb;\r\n /** Handle nativo do SDK — atalho para a superfície `client.db`. */\r\n readonly ref: DbCollectionRef<T>;\r\n}\r\n\r\nexport interface DocumentReference<T = Record<string, unknown>> {\r\n readonly type: 'document';\r\n readonly __kind: 'document';\r\n readonly id: string;\r\n readonly path: string;\r\n readonly collection: string;\r\n readonly db: NeetruDb;\r\n /** Marcador de tipo (phantom) — preserva `T` para inferência do caller. */\r\n readonly __doc?: T;\r\n}\r\n\r\n/** Aceita tanto `Firestore` quanto `NeetruDb`/`NeetruClient` direto. */\r\ntype DbArg = Firestore | FirestoreLike;\r\n\r\n/**\r\n * `collection(db, name)` — referência a uma coleção.\r\n *\r\n * Aceita `db` como `Firestore` (de `getFirestore`), `NeetruClient` ou `client.db`.\r\n */\r\nexport function collection<T = Record<string, unknown>>(\r\n db: DbArg,\r\n name: string,\r\n): CollectionReference<T> {\r\n const resolved = resolveDb(db);\r\n return {\r\n type: 'collection',\r\n __kind: 'collection',\r\n id: name,\r\n path: name,\r\n db: resolved,\r\n ref: resolved.collection<T>(name),\r\n };\r\n}\r\n\r\n/**\r\n * `doc(...)` — referência a um documento. Suporta as 3 assinaturas legadas:\r\n * doc(db, 'collection', 'id') → coleção + id\r\n * doc(collectionRef, 'id') → id dentro de uma CollectionReference\r\n * doc(db, 'collection/id') → path com barra (id auto-gerado se id ausente)\r\n *\r\n * Quando o `id` é omitido, gera um id v4 (espelha `doc(collectionRef)` do Firebase\r\n * que cria uma ref com id automático antes do `setDoc`).\r\n */\r\nexport function doc<T = Record<string, unknown>>(\r\n dbOrCollection: DbArg | CollectionReference<T>,\r\n ...path: string[]\r\n): DocumentReference<T> {\r\n // Forma doc(collectionRef, id?)\r\n if (\r\n typeof dbOrCollection === 'object' &&\r\n dbOrCollection !== null &&\r\n (dbOrCollection as CollectionReference<T>).__kind === 'collection'\r\n ) {\r\n const col = dbOrCollection as CollectionReference<T>;\r\n const id = path[0] ?? generateId();\r\n return makeDocRef<T>(col.db, col.id, id);\r\n }\r\n\r\n const resolved = resolveDb(dbOrCollection as DbArg);\r\n\r\n if (path.length >= 2) {\r\n return makeDocRef<T>(resolved, path[0], path.slice(1).join('/'));\r\n }\r\n if (path[0]?.includes('/')) {\r\n const parts = path[0].split('/');\r\n return makeDocRef<T>(resolved, parts[0], parts.slice(1).join('/') || generateId());\r\n }\r\n // doc(db, 'collection') — id auto-gerado\r\n return makeDocRef<T>(resolved, path[0] ?? '', generateId());\r\n}\r\n\r\nfunction makeDocRef<T>(db: NeetruDb, collectionName: string, id: string): DocumentReference<T> {\r\n return {\r\n type: 'document',\r\n __kind: 'document',\r\n id,\r\n collection: collectionName,\r\n path: `${collectionName}/${id}`,\r\n db,\r\n };\r\n}\r\n\r\n/**\r\n * Gera um id estilo Firestore (20 chars alfanuméricos) com aleatoriedade\r\n * CRIPTOGRÁFICA.\r\n *\r\n * Antes usava `Math.random()` — previsível e colisível (PRNG não-criptográfico,\r\n * estado recuperável). IDs de documento previsíveis permitem enumeração/forja.\r\n * Agora usa `globalThis.crypto.getRandomValues` (WebCrypto), disponível tanto em\r\n * Node ≥20 quanto em browsers — sem importar `node:crypto` (que quebraria o\r\n * bundle browser). Rejeita bytes ≥ 248 (4×62) para eliminar o viés de módulo\r\n * sobre o alfabeto de 62 caracteres.\r\n */\r\nfunction generateId(): string {\r\n const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; // 62\r\n const cryptoObj = (globalThis as { crypto?: Crypto }).crypto;\r\n if (!cryptoObj?.getRandomValues) {\r\n throw new Error(\r\n '[Neetru SDK / firestore-compat] WebCrypto indisponível: generateId() exige ' +\r\n 'globalThis.crypto.getRandomValues (Node ≥20 ou ambiente browser).',\r\n );\r\n }\r\n let out = '';\r\n while (out.length < 20) {\r\n const buf = new Uint8Array(20 - out.length);\r\n cryptoObj.getRandomValues(buf);\r\n for (const byte of buf) {\r\n if (byte < 248) {\r\n out += CHARS[byte % 62];\r\n if (out.length === 20) break;\r\n }\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// QUERY — acumula constraints e gera DbQuery quando consumido\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type QueryConstraintType = 'where' | 'orderBy' | 'limit';\r\n\r\nexport interface QueryConstraint {\r\n readonly __constraint: QueryConstraintType;\r\n}\r\n\r\ninterface WhereConstraint extends QueryConstraint {\r\n readonly __constraint: 'where';\r\n readonly field: string;\r\n readonly op: WhereFilterOp;\r\n readonly value: unknown;\r\n}\r\n\r\ninterface OrderByConstraint extends QueryConstraint {\r\n readonly __constraint: 'orderBy';\r\n readonly field: string;\r\n readonly direction: 'asc' | 'desc';\r\n}\r\n\r\ninterface LimitConstraint extends QueryConstraint {\r\n readonly __constraint: 'limit';\r\n readonly value: number;\r\n}\r\n\r\nexport interface Query<T = Record<string, unknown>> {\r\n readonly type: 'query';\r\n readonly __kind: 'query';\r\n readonly path: string;\r\n readonly collection: string;\r\n readonly db: NeetruDb;\r\n readonly ref: DbCollectionRef<T>;\r\n readonly constraints: QueryConstraint[];\r\n}\r\n\r\n/**\r\n * `query(collectionRef, ...constraints)` — combina `where`/`orderBy`/`limit`.\r\n * Aceita também uma `Query` como fonte (encadeamento de constraints).\r\n */\r\nexport function query<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T>,\r\n ...constraints: QueryConstraint[]\r\n): Query<T> {\r\n const base: Query<T> =\r\n source.__kind === 'query'\r\n ? source\r\n : {\r\n type: 'query',\r\n __kind: 'query',\r\n path: source.path,\r\n collection: source.id,\r\n db: source.db,\r\n ref: source.ref,\r\n constraints: [],\r\n };\r\n return { ...base, constraints: [...base.constraints, ...constraints] };\r\n}\r\n\r\nexport function where(field: string, op: WhereFilterOp, value: unknown): WhereConstraint {\r\n return { __constraint: 'where', field, op, value };\r\n}\r\n\r\nexport function orderBy(field: string, direction: 'asc' | 'desc' = 'asc'): OrderByConstraint {\r\n return { __constraint: 'orderBy', field, direction };\r\n}\r\n\r\nexport function limit(value: number): LimitConstraint {\r\n return { __constraint: 'limit', value };\r\n}\r\n\r\n/**\r\n * Converte os constraints de uma `Query` compat para o `DbQuery` nativo do SDK.\r\n *\r\n * Exportado para que os hooks React (`@neetru/sdk/react`) possam reusar a mesma\r\n * tradução — fonte única de verdade da conversão de constraints.\r\n */\r\nexport function toDbQuery(constraints: QueryConstraint[]): DbQuery {\r\n const whereFilters: DbWhereFilter[] = [];\r\n let orderByClause: DbQuery['orderBy'];\r\n let limitValue: number | undefined;\r\n\r\n for (const c of constraints) {\r\n if (c.__constraint === 'where') {\r\n const w = c as WhereConstraint;\r\n whereFilters.push({ field: w.field, op: normalizeOp(w.op), value: w.value });\r\n } else if (c.__constraint === 'orderBy') {\r\n const o = c as OrderByConstraint;\r\n orderByClause = { field: o.field, direction: o.direction };\r\n } else if (c.__constraint === 'limit') {\r\n limitValue = (c as LimitConstraint).value;\r\n }\r\n }\r\n\r\n const out: DbQuery = {};\r\n if (whereFilters.length > 0) out.where = whereFilters;\r\n if (orderByClause) out.orderBy = orderByClause;\r\n if (limitValue !== undefined) out.limit = limitValue;\r\n return out;\r\n}\r\n\r\n/**\r\n * O `DbWhereFilter` do SDK não declara `array-contains-any`/`not-in` no union.\r\n * Passamos o op como-está; o Core valida server-side e retorna erro explícito\r\n * se não suportado.\r\n *\r\n * Bug corrigido: remapeava 'array-contains-any' → 'array-contains' silenciosamente,\r\n * o que tem semântica completamente diferente (checa literal do array, não interseção)\r\n * e nunca retornava resultados para queries reais.\r\n */\r\nfunction normalizeOp(op: WhereFilterOp): DbWhereFilter['op'] {\r\n // Todas as ops passam direto; o Core valida server-side.\r\n return op as DbWhereFilter['op'];\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// SNAPSHOTS — emulam a API antiga do Firebase\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Remove o campo `id` dos dados (o Firestore expõe id via `.id`, não em `data()`). */\r\nfunction stripId<T>(raw: Record<string, unknown>): T {\r\n if (raw && typeof raw === 'object' && 'id' in raw) {\r\n const { id: _omit, ...rest } = raw;\r\n return rest as T;\r\n }\r\n return raw as T;\r\n}\r\n\r\nexport class DocumentSnapshot<T = Record<string, unknown>> {\r\n constructor(\r\n public readonly id: string,\r\n private readonly _data: T | null,\r\n public readonly ref?: DocumentReference<T>,\r\n ) {}\r\n\r\n exists(): boolean {\r\n return this._data != null;\r\n }\r\n\r\n data(): T | undefined {\r\n return this._data ?? undefined;\r\n }\r\n\r\n /** Compat: `snap.get('field')` lê um campo top-level. */\r\n get(field: string): unknown {\r\n return this._data ? (this._data as Record<string, unknown>)[field] : undefined;\r\n }\r\n}\r\n\r\nexport interface QueryDocumentSnapshot<T = Record<string, unknown>> {\r\n readonly id: string;\r\n data(): T;\r\n exists(): boolean;\r\n get(field: string): unknown;\r\n readonly ref: DocumentReference<T>;\r\n}\r\n\r\nexport class QuerySnapshot<T = Record<string, unknown>> {\r\n public readonly docs: Array<QueryDocumentSnapshot<T>>;\r\n public readonly empty: boolean;\r\n public readonly size: number;\r\n\r\n constructor(\r\n rows: Array<{ id: string; data: Record<string, unknown> }>,\r\n db?: NeetruDb,\r\n collectionName?: string,\r\n ) {\r\n this.docs = rows.map(({ id, data }) => {\r\n const stripped = stripId<T>(data);\r\n const ref: DocumentReference<T> | undefined =\r\n db && collectionName ? makeDocRef<T>(db, collectionName, id) : undefined;\r\n return {\r\n id,\r\n data: () => stripped,\r\n exists: () => true,\r\n get: (field: string) => (stripped as Record<string, unknown>)?.[field],\r\n // ref sempre presente quando db+collection conhecidos (caso comum).\r\n ref: ref as DocumentReference<T>,\r\n };\r\n });\r\n this.empty = this.docs.length === 0;\r\n this.size = this.docs.length;\r\n }\r\n\r\n forEach(cb: (doc: QueryDocumentSnapshot<T>) => void): void {\r\n this.docs.forEach(cb);\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// READS\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Extrai os dados crus do primeiro doc de um `DbGetResult`. */\r\nfunction firstDocData(result: DbGetResult<Record<string, unknown>> | null): {\r\n data: Record<string, unknown> | null;\r\n id: string | null;\r\n} {\r\n const first = result?.docs[0];\r\n return { data: first ? (first.data as Record<string, unknown>) : null, id: first?.id ?? null };\r\n}\r\n\r\nexport async function getDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n): Promise<DocumentSnapshot<T>> {\r\n const result = (await ref.db.collection(ref.collection).get(ref.id)) as DbGetResult<\r\n Record<string, unknown>\r\n > | null;\r\n const { data } = firstDocData(result);\r\n return new DocumentSnapshot<T>(\r\n ref.id,\r\n data ? (stripId<T>({ id: ref.id, ...data })) : null,\r\n ref,\r\n );\r\n}\r\n\r\n/** Mapeia um `DbListResult` para linhas `{ id, data }` (id garantido). */\r\nfunction listResultToRows(\r\n result: DbListResult<Record<string, unknown>>,\r\n): Array<{ id: string; data: Record<string, unknown> }> {\r\n return result.docs.map((d) => ({ id: d.id, data: { ...(d.data as Record<string, unknown>) } }));\r\n}\r\n\r\nexport async function getDocs<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T>,\r\n): Promise<QuerySnapshot<T>> {\r\n const dbQuery = source.__kind === 'query' ? toDbQuery(source.constraints) : undefined;\r\n const collectionName = source.__kind === 'query' ? source.collection : source.id;\r\n const result = (await source.db\r\n .collection(collectionName)\r\n .list(dbQuery)) as DbListResult<Record<string, unknown>>;\r\n return new QuerySnapshot<T>(listResultToRows(result), source.db, collectionName);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// WRITES\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface SetOptions {\r\n merge?: boolean;\r\n mergeFields?: string[];\r\n}\r\n\r\nexport async function setDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n options: SetOptions = {},\r\n): Promise<void> {\r\n const col = ref.db.collection(ref.collection);\r\n const payload = data as Record<string, unknown>;\r\n if (options.merge || (options.mergeFields && options.mergeFields.length > 0)) {\r\n // bug_0273e4dd: ANTES o check de existência usava o cache LOCAL (col.get é\r\n // offline-first). Doc que existia só no SERVIDOR dava docExists=false → caía\r\n // em col.set() (replace) e APAGAVA todos os campos ausentes do payload →\r\n // perda de dados silenciosa. Agora a decisão é DO SERVIDOR via merge-upsert:\r\n // update({upsert:true}) cria-se-ausente OU mescla-se-presente, sem nunca\r\n // apagar campos não enviados — exatamente o setDoc({merge:true}) do Firebase.\r\n // Contrato Core acordado (deploy0632): PATCH com upsert=true não 404a em\r\n // ausente e faz ref.set(data,{merge:true}).\r\n await col.update(ref.id, payload, { upsert: true });\r\n } else {\r\n await col.set(ref.id, payload);\r\n }\r\n}\r\n\r\nexport async function addDoc<T = Record<string, unknown>>(\r\n source: CollectionReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n): Promise<DocumentReference<T>> {\r\n const result = await source.db.collection(source.id).add(data as Record<string, unknown>);\r\n return makeDocRef<T>(source.db, source.id, result.id);\r\n}\r\n\r\nexport async function updateDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n): Promise<void> {\r\n await ref.db.collection(ref.collection).update(ref.id, data as Record<string, unknown>);\r\n}\r\n\r\nexport async function deleteDoc(ref: DocumentReference): Promise<void> {\r\n await ref.db.collection(ref.collection).remove(ref.id);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// WRITE BATCH\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\ninterface PendingBatchOp {\r\n collection: string;\r\n op:\r\n | { kind: 'set'; id: string; data: Record<string, unknown> }\r\n | { kind: 'update'; id: string; data: Record<string, unknown>; upsert?: boolean }\r\n | { kind: 'remove'; id: string };\r\n}\r\n\r\nexport class WriteBatch {\r\n private ops: PendingBatchOp[] = [];\r\n\r\n constructor(private readonly db: NeetruDb) {}\r\n\r\n set<T>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n options?: SetOptions,\r\n ): this {\r\n // bug_0273e4dd: SetOptions.merge era DESCARTADO → batch.set(ref,data,{merge:true})\r\n // virava replace e APAGAVA campos ausentes (perda de dados). Agora merge vira\r\n // update com upsert (merge-server-side, cria-se-ausente), igual ao setDoc.\r\n const merge = !!(options?.merge || (options?.mergeFields && options.mergeFields.length > 0));\r\n this.ops.push({\r\n collection: ref.collection,\r\n op: merge\r\n ? { kind: 'update', id: ref.id, data: data as Record<string, unknown>, upsert: true }\r\n : { kind: 'set', id: ref.id, data: data as Record<string, unknown> },\r\n });\r\n return this;\r\n }\r\n\r\n update<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>): this {\r\n this.ops.push({\r\n collection: ref.collection,\r\n op: { kind: 'update', id: ref.id, data: data as Record<string, unknown> },\r\n });\r\n return this;\r\n }\r\n\r\n delete(ref: DocumentReference): this {\r\n this.ops.push({ collection: ref.collection, op: { kind: 'remove', id: ref.id } });\r\n return this;\r\n }\r\n\r\n async commit(): Promise<void> {\r\n // Agrupa por coleção — o `DbCollectionRef.batch` opera numa coleção por vez.\r\n const byCol = new Map<string, PendingBatchOp['op'][]>();\r\n for (const { collection: name, op } of this.ops) {\r\n const list = byCol.get(name) ?? [];\r\n list.push(op);\r\n byCol.set(name, list);\r\n }\r\n for (const [name, ops] of byCol.entries()) {\r\n // O union nativo `DbBatchOp` casa 1:1 com nossas variantes (set/update/remove).\r\n await this.db.collection(name).batch(ops as never);\r\n }\r\n this.ops = [];\r\n }\r\n}\r\n\r\nexport function writeBatch(db: DbArg): WriteBatch {\r\n return new WriteBatch(resolveDb(db));\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// runTransaction — emulação otimista (read → write) sobre client.db\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Handle de transação compat. O SDK não expõe transações multi-doc atômicas\r\n * com lock server-side, então emulamos a forma do Firebase: `tx.get` lê do\r\n * `client.db`, e `tx.set/update/delete` enfileiram escritas que são aplicadas\r\n * no fim do callback.\r\n *\r\n * ⚠️ **LIMITAÇÃO CONHECIDA — sem isolamento server-side (lost-update silencioso):**\r\n *\r\n * Diferente do Firestore nativo (que usa serializable snapshot isolation com\r\n * contention detection), este shim executa reads e writes em rounds separados\r\n * sem garantia de atomicidade:\r\n *\r\n * 1. `tx.get(ref)` faz um read imediato via `client.db`.\r\n * 2. As escritas (`tx.set/update/delete`) são **enfileiradas** — não bloqueiam\r\n * outros writers.\r\n * 3. No fim do callback, `batch.commit()` aplica as escritas. Se outro processo\r\n * tiver modificado o mesmo documento entre o read e o commit, a escrita\r\n * será um **lost-update silencioso** (sem erro, sem retry automático).\r\n *\r\n * **Quando isso importa:** counters, saldos, estoques — qualquer dado\r\n * que precise de leitura-modificação-escrita atômica.\r\n *\r\n * **Alternativas seguras:**\r\n * - Use **Server Actions no Core** (`src/lib/actions/`) que rodam dentro\r\n * de transações Firestore Admin SDK reais (com locks server-side).\r\n * - Use `increment()` sentinel para contadores numéricos — server-side atomic.\r\n * - Use `arrayUnion()`/`arrayRemove()` para sets (também server-side atomic).\r\n *\r\n * Esta emulação é aceitável para migrações transitórias e para operações\r\n * onde a probabilidade de concorrência é baixa (ex: dados de um único usuário\r\n * sem write concorrente). **Não use para dados financeiros ou de inventário.**\r\n */\r\nexport interface Transaction {\r\n get<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>>;\r\n set<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>, options?: SetOptions): Transaction;\r\n update<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>): Transaction;\r\n delete(ref: DocumentReference): Transaction;\r\n}\r\n\r\n/** Flag para emitir o aviso de limitação uma só vez por processo (não spammar). */\r\nlet _runTransactionWarnEmitted = false;\r\n\r\nexport async function runTransaction<R>(\r\n db: DbArg,\r\n updateFunction: (transaction: Transaction) => Promise<R>,\r\n): Promise<R> {\r\n // Aviso em desenvolvimento/staging: sem isolamento server-side.\r\n if (!_runTransactionWarnEmitted) {\r\n _runTransactionWarnEmitted = true;\r\n console.warn(\r\n '[Neetru SDK / firestore-compat] runTransaction(): esta emulação NÃO oferece' +\r\n ' isolamento server-side. Reads e writes são executados em rounds separados;' +\r\n ' um write concorrente entre o get() e o commit() resultará em lost-update' +\r\n ' silencioso. Para atomicidade real, use Server Actions no Core ou sentinels' +\r\n ' increment()/arrayUnion(). Veja a doc em src/firestore-compat.ts § runTransaction.',\r\n );\r\n }\r\n\r\n const resolved = resolveDb(db);\r\n const batch = new WriteBatch(resolved);\r\n\r\n const tx: Transaction = {\r\n async get<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>> {\r\n return getDoc(ref);\r\n },\r\n set(ref, data, options) {\r\n batch.set(ref, data, options);\r\n return tx;\r\n },\r\n update(ref, data) {\r\n batch.update(ref, data);\r\n return tx;\r\n },\r\n delete(ref) {\r\n batch.delete(ref);\r\n return tx;\r\n },\r\n };\r\n\r\n const result = await updateFunction(tx);\r\n await batch.commit();\r\n return result;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// onSnapshot — realtime via client.db (com fallback inicial)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type Unsubscribe = () => void;\r\n\r\ntype DocOnNext<T> = (snap: DocumentSnapshot<T>) => void;\r\ntype QueryOnNext<T> = (snap: QuerySnapshot<T>) => void;\r\ntype OnError = (err: Error) => void;\r\n\r\nexport function onSnapshot<T>(\r\n ref: DocumentReference<T>,\r\n onNext: DocOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe;\r\nexport function onSnapshot<T>(\r\n source: Query<T> | CollectionReference<T>,\r\n onNext: QueryOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe;\r\nexport function onSnapshot<T>(\r\n source: DocumentReference<T> | Query<T> | CollectionReference<T>,\r\n onNext: DocOnNext<T> | QueryOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe {\r\n // Documento único\r\n if (source.__kind === 'document') {\r\n const ref = source as DocumentReference<T>;\r\n const docRef = ref.db.collection(ref.collection).doc(ref.id);\r\n try {\r\n return docRef.onSnapshot((snap) => {\r\n const data = snap?.docs[0]?.data as Record<string, unknown> | undefined;\r\n (onNext as DocOnNext<T>)(\r\n new DocumentSnapshot<T>(\r\n ref.id,\r\n data ? stripId<T>({ id: ref.id, ...data }) : null,\r\n ref,\r\n ),\r\n );\r\n });\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n return () => {};\r\n }\r\n }\r\n\r\n // Coleção / Query\r\n const collectionName = source.__kind === 'query' ? source.collection : (source as CollectionReference<T>).id;\r\n const dbQuery = source.__kind === 'query' ? toDbQuery(source.constraints) : undefined;\r\n const col = source.db.collection(collectionName);\r\n try {\r\n return col.onSnapshot(dbQuery, (snap: DbListResult<Record<string, unknown>>) => {\r\n (onNext as QueryOnNext<T>)(\r\n new QuerySnapshot<T>(listResultToRows(snap), source.db, collectionName),\r\n );\r\n });\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n return () => {};\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Timestamp — classe leve compat\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Reimplementação compat de `Timestamp` do Firestore. Sem proto do Firebase.\r\n * Cobre `seconds`, `nanoseconds`, `toDate()`, `toMillis()`, `isEqual()`,\r\n * `valueOf()`, `toJSON()` + estáticos `now()`, `fromDate()`, `fromMillis()`.\r\n */\r\nexport class Timestamp {\r\n constructor(\r\n public readonly seconds: number,\r\n public readonly nanoseconds: number,\r\n ) {}\r\n\r\n static now(): Timestamp {\r\n return Timestamp.fromMillis(Date.now());\r\n }\r\n\r\n static fromDate(date: Date): Timestamp {\r\n return Timestamp.fromMillis(date.getTime());\r\n }\r\n\r\n static fromMillis(millis: number): Timestamp {\r\n const seconds = Math.floor(millis / 1000);\r\n const nanoseconds = (millis - seconds * 1000) * 1e6;\r\n return new Timestamp(seconds, nanoseconds);\r\n }\r\n\r\n toDate(): Date {\r\n return new Date(this.toMillis());\r\n }\r\n\r\n toMillis(): number {\r\n return this.seconds * 1000 + Math.floor(this.nanoseconds / 1e6);\r\n }\r\n\r\n isEqual(other: Timestamp): boolean {\r\n return this.seconds === other.seconds && this.nanoseconds === other.nanoseconds;\r\n }\r\n\r\n valueOf(): string {\r\n // Estável para ordenação por string (espelha Firebase).\r\n return `${this.seconds.toString().padStart(12, '0')}.${this.nanoseconds\r\n .toString()\r\n .padStart(9, '0')}`;\r\n }\r\n\r\n toJSON(): { seconds: number; nanoseconds: number } {\r\n return { seconds: this.seconds, nanoseconds: this.nanoseconds };\r\n }\r\n\r\n toString(): string {\r\n return `Timestamp(seconds=${this.seconds}, nanoseconds=${this.nanoseconds})`;\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// FieldValue + sentinels\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Sentinel de `arrayUnion`/`arrayRemove`. O SDK ainda não tem sentinel de array\r\n * server-side, então emitimos um marker namespaceado que o Core reconhece\r\n * (mesma família de `__neetru__`). Se o backing não suportar, o produto deve\r\n * preferir read-modify-write via Server Action. Documentado como compat.\r\n */\r\nexport interface ArrayUnionSentinel {\r\n readonly __neetru__: 'arrayUnion';\r\n readonly elements: unknown[];\r\n}\r\nexport interface ArrayRemoveSentinel {\r\n readonly __neetru__: 'arrayRemove';\r\n readonly elements: unknown[];\r\n}\r\n\r\n/**\r\n * Sentinel de campo a deletar (compat `deleteField()`).\r\n */\r\nexport interface DeleteFieldSentinel {\r\n readonly __neetru__: 'deleteField';\r\n}\r\n\r\n/**\r\n * `serverTimestamp()` — delega ao sentinel do SDK (resolvido SERVER-SIDE).\r\n * NÃO retorna mais ISO/Date do cliente (que era spoofável).\r\n */\r\nexport function serverTimestamp(): FieldSentinel {\r\n return sdkServerTimestamp();\r\n}\r\n\r\n/**\r\n * `increment(n)` — incremento atômico server-side (delega ao sentinel do SDK).\r\n */\r\nexport function increment(n: number): FieldSentinel {\r\n return sdkIncrement(n);\r\n}\r\n\r\n/** `arrayUnion(...elements)` — sentinel compat (resolvido pelo Core). */\r\nexport function arrayUnion(...elements: unknown[]): ArrayUnionSentinel {\r\n return { __neetru__: 'arrayUnion', elements };\r\n}\r\n\r\n/** `arrayRemove(...elements)` — sentinel compat (resolvido pelo Core). */\r\nexport function arrayRemove(...elements: unknown[]): ArrayRemoveSentinel {\r\n return { __neetru__: 'arrayRemove', elements };\r\n}\r\n\r\n/** `deleteField()` — sentinel compat para remover um campo. */\r\nexport function deleteField(): DeleteFieldSentinel {\r\n return { __neetru__: 'deleteField' };\r\n}\r\n\r\n/**\r\n * Namespace estático `FieldValue` — espelha `firebase/firestore`'s `FieldValue`.\r\n * Os métodos delegam às funções modulares acima.\r\n *\r\n * @example\r\n * ```ts\r\n * import { FieldValue } from '@neetru/sdk/firestore-compat';\r\n * await updateDoc(ref, {\r\n * tags: FieldValue.arrayUnion('novo'),\r\n * counter: FieldValue.increment(1),\r\n * updatedAt: FieldValue.serverTimestamp(),\r\n * });\r\n * ```\r\n */\r\nexport const FieldValue = {\r\n serverTimestamp,\r\n increment,\r\n arrayUnion,\r\n arrayRemove,\r\n delete: deleteField,\r\n} as const;\r\n"]}
package/dist/index.cjs CHANGED
@@ -448,7 +448,7 @@ __export(sql_client_exports, {
448
448
  function mapEnvToCore(sdkEnv) {
449
449
  if (sdkEnv === "workspace") return "staging";
450
450
  if (sdkEnv === "prod") return "production";
451
- return "dev";
451
+ return "dev-local";
452
452
  }
453
453
  function createHttpLeaseFetcher(config) {
454
454
  return async (opts) => {
@@ -957,7 +957,11 @@ function createTelemetryNamespace(config) {
957
957
  path: "/api/sdk/v1/telemetry/event",
958
958
  body,
959
959
  requireAuth: true,
960
- retries: 1
960
+ retries: 1,
961
+ // caça-bruxas: retry sem Idempotency-Key duplicava o evento se o
962
+ // servidor já tinha persistido antes da falha transiente. Mesmo
963
+ // padrão do usage.report (key estável por chamada, reusada no retry).
964
+ idempotencyKey: true
961
965
  });
962
966
  } catch (err) {
963
967
  if (typeof console !== "undefined") {
@@ -1161,8 +1165,8 @@ function createUsageNamespace(config) {
1161
1165
  if (!resource || typeof resource !== "string") {
1162
1166
  throw new exports.NeetruError("validation_failed", "resource is required");
1163
1167
  }
1164
- if (!Number.isFinite(qty) || qty <= 0) {
1165
- throw new exports.NeetruError("validation_failed", "qty must be positive integer");
1168
+ if (!Number.isFinite(qty) || qty <= 0 || !Number.isInteger(qty)) {
1169
+ throw new exports.NeetruError("validation_failed", "qty must be a positive integer");
1166
1170
  }
1167
1171
  const productId = options?.productId ?? config.productId;
1168
1172
  const tenantId = options?.tenantId ?? config.tenantId;
@@ -1411,7 +1415,12 @@ function evaluateFilter(data, filter) {
1411
1415
  if (!Array.isArray(value)) return true;
1412
1416
  return !value.includes(fieldValue);
1413
1417
  default:
1414
- return false;
1418
+ if (op === "array-contains-any") {
1419
+ return Array.isArray(fieldValue) && Array.isArray(value) && value.some((v) => fieldValue.includes(v));
1420
+ }
1421
+ throw new Error(
1422
+ `QueryEngine: operador de filtro '${String(op)}' n\xE3o suportado em leitura offline/cache. Use um operador suportado (==, !=, <, <=, >, >=, array-contains, array-contains-any, in, not-in) ou fa\xE7a a query server-side.`
1423
+ );
1415
1424
  }
1416
1425
  }
1417
1426
  function matchesAllFilters(data, filters) {
@@ -1941,7 +1950,7 @@ var MutationQueue = class {
1941
1950
  * - `{ removed: true }` — a mutação existente deve ser deletada (add+remove)
1942
1951
  * - `null` — não é possível coalescir
1943
1952
  */
1944
- tryCoalesce(existing, newOp, newPayload, newBatchId) {
1953
+ tryCoalesce(existing, newOp, newPayload, newBatchId, newUpsert) {
1945
1954
  if (existing.batchId !== newBatchId) return null;
1946
1955
  if (existing.status === "inflight") return null;
1947
1956
  const existingOp = existing.op;
@@ -1949,10 +1958,12 @@ var MutationQueue = class {
1949
1958
  return { removed: true };
1950
1959
  }
1951
1960
  if (existingOp === "update" && newOp === "update") {
1961
+ const mergedUpsert = existing.upsert || newUpsert;
1952
1962
  return {
1953
1963
  coalesced: {
1954
1964
  ...existing,
1955
- payload: { ...existing.payload ?? {}, ...newPayload ?? {} }
1965
+ payload: { ...existing.payload ?? {}, ...newPayload ?? {} },
1966
+ ...mergedUpsert ? { upsert: true } : {}
1956
1967
  }
1957
1968
  };
1958
1969
  }
@@ -2001,13 +2012,13 @@ var MutationQueue = class {
2001
2012
  * Atômico: a persistência final é uma única operação putMutation.
2002
2013
  */
2003
2014
  async enqueue(params) {
2004
- const { collection, op, payload, baseVersion, batchId } = params;
2015
+ const { collection, op, payload, baseVersion, batchId, upsert } = params;
2005
2016
  const docId = params.docId ?? (op === "add" ? generateUUIDv4() : "");
2006
2017
  await this.syncSeq();
2007
2018
  const existingMutations = await this.store.listMutations({ collection, docId });
2008
2019
  const lastQueued = existingMutations.filter((m) => m.status === "queued").sort((a, b) => b.seq - a.seq)[0];
2009
2020
  if (lastQueued !== void 0) {
2010
- const coalesceResult = this.tryCoalesce(lastQueued, op, payload, batchId);
2021
+ const coalesceResult = this.tryCoalesce(lastQueued, op, payload, batchId, upsert);
2011
2022
  if (coalesceResult !== null) {
2012
2023
  if ("removed" in coalesceResult) {
2013
2024
  await this.store.deleteMutation(lastQueued.mutationId);
@@ -2045,7 +2056,9 @@ var MutationQueue = class {
2045
2056
  attempts: 0,
2046
2057
  lastError: null,
2047
2058
  status: "queued",
2048
- batchId
2059
+ batchId,
2060
+ // bug_0273e4dd: só persiste o flag quando verdadeiro (mantém o doc limpo).
2061
+ ...upsert ? { upsert: true } : {}
2049
2062
  };
2050
2063
  await this.store.putMutation(mutation);
2051
2064
  return mutation;
@@ -2385,10 +2398,15 @@ var SyncEngine = class {
2385
2398
  await this._applyRejected(mut, res, busChanges);
2386
2399
  }
2387
2400
  }
2401
+ const processedIds = new Set(result.results.map((r) => r.mutationId));
2402
+ const unprocessed = mutations.filter((m) => !processedIds.has(m.mutationId));
2403
+ for (const mut of unprocessed) {
2404
+ await this._queue.markRetry(mut.mutationId, "partial_push: muta\xE7\xE3o n\xE3o processada no lote");
2405
+ }
2388
2406
  if (busChanges.length > 0) {
2389
2407
  this._bus.emit(busChanges);
2390
2408
  }
2391
- return true;
2409
+ return unprocessed.length === 0;
2392
2410
  }
2393
2411
  /** Atualiza o cache após confirmação de escrita (outcome=confirmed). */
2394
2412
  async _applySyncedDoc(collection, docId, serverVersion, serverTimestamp2, mut, busChanges) {
@@ -3695,7 +3713,7 @@ var DbCollectionRefImpl = class {
3695
3713
  }]);
3696
3714
  return { ok: true };
3697
3715
  }
3698
- async update(id, data) {
3716
+ async update(id, data, opts) {
3699
3717
  assertValidId(id);
3700
3718
  const existing = await this._store.getDoc(this._collection, id);
3701
3719
  const rawData = data;
@@ -3708,7 +3726,9 @@ var DbCollectionRefImpl = class {
3708
3726
  op: "update",
3709
3727
  payload: rawData,
3710
3728
  baseVersion: existing?.meta.serverVersion ?? null,
3711
- batchId: null
3729
+ batchId: null,
3730
+ // bug_0273e4dd: propaga merge-upsert (setDoc({merge:true}) cria-se-ausente).
3731
+ upsert: opts?.upsert
3712
3732
  });
3713
3733
  await this._store.putDoc({
3714
3734
  collection: this._collection,
@@ -3832,7 +3852,9 @@ var DbCollectionRefImpl = class {
3832
3852
  op: "update",
3833
3853
  payload: data,
3834
3854
  baseVersion: existing?.meta.serverVersion ?? null,
3835
- batchId
3855
+ batchId,
3856
+ // bug_0273e4dd: propaga merge-upsert (writeBatch.set(merge) cria-se-ausente).
3857
+ upsert: op.upsert
3836
3858
  });
3837
3859
  await this._store.putDoc({
3838
3860
  collection,
@@ -4094,6 +4116,10 @@ async function createOfflineDocumentsNamespace(opts) {
4094
4116
  connectivity,
4095
4117
  periodicSyncIntervalMs: opts.periodicSyncIntervalMs ?? 5 * 60 * 1e3
4096
4118
  });
4119
+ connectivity.start();
4120
+ if (!opts.singleTab && tabCoordinatorLike instanceof TabCoordinator) {
4121
+ tabCoordinatorLike.start();
4122
+ }
4097
4123
  return new NeetruDbDocumentsImpl({
4098
4124
  store,
4099
4125
  queue,
@@ -4563,11 +4589,11 @@ function createRestSyncTransport(config) {
4563
4589
  let raw;
4564
4590
  if (m.op === "add") {
4565
4591
  raw = await httpRequest2(config, {
4566
- method: "POST",
4567
- path: DATASTORE_COLLECTION_ENDPOINT(m.collection),
4592
+ method: "PUT",
4593
+ path: DATASTORE_DOC_ENDPOINT(m.collection, m.docId),
4568
4594
  body: { data: m.payload ?? {} },
4569
4595
  requireAuth: true,
4570
- retries: 0,
4596
+ retries: 2,
4571
4597
  headers: tenantHeaders
4572
4598
  });
4573
4599
  } else if (m.op === "set") {
@@ -4583,7 +4609,7 @@ function createRestSyncTransport(config) {
4583
4609
  raw = await httpRequest2(config, {
4584
4610
  method: "PATCH",
4585
4611
  path: DATASTORE_DOC_ENDPOINT(m.collection, m.docId),
4586
- body: { data: m.payload ?? {} },
4612
+ body: { data: m.payload ?? {}, ...m.upsert ? { upsert: true } : {} },
4587
4613
  requireAuth: true,
4588
4614
  retries: 2,
4589
4615
  headers: tenantHeaders
@@ -4606,7 +4632,24 @@ function createRestSyncTransport(config) {
4606
4632
  });
4607
4633
  } catch (err) {
4608
4634
  const msg = err instanceof Error ? err.message : String(err);
4609
- throw new exports.NeetruDbError("db_unavailable", `pushMutations falhou: ${msg}`);
4635
+ const code = err?.code;
4636
+ const status = err?.status;
4637
+ const isPermanent = code === "forbidden" || code === "unauthorized" || code === "not_found" || code === "conflict" || code === "validation_failed" || typeof status === "number" && status >= 400 && status < 500 && status !== 408 && status !== 429;
4638
+ if (isPermanent) {
4639
+ const reason = code === "forbidden" || code === "unauthorized" ? "rejected_permission" : "rejected_validation";
4640
+ results.push({
4641
+ mutationId: m.mutationId,
4642
+ outcome: "rejected",
4643
+ reason,
4644
+ serverVersion: null,
4645
+ serverData: null
4646
+ });
4647
+ continue;
4648
+ }
4649
+ if (results.length === 0) {
4650
+ throw new exports.NeetruDbError("db_unavailable", `pushMutations falhou: ${msg}`);
4651
+ }
4652
+ break;
4610
4653
  }
4611
4654
  }
4612
4655
  return { results };
@@ -6520,15 +6563,15 @@ function createOidcAuthNamespace(config) {
6520
6563
  const overrideAuthUrl = typeof globalThis.NEETRU_AUTH_URL === "string" ? globalThis.NEETRU_AUTH_URL : null;
6521
6564
  const idpOrigin = overrideAuthUrl ?? config.baseUrl.replace(/^https?:\/\/api\./, "https://auth.");
6522
6565
  const issuer = idpOrigin;
6523
- const jwksUrl = `${idpOrigin}/api/v1/oauth/.well-known/jwks.json`;
6566
+ const jwksUrl = `${idpOrigin}/api/v1/oauth/.well-known/jwks`;
6524
6567
  const cacheTtlMs = options?.jwksCacheTtlMs ?? DEFAULT_JWKS_TTL_MS;
6525
6568
  const audienceFromOptions = options?.audience;
6526
6569
  const parsedKey = tryParseApiKey(config.apiKey);
6527
- const audience = audienceFromOptions ?? parsedKey?.keyId;
6570
+ const audience = audienceFromOptions ?? config.oidcClientId ?? parsedKey?.keyId;
6528
6571
  if (!audience) {
6529
6572
  throw new exports.NeetruError(
6530
6573
  "invalid_config",
6531
- "verifyToken requires an audience: provide apiKey on the client or pass options.audience. Calling jwtVerify without an audience allows tokens issued for other OIDC clients to pass."
6574
+ "verifyToken requires an audience: provide oidcClientId/apiKey on the client or pass options.audience. Calling jwtVerify without an audience allows tokens issued for other OIDC clients to pass."
6532
6575
  );
6533
6576
  }
6534
6577
  const jwks = _getJwks(jwksUrl, cacheTtlMs);
@@ -6611,7 +6654,7 @@ function createNeetruClient(config = {}) {
6611
6654
  init_errors();
6612
6655
  init_db_errors();
6613
6656
  init_http();
6614
- var VERSION = "3.1.6";
6657
+ var VERSION = "3.1.9";
6615
6658
 
6616
6659
  exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
6617
6660
  exports.DEV_FIXTURE_USER = DEV_FIXTURE_USER;