@agent-native/core 0.116.0 → 0.117.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +15 -0
  3. package/corpus/core/package.json +4 -1
  4. package/corpus/core/src/client/analytics.ts +7 -0
  5. package/corpus/core/src/client/use-db-sync.ts +354 -15
  6. package/corpus/core/src/collab/client.ts +26 -2
  7. package/corpus/core/src/db/ddl-guard.ts +49 -10
  8. package/corpus/core/src/deploy/build.ts +25 -2
  9. package/corpus/core/src/realtime-protocol.ts +92 -0
  10. package/corpus/core/src/server/core-routes-plugin.ts +7 -0
  11. package/corpus/core/src/server/poll-events.ts +29 -8
  12. package/corpus/core/src/server/poll.ts +1123 -865
  13. package/corpus/core/src/server/realtime-token.ts +106 -0
  14. package/corpus/core/src/server/sentry-config.ts +40 -0
  15. package/corpus/core/src/server/short-lived-token.ts +156 -0
  16. package/corpus/core/src/server/sse.ts +15 -0
  17. package/corpus/core/src/server/ssr-handler.ts +9 -3
  18. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +12 -0
  19. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  20. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +566 -299
  21. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +14 -0
  22. package/corpus/templates/content/app/i18n-data.ts +90 -0
  23. package/dist/client/analytics.d.ts +10 -0
  24. package/dist/client/analytics.d.ts.map +1 -1
  25. package/dist/client/analytics.js.map +1 -1
  26. package/dist/client/use-db-sync.d.ts +1 -1
  27. package/dist/client/use-db-sync.d.ts.map +1 -1
  28. package/dist/client/use-db-sync.js +315 -15
  29. package/dist/client/use-db-sync.js.map +1 -1
  30. package/dist/collab/client.d.ts.map +1 -1
  31. package/dist/collab/client.js +26 -2
  32. package/dist/collab/client.js.map +1 -1
  33. package/dist/db/ddl-guard.d.ts +7 -2
  34. package/dist/db/ddl-guard.d.ts.map +1 -1
  35. package/dist/db/ddl-guard.js +21 -9
  36. package/dist/db/ddl-guard.js.map +1 -1
  37. package/dist/deploy/build.d.ts.map +1 -1
  38. package/dist/deploy/build.js +25 -2
  39. package/dist/deploy/build.js.map +1 -1
  40. package/dist/notifications/routes.d.ts +3 -3
  41. package/dist/observability/routes.d.ts +5 -5
  42. package/dist/progress/routes.d.ts +1 -1
  43. package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
  44. package/dist/provider-api/actions/provider-api.d.ts +6 -6
  45. package/dist/realtime-protocol.d.ts +40 -0
  46. package/dist/realtime-protocol.d.ts.map +1 -0
  47. package/dist/realtime-protocol.js +69 -0
  48. package/dist/realtime-protocol.js.map +1 -0
  49. package/dist/resources/handlers.d.ts +1 -1
  50. package/dist/secrets/routes.d.ts +3 -3
  51. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  52. package/dist/server/core-routes-plugin.js +3 -0
  53. package/dist/server/core-routes-plugin.js.map +1 -1
  54. package/dist/server/poll-events.d.ts +7 -1
  55. package/dist/server/poll-events.d.ts.map +1 -1
  56. package/dist/server/poll-events.js +24 -7
  57. package/dist/server/poll-events.js.map +1 -1
  58. package/dist/server/poll.d.ts +223 -42
  59. package/dist/server/poll.d.ts.map +1 -1
  60. package/dist/server/poll.js +849 -737
  61. package/dist/server/poll.js.map +1 -1
  62. package/dist/server/realtime-token.d.ts +34 -0
  63. package/dist/server/realtime-token.d.ts.map +1 -0
  64. package/dist/server/realtime-token.js +85 -0
  65. package/dist/server/realtime-token.js.map +1 -0
  66. package/dist/server/sentry-config.d.ts +12 -0
  67. package/dist/server/sentry-config.d.ts.map +1 -1
  68. package/dist/server/sentry-config.js +34 -0
  69. package/dist/server/sentry-config.js.map +1 -1
  70. package/dist/server/short-lived-token.d.ts +46 -0
  71. package/dist/server/short-lived-token.d.ts.map +1 -1
  72. package/dist/server/short-lived-token.js +104 -0
  73. package/dist/server/short-lived-token.js.map +1 -1
  74. package/dist/server/sse.d.ts +1 -1
  75. package/dist/server/sse.d.ts.map +1 -1
  76. package/dist/server/sse.js +4 -0
  77. package/dist/server/sse.js.map +1 -1
  78. package/dist/server/ssr-handler.d.ts.map +1 -1
  79. package/dist/server/ssr-handler.js +5 -3
  80. package/dist/server/ssr-handler.js.map +1 -1
  81. package/dist/server/transcribe-voice.d.ts +1 -1
  82. package/package.json +4 -1
  83. package/src/client/analytics.ts +7 -0
  84. package/src/client/use-db-sync.ts +354 -15
  85. package/src/collab/client.ts +26 -2
  86. package/src/db/ddl-guard.ts +49 -10
  87. package/src/deploy/build.ts +25 -2
  88. package/src/realtime-protocol.ts +92 -0
  89. package/src/server/core-routes-plugin.ts +7 -0
  90. package/src/server/poll-events.ts +29 -8
  91. package/src/server/poll.ts +1123 -865
  92. package/src/server/realtime-token.ts +106 -0
  93. package/src/server/sentry-config.ts +40 -0
  94. package/src/server/short-lived-token.ts +156 -0
  95. package/src/server/sse.ts +15 -0
  96. package/src/server/ssr-handler.ts +9 -3
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/collab/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EACL,wBAAwB,GAEzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAkB,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EACL,wBAAwB,EACxB,YAAY,EACZ,WAAW,EACX,qBAAqB,GAEtB,MAAM,iCAAiC,CAAC;AAoCzC,SAAS,gBAAgB;IACvB,OAAO,CACL,OAAO,QAAQ,KAAK,WAAW,IAAI,QAAQ,CAAC,eAAe,KAAK,QAAQ,CACzE,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,gBAAgB,CAAC,CAAe,EAAE,CAAe;IACxD,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QACnB,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QACpB,IACE,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK;YAC1B,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI;YACxB,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,EAC1B,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAOD,MAAM,UAAU,8BAA8B,CAC5C,MAA4B,EAC5B,aAAqB,EACrB,YAAuC;IAEvC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;QAClC,IACE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC;YACjC,MAAM,CAAC,QAAQ,KAAK,aAAa,EACjC,CAAC;YACD,SAAS;QACX,CAAC;QACD,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1C,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC;IAED,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;QACjD,IAAI,QAAQ,KAAK,aAAa;YAAE,SAAS;QACzC,IAAI,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,SAAS;QACrC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACrC,CAAC;AAED,iBAAiB;AACjB,SAAS,kBAAkB,CAAC,GAAe;IACzC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAW;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,iEAAiE;AACjE,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAE9B,4EAA4E;AAC5E,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAEvC,mEAAmE;AACnE,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAElC,kEAAkE;AAClE,MAAM,eAAe,GAAG,GAAG,CAAC;AAC5B,MAAM,cAAc,GAAG,MAAM,CAAC;AAE9B,SAAS,WAAW,CAAC,iBAAyB;IAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACjD,mBAAmB;IACnB,MAAM,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AAClD,CAAC;AAED,8EAA8E;AAC9E,yEAAyE;AACzE,8EAA8E;AAC9E,8EAA8E;AAE9E,MAAM,wBAAwB,GAAG,IAAI,GAAG,EAGrC,CAAC;AAEJ,SAAS,qBAAqB,CAC5B,OAAe,EACf,KAAa,EACb,QAAgB,EAChB,QAA8C;IAE9C,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO;IAC1C,MAAM,GAAG,GAAG,GAAG,KAAK,KAAK,QAAQ,EAAE,CAAC;IACpC,IAAI,wBAAwB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,oBAAoB;IAEnE,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;QAC5B,wBAAwB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;QACzB,KAAK,CAAC,GAAG,OAAO,IAAI,KAAK,YAAY,EAAE;YACrC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,QAAQ;gBACR,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI;aAC5C,CAAC;SACH,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC,mDAAmD;IACzE,CAAC,EAAE,GAAG,CAAC,CAAC;IAER,wBAAwB,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAC3C,CAAC;AAqDD,MAAM,cAAc,GAAsB,MAAM,CAAC,MAAM,CAAC;IACtD,SAAS,EAAE,KAAK;IAChB,QAAQ,EAAE,KAAK;IACf,WAAW,EAAE,EAAE;IACf,WAAW,EAAE,KAAK;IAClB,YAAY,EAAE,KAAK;CACpB,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,MAAM,mBAAmB;IAiCZ,KAAK;IACG,OAAO;IAOf,QAAQ;IAxCV,IAAI,CAAQ;IACZ,SAAS,CAAY;IAC9B,4EAA4E;IAC5E,QAAQ,CAAoB;IAC5B,QAAQ,GAAG,KAAK,CAAC;IAET,WAAW,GAAG,IAAI,GAAG,EAAiC,CAAC;IACvD,YAAY,GAAyC,IAAI,CAAC;IAC1D,OAAO,GAAG,KAAK,CAAC;IAChB,UAAU,GAAG,KAAK,CAAC;IACnB,aAAa,CAAqB;IAClC,WAAW,GAAsB,IAAI,CAAC;IAE9C,qEAAqE;IAC7D,cAAc,GAAiB,EAAE,CAAC;IAClC,UAAU,GAAyC,IAAI,CAAC;IACxD,qBAAqB,GAAG,KAAK,CAAC;IAEtC,6BAA6B;IACrB,UAAU,GAAG,KAAK,CAAC;IACnB,SAAS,GAAyC,IAAI,CAAC;IACvD,iBAAiB,GAAG,CAAC,CAAC;IACtB,cAAc,GAAG,CAAC,CAAC;IACnB,WAAW,GAAG,CAAC,CAAC;IAChB,iBAAiB,GAAG,CAAC,CAAC;IACtB,SAAS,GAAG,KAAK,CAAC;IAClB,sBAAsB,GAAmB,IAAI,CAAC;IAC9C,uBAAuB,GAAwB,IAAI,CAAC;IACpD,0BAA0B,GAAwB,IAAI,CAAC;IACvD,UAAU,GAAyC,IAAI,CAAC;IAEhE,YACW,KAAa,EACL,OAAe;IAChC;;;;;OAKG;IACM,QAAQ,GAAG,KAAK;qBARhB,KAAK;uBACG,OAAO;wBAOf,QAAQ;QAEjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC,SAAS,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,GAAG;YACd,SAAS,EAAE,IAAI;YACf,QAAQ,EAAE,KAAK;YACf,WAAW,EAAE,EAAE;YACf,WAAW,EAAE,KAAK;YAClB,YAAY,EAAE,KAAK;SACpB,CAAC;QAEF,wEAAwE;QACxE,sEAAsE;QACtE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,qBAAqB,CAAC,CAAC;QAExD,wEAAwE;QACxE,uEAAuE;QACvE,qEAAqE;QACrE,qBAAqB;QACrB,IAAI,CAAC,QAAQ;YAAE,IAAI,CAAC,eAAe,EAAE,CAAC;IACxC,CAAC;IAED,IAAY,WAAW;QACrB,OAAO,iBAAiB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACrD,CAAC;IAED,4EAA4E;IAC5E,uCAAuC;IACvC,4EAA4E;IAE5E,GAAG,CAAC,EAAU,EAAE,GAA0B;QACxC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAC9B,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAChC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;QACD,oEAAoE;QACpE,sDAAsD;QACtD,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,GAAG,CAAC,aAAa,EAAE,CAAC;YAC7C,IAAI,CAAC,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC;QACzC,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;aAAM,CAAC;YACN,oEAAoE;YACpE,qEAAqE;YACrE,IAAI,CAAC,qCAAqC,EAAE,CAAC;YAC7C,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;IAED,MAAM,CAAC,EAAU;QACf,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC5B,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAChC,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,qCAAqC,EAAE,CAAC;YAC7C,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;IAEO,eAAe;QACrB,IAAI,IAAI,CAAC,YAAY;YAAE,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACvD,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;YAClC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC,EAAE,iBAAiB,CAAC,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,OAAO;QACL,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAChC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,IAAI,CAAC,0BAA0B,EAAE,EAAE,CAAC;QACpC,IAAI,CAAC,0BAA0B,GAAG,IAAI,CAAC;QACvC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QACD,gEAAgE;QAChE,uEAAuE;QACvE,qCAAqC;QACrC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,qBAAqB,CAAC,CAAC;QACzD,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACpB,IAAI,wBAAwB,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,IAAI,EAAE,CAAC;YAC5D,wBAAwB,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,uDAAuD;IACvD,4EAA4E;IAE5E,IAAY,qBAAqB;QAC/B,IAAI,GAAG,GAAG,QAAQ,CAAC;QACnB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;YAC5C,IAAI,GAAG,CAAC,YAAY,GAAG,GAAG;gBAAE,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC;QACrD,CAAC;QACD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACpC,CAAC;IAED,IAAY,4BAA4B;QACtC,IAAI,GAAG,GAAG,QAAQ,CAAC;QACnB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;YAC5C,IAAI,GAAG,CAAC,mBAAmB,GAAG,GAAG;gBAAE,GAAG,GAAG,GAAG,CAAC,mBAAmB,CAAC;QACnE,CAAC;QACD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;IACtC,CAAC;IAED,IAAY,wBAAwB;QAClC,+CAA+C;QAC/C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;YAC5C,IAAI,CAAC,GAAG,CAAC,eAAe;gBAAE,OAAO,KAAK,CAAC;QACzC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4EAA4E;IAC5E,uBAAuB;IACvB,4EAA4E;IAEpE,WAAW,CAAC,KAAiC;QACnD,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,KAAK,EAAE,CAAC;QAC/C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;YAC5C,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAEO,eAAe;QACrB,IAAI,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,IAAI,IAAI,CAAC,UAAU;YAAE,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACnD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;YAChC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACvB,IAAI,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3C,CAAC,EAAE,IAAI,CAAC,CAAC;IACX,CAAC;IAED,4EAA4E;IAC5E,6DAA6D;IAC7D,4EAA4E;IAE5E;;;;OAIG;IACH,OAAO,CAAC,IAAgB;QACtB,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC;QAC9B,IACE,IAAI;YACJ,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;YACvB,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK;YACzB,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EACzB,CAAC;YACD,OAAO;QACT,CAAC;QACD,IAAI,CAAC,WAAW,GAAG;YACjB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,MAAM,EAAE;YACxC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC,CAAC;QACH,yEAAyE;QACzE,2EAA2E;QAC3E,mDAAmD;QACnD,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,SAAS,EAAE,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACpE,CAAC;IAEO,qBAAqB,GAAG,CAC9B,QAAoE,EACpE,MAAgB,EACV,EAAE;QACR,MAAM,KAAK,GAAiB,EAAE,CAAC;QAC/B,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;YACrD,IAAI,QAAQ,KAAK,IAAI,CAAC,IAAI,CAAC,QAAQ;gBAAE,OAAO,CAAC,YAAY;YACzD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;gBACf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAkB,CAAC,CAAC;gBACrC,IAAK,KAAK,CAAC,IAAmB,CAAC,KAAK,KAAK,cAAc,EAAE,CAAC;oBACxD,QAAQ,GAAG,IAAI,CAAC;gBAClB,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QACH,MAAM,eAAe,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;QACxD,yEAAyE;QACzE,oEAAoE;QACpE,mEAAmE;QACnE,oEAAoE;QACpE,+DAA+D;QAC/D,gEAAgE;QAChE,qEAAqE;QACrE,+DAA+D;QAC/D,+DAA+D;QAC/D,mEAAmE;QACnE,uBAAuB;QACvB,MAAM,WAAW,GAAG,gBAAgB,CAClC,IAAI,CAAC,QAAQ,CAAC,WAAW,EACzB,eAAe,CAChB;YACC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW;YAC3B,CAAC,CAAC,eAAe,CAAC;QACpB,IACE,WAAW,KAAK,IAAI,CAAC,QAAQ,CAAC,WAAW;YACzC,QAAQ,KAAK,IAAI,CAAC,QAAQ,CAAC,YAAY,EACvC,CAAC;YACD,IAAI,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC5D,CAAC;QAED,sEAAsE;QACtE,yEAAyE;QACzE,wEAAwE;QACxE,mEAAmE;QACnE,uEAAuE;QACvE,sDAAsD;QACtD,mEAAmE;QACnE,uEAAuE;QACvE,sEAAsE;QACtE,uEAAuE;QACvE,yEAAyE;QACzE,wEAAwE;QACxE,wEAAwE;QACxE,IAAI,IAAI,CAAC,WAAW,IAAI,MAAM,KAAK,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC7D,qBAAqB,CACnB,IAAI,CAAC,OAAO,EACZ,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,IAAI,CAAC,QAAQ,EAClB,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,EAAoC,CACvE,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;IAEF,4EAA4E;IAC5E,2DAA2D;IAC3D,4EAA4E;IAEpE,KAAK;QACX,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,IAAI,CAAC,SAAS,EAAE,CAAC;QAEjB,wEAAwE;QACxE,wEAAwE;QACxE,uEAAuE;QACvE,yEAAyE;QACzE,IAAI,CAAC,0BAA0B,GAAG,mBAAmB,CAAC;YACpD,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;gBACnB,IAAI,IAAI,CAAC,QAAQ;oBAAE,OAAO;gBAC1B,KAAK,MAAM,IAAI,IAAI,MAAM;oBAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAC5D,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAEO,iBAAiB;QACvB,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,QAAQ,CAAC;aACzC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YAClB,IAAI,IAAI,CAAC,QAAQ;gBAAE,OAAO;YAC1B,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC7C,IAAI,CAAC,cAAc,EAAE,CAAC;gBACtB,OAAO;YACT,CAAC;YACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAExC,CAAC;YACT,IAAI,IAAI,CAAC,QAAQ;gBAAE,OAAO;YAC1B,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;gBAChB,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC9C,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACtB,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;gBAC7C,CAAC;YACH,CAAC;YACD,IAAI,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE;YACV,IAAI,IAAI,CAAC,QAAQ;gBAAE,OAAO;YAC1B,IAAI,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;;;;OAKG;IACK,cAAc;QACpB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,IAAI,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,4EAA4E;IAC5E,wBAAwB;IACxB,4EAA4E;IAEpE,eAAe,GAAG,CAAC,MAAkB,EAAE,MAAe,EAAQ,EAAE;QACtE,IAAI,MAAM,KAAK,QAAQ;YAAE,OAAO;QAChC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,IAAI,CAAC,UAAU;YAAE,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACnD,IAAI,CAAC,UAAU,GAAG,UAAU,CAC1B,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAChC,kBAAkB,CACnB,CAAC;IACJ,CAAC,CAAC;IAEM,cAAc,GAAG,GAAS,EAAE;QAClC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACjD,CAAC,CAAC;IAEM,mBAAmB;QACzB,IAAI,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC1D,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QAC7C,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAEO,mBAAmB;QACzB,IAAI,CAAC,IAAI,CAAC,qBAAqB;YAAE,OAAO;QACxC,IAAI,CAAC,qBAAqB,GAAG,KAAK,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QAC9C,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAEO,mBAAmB,CAAC,SAAS,GAAG,KAAK;QAC3C,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QACD,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC;QACnC,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;QAEzB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACxE,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,SAAS,EAAE;YAC5C,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,MAAM,EAAE,kBAAkB,CAAC,MAAM,CAAC;gBAClC,aAAa,EAAE,IAAI,CAAC,aAAa;aAClC,CAAC;YACF,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1C,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACrB,CAAC;IAED,4EAA4E;IAC5E,kDAAkD;IAClD,4EAA4E;IAEpE,SAAS;QACf,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAChE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QAEvB,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAE7B,IAAI,CAAC,IAAI,CAAC,wBAAwB,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC;YAC1D,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;QACnB,CAAC;QACD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACnD,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAC7E,CAAC;IAEO,QAAQ;QACd,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO;QAC7B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;QACD,IAAI,CAAC,uBAAuB,EAAE,EAAE,CAAC;QACjC,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC;QACpC,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC;QACnC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACtD,QAAQ,CAAC,mBAAmB,CAC1B,kBAAkB,EAClB,IAAI,CAAC,sBAAsB,CAC5B,CAAC;IACJ,CAAC;IAED;;;;;;;;;OASG;IACK,qBAAqB;QAC3B,MAAM,eAAe,GAAG,IAAI,CAAC,wBAAwB,CAAC;QACtD,IAAI,CAAC,sBAAsB,GAAG,eAAe,CAAC;QAC9C,IAAI,CAAC,uBAAuB,GAAG,mBAAmB,CAAC;YACjD,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;gBACnB,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU;oBAAE,OAAO;gBAC9C,KAAK,MAAM,MAAM,IAAI,MAAM;oBAAE,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAC9D,CAAC;YACD,gBAAgB,EAAE,CAAC,SAAS,EAAE,EAAE;gBAC9B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;gBAC3B,IAAI,SAAS;oBAAE,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;YAC5C,CAAC;YACD,eAAe;SAChB,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACK,qCAAqC;QAC3C,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO;QAC7B,IAAI,IAAI,CAAC,sBAAsB,KAAK,IAAI,CAAC,wBAAwB;YAAE,OAAO;QAC1E,IAAI,CAAC,uBAAuB,EAAE,EAAE,CAAC;QACjC,IAAI,CAAC,qBAAqB,EAAE,CAAC;IAC/B,CAAC;IAEO,iBAAiB,CAAC,MAAiB;QACzC,IACE,MAAM,CAAC,MAAM,KAAK,QAAQ;YAC1B,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK;YAC3B,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,EACjC,CAAC;YACD,qEAAqE;YACrE,kEAAkE;YAClE,+BAA+B;YAC/B,IAAI,IAAI,CAAC,aAAa,IAAI,MAAM,CAAC,aAAa,KAAK,IAAI,CAAC,aAAa,EAAE,CAAC;gBACtE,OAAO;YACT,CAAC;YACD,IAAI,CAAC;gBACH,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC;YACxE,CAAC;YAAC,MAAM,CAAC;gBACP,6DAA6D;YAC/D,CAAC;YAED,IAAI,MAAM,CAAC,aAAa,KAAK,OAAO,EAAE,CAAC;gBACrC,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,CAAC;QACH,CAAC;QAED,wEAAwE;QACxE,qDAAqD;QACrD,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YACvC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAEO,qBAAqB;QAC3B,OAAO,IAAI,CAAC,SAAS;YACnB,CAAC,CAAC,IAAI,CAAC,4BAA4B;YACnC,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC;IACjC,CAAC;IAEO,YAAY;QAClB,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC9C,IAAI,IAAI,CAAC,wBAAwB,IAAI,gBAAgB,EAAE;YAAE,OAAO;QAChE,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC/B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;QACnB,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,CAAC,CAAC;IACnC,CAAC;IAED;;;;;OAKG;IACK,cAAc;QACpB,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI;YAAE,OAAO;QACpC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,gBAAgB;QAC5B,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,kBAAkB,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACvE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAC1B,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,sBAAsB,kBAAkB,CAAC,WAAW,CAAC,EAAE,CACrF,CAAC;YACF,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAChB,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAElD,CAAC;gBACT,IAAI,IAAI,CAAC,QAAQ;oBAAE,OAAO;gBAC1B,IAAI,SAAS,EAAE,KAAK,EAAE,CAAC;oBACrB,MAAM,MAAM,GAAG,kBAAkB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;oBACnD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACtB,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;oBAC7C,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,4CAA4C;QAC9C,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAE9C,uEAAuE;QACvE,8CAA8C;QAC9C,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAE3B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,eAAe,CAAC,6BAA6B,IAAI,CAAC,WAAW,EAAE,CAAC,CACjE,CAAC;YACF,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;YAEnD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,QAAQ;gBAAE,OAAO;YAC9C,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAQ3B,CAAC;YAEF,sEAAsE;YACtE,wEAAwE;YACxE,8BAA8B;YAC9B,MAAM,UAAU,GAAG,OAAO,GAAG,IAAI,CAAC,iBAAiB,CAAC;YACpD,MAAM,MAAM,GAAG,UAAU,GAAG,qBAAqB,CAAC;YAElD,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;gBACzB,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;oBACtE,IAAI,IAAI,CAAC,aAAa,IAAI,GAAG,CAAC,aAAa,KAAK,IAAI,CAAC,aAAa,EAAE,CAAC;wBACnE,SAAS;oBACX,CAAC;oBACD,IAAI,CAAC;wBACH,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC;oBACrE,CAAC;oBAAC,MAAM,CAAC;wBACP,kDAAkD;wBAClD,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;oBAChC,CAAC;oBAED,IAAI,GAAG,CAAC,aAAa,KAAK,OAAO,EAAE,CAAC;wBAClC,IAAI,CAAC,eAAe,EAAE,CAAC;oBACzB,CAAC;gBACH,CAAC;YACH,CAAC;YAED,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;YAC3B,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC;YACjC,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;YAE3B,uCAAuC;YACvC,sDAAsD;YACtD,0EAA0E;YAC1E,4EAA4E;YAC5E,MAAM,sBAAsB,GAC1B,MAAM,IAAI,IAAI,CAAC,cAAc,GAAG,2BAA2B,KAAK,CAAC,CAAC;YAEpE,IAAI,sBAAsB,EAAE,CAAC;gBAC3B,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAChC,CAAC;YAED,oCAAoC;YACpC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,CAAC;YAClD,IAAI,UAAU,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACjC,IAAI,CAAC;oBACH,MAAM,YAAY,GAAG,MAAM,KAAK,CAC9B,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,YAAY,EACzC;wBACE,MAAM,EAAE,MAAM;wBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;wBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ;4BAC5B,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;yBAClC,CAAC;qBACH,CACF,CAAC;oBACF,IAAI,YAAY,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;wBACtC,MAAM,aAAa,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,CAAC;wBAChD,MAAM,YAAY,GAA8B,EAAE,CAAC;wBACnD,KAAK,MAAM,MAAM,IAAI,aAAa,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;4BAChD,IAAI,CAAC;gCACH,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gCAC7C,YAAY,CAAC,IAAI,CAAC;oCAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;oCACjC,KAAK,EAAE,WAAW;iCACnB,CAAC,CAAC;4BACL,CAAC;4BAAC,MAAM,CAAC;gCACP,uBAAuB;4BACzB,CAAC;wBACH,CAAC;wBACD,IAAI,IAAI,CAAC,QAAQ;4BAAE,OAAO;wBAC1B,MAAM,OAAO,GAAG,8BAA8B,CAC5C,IAAI,CAAC,SAAS,CAAC,SAAS,EAA0B,EAClD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAClB,YAAY,CACb,CAAC;wBACF,IACE,OAAO,CAAC,KAAK,CAAC,MAAM;4BACpB,OAAO,CAAC,OAAO,CAAC,MAAM;4BACtB,OAAO,CAAC,OAAO,CAAC,MAAM,EACtB,CAAC;4BACD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;wBACrD,CAAC;oBACH,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,sCAAsC;gBACxC,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,sCAAsC;YACtC,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YACpD,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACtC,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;oBAC/B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;oBACtB,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;gBACnB,CAAC,EAAE,OAAO,CAAC,CAAC;YACd,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC9C,IAAI,IAAI,CAAC,wBAAwB,IAAI,gBAAgB,EAAE;YAAE,OAAO;QAChE,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;QACD,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;IACnB,CAAC;IAED;;;;;OAKG;IACK,iBAAiB,CAAC,OAAgB;QACxC,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACtD,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,CAAC;QAClD,IAAI,CAAC,UAAU;YAAE,OAAO;QACxB,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,YAAY,EAAE;YAC/C,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ;gBAC5B,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;aAClC,CAAC;YACF,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACrB,CAAC;IAEO,sBAAsB,GAAG,GAAS,EAAE;QAC1C,MAAM,OAAO,GAAG,QAAQ,CAAC,eAAe,KAAK,SAAS,CAAC;QACvD,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,OAAO,EAAE,CAAC;YACZ,4DAA4D;YAC5D,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC;aAAM,IAAI,IAAI,CAAC,wBAAwB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC3D,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;IACH,CAAC,CAAC;IAEM,WAAW,GAAG,GAAS,EAAE;QAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC,CAAC;IAEF,4EAA4E;IAC5E,0BAA0B;IAC1B,4EAA4E;IAEpE,mBAAmB,CAAC,IAAe;QACzC,IACE,IAAI,CAAC,MAAM,KAAK,WAAW;YAC3B,IAAI,CAAC,IAAI,KAAK,kBAAkB;YAChC,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EACzB,CAAC;YACD,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;YACvC,CAAC,CAAE,IAAI,CAAC,MAAqD;YAC7D,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,YAAY,GAA8B,EAAE,CAAC;QACnD,KAAK,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,YAAY,CAAC,IAAI,CAAC;oBAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;oBACjC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC;iBAChC,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,6BAA6B;YAC/B,CAAC;QACH,CAAC;QACD,MAAM,OAAO,GAAG,8BAA8B,CAC5C,IAAI,CAAC,SAAS,CAAC,SAAS,EAA0B,EAClD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAClB,YAAY,CACb,CAAC;QACF,IACE,OAAO,CAAC,KAAK,CAAC,MAAM;YACpB,OAAO,CAAC,OAAO,CAAC,MAAM;YACtB,OAAO,CAAC,OAAO,CAAC,MAAM,EACtB,CAAC;YACD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,wBAAwB,GAAG,IAAI,GAAG,EAA+B,CAAC;AAExE,SAAS,iBAAiB,CAAC,KAAa,EAAE,OAAe;IACvD,OAAO,GAAG,OAAO,KAAK,KAAK,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,2BAA2B,CAClC,KAAa,EACb,OAAe;IAEf,MAAM,GAAG,GAAG,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAI,IAAI,GAAG,wBAAwB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC3B,IAAI,GAAG,IAAI,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC/C,wBAAwB,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8EAA8E;AAC9E,+EAA+E;AAC/E,8EAA8E;AAC9E,gBAAgB;AAChB,MAAM,UAAU,+BAA+B;IAC7C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;QACjE,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IACD,wBAAwB,CAAC,KAAK,EAAE,CAAC;AACnC,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,8BAA8B;IAC5C,OAAO,wBAAwB,CAAC,IAAI,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,OAAmC;IAEnC,MAAM,EACJ,KAAK,EACL,YAAY,GAAG,IAAI,EACnB,mBAAmB,GAAG,KAAK,EAC3B,eAAe,GAAG,IAAI,EACtB,OAAO,GAAG,eAAe,CAAC,uBAAuB,CAAC,EAClD,aAAa,EACb,IAAI,GACL,GAAG,OAAO,CAAC;IAEZ,2EAA2E;IAC3E,uEAAuE;IACvE,oDAAoD;IACpD,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEhD,4EAA4E;IAC5E,yEAAyE;IACzE,uEAAuE;IACvE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE;QACxB,KAAK,UAAU,CAAC;QAChB,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,gEAAgE;YAChE,6CAA6C;YAC7C,OAAO,IAAI,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,2BAA2B,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;IAEjC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAoB,GAAG,EAAE,CAC/D,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CACtC,CAAC;IAEF,2EAA2E;IAC3E,4DAA4D;IAC5D,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC/C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,WAAW,CAAC,IAAI,CAAC,CAAC;QAClB,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IACrD,CAAC;IAED,0EAA0E;IAC1E,2CAA2C;IAC3C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QACnC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,qEAAqE;YACrE,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5B,OAAO;QACT,CAAC;QACD,MAAM,EAAE,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;QACzC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;YACX,YAAY;YACZ,mBAAmB;YACnB,eAAe;YACf,aAAa;YACb,UAAU,EAAE,WAAW;SACxB,CAAC,CAAC;QACH,0EAA0E;QAC1E,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC,CAAC;IAE9E,uEAAuE;IACvE,wEAAwE;IACxE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI;YAAE,OAAO;QAC5C,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1E,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAEjD,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;QAC7B,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;QACvC,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,YAAY,EAAE,QAAQ,CAAC,YAAY;KACpC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Client-side hook for collaborative document editing via Yjs.\n *\n * Creates a STABLE Y.Doc per docId that never changes identity. This allows\n * TipTap's Collaboration extension to bind once without editor recreation.\n * Server state is applied to the existing doc when it arrives.\n *\n * Also manages Yjs Awareness for cursor positions and user presence,\n * synced via polling to the server's awareness endpoint.\n *\n * Connection sharing: connections live in a module-level, ref-counted registry\n * keyed by docId (mirroring the SyncTransport registry in use-db-sync.ts).\n * Every `useCollaborativeDoc` mount for the same docId attaches to ONE shared\n * connection — one Y.Doc, one Awareness, one state fetch, one poll loop, one\n * awareness POST cycle — instead of each hook instance opening an independent\n * connection and doubling all collab traffic (e.g. a presence bar and the\n * editor mounting the hook for the same doc). The first subscriber starts the\n * connection; the last one leaving tears it down after a short linger so\n * StrictMode double-mounts and rapid unmount/remounts don't thrash the doc.\n *\n * Transport improvements (vs previous version):\n * - Local update POSTs are debounced and coalesced with Y.mergeUpdates (~80ms)\n * to avoid per-keystroke requests. The batch is flushed immediately on\n * visibilitychange/pagehide and before each poll/awareness cycle.\n * - GET state?stateVector= is NOT fetched on every poll cycle. It is fetched:\n * (a) on (re)connect / initial load, (b) when a poll response indicates a\n * gap (version jump > ring-buffer size), (c) after applying an update fails,\n * and (d) as a low-frequency safety net every STATE_VECTOR_FETCH_INTERVAL\n * poll cycles (~15×).\n * - Network errors use exponential backoff with jitter (cap ~15s), reset on\n * success.\n * - SSE fast-path: collab events are received push-style from\n * /_agent-native/events (the framework SSE stream). While SSE is\n * healthy the poll loop relaxes to a slow cadence (10–15s). If SSE is\n * unavailable the 2s poll resumes automatically.\n */\n\nimport {\n dedupeCollabUsersByEmail,\n type CollabUser,\n} from \"@agent-native/toolkit/collab-ui\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { Awareness } from \"y-protocols/awareness\";\nimport * as Y from \"yjs\";\n\nimport { agentNativePath } from \"../client/api-path.js\";\nimport { subscribeSyncEvents, type SyncEvent } from \"../client/use-db-sync.js\";\nexport {\n dedupeCollabUsersByEmail,\n emailToColor,\n emailToName,\n isReconcileLeadClient,\n type CollabUser,\n} from \"@agent-native/toolkit/collab-ui\";\n\nexport interface UseCollaborativeDocOptions {\n /** Document ID to collaborate on. Pass null to disable. */\n docId: string | null;\n /** Poll interval in ms when SSE is unavailable. Default: 2000 */\n pollInterval?: number;\n /** Poll interval in ms while SSE is healthy. Default: 12000 */\n pollIntervalWithSse?: number;\n /** Pause remote update/presence polling while the tab is hidden. Default: true */\n pauseWhenHidden?: boolean;\n /** Base URL for collab endpoints. Default: \"/_agent-native/collab\" */\n baseUrl?: string;\n /** Request source ID for jitter prevention (e.g., tab ID). */\n requestSource?: string;\n /** Current user info for cursor labels. */\n user?: CollabUser;\n}\n\nexport interface UseCollaborativeDocResult {\n /** The Yjs document instance. Stable per docId — never changes identity. */\n ydoc: Y.Doc | null;\n /** Yjs Awareness instance for cursor/presence sync. */\n awareness: Awareness | null;\n /** Whether the initial state is still loading from the server. */\n isLoading: boolean;\n /** Whether the doc is synced with the server. */\n isSynced: boolean;\n /** Active users on this document (from awareness). */\n activeUsers: CollabUser[];\n /** True briefly when the AI agent makes an edit (for presence indicator). */\n agentActive: boolean;\n /** True when the AI agent has an active awareness entry (durable presence). */\n agentPresent: boolean;\n}\n\nfunction isDocumentHidden(): boolean {\n return (\n typeof document !== \"undefined\" && document.visibilityState === \"hidden\"\n );\n}\n\n/**\n * Content equality for deduped active-user lists. `dedupeCollabUsersByEmail`\n * always allocates a fresh array (and fresh per-user objects), so callers\n * that only want to know \"did the actual user set change\" need this instead\n * of an identity check. Order-sensitive: the dedupe helper iterates awareness\n * states in a stable Map insertion order, so a reordering here does reflect\n * a real change (a client joined/left and re-joined).\n */\nfunction collabUsersEqual(a: CollabUser[], b: CollabUser[]): boolean {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const left = a[i]!;\n const right = b[i]!;\n if (\n left.email !== right.email ||\n left.name !== right.name ||\n left.color !== right.color\n ) {\n return false;\n }\n }\n return true;\n}\n\nexport interface RemoteAwarenessSnapshot {\n clientId: number;\n state: unknown;\n}\n\nexport function reconcileRemoteAwarenessStates(\n states: Map<number, unknown>,\n localClientId: number,\n remoteStates: RemoteAwarenessSnapshot[],\n): { added: number[]; updated: number[]; removed: number[] } {\n const incoming = new Set<number>();\n const added: number[] = [];\n const updated: number[] = [];\n const removed: number[] = [];\n\n for (const remote of remoteStates) {\n if (\n !Number.isFinite(remote.clientId) ||\n remote.clientId === localClientId\n ) {\n continue;\n }\n incoming.add(remote.clientId);\n const hadState = states.has(remote.clientId);\n states.set(remote.clientId, remote.state);\n (hadState ? updated : added).push(remote.clientId);\n }\n\n for (const clientId of Array.from(states.keys())) {\n if (clientId === localClientId) continue;\n if (incoming.has(clientId)) continue;\n states.delete(clientId);\n removed.push(clientId);\n }\n\n return { added, updated, removed };\n}\n\n// Base64 helpers\nfunction uint8ArrayToBase64(arr: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < arr.length; i++) {\n binary += String.fromCharCode(arr[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToUint8Array(b64: string): Uint8Array {\n const binary = atob(b64);\n const arr = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n arr[i] = binary.charCodeAt(i);\n }\n return arr;\n}\n\n/** Debounce delay for coalescing local Yjs update POSTs (ms). */\nconst UPDATE_DEBOUNCE_MS = 80;\n\n/** Fetch state-vector every N poll cycles as a low-frequency safety net. */\nconst STATE_VECTOR_FETCH_INTERVAL = 15;\n\n/** Poll ring-buffer size on the server (MAX_BUFFER in poll.ts). */\nconst POLL_RING_BUFFER_SIZE = 200;\n\n/** Exponential backoff: base delay (ms), multiplier, cap (ms). */\nconst BACKOFF_BASE_MS = 500;\nconst BACKOFF_MAX_MS = 15_000;\n\nfunction calcBackoff(consecutiveErrors: number): number {\n const exp = Math.min(consecutiveErrors, 10);\n const delay = BACKOFF_BASE_MS * Math.pow(2, exp);\n // Add jitter: ±25%\n const jitter = delay * 0.25 * (Math.random() * 2 - 1);\n return Math.min(delay + jitter, BACKOFF_MAX_MS);\n}\n\n// ---------------------------------------------------------------------------\n// Fast awareness helper — throttled per (docId, ydocId) pair so multiple\n// setLocalStateField calls within a 150ms window are coalesced into one POST.\n// ---------------------------------------------------------------------------\n\nconst _awarenessThrottleTimers = new Map<\n string,\n ReturnType<typeof setTimeout>\n>();\n\nfunction scheduleAwarenessPush(\n baseUrl: string,\n docId: string,\n clientId: number,\n getState: () => Record<string, unknown> | null,\n): void {\n if (typeof window === \"undefined\") return;\n const key = `${docId}::${clientId}`;\n if (_awarenessThrottleTimers.has(key)) return; // already scheduled\n\n const timer = setTimeout(() => {\n _awarenessThrottleTimers.delete(key);\n const state = getState();\n fetch(`${baseUrl}/${docId}/awareness`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId,\n state: state ? JSON.stringify(state) : null,\n }),\n }).catch(() => {}); // best-effort; poll cycle is the baseline fallback\n }, 150);\n\n _awarenessThrottleTimers.set(key, timer);\n}\n\n// ---------------------------------------------------------------------------\n// Shared per-docId collab connection\n//\n// One CollabDocConnection per (baseUrl, docId) pair is held in a module-level\n// registry (the same pattern as SyncTransport in use-db-sync.ts). Every\n// `useCollaborativeDoc` mount for a docId subscribes to the shared connection,\n// so a tab runs exactly ONE state fetch, ONE poll fallback loop, and ONE\n// awareness POST cycle per document no matter how many components mount the\n// hook (e.g. a presence bar + the editor for the same doc).\n//\n// Lifecycle: the connection is created on first render that needs it (the\n// Y.Doc must exist during render so TipTap can bind on mount), starts its\n// network activity when the first subscriber attaches, and is disposed a short\n// linger after the last subscriber detaches. The linger keeps StrictMode's\n// mount→unmount→mount and rapid route remounts from destroying and refetching\n// the doc, and doubles as the cleanup path for connections created by a render\n// that React later discarded (no subscriber ever attaches).\n// ---------------------------------------------------------------------------\n\n/**\n * Shared reactive state each subscriber mirrors into its own React state.\n * Replaced immutably on every change so hooks can bail out on identity.\n */\ninterface CollabDocSnapshot {\n isLoading: boolean;\n isSynced: boolean;\n activeUsers: CollabUser[];\n agentActive: boolean;\n agentPresent: boolean;\n}\n\ninterface CollabDocSubscription {\n /** Requested poll interval; the connection uses the MIN across subscribers. */\n pollInterval: number;\n /** Requested relaxed interval while SSE is healthy (MIN across subscribers). */\n pollIntervalWithSse: number;\n /**\n * Whether this subscriber wants polling paused while the tab is hidden.\n * The connection pauses only when ALL subscribers request it.\n */\n pauseWhenHidden: boolean;\n /**\n * Echo-suppression tag. The connection adopts the first defined value and\n * uses it for BOTH tagging outgoing updates and filtering incoming echoes,\n * so suppression stays self-consistent even when subscribers pass different\n * per-tab IDs (all values identify the same tab).\n */\n requestSource?: string;\n onSnapshot: (snapshot: CollabDocSnapshot) => void;\n}\n\nconst EMPTY_SNAPSHOT: CollabDocSnapshot = Object.freeze({\n isLoading: false,\n isSynced: false,\n activeUsers: [],\n agentActive: false,\n agentPresent: false,\n});\n\n/**\n * How long a connection with zero subscribers lingers before disposal.\n * Long enough to absorb StrictMode double-mounts and route-level remounts\n * (which would otherwise destroy the Y.Doc, refetch server state, and lose\n * echo-suppression context), short enough that navigating away stops the\n * doc's traffic promptly.\n */\nconst DISPOSE_LINGER_MS = 1000;\n\nclass CollabDocConnection {\n readonly ydoc: Y.Doc;\n readonly awareness: Awareness;\n /** Immutable snapshot of the shared reactive state (replaced on change). */\n snapshot: CollabDocSnapshot;\n disposed = false;\n\n private subscribers = new Map<symbol, CollabDocSubscription>();\n private disposeTimer: ReturnType<typeof setTimeout> | null = null;\n private started = false;\n private docMissing = false;\n private requestSource: string | undefined;\n private lastSetUser: CollabUser | null = null;\n\n // Local-update batching (debounced + coalesced with Y.mergeUpdates).\n private pendingUpdates: Uint8Array[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private updateHandlerAttached = false;\n\n // Poll loop + SSE fast path.\n private syncActive = false;\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n private consecutiveErrors = 0;\n private pollCycleCount = 0;\n private pollVersion = 0;\n private lastPolledVersion = 0;\n private sseActive = false;\n private sseSubscribedWithPause: boolean | null = null;\n private unsubscribeCollabEvents: (() => void) | null = null;\n private unsubscribeAwarenessEvents: (() => void) | null = null;\n private agentTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(\n readonly docId: string,\n private readonly baseUrl: string,\n /**\n * Detached connections are created for SSR renders only: a per-hook\n * Y.Doc/Awareness with no registry entry and no network activity\n * (matching the previous per-hook behavior on the server). They are\n * never started and are reclaimed by GC with the render.\n */\n readonly detached = false,\n ) {\n this.ydoc = new Y.Doc();\n this.awareness = new Awareness(this.ydoc);\n this.snapshot = {\n isLoading: true,\n isSynced: false,\n activeUsers: [],\n agentActive: false,\n agentPresent: false,\n };\n\n // Track active users / agent presence from awareness changes, and drive\n // the fast (throttled) awareness push once a local user is published.\n this.awareness.on(\"change\", this.handleAwarenessChange);\n\n // Orphan guard: the connection is created during render (the Y.Doc must\n // exist before mount), so a discarded render can create one that never\n // gets a subscriber. The linger timer disposes it; the first `add()`\n // cancels the timer.\n if (!detached) this.scheduleDispose();\n }\n\n private get registryKey(): string {\n return collabRegistryKey(this.docId, this.baseUrl);\n }\n\n // -------------------------------------------------------------------------\n // Subscriber management (ref-counting)\n // -------------------------------------------------------------------------\n\n add(id: symbol, sub: CollabDocSubscription): void {\n this.subscribers.set(id, sub);\n if (this.disposeTimer) {\n clearTimeout(this.disposeTimer);\n this.disposeTimer = null;\n }\n // Adopt the first defined echo-suppression tag for the connection's\n // lifetime (see CollabDocSubscription.requestSource).\n if (!this.requestSource && sub.requestSource) {\n this.requestSource = sub.requestSource;\n }\n if (!this.started) {\n this.started = true;\n this.start();\n } else {\n // A joining subscriber can change the effective poll cadence or the\n // pause-when-hidden aggregate; pick both up without waiting a cycle.\n this.resubscribeCollabEventsIfPauseChanged();\n this.reschedulePoll();\n }\n }\n\n remove(id: symbol): void {\n this.subscribers.delete(id);\n if (this.subscribers.size === 0) {\n this.scheduleDispose();\n } else {\n this.resubscribeCollabEventsIfPauseChanged();\n this.reschedulePoll();\n }\n }\n\n private scheduleDispose(): void {\n if (this.disposeTimer) clearTimeout(this.disposeTimer);\n this.disposeTimer = setTimeout(() => {\n this.disposeTimer = null;\n this.dispose();\n }, DISPOSE_LINGER_MS);\n }\n\n /**\n * Tear the connection down: stop all loops and listeners, flush any pending\n * local updates, clear awareness local state (via destroy), and destroy the\n * Y.Doc. Also used by the test-only registry reset. @internal\n */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n if (this.disposeTimer) {\n clearTimeout(this.disposeTimer);\n this.disposeTimer = null;\n }\n this.stopSync();\n this.unsubscribeAwarenessEvents?.();\n this.unsubscribeAwarenessEvents = null;\n this.flushPendingUpdates(true);\n this.detachUpdateHandler();\n if (this.agentTimer) {\n clearTimeout(this.agentTimer);\n this.agentTimer = null;\n }\n // Detach our change listener BEFORE destroy so the destroy-time\n // `setLocalState(null)` does not schedule a push (matches the previous\n // per-hook effect cleanup ordering).\n this.awareness.off(\"change\", this.handleAwarenessChange);\n this.awareness.destroy();\n this.ydoc.destroy();\n if (collabConnectionRegistry.get(this.registryKey) === this) {\n collabConnectionRegistry.delete(this.registryKey);\n }\n }\n\n // -------------------------------------------------------------------------\n // Derived settings (aggregate over active subscribers)\n // -------------------------------------------------------------------------\n\n private get effectivePollInterval(): number {\n let min = Infinity;\n for (const sub of this.subscribers.values()) {\n if (sub.pollInterval < min) min = sub.pollInterval;\n }\n return isFinite(min) ? min : 2000;\n }\n\n private get effectivePollIntervalWithSse(): number {\n let min = Infinity;\n for (const sub of this.subscribers.values()) {\n if (sub.pollIntervalWithSse < min) min = sub.pollIntervalWithSse;\n }\n return isFinite(min) ? min : 12_000;\n }\n\n private get effectivePauseWhenHidden(): boolean {\n // Pause only if every subscriber has opted in.\n for (const sub of this.subscribers.values()) {\n if (!sub.pauseWhenHidden) return false;\n }\n return true;\n }\n\n // -------------------------------------------------------------------------\n // Shared state fan-out\n // -------------------------------------------------------------------------\n\n private setSnapshot(patch: Partial<CollabDocSnapshot>): void {\n this.snapshot = { ...this.snapshot, ...patch };\n for (const sub of this.subscribers.values()) {\n sub.onSnapshot(this.snapshot);\n }\n }\n\n private markAgentActive(): void {\n this.setSnapshot({ agentActive: true });\n if (this.agentTimer) clearTimeout(this.agentTimer);\n this.agentTimer = setTimeout(() => {\n this.agentTimer = null;\n this.setSnapshot({ agentActive: false });\n }, 3000);\n }\n\n // -------------------------------------------------------------------------\n // Awareness: local identity, active-user tracking, fast push\n // -------------------------------------------------------------------------\n\n /**\n * Publish the local user identity (cursor label) and current visibility.\n * Set once per tab — repeated calls with the same identity are no-ops so\n * multiple subscribers for the same user don't re-emit awareness updates.\n */\n setUser(user: CollabUser): void {\n if (this.disposed) return;\n const prev = this.lastSetUser;\n if (\n prev &&\n prev.name === user.name &&\n prev.email === user.email &&\n prev.color === user.color\n ) {\n return;\n }\n this.lastSetUser = {\n name: user.name,\n email: user.email,\n color: user.color,\n };\n this.awareness.setLocalStateField(\"user\", {\n name: user.name,\n email: user.email,\n color: user.color,\n });\n // Also publish this tab's visibility so peers can elect a VISIBLE client\n // to apply external snapshots (see isReconcileLeadClient) — a backgrounded\n // tab pauses its poll and must not hold that role.\n this.awareness.setLocalStateField(\"visible\", !isDocumentHidden());\n }\n\n private handleAwarenessChange = (\n _changes?: { added: number[]; updated: number[]; removed: number[] },\n origin?: unknown,\n ): void => {\n const users: CollabUser[] = [];\n let hasAgent = false;\n this.awareness.getStates().forEach((state, clientId) => {\n if (clientId === this.ydoc.clientID) return; // Skip self\n if (state.user) {\n users.push(state.user as CollabUser);\n if ((state.user as CollabUser).email === \"agent@system\") {\n hasAgent = true;\n }\n }\n });\n const nextActiveUsers = dedupeCollabUsersByEmail(users);\n // Awareness \"change\" fires for every remote broadcast — including cursor\n // jiggles, re-published-but-unchanged heartbeat state, and edits to\n // presence fields (recentEdits, selection) that don't affect who's\n // active. dedupeCollabUsersByEmail always returns a fresh array, so\n // without this comparison every one of those events would hand\n // `activeUsers` a new identity and force every consumer (e.g. a\n // full-page editor keying a useMemo/effect off it) to re-render even\n // though the actual user list is unchanged. Reuse the previous\n // reference when the deduped content is the same, matching the\n // stable-ref discipline `usePresence`'s shallowEqualOthers already\n // applies to `others`.\n const activeUsers = collabUsersEqual(\n this.snapshot.activeUsers,\n nextActiveUsers,\n )\n ? this.snapshot.activeUsers\n : nextActiveUsers;\n if (\n activeUsers !== this.snapshot.activeUsers ||\n hasAgent !== this.snapshot.agentPresent\n ) {\n this.setSnapshot({ activeUsers, agentPresent: hasAgent });\n }\n\n // Fast awareness push: whenever OUR OWN awareness state changes (e.g.\n // cursor moves, setPresence() calls), schedule a throttled POST so peers\n // receive updates at ~150ms instead of waiting for the next poll cycle.\n // Gated on origin === \"local\": this listener also fires for remote\n // awareness changes (poll/SSE call `awareness.emit(\"change\", [changes,\n // \"remote\"])` after reconciling incoming states — see\n // applyAwarenessEvent/poll above). Without the origin check, every\n // peer's cursor move would also re-trigger THIS client to re-broadcast\n // its own unchanged state, turning one person moving their mouse into\n // O(n) redundant POSTs from every other connected client (an awareness\n // storm that gets worse as more people join the doc). Only active once a\n // local user identity has been published (matches the previous per-hook\n // gating on `user`). The poll cycle remains the authoritative baseline.\n if (this.lastSetUser && origin === \"local\" && !this.disposed) {\n scheduleAwarenessPush(\n this.baseUrl,\n this.docId,\n this.ydoc.clientID,\n () => this.awareness.getLocalState() as Record<string, unknown> | null,\n );\n }\n };\n\n // -------------------------------------------------------------------------\n // Startup: initial state fetch, update handler, sync loops\n // -------------------------------------------------------------------------\n\n private start(): void {\n this.attachUpdateHandler();\n this.fetchInitialState();\n this.startSync();\n\n // SSE fast-path for awareness: listen on the SHARED framework transport\n // and apply awareness-change events immediately so peers receive cursor\n // moves push-style without waiting for the next poll cycle. Kept alive\n // even when the doc is missing (matches the previous per-hook behavior).\n this.unsubscribeAwarenessEvents = subscribeSyncEvents({\n onEvents: (events) => {\n if (this.disposed) return;\n for (const data of events) this.applyAwarenessEvent(data);\n },\n });\n }\n\n private fetchInitialState(): void {\n fetch(`${this.baseUrl}/${this.docId}/state`)\n .then(async (res) => {\n if (this.disposed) return;\n if (res.status === 404 || res.status === 403) {\n this.markDocMissing();\n return;\n }\n const data = (await res.json().catch(() => null)) as {\n state?: string;\n } | null;\n if (this.disposed) return;\n if (data?.state) {\n const binary = base64ToUint8Array(data.state);\n if (binary.length > 4) {\n Y.applyUpdate(this.ydoc, binary, \"remote\");\n }\n }\n this.setSnapshot({ isLoading: false, isSynced: true });\n })\n .catch(() => {\n if (this.disposed) return;\n this.setSnapshot({ isLoading: false, isSynced: true });\n });\n }\n\n /**\n * The initial state fetch returned 404/403 — the doc doesn't exist or isn't\n * accessible. Stop doc-update traffic (poll loop, update POSTs, collab SSE\n * handling) so we don't spam the console with errors against it. The\n * awareness SSE subscription stays (matches previous behavior).\n */\n private markDocMissing(): void {\n this.docMissing = true;\n this.flushPendingUpdates(true);\n this.detachUpdateHandler();\n this.stopSync();\n this.setSnapshot({ isLoading: false, isSynced: true });\n }\n\n // -------------------------------------------------------------------------\n // Local update batching\n // -------------------------------------------------------------------------\n\n private handleDocUpdate = (update: Uint8Array, origin: unknown): void => {\n if (origin === \"remote\") return;\n this.pendingUpdates.push(update);\n if (this.flushTimer) clearTimeout(this.flushTimer);\n this.flushTimer = setTimeout(\n () => this.flushPendingUpdates(),\n UPDATE_DEBOUNCE_MS,\n );\n };\n\n private handlePageHide = (): void => {\n this.flushPendingUpdates(true /* keepalive */);\n };\n\n private attachUpdateHandler(): void {\n if (this.updateHandlerAttached || this.docMissing) return;\n this.updateHandlerAttached = true;\n this.ydoc.on(\"update\", this.handleDocUpdate);\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"pagehide\", this.handlePageHide);\n }\n }\n\n private detachUpdateHandler(): void {\n if (!this.updateHandlerAttached) return;\n this.updateHandlerAttached = false;\n this.ydoc.off(\"update\", this.handleDocUpdate);\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"pagehide\", this.handlePageHide);\n }\n }\n\n private flushPendingUpdates(keepalive = false): void {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n if (this.pendingUpdates.length === 0) return;\n const toSend = this.pendingUpdates;\n this.pendingUpdates = [];\n\n const merged = toSend.length === 1 ? toSend[0] : Y.mergeUpdates(toSend);\n fetch(`${this.baseUrl}/${this.docId}/update`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n update: uint8ArrayToBase64(merged),\n requestSource: this.requestSource,\n }),\n ...(keepalive ? { keepalive: true } : {}),\n }).catch(() => {});\n }\n\n // -------------------------------------------------------------------------\n // Remote sync: SSE fast path + poll fallback loop\n // -------------------------------------------------------------------------\n\n private startSync(): void {\n if (this.syncActive || this.docMissing || this.disposed) return;\n this.syncActive = true;\n\n this.subscribeCollabEvents();\n\n if (!this.effectivePauseWhenHidden || !isDocumentHidden()) {\n void this.poll();\n }\n window.addEventListener(\"focus\", this.handleFocus);\n document.addEventListener(\"visibilitychange\", this.handleVisibilityChange);\n }\n\n private stopSync(): void {\n if (!this.syncActive) return;\n this.syncActive = false;\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.unsubscribeCollabEvents?.();\n this.unsubscribeCollabEvents = null;\n this.sseSubscribedWithPause = null;\n window.removeEventListener(\"focus\", this.handleFocus);\n document.removeEventListener(\n \"visibilitychange\",\n this.handleVisibilityChange,\n );\n }\n\n /**\n * SSE fast-path: subscribe to the SHARED framework transport for\n * /_agent-native/events instead of opening a dedicated EventSource per\n * collab doc. A tab holds exactly one SSE connection regardless of how many\n * docs are mounted. Collab update events arrive push-style; we apply them\n * immediately, avoiding ~2s polling latency for peer edits.\n *\n * NOTE: SSE events are subject to the same server-side access scoping as\n * polling — the server only pushes events that canSeeChangeForUser allows.\n */\n private subscribeCollabEvents(): void {\n const pauseWhenHidden = this.effectivePauseWhenHidden;\n this.sseSubscribedWithPause = pauseWhenHidden;\n this.unsubscribeCollabEvents = subscribeSyncEvents({\n onEvents: (events) => {\n if (this.disposed || !this.syncActive) return;\n for (const change of events) this.handleSharedEvent(change);\n },\n onSseStateChange: (connected) => {\n this.sseActive = connected;\n if (connected) this.consecutiveErrors = 0;\n },\n pauseWhenHidden,\n });\n }\n\n /**\n * The pause-when-hidden aggregate can flip when subscribers join or leave;\n * the shared-transport subscription captures it at subscribe time, so\n * re-subscribe when it changes.\n */\n private resubscribeCollabEventsIfPauseChanged(): void {\n if (!this.syncActive) return;\n if (this.sseSubscribedWithPause === this.effectivePauseWhenHidden) return;\n this.unsubscribeCollabEvents?.();\n this.subscribeCollabEvents();\n }\n\n private handleSharedEvent(change: SyncEvent): void {\n if (\n change.source === \"collab\" &&\n change.docId === this.docId &&\n typeof change.update === \"string\"\n ) {\n // Own echo — skip entirely (including the version tracking below, so\n // the next poll re-delivers and dedupe happens there; matches the\n // previous per-hook behavior).\n if (this.requestSource && change.requestSource === this.requestSource) {\n return;\n }\n try {\n Y.applyUpdate(this.ydoc, base64ToUint8Array(change.update), \"remote\");\n } catch {\n // Malformed update — trigger state-vector fetch on next poll\n }\n\n if (change.requestSource === \"agent\") {\n this.markAgentActive();\n }\n }\n\n // Keep the poll cursor updated from shared-transport events so the poll\n // loop starts from the right version when SSE drops.\n if (typeof change.version === \"number\") {\n this.pollVersion = Math.max(this.pollVersion, change.version);\n }\n }\n\n private getActivePollInterval(): number {\n return this.sseActive\n ? this.effectivePollIntervalWithSse\n : this.effectivePollInterval;\n }\n\n private schedulePoll(): void {\n if (!this.syncActive || this.disposed) return;\n if (this.effectivePauseWhenHidden && isDocumentHidden()) return;\n this.pollTimer = setTimeout(() => {\n this.pollTimer = null;\n void this.poll();\n }, this.getActivePollInterval());\n }\n\n /**\n * A subscriber joined or left, potentially changing the effective interval.\n * If a poll is already scheduled, reschedule with the new cadence (the next\n * natural tick would pick it up anyway; this just avoids a stale long wait\n * when a faster subscriber joins).\n */\n private reschedulePoll(): void {\n if (this.pollTimer === null) return;\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n this.schedulePoll();\n }\n\n private async fetchStateVector(): Promise<void> {\n try {\n const stateVector = uint8ArrayToBase64(Y.encodeStateVector(this.ydoc));\n const stateRes = await fetch(\n `${this.baseUrl}/${this.docId}/state?stateVector=${encodeURIComponent(stateVector)}`,\n );\n if (stateRes.ok) {\n const stateData = (await stateRes.json().catch(() => null)) as {\n state?: string;\n } | null;\n if (this.disposed) return;\n if (stateData?.state) {\n const binary = base64ToUint8Array(stateData.state);\n if (binary.length > 2) {\n Y.applyUpdate(this.ydoc, binary, \"remote\");\n }\n }\n }\n } catch {\n // Non-fatal; the next poll cycle will retry\n }\n }\n\n private async poll(): Promise<void> {\n if (!this.syncActive || this.disposed) return;\n\n // Flush any pending local updates before polling so the server has the\n // latest state before we read remote changes.\n this.flushPendingUpdates();\n\n try {\n const res = await fetch(\n agentNativePath(`/_agent-native/poll?since=${this.pollVersion}`),\n );\n if (!res.ok) throw new Error(\"HTTP \" + res.status);\n\n const data = await res.json();\n if (!this.syncActive || this.disposed) return;\n const { version, events } = data as {\n version: number;\n events: Array<{\n source: string;\n docId?: string;\n update?: string;\n requestSource?: string;\n }>;\n };\n\n // Detect ring-buffer overflow: if the version jumped by more than the\n // ring buffer size, some events were evicted and we need a state-vector\n // fetch to reconcile the gap.\n const versionGap = version - this.lastPolledVersion;\n const hadGap = versionGap > POLL_RING_BUFFER_SIZE;\n\n for (const evt of events) {\n if (evt.source === \"collab\" && evt.docId === this.docId && evt.update) {\n if (this.requestSource && evt.requestSource === this.requestSource) {\n continue;\n }\n try {\n Y.applyUpdate(this.ydoc, base64ToUint8Array(evt.update), \"remote\");\n } catch {\n // Failed to apply — fetch full state-vector below\n await this.fetchStateVector();\n }\n\n if (evt.requestSource === \"agent\") {\n this.markAgentActive();\n }\n }\n }\n\n this.pollVersion = version;\n this.lastPolledVersion = version;\n this.pollCycleCount++;\n this.consecutiveErrors = 0;\n\n // Fetch state-vector only when needed:\n // 1. Ring-buffer overflow detected (missed events).\n // 2. Low-frequency safety net every STATE_VECTOR_FETCH_INTERVAL cycles.\n // 3. NOT on every cycle (the previous behavior causing 3 requests/cycle).\n const shouldFetchStateVector =\n hadGap || this.pollCycleCount % STATE_VECTOR_FETCH_INTERVAL === 0;\n\n if (shouldFetchStateVector) {\n await this.fetchStateVector();\n }\n\n // Sync awareness (cursor positions)\n const localState = this.awareness.getLocalState();\n if (localState && !this.disposed) {\n try {\n const awarenessRes = await fetch(\n `${this.baseUrl}/${this.docId}/awareness`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId: this.ydoc.clientID,\n state: JSON.stringify(localState),\n }),\n },\n );\n if (awarenessRes.ok && !this.disposed) {\n const awarenessData = await awarenessRes.json();\n const remoteStates: RemoteAwarenessSnapshot[] = [];\n for (const remote of awarenessData.states || []) {\n try {\n const remoteState = JSON.parse(remote.state);\n remoteStates.push({\n clientId: Number(remote.clientId),\n state: remoteState,\n });\n } catch {\n // Invalid state — skip\n }\n }\n if (this.disposed) return;\n const changes = reconcileRemoteAwarenessStates(\n this.awareness.getStates() as Map<number, unknown>,\n this.ydoc.clientID,\n remoteStates,\n );\n if (\n changes.added.length ||\n changes.updated.length ||\n changes.removed.length\n ) {\n this.awareness.emit(\"change\", [changes, \"remote\"]);\n }\n }\n } catch {\n // Awareness sync failure is non-fatal\n }\n }\n } catch {\n // Network error — exponential backoff\n this.consecutiveErrors++;\n const backoff = calcBackoff(this.consecutiveErrors);\n if (this.syncActive && !this.disposed) {\n this.pollTimer = setTimeout(() => {\n this.pollTimer = null;\n void this.poll();\n }, backoff);\n }\n return;\n }\n\n this.schedulePoll();\n }\n\n private pollNow(): void {\n if (!this.syncActive || this.disposed) return;\n if (this.effectivePauseWhenHidden && isDocumentHidden()) return;\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n void this.poll();\n }\n\n /**\n * Publish this tab's visibility to peers. A hidden tab pauses its poll, so\n * we push the state immediately (keepalive) instead of waiting for the next\n * cycle — otherwise peers keep treating a backgrounded tab as the visible\n * lead and an agent edit never lands on the tab the user is actually viewing.\n */\n private publishVisibility(visible: boolean): void {\n this.awareness.setLocalStateField(\"visible\", visible);\n const localState = this.awareness.getLocalState();\n if (!localState) return;\n fetch(`${this.baseUrl}/${this.docId}/awareness`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId: this.ydoc.clientID,\n state: JSON.stringify(localState),\n }),\n keepalive: true,\n }).catch(() => {});\n }\n\n private handleVisibilityChange = (): void => {\n const visible = document.visibilityState === \"visible\";\n this.publishVisibility(visible);\n if (visible) {\n // Also flush any pending updates when coming back into view\n this.flushPendingUpdates();\n this.pollNow();\n } else if (this.effectivePauseWhenHidden && this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n };\n\n private handleFocus = (): void => {\n this.pollNow();\n };\n\n // -------------------------------------------------------------------------\n // Awareness SSE fast-path\n // -------------------------------------------------------------------------\n\n private applyAwarenessEvent(data: SyncEvent): void {\n if (\n data.source !== \"awareness\" ||\n data.type !== \"awareness-change\" ||\n data.docId !== this.docId\n ) {\n return;\n }\n const states = Array.isArray(data.states)\n ? (data.states as Array<{ clientId: number; state: string }>)\n : [];\n const remoteStates: RemoteAwarenessSnapshot[] = [];\n for (const remote of states) {\n try {\n remoteStates.push({\n clientId: Number(remote.clientId),\n state: JSON.parse(remote.state),\n });\n } catch {\n // Invalid state entry — skip\n }\n }\n const changes = reconcileRemoteAwarenessStates(\n this.awareness.getStates() as Map<number, unknown>,\n this.ydoc.clientID,\n remoteStates,\n );\n if (\n changes.added.length ||\n changes.updated.length ||\n changes.removed.length\n ) {\n this.awareness.emit(\"change\", [changes, \"remote\"]);\n }\n }\n}\n\n/**\n * Registry of active connections keyed by \"<baseUrl>\\0<docId>\".\n * Module-level singleton: survives React render cycles, shared across all\n * hook instances in the same browser tab.\n */\nconst collabConnectionRegistry = new Map<string, CollabDocConnection>();\n\nfunction collabRegistryKey(docId: string, baseUrl: string): string {\n return `${baseUrl}\\0${docId}`;\n}\n\nfunction getOrCreateCollabConnection(\n docId: string,\n baseUrl: string,\n): CollabDocConnection {\n const key = collabRegistryKey(docId, baseUrl);\n let conn = collabConnectionRegistry.get(key);\n if (!conn || conn.disposed) {\n conn = new CollabDocConnection(docId, baseUrl);\n collabConnectionRegistry.set(key, conn);\n }\n return conn;\n}\n\n// ---------------------------------------------------------------------------\n// Internal test helpers — reset/inspect the connection registry between tests.\n// ---------------------------------------------------------------------------\n/** @internal */\nexport function _resetCollabDocRegistryForTests(): void {\n for (const conn of Array.from(collabConnectionRegistry.values())) {\n conn.dispose();\n }\n collabConnectionRegistry.clear();\n}\n\n/** @internal — current registry size, for leak assertions in tests. */\nexport function _collabDocRegistrySizeForTests(): number {\n return collabConnectionRegistry.size;\n}\n\nexport function useCollaborativeDoc(\n options: UseCollaborativeDocOptions,\n): UseCollaborativeDocResult {\n const {\n docId,\n pollInterval = 2000,\n pollIntervalWithSse = 12000,\n pauseWhenHidden = true,\n baseUrl = agentNativePath(\"/_agent-native/collab\"),\n requestSource,\n user,\n } = options;\n\n // Bumped when the effect finds the memoized connection was disposed in the\n // render→effect gap (rare: a suspended transition outliving the linger\n // window) so the memo below re-acquires a live one.\n const [generation, setGeneration] = useState(0);\n\n // Shared connection per docId. Acquired during render so the Y.Doc identity\n // is available on first render (TipTap binds on mount); get-or-create is\n // idempotent, so StrictMode's double render returns the same instance.\n const conn = useMemo(() => {\n void generation;\n if (!docId) return null;\n if (typeof window === \"undefined\") {\n // SSR render: per-hook detached doc, never started (matches the\n // previous per-hook behavior on the server).\n return new CollabDocConnection(docId, baseUrl, true);\n }\n return getOrCreateCollabConnection(docId, baseUrl);\n }, [docId, baseUrl, generation]);\n\n const [snapshot, setSnapshot] = useState<CollabDocSnapshot>(() =>\n conn ? conn.snapshot : EMPTY_SNAPSHOT,\n );\n\n // Render-time reset when the connection identity changes (docId switch) so\n // consumers never see the previous doc's state for a frame.\n const [prevConn, setPrevConn] = useState(conn);\n if (prevConn !== conn) {\n setPrevConn(conn);\n setSnapshot(conn ? conn.snapshot : EMPTY_SNAPSHOT);\n }\n\n // Subscribe (ref-count) — the first subscriber starts the connection, the\n // last one leaving schedules its teardown.\n useEffect(() => {\n if (!conn || conn.detached) return;\n if (conn.disposed) {\n // Disposed between render and effect — re-acquire a live connection.\n setGeneration((g) => g + 1);\n return;\n }\n const id = Symbol(\"useCollaborativeDoc\");\n conn.add(id, {\n pollInterval,\n pollIntervalWithSse,\n pauseWhenHidden,\n requestSource,\n onSnapshot: setSnapshot,\n });\n // Re-sync in case the connection changed state between render and effect.\n setSnapshot(conn.snapshot);\n return () => {\n conn.remove(id);\n };\n }, [conn, pollInterval, pollIntervalWithSse, pauseWhenHidden, requestSource]);\n\n // Publish local user identity for cursor labels (set once per tab; the\n // connection dedupes repeated identical identities across subscribers).\n useEffect(() => {\n if (!conn || conn.detached || !user) return;\n conn.setUser({ name: user.name, email: user.email, color: user.color });\n }, [conn, user?.name, user?.email, user?.color]);\n\n return {\n ydoc: conn ? conn.ydoc : null,\n awareness: conn ? conn.awareness : null,\n isLoading: snapshot.isLoading,\n isSynced: snapshot.isSynced,\n activeUsers: snapshot.activeUsers,\n agentActive: snapshot.agentActive,\n agentPresent: snapshot.agentPresent,\n };\n}\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/collab/client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Client-side hook for collaborative document editing via Yjs.\n *\n * Creates a STABLE Y.Doc per docId that never changes identity. This allows\n * TipTap's Collaboration extension to bind once without editor recreation.\n * Server state is applied to the existing doc when it arrives.\n *\n * Also manages Yjs Awareness for cursor positions and user presence,\n * synced via polling to the server's awareness endpoint.\n *\n * Connection sharing: connections live in a module-level, ref-counted registry\n * keyed by docId (mirroring the SyncTransport registry in use-db-sync.ts).\n * Every `useCollaborativeDoc` mount for the same docId attaches to ONE shared\n * connection — one Y.Doc, one Awareness, one state fetch, one poll loop, one\n * awareness POST cycle — instead of each hook instance opening an independent\n * connection and doubling all collab traffic (e.g. a presence bar and the\n * editor mounting the hook for the same doc). The first subscriber starts the\n * connection; the last one leaving tears it down after a short linger so\n * StrictMode double-mounts and rapid unmount/remounts don't thrash the doc.\n *\n * Transport improvements (vs previous version):\n * - Local update POSTs are debounced and coalesced with Y.mergeUpdates (~80ms)\n * to avoid per-keystroke requests. The batch is flushed immediately on\n * visibilitychange/pagehide and before each poll/awareness cycle.\n * - GET state?stateVector= is NOT fetched on every poll cycle. It is fetched:\n * (a) on (re)connect / initial load, (b) when a poll response indicates a\n * gap (version jump > ring-buffer size), (c) after applying an update fails,\n * and (d) as a low-frequency safety net every STATE_VECTOR_FETCH_INTERVAL\n * poll cycles (~15×).\n * - Network errors use exponential backoff with jitter (cap ~15s), reset on\n * success.\n * - SSE fast-path: collab events are received push-style from\n * /_agent-native/events (the framework SSE stream). While SSE is\n * healthy the poll loop relaxes to a slow cadence (10–15s). If SSE is\n * unavailable the 2s poll resumes automatically.\n */\n\nimport {\n dedupeCollabUsersByEmail,\n type CollabUser,\n} from \"@agent-native/toolkit/collab-ui\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { Awareness } from \"y-protocols/awareness\";\nimport * as Y from \"yjs\";\n\nimport { agentNativePath } from \"../client/api-path.js\";\nimport { subscribeSyncEvents, type SyncEvent } from \"../client/use-db-sync.js\";\nimport { REALTIME_CAP_NO_AWARENESS } from \"../realtime-protocol.js\";\nexport {\n dedupeCollabUsersByEmail,\n emailToColor,\n emailToName,\n isReconcileLeadClient,\n type CollabUser,\n} from \"@agent-native/toolkit/collab-ui\";\n\nexport interface UseCollaborativeDocOptions {\n /** Document ID to collaborate on. Pass null to disable. */\n docId: string | null;\n /** Poll interval in ms when SSE is unavailable. Default: 2000 */\n pollInterval?: number;\n /** Poll interval in ms while SSE is healthy. Default: 12000 */\n pollIntervalWithSse?: number;\n /** Pause remote update/presence polling while the tab is hidden. Default: true */\n pauseWhenHidden?: boolean;\n /** Base URL for collab endpoints. Default: \"/_agent-native/collab\" */\n baseUrl?: string;\n /** Request source ID for jitter prevention (e.g., tab ID). */\n requestSource?: string;\n /** Current user info for cursor labels. */\n user?: CollabUser;\n}\n\nexport interface UseCollaborativeDocResult {\n /** The Yjs document instance. Stable per docId — never changes identity. */\n ydoc: Y.Doc | null;\n /** Yjs Awareness instance for cursor/presence sync. */\n awareness: Awareness | null;\n /** Whether the initial state is still loading from the server. */\n isLoading: boolean;\n /** Whether the doc is synced with the server. */\n isSynced: boolean;\n /** Active users on this document (from awareness). */\n activeUsers: CollabUser[];\n /** True briefly when the AI agent makes an edit (for presence indicator). */\n agentActive: boolean;\n /** True when the AI agent has an active awareness entry (durable presence). */\n agentPresent: boolean;\n}\n\nfunction isDocumentHidden(): boolean {\n return (\n typeof document !== \"undefined\" && document.visibilityState === \"hidden\"\n );\n}\n\n/**\n * Content equality for deduped active-user lists. `dedupeCollabUsersByEmail`\n * always allocates a fresh array (and fresh per-user objects), so callers\n * that only want to know \"did the actual user set change\" need this instead\n * of an identity check. Order-sensitive: the dedupe helper iterates awareness\n * states in a stable Map insertion order, so a reordering here does reflect\n * a real change (a client joined/left and re-joined).\n */\nfunction collabUsersEqual(a: CollabUser[], b: CollabUser[]): boolean {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const left = a[i]!;\n const right = b[i]!;\n if (\n left.email !== right.email ||\n left.name !== right.name ||\n left.color !== right.color\n ) {\n return false;\n }\n }\n return true;\n}\n\nexport interface RemoteAwarenessSnapshot {\n clientId: number;\n state: unknown;\n}\n\nexport function reconcileRemoteAwarenessStates(\n states: Map<number, unknown>,\n localClientId: number,\n remoteStates: RemoteAwarenessSnapshot[],\n): { added: number[]; updated: number[]; removed: number[] } {\n const incoming = new Set<number>();\n const added: number[] = [];\n const updated: number[] = [];\n const removed: number[] = [];\n\n for (const remote of remoteStates) {\n if (\n !Number.isFinite(remote.clientId) ||\n remote.clientId === localClientId\n ) {\n continue;\n }\n incoming.add(remote.clientId);\n const hadState = states.has(remote.clientId);\n states.set(remote.clientId, remote.state);\n (hadState ? updated : added).push(remote.clientId);\n }\n\n for (const clientId of Array.from(states.keys())) {\n if (clientId === localClientId) continue;\n if (incoming.has(clientId)) continue;\n states.delete(clientId);\n removed.push(clientId);\n }\n\n return { added, updated, removed };\n}\n\n// Base64 helpers\nfunction uint8ArrayToBase64(arr: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < arr.length; i++) {\n binary += String.fromCharCode(arr[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToUint8Array(b64: string): Uint8Array {\n const binary = atob(b64);\n const arr = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n arr[i] = binary.charCodeAt(i);\n }\n return arr;\n}\n\n/** Debounce delay for coalescing local Yjs update POSTs (ms). */\nconst UPDATE_DEBOUNCE_MS = 80;\n\n/** Fetch state-vector every N poll cycles as a low-frequency safety net. */\nconst STATE_VECTOR_FETCH_INTERVAL = 15;\n\n/** Poll ring-buffer size on the server (MAX_BUFFER in poll.ts). */\nconst POLL_RING_BUFFER_SIZE = 200;\n\n/** Exponential backoff: base delay (ms), multiplier, cap (ms). */\nconst BACKOFF_BASE_MS = 500;\nconst BACKOFF_MAX_MS = 15_000;\n\nfunction calcBackoff(consecutiveErrors: number): number {\n const exp = Math.min(consecutiveErrors, 10);\n const delay = BACKOFF_BASE_MS * Math.pow(2, exp);\n // Add jitter: ±25%\n const jitter = delay * 0.25 * (Math.random() * 2 - 1);\n return Math.min(delay + jitter, BACKOFF_MAX_MS);\n}\n\n// ---------------------------------------------------------------------------\n// Fast awareness helper — throttled per (docId, ydocId) pair so multiple\n// setLocalStateField calls within a 150ms window are coalesced into one POST.\n// ---------------------------------------------------------------------------\n\nconst _awarenessThrottleTimers = new Map<\n string,\n ReturnType<typeof setTimeout>\n>();\n\nfunction scheduleAwarenessPush(\n baseUrl: string,\n docId: string,\n clientId: number,\n getState: () => Record<string, unknown> | null,\n): void {\n if (typeof window === \"undefined\") return;\n const key = `${docId}::${clientId}`;\n if (_awarenessThrottleTimers.has(key)) return; // already scheduled\n\n const timer = setTimeout(() => {\n _awarenessThrottleTimers.delete(key);\n const state = getState();\n fetch(`${baseUrl}/${docId}/awareness`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId,\n state: state ? JSON.stringify(state) : null,\n }),\n }).catch(() => {}); // best-effort; poll cycle is the baseline fallback\n }, 150);\n\n _awarenessThrottleTimers.set(key, timer);\n}\n\n// ---------------------------------------------------------------------------\n// Shared per-docId collab connection\n//\n// One CollabDocConnection per (baseUrl, docId) pair is held in a module-level\n// registry (the same pattern as SyncTransport in use-db-sync.ts). Every\n// `useCollaborativeDoc` mount for a docId subscribes to the shared connection,\n// so a tab runs exactly ONE state fetch, ONE poll fallback loop, and ONE\n// awareness POST cycle per document no matter how many components mount the\n// hook (e.g. a presence bar + the editor for the same doc).\n//\n// Lifecycle: the connection is created on first render that needs it (the\n// Y.Doc must exist during render so TipTap can bind on mount), starts its\n// network activity when the first subscriber attaches, and is disposed a short\n// linger after the last subscriber detaches. The linger keeps StrictMode's\n// mount→unmount→mount and rapid route remounts from destroying and refetching\n// the doc, and doubles as the cleanup path for connections created by a render\n// that React later discarded (no subscriber ever attaches).\n// ---------------------------------------------------------------------------\n\n/**\n * Shared reactive state each subscriber mirrors into its own React state.\n * Replaced immutably on every change so hooks can bail out on identity.\n */\ninterface CollabDocSnapshot {\n isLoading: boolean;\n isSynced: boolean;\n activeUsers: CollabUser[];\n agentActive: boolean;\n agentPresent: boolean;\n}\n\ninterface CollabDocSubscription {\n /** Requested poll interval; the connection uses the MIN across subscribers. */\n pollInterval: number;\n /** Requested relaxed interval while SSE is healthy (MIN across subscribers). */\n pollIntervalWithSse: number;\n /**\n * Whether this subscriber wants polling paused while the tab is hidden.\n * The connection pauses only when ALL subscribers request it.\n */\n pauseWhenHidden: boolean;\n /**\n * Echo-suppression tag. The connection adopts the first defined value and\n * uses it for BOTH tagging outgoing updates and filtering incoming echoes,\n * so suppression stays self-consistent even when subscribers pass different\n * per-tab IDs (all values identify the same tab).\n */\n requestSource?: string;\n onSnapshot: (snapshot: CollabDocSnapshot) => void;\n}\n\nconst EMPTY_SNAPSHOT: CollabDocSnapshot = Object.freeze({\n isLoading: false,\n isSynced: false,\n activeUsers: [],\n agentActive: false,\n agentPresent: false,\n});\n\n/**\n * How long a connection with zero subscribers lingers before disposal.\n * Long enough to absorb StrictMode double-mounts and route-level remounts\n * (which would otherwise destroy the Y.Doc, refetch server state, and lose\n * echo-suppression context), short enough that navigating away stops the\n * doc's traffic promptly.\n */\nconst DISPOSE_LINGER_MS = 1000;\n\nclass CollabDocConnection {\n readonly ydoc: Y.Doc;\n readonly awareness: Awareness;\n /** Immutable snapshot of the shared reactive state (replaced on change). */\n snapshot: CollabDocSnapshot;\n disposed = false;\n\n private subscribers = new Map<symbol, CollabDocSubscription>();\n private disposeTimer: ReturnType<typeof setTimeout> | null = null;\n private started = false;\n private docMissing = false;\n private requestSource: string | undefined;\n private lastSetUser: CollabUser | null = null;\n\n // Local-update batching (debounced + coalesced with Y.mergeUpdates).\n private pendingUpdates: Uint8Array[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private updateHandlerAttached = false;\n\n // Poll loop + SSE fast path.\n private syncActive = false;\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n private consecutiveErrors = 0;\n private pollCycleCount = 0;\n private pollVersion = 0;\n private lastPolledVersion = 0;\n private sseActive = false;\n // Whether the active SSE stream actually forwards awareness. The hosted\n // Realtime Gateway advertises `no-awareness` (it can't see the in-process\n // awareness emitter), so on that transport we must NOT relax the presence\n // poll cadence — otherwise remote cursors go stale. In-process SSE forwards\n // awareness and sends no handshake, so this stays true there.\n private sseAwarenessCovered = false;\n private sseSubscribedWithPause: boolean | null = null;\n private unsubscribeCollabEvents: (() => void) | null = null;\n private unsubscribeAwarenessEvents: (() => void) | null = null;\n private agentTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(\n readonly docId: string,\n private readonly baseUrl: string,\n /**\n * Detached connections are created for SSR renders only: a per-hook\n * Y.Doc/Awareness with no registry entry and no network activity\n * (matching the previous per-hook behavior on the server). They are\n * never started and are reclaimed by GC with the render.\n */\n readonly detached = false,\n ) {\n this.ydoc = new Y.Doc();\n this.awareness = new Awareness(this.ydoc);\n this.snapshot = {\n isLoading: true,\n isSynced: false,\n activeUsers: [],\n agentActive: false,\n agentPresent: false,\n };\n\n // Track active users / agent presence from awareness changes, and drive\n // the fast (throttled) awareness push once a local user is published.\n this.awareness.on(\"change\", this.handleAwarenessChange);\n\n // Orphan guard: the connection is created during render (the Y.Doc must\n // exist before mount), so a discarded render can create one that never\n // gets a subscriber. The linger timer disposes it; the first `add()`\n // cancels the timer.\n if (!detached) this.scheduleDispose();\n }\n\n private get registryKey(): string {\n return collabRegistryKey(this.docId, this.baseUrl);\n }\n\n // -------------------------------------------------------------------------\n // Subscriber management (ref-counting)\n // -------------------------------------------------------------------------\n\n add(id: symbol, sub: CollabDocSubscription): void {\n this.subscribers.set(id, sub);\n if (this.disposeTimer) {\n clearTimeout(this.disposeTimer);\n this.disposeTimer = null;\n }\n // Adopt the first defined echo-suppression tag for the connection's\n // lifetime (see CollabDocSubscription.requestSource).\n if (!this.requestSource && sub.requestSource) {\n this.requestSource = sub.requestSource;\n }\n if (!this.started) {\n this.started = true;\n this.start();\n } else {\n // A joining subscriber can change the effective poll cadence or the\n // pause-when-hidden aggregate; pick both up without waiting a cycle.\n this.resubscribeCollabEventsIfPauseChanged();\n this.reschedulePoll();\n }\n }\n\n remove(id: symbol): void {\n this.subscribers.delete(id);\n if (this.subscribers.size === 0) {\n this.scheduleDispose();\n } else {\n this.resubscribeCollabEventsIfPauseChanged();\n this.reschedulePoll();\n }\n }\n\n private scheduleDispose(): void {\n if (this.disposeTimer) clearTimeout(this.disposeTimer);\n this.disposeTimer = setTimeout(() => {\n this.disposeTimer = null;\n this.dispose();\n }, DISPOSE_LINGER_MS);\n }\n\n /**\n * Tear the connection down: stop all loops and listeners, flush any pending\n * local updates, clear awareness local state (via destroy), and destroy the\n * Y.Doc. Also used by the test-only registry reset. @internal\n */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n if (this.disposeTimer) {\n clearTimeout(this.disposeTimer);\n this.disposeTimer = null;\n }\n this.stopSync();\n this.unsubscribeAwarenessEvents?.();\n this.unsubscribeAwarenessEvents = null;\n this.flushPendingUpdates(true);\n this.detachUpdateHandler();\n if (this.agentTimer) {\n clearTimeout(this.agentTimer);\n this.agentTimer = null;\n }\n // Detach our change listener BEFORE destroy so the destroy-time\n // `setLocalState(null)` does not schedule a push (matches the previous\n // per-hook effect cleanup ordering).\n this.awareness.off(\"change\", this.handleAwarenessChange);\n this.awareness.destroy();\n this.ydoc.destroy();\n if (collabConnectionRegistry.get(this.registryKey) === this) {\n collabConnectionRegistry.delete(this.registryKey);\n }\n }\n\n // -------------------------------------------------------------------------\n // Derived settings (aggregate over active subscribers)\n // -------------------------------------------------------------------------\n\n private get effectivePollInterval(): number {\n let min = Infinity;\n for (const sub of this.subscribers.values()) {\n if (sub.pollInterval < min) min = sub.pollInterval;\n }\n return isFinite(min) ? min : 2000;\n }\n\n private get effectivePollIntervalWithSse(): number {\n let min = Infinity;\n for (const sub of this.subscribers.values()) {\n if (sub.pollIntervalWithSse < min) min = sub.pollIntervalWithSse;\n }\n return isFinite(min) ? min : 12_000;\n }\n\n private get effectivePauseWhenHidden(): boolean {\n // Pause only if every subscriber has opted in.\n for (const sub of this.subscribers.values()) {\n if (!sub.pauseWhenHidden) return false;\n }\n return true;\n }\n\n // -------------------------------------------------------------------------\n // Shared state fan-out\n // -------------------------------------------------------------------------\n\n private setSnapshot(patch: Partial<CollabDocSnapshot>): void {\n this.snapshot = { ...this.snapshot, ...patch };\n for (const sub of this.subscribers.values()) {\n sub.onSnapshot(this.snapshot);\n }\n }\n\n private markAgentActive(): void {\n this.setSnapshot({ agentActive: true });\n if (this.agentTimer) clearTimeout(this.agentTimer);\n this.agentTimer = setTimeout(() => {\n this.agentTimer = null;\n this.setSnapshot({ agentActive: false });\n }, 3000);\n }\n\n // -------------------------------------------------------------------------\n // Awareness: local identity, active-user tracking, fast push\n // -------------------------------------------------------------------------\n\n /**\n * Publish the local user identity (cursor label) and current visibility.\n * Set once per tab — repeated calls with the same identity are no-ops so\n * multiple subscribers for the same user don't re-emit awareness updates.\n */\n setUser(user: CollabUser): void {\n if (this.disposed) return;\n const prev = this.lastSetUser;\n if (\n prev &&\n prev.name === user.name &&\n prev.email === user.email &&\n prev.color === user.color\n ) {\n return;\n }\n this.lastSetUser = {\n name: user.name,\n email: user.email,\n color: user.color,\n };\n this.awareness.setLocalStateField(\"user\", {\n name: user.name,\n email: user.email,\n color: user.color,\n });\n // Also publish this tab's visibility so peers can elect a VISIBLE client\n // to apply external snapshots (see isReconcileLeadClient) — a backgrounded\n // tab pauses its poll and must not hold that role.\n this.awareness.setLocalStateField(\"visible\", !isDocumentHidden());\n }\n\n private handleAwarenessChange = (\n _changes?: { added: number[]; updated: number[]; removed: number[] },\n origin?: unknown,\n ): void => {\n const users: CollabUser[] = [];\n let hasAgent = false;\n this.awareness.getStates().forEach((state, clientId) => {\n if (clientId === this.ydoc.clientID) return; // Skip self\n if (state.user) {\n users.push(state.user as CollabUser);\n if ((state.user as CollabUser).email === \"agent@system\") {\n hasAgent = true;\n }\n }\n });\n const nextActiveUsers = dedupeCollabUsersByEmail(users);\n // Awareness \"change\" fires for every remote broadcast — including cursor\n // jiggles, re-published-but-unchanged heartbeat state, and edits to\n // presence fields (recentEdits, selection) that don't affect who's\n // active. dedupeCollabUsersByEmail always returns a fresh array, so\n // without this comparison every one of those events would hand\n // `activeUsers` a new identity and force every consumer (e.g. a\n // full-page editor keying a useMemo/effect off it) to re-render even\n // though the actual user list is unchanged. Reuse the previous\n // reference when the deduped content is the same, matching the\n // stable-ref discipline `usePresence`'s shallowEqualOthers already\n // applies to `others`.\n const activeUsers = collabUsersEqual(\n this.snapshot.activeUsers,\n nextActiveUsers,\n )\n ? this.snapshot.activeUsers\n : nextActiveUsers;\n if (\n activeUsers !== this.snapshot.activeUsers ||\n hasAgent !== this.snapshot.agentPresent\n ) {\n this.setSnapshot({ activeUsers, agentPresent: hasAgent });\n }\n\n // Fast awareness push: whenever OUR OWN awareness state changes (e.g.\n // cursor moves, setPresence() calls), schedule a throttled POST so peers\n // receive updates at ~150ms instead of waiting for the next poll cycle.\n // Gated on origin === \"local\": this listener also fires for remote\n // awareness changes (poll/SSE call `awareness.emit(\"change\", [changes,\n // \"remote\"])` after reconciling incoming states — see\n // applyAwarenessEvent/poll above). Without the origin check, every\n // peer's cursor move would also re-trigger THIS client to re-broadcast\n // its own unchanged state, turning one person moving their mouse into\n // O(n) redundant POSTs from every other connected client (an awareness\n // storm that gets worse as more people join the doc). Only active once a\n // local user identity has been published (matches the previous per-hook\n // gating on `user`). The poll cycle remains the authoritative baseline.\n if (this.lastSetUser && origin === \"local\" && !this.disposed) {\n scheduleAwarenessPush(\n this.baseUrl,\n this.docId,\n this.ydoc.clientID,\n () => this.awareness.getLocalState() as Record<string, unknown> | null,\n );\n }\n };\n\n // -------------------------------------------------------------------------\n // Startup: initial state fetch, update handler, sync loops\n // -------------------------------------------------------------------------\n\n private start(): void {\n this.attachUpdateHandler();\n this.fetchInitialState();\n this.startSync();\n\n // SSE fast-path for awareness: listen on the SHARED framework transport\n // and apply awareness-change events immediately so peers receive cursor\n // moves push-style without waiting for the next poll cycle. Kept alive\n // even when the doc is missing (matches the previous per-hook behavior).\n this.unsubscribeAwarenessEvents = subscribeSyncEvents({\n onEvents: (events) => {\n if (this.disposed) return;\n for (const data of events) this.applyAwarenessEvent(data);\n },\n });\n }\n\n private fetchInitialState(): void {\n fetch(`${this.baseUrl}/${this.docId}/state`)\n .then(async (res) => {\n if (this.disposed) return;\n if (res.status === 404 || res.status === 403) {\n this.markDocMissing();\n return;\n }\n const data = (await res.json().catch(() => null)) as {\n state?: string;\n } | null;\n if (this.disposed) return;\n if (data?.state) {\n const binary = base64ToUint8Array(data.state);\n if (binary.length > 4) {\n Y.applyUpdate(this.ydoc, binary, \"remote\");\n }\n }\n this.setSnapshot({ isLoading: false, isSynced: true });\n })\n .catch(() => {\n if (this.disposed) return;\n this.setSnapshot({ isLoading: false, isSynced: true });\n });\n }\n\n /**\n * The initial state fetch returned 404/403 — the doc doesn't exist or isn't\n * accessible. Stop doc-update traffic (poll loop, update POSTs, collab SSE\n * handling) so we don't spam the console with errors against it. The\n * awareness SSE subscription stays (matches previous behavior).\n */\n private markDocMissing(): void {\n this.docMissing = true;\n this.flushPendingUpdates(true);\n this.detachUpdateHandler();\n this.stopSync();\n this.setSnapshot({ isLoading: false, isSynced: true });\n }\n\n // -------------------------------------------------------------------------\n // Local update batching\n // -------------------------------------------------------------------------\n\n private handleDocUpdate = (update: Uint8Array, origin: unknown): void => {\n if (origin === \"remote\") return;\n this.pendingUpdates.push(update);\n if (this.flushTimer) clearTimeout(this.flushTimer);\n this.flushTimer = setTimeout(\n () => this.flushPendingUpdates(),\n UPDATE_DEBOUNCE_MS,\n );\n };\n\n private handlePageHide = (): void => {\n this.flushPendingUpdates(true /* keepalive */);\n };\n\n private attachUpdateHandler(): void {\n if (this.updateHandlerAttached || this.docMissing) return;\n this.updateHandlerAttached = true;\n this.ydoc.on(\"update\", this.handleDocUpdate);\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"pagehide\", this.handlePageHide);\n }\n }\n\n private detachUpdateHandler(): void {\n if (!this.updateHandlerAttached) return;\n this.updateHandlerAttached = false;\n this.ydoc.off(\"update\", this.handleDocUpdate);\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"pagehide\", this.handlePageHide);\n }\n }\n\n private flushPendingUpdates(keepalive = false): void {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n if (this.pendingUpdates.length === 0) return;\n const toSend = this.pendingUpdates;\n this.pendingUpdates = [];\n\n const merged = toSend.length === 1 ? toSend[0] : Y.mergeUpdates(toSend);\n fetch(`${this.baseUrl}/${this.docId}/update`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n update: uint8ArrayToBase64(merged),\n requestSource: this.requestSource,\n }),\n ...(keepalive ? { keepalive: true } : {}),\n }).catch(() => {});\n }\n\n // -------------------------------------------------------------------------\n // Remote sync: SSE fast path + poll fallback loop\n // -------------------------------------------------------------------------\n\n private startSync(): void {\n if (this.syncActive || this.docMissing || this.disposed) return;\n this.syncActive = true;\n\n this.subscribeCollabEvents();\n\n if (!this.effectivePauseWhenHidden || !isDocumentHidden()) {\n void this.poll();\n }\n window.addEventListener(\"focus\", this.handleFocus);\n document.addEventListener(\"visibilitychange\", this.handleVisibilityChange);\n }\n\n private stopSync(): void {\n if (!this.syncActive) return;\n this.syncActive = false;\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.unsubscribeCollabEvents?.();\n this.unsubscribeCollabEvents = null;\n this.sseSubscribedWithPause = null;\n window.removeEventListener(\"focus\", this.handleFocus);\n document.removeEventListener(\n \"visibilitychange\",\n this.handleVisibilityChange,\n );\n }\n\n /**\n * SSE fast-path: subscribe to the SHARED framework transport for\n * /_agent-native/events instead of opening a dedicated EventSource per\n * collab doc. A tab holds exactly one SSE connection regardless of how many\n * docs are mounted. Collab update events arrive push-style; we apply them\n * immediately, avoiding ~2s polling latency for peer edits.\n *\n * NOTE: SSE events are subject to the same server-side access scoping as\n * polling — the server only pushes events that canSeeChangeForUser allows.\n */\n private subscribeCollabEvents(): void {\n const pauseWhenHidden = this.effectivePauseWhenHidden;\n this.sseSubscribedWithPause = pauseWhenHidden;\n this.unsubscribeCollabEvents = subscribeSyncEvents({\n onEvents: (events) => {\n if (this.disposed || !this.syncActive) return;\n for (const change of events) this.handleSharedEvent(change);\n },\n onSseStateChange: (connected, capabilities) => {\n const wasActive = this.sseActive;\n this.sseActive = connected;\n // Only treat SSE as covering awareness when it's connected AND does not\n // advertise `no-awareness` (the hosted gateway does). Drives whether we\n // relax the presence poll — see getActivePollInterval.\n const awarenessCovered =\n connected && !capabilities?.includes(REALTIME_CAP_NO_AWARENESS);\n const coverageFlipped = awarenessCovered !== this.sseAwarenessCovered;\n this.sseAwarenessCovered = awarenessCovered;\n // The gateway handshake lands AFTER onopen, so a relaxed poll timer\n // scheduled at connect can already be pending when `no-awareness`\n // arrives. Reschedule only for that mid-connection capability flip so\n // the fast presence cadence applies immediately; connect/disconnect\n // transitions keep the pre-existing next-natural-tick behavior.\n if (coverageFlipped && connected === wasActive) this.reschedulePoll();\n if (connected) this.consecutiveErrors = 0;\n },\n pauseWhenHidden,\n });\n }\n\n /**\n * The pause-when-hidden aggregate can flip when subscribers join or leave;\n * the shared-transport subscription captures it at subscribe time, so\n * re-subscribe when it changes.\n */\n private resubscribeCollabEventsIfPauseChanged(): void {\n if (!this.syncActive) return;\n if (this.sseSubscribedWithPause === this.effectivePauseWhenHidden) return;\n this.unsubscribeCollabEvents?.();\n this.subscribeCollabEvents();\n }\n\n private handleSharedEvent(change: SyncEvent): void {\n if (\n change.source === \"collab\" &&\n change.docId === this.docId &&\n typeof change.update === \"string\"\n ) {\n // Own echo — skip entirely (including the version tracking below, so\n // the next poll re-delivers and dedupe happens there; matches the\n // previous per-hook behavior).\n if (this.requestSource && change.requestSource === this.requestSource) {\n return;\n }\n try {\n Y.applyUpdate(this.ydoc, base64ToUint8Array(change.update), \"remote\");\n } catch {\n // Malformed update — trigger state-vector fetch on next poll\n }\n\n if (change.requestSource === \"agent\") {\n this.markAgentActive();\n }\n }\n\n // Keep the poll cursor updated from shared-transport events so the poll\n // loop starts from the right version when SSE drops.\n if (typeof change.version === \"number\") {\n this.pollVersion = Math.max(this.pollVersion, change.version);\n }\n }\n\n private getActivePollInterval(): number {\n // Relax to the slow cadence only when SSE is genuinely carrying awareness;\n // on a `no-awareness` hosted stream keep the fast cadence so presence/\n // cursor state doesn't go stale (the gateway doesn't forward awareness).\n return this.sseActive && this.sseAwarenessCovered\n ? this.effectivePollIntervalWithSse\n : this.effectivePollInterval;\n }\n\n private schedulePoll(): void {\n if (!this.syncActive || this.disposed) return;\n if (this.effectivePauseWhenHidden && isDocumentHidden()) return;\n this.pollTimer = setTimeout(() => {\n this.pollTimer = null;\n void this.poll();\n }, this.getActivePollInterval());\n }\n\n /**\n * A subscriber joined or left, potentially changing the effective interval.\n * If a poll is already scheduled, reschedule with the new cadence (the next\n * natural tick would pick it up anyway; this just avoids a stale long wait\n * when a faster subscriber joins).\n */\n private reschedulePoll(): void {\n if (this.pollTimer === null) return;\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n this.schedulePoll();\n }\n\n private async fetchStateVector(): Promise<void> {\n try {\n const stateVector = uint8ArrayToBase64(Y.encodeStateVector(this.ydoc));\n const stateRes = await fetch(\n `${this.baseUrl}/${this.docId}/state?stateVector=${encodeURIComponent(stateVector)}`,\n );\n if (stateRes.ok) {\n const stateData = (await stateRes.json().catch(() => null)) as {\n state?: string;\n } | null;\n if (this.disposed) return;\n if (stateData?.state) {\n const binary = base64ToUint8Array(stateData.state);\n if (binary.length > 2) {\n Y.applyUpdate(this.ydoc, binary, \"remote\");\n }\n }\n }\n } catch {\n // Non-fatal; the next poll cycle will retry\n }\n }\n\n private async poll(): Promise<void> {\n if (!this.syncActive || this.disposed) return;\n\n // Flush any pending local updates before polling so the server has the\n // latest state before we read remote changes.\n this.flushPendingUpdates();\n\n try {\n const res = await fetch(\n agentNativePath(`/_agent-native/poll?since=${this.pollVersion}`),\n );\n if (!res.ok) throw new Error(\"HTTP \" + res.status);\n\n const data = await res.json();\n if (!this.syncActive || this.disposed) return;\n const { version, events } = data as {\n version: number;\n events: Array<{\n source: string;\n docId?: string;\n update?: string;\n requestSource?: string;\n }>;\n };\n\n // Detect ring-buffer overflow: if the version jumped by more than the\n // ring buffer size, some events were evicted and we need a state-vector\n // fetch to reconcile the gap.\n const versionGap = version - this.lastPolledVersion;\n const hadGap = versionGap > POLL_RING_BUFFER_SIZE;\n\n for (const evt of events) {\n if (evt.source === \"collab\" && evt.docId === this.docId && evt.update) {\n if (this.requestSource && evt.requestSource === this.requestSource) {\n continue;\n }\n try {\n Y.applyUpdate(this.ydoc, base64ToUint8Array(evt.update), \"remote\");\n } catch {\n // Failed to apply — fetch full state-vector below\n await this.fetchStateVector();\n }\n\n if (evt.requestSource === \"agent\") {\n this.markAgentActive();\n }\n }\n }\n\n this.pollVersion = version;\n this.lastPolledVersion = version;\n this.pollCycleCount++;\n this.consecutiveErrors = 0;\n\n // Fetch state-vector only when needed:\n // 1. Ring-buffer overflow detected (missed events).\n // 2. Low-frequency safety net every STATE_VECTOR_FETCH_INTERVAL cycles.\n // 3. NOT on every cycle (the previous behavior causing 3 requests/cycle).\n const shouldFetchStateVector =\n hadGap || this.pollCycleCount % STATE_VECTOR_FETCH_INTERVAL === 0;\n\n if (shouldFetchStateVector) {\n await this.fetchStateVector();\n }\n\n // Sync awareness (cursor positions)\n const localState = this.awareness.getLocalState();\n if (localState && !this.disposed) {\n try {\n const awarenessRes = await fetch(\n `${this.baseUrl}/${this.docId}/awareness`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId: this.ydoc.clientID,\n state: JSON.stringify(localState),\n }),\n },\n );\n if (awarenessRes.ok && !this.disposed) {\n const awarenessData = await awarenessRes.json();\n const remoteStates: RemoteAwarenessSnapshot[] = [];\n for (const remote of awarenessData.states || []) {\n try {\n const remoteState = JSON.parse(remote.state);\n remoteStates.push({\n clientId: Number(remote.clientId),\n state: remoteState,\n });\n } catch {\n // Invalid state — skip\n }\n }\n if (this.disposed) return;\n const changes = reconcileRemoteAwarenessStates(\n this.awareness.getStates() as Map<number, unknown>,\n this.ydoc.clientID,\n remoteStates,\n );\n if (\n changes.added.length ||\n changes.updated.length ||\n changes.removed.length\n ) {\n this.awareness.emit(\"change\", [changes, \"remote\"]);\n }\n }\n } catch {\n // Awareness sync failure is non-fatal\n }\n }\n } catch {\n // Network error — exponential backoff\n this.consecutiveErrors++;\n const backoff = calcBackoff(this.consecutiveErrors);\n if (this.syncActive && !this.disposed) {\n this.pollTimer = setTimeout(() => {\n this.pollTimer = null;\n void this.poll();\n }, backoff);\n }\n return;\n }\n\n this.schedulePoll();\n }\n\n private pollNow(): void {\n if (!this.syncActive || this.disposed) return;\n if (this.effectivePauseWhenHidden && isDocumentHidden()) return;\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n void this.poll();\n }\n\n /**\n * Publish this tab's visibility to peers. A hidden tab pauses its poll, so\n * we push the state immediately (keepalive) instead of waiting for the next\n * cycle — otherwise peers keep treating a backgrounded tab as the visible\n * lead and an agent edit never lands on the tab the user is actually viewing.\n */\n private publishVisibility(visible: boolean): void {\n this.awareness.setLocalStateField(\"visible\", visible);\n const localState = this.awareness.getLocalState();\n if (!localState) return;\n fetch(`${this.baseUrl}/${this.docId}/awareness`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId: this.ydoc.clientID,\n state: JSON.stringify(localState),\n }),\n keepalive: true,\n }).catch(() => {});\n }\n\n private handleVisibilityChange = (): void => {\n const visible = document.visibilityState === \"visible\";\n this.publishVisibility(visible);\n if (visible) {\n // Also flush any pending updates when coming back into view\n this.flushPendingUpdates();\n this.pollNow();\n } else if (this.effectivePauseWhenHidden && this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n };\n\n private handleFocus = (): void => {\n this.pollNow();\n };\n\n // -------------------------------------------------------------------------\n // Awareness SSE fast-path\n // -------------------------------------------------------------------------\n\n private applyAwarenessEvent(data: SyncEvent): void {\n if (\n data.source !== \"awareness\" ||\n data.type !== \"awareness-change\" ||\n data.docId !== this.docId\n ) {\n return;\n }\n const states = Array.isArray(data.states)\n ? (data.states as Array<{ clientId: number; state: string }>)\n : [];\n const remoteStates: RemoteAwarenessSnapshot[] = [];\n for (const remote of states) {\n try {\n remoteStates.push({\n clientId: Number(remote.clientId),\n state: JSON.parse(remote.state),\n });\n } catch {\n // Invalid state entry — skip\n }\n }\n const changes = reconcileRemoteAwarenessStates(\n this.awareness.getStates() as Map<number, unknown>,\n this.ydoc.clientID,\n remoteStates,\n );\n if (\n changes.added.length ||\n changes.updated.length ||\n changes.removed.length\n ) {\n this.awareness.emit(\"change\", [changes, \"remote\"]);\n }\n }\n}\n\n/**\n * Registry of active connections keyed by \"<baseUrl>\\0<docId>\".\n * Module-level singleton: survives React render cycles, shared across all\n * hook instances in the same browser tab.\n */\nconst collabConnectionRegistry = new Map<string, CollabDocConnection>();\n\nfunction collabRegistryKey(docId: string, baseUrl: string): string {\n return `${baseUrl}\\0${docId}`;\n}\n\nfunction getOrCreateCollabConnection(\n docId: string,\n baseUrl: string,\n): CollabDocConnection {\n const key = collabRegistryKey(docId, baseUrl);\n let conn = collabConnectionRegistry.get(key);\n if (!conn || conn.disposed) {\n conn = new CollabDocConnection(docId, baseUrl);\n collabConnectionRegistry.set(key, conn);\n }\n return conn;\n}\n\n// ---------------------------------------------------------------------------\n// Internal test helpers — reset/inspect the connection registry between tests.\n// ---------------------------------------------------------------------------\n/** @internal */\nexport function _resetCollabDocRegistryForTests(): void {\n for (const conn of Array.from(collabConnectionRegistry.values())) {\n conn.dispose();\n }\n collabConnectionRegistry.clear();\n}\n\n/** @internal — current registry size, for leak assertions in tests. */\nexport function _collabDocRegistrySizeForTests(): number {\n return collabConnectionRegistry.size;\n}\n\nexport function useCollaborativeDoc(\n options: UseCollaborativeDocOptions,\n): UseCollaborativeDocResult {\n const {\n docId,\n pollInterval = 2000,\n pollIntervalWithSse = 12000,\n pauseWhenHidden = true,\n baseUrl = agentNativePath(\"/_agent-native/collab\"),\n requestSource,\n user,\n } = options;\n\n // Bumped when the effect finds the memoized connection was disposed in the\n // render→effect gap (rare: a suspended transition outliving the linger\n // window) so the memo below re-acquires a live one.\n const [generation, setGeneration] = useState(0);\n\n // Shared connection per docId. Acquired during render so the Y.Doc identity\n // is available on first render (TipTap binds on mount); get-or-create is\n // idempotent, so StrictMode's double render returns the same instance.\n const conn = useMemo(() => {\n void generation;\n if (!docId) return null;\n if (typeof window === \"undefined\") {\n // SSR render: per-hook detached doc, never started (matches the\n // previous per-hook behavior on the server).\n return new CollabDocConnection(docId, baseUrl, true);\n }\n return getOrCreateCollabConnection(docId, baseUrl);\n }, [docId, baseUrl, generation]);\n\n const [snapshot, setSnapshot] = useState<CollabDocSnapshot>(() =>\n conn ? conn.snapshot : EMPTY_SNAPSHOT,\n );\n\n // Render-time reset when the connection identity changes (docId switch) so\n // consumers never see the previous doc's state for a frame.\n const [prevConn, setPrevConn] = useState(conn);\n if (prevConn !== conn) {\n setPrevConn(conn);\n setSnapshot(conn ? conn.snapshot : EMPTY_SNAPSHOT);\n }\n\n // Subscribe (ref-count) — the first subscriber starts the connection, the\n // last one leaving schedules its teardown.\n useEffect(() => {\n if (!conn || conn.detached) return;\n if (conn.disposed) {\n // Disposed between render and effect — re-acquire a live connection.\n setGeneration((g) => g + 1);\n return;\n }\n const id = Symbol(\"useCollaborativeDoc\");\n conn.add(id, {\n pollInterval,\n pollIntervalWithSse,\n pauseWhenHidden,\n requestSource,\n onSnapshot: setSnapshot,\n });\n // Re-sync in case the connection changed state between render and effect.\n setSnapshot(conn.snapshot);\n return () => {\n conn.remove(id);\n };\n }, [conn, pollInterval, pollIntervalWithSse, pauseWhenHidden, requestSource]);\n\n // Publish local user identity for cursor labels (set once per tab; the\n // connection dedupes repeated identical identities across subscribers).\n useEffect(() => {\n if (!conn || conn.detached || !user) return;\n conn.setUser({ name: user.name, email: user.email, color: user.color });\n }, [conn, user?.name, user?.email, user?.color]);\n\n return {\n ydoc: conn ? conn.ydoc : null,\n awareness: conn ? conn.awareness : null,\n isLoading: snapshot.isLoading,\n isSynced: snapshot.isSynced,\n activeUsers: snapshot.activeUsers,\n agentActive: snapshot.agentActive,\n agentPresent: snapshot.agentPresent,\n };\n}\n"]}
@@ -39,7 +39,7 @@ import { type DbExec } from "./client.js";
39
39
  * This is a plain read (no lock), so it never blocks on an `ACCESS EXCLUSIVE`
40
40
  * lock the way `CREATE`/`ALTER` would.
41
41
  */
42
- export declare function pgTableExists(table: string, injectedClient?: DbExec): Promise<boolean>;
42
+ export declare function pgTableExists(table: string, injectedClient?: DbExec, dialectIsPostgres?: boolean): Promise<boolean>;
43
43
  /**
44
44
  * True when running against Postgres AND the given column already exists on the
45
45
  * given table in the `public` schema. Returns `false` on SQLite, for invalid
@@ -58,7 +58,7 @@ export declare function pgColumnExists(table: string, column: string, injectedCl
58
58
  * connection this can hang just like a `CREATE`/`ALTER` would; checking first
59
59
  * skips the lock on the already-migrated hot path.
60
60
  */
61
- export declare function pgIndexExists(indexName: string, injectedClient?: DbExec): Promise<boolean>;
61
+ export declare function pgIndexExists(indexName: string, injectedClient?: DbExec, dialectIsPostgres?: boolean): Promise<boolean>;
62
62
  /**
63
63
  * Probe → guarded-DDL → re-probe sequence for one piece of on-demand schema.
64
64
  *
@@ -96,6 +96,8 @@ export declare function ensureSchemaObject(options: {
96
96
  lockTimeout?: string;
97
97
  /** Injectable client for tests. */
98
98
  injectedClient?: DbExec;
99
+ /** Dialect override for injected per-app clients; defaults to the global. */
100
+ dialectIsPostgres?: boolean;
99
101
  }): Promise<boolean>;
100
102
  /**
101
103
  * Convenience wrapper: ensure a TABLE exists (probe via `pgTableExists`).
@@ -105,6 +107,7 @@ export declare function ensureSchemaObject(options: {
105
107
  export declare function ensureTableExists(table: string, createSql: string, options?: {
106
108
  lockTimeout?: string;
107
109
  injectedClient?: DbExec;
110
+ dialectIsPostgres?: boolean;
108
111
  }): Promise<boolean>;
109
112
  /**
110
113
  * Convenience wrapper: ensure a COLUMN exists (probe via `pgColumnExists`).
@@ -121,6 +124,7 @@ export declare function ensureColumnExists(table: string, column: string, addCol
121
124
  export declare function ensureIndexExists(indexName: string, createIndexSql: string, options?: {
122
125
  lockTimeout?: string;
123
126
  injectedClient?: DbExec;
127
+ dialectIsPostgres?: boolean;
124
128
  }): Promise<boolean>;
125
129
  /** True when an error looks like a Postgres `lock_timeout` (SQLSTATE 55P03). */
126
130
  export declare function isLockTimeoutError(err: unknown): boolean;
@@ -147,5 +151,6 @@ export declare function isLockTimeoutError(err: unknown): boolean;
147
151
  export declare function runGuardedDdl(ddl: string, options?: {
148
152
  lockTimeout?: string;
149
153
  injectedClient?: DbExec;
154
+ dialectIsPostgres?: boolean;
150
155
  }): Promise<boolean>;
151
156
  //# sourceMappingURL=ddl-guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ddl-guard.d.ts","sourceRoot":"","sources":["../../src/db/ddl-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAyB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAIjE;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,OAAO,CAAC,CAelB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,OAAO,CAAC,CAiBlB;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,OAAO,CAAC,CAalB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,qEAAqE;IACrE,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC,OAAO,CAAC,CAiBnB;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9D,OAAO,CAAC,OAAO,CAAC,CAQlB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9D,OAAO,CAAC,OAAO,CAAC,CAQlB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9D,OAAO,CAAC,OAAO,CAAC,CAQlB;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAKxD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9D,OAAO,CAAC,OAAO,CAAC,CAmClB"}
1
+ {"version":3,"file":"ddl-guard.d.ts","sourceRoot":"","sources":["../../src/db/ddl-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAyB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAIjE;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,MAAM,EACvB,iBAAiB,CAAC,EAAE,OAAO,GAC1B,OAAO,CAAC,OAAO,CAAC,CAoBlB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,OAAO,CAAC,CAiBlB;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,cAAc,CAAC,EAAE,MAAM,EACvB,iBAAiB,CAAC,EAAE,OAAO,GAC1B,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,qEAAqE;IACrE,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,GAAG,OAAO,CAAC,OAAO,CAAC,CAsBnB;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CACxB,GACL,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9D,OAAO,CAAC,OAAO,CAAC,CAQlB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IACP,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CACxB,GACL,OAAO,CAAC,OAAO,CAAC,CAclB;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAKxD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IACP,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CACxB,GACL,OAAO,CAAC,OAAO,CAAC,CAmClB"}
@@ -40,9 +40,13 @@ const PLAIN_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
40
40
  * This is a plain read (no lock), so it never blocks on an `ACCESS EXCLUSIVE`
41
41
  * lock the way `CREATE`/`ALTER` would.
42
42
  */
43
- export async function pgTableExists(table, injectedClient) {
44
- if (!isPostgres() || !PLAIN_IDENTIFIER.test(table))
43
+ export async function pgTableExists(table, injectedClient, dialectIsPostgres) {
44
+ // The dialect override travels with an injected client: a multi-app process
45
+ // (the hosted Realtime Gateway) probes a per-app Postgres DB even when its
46
+ // own process-global DB is absent or SQLite.
47
+ if (!(dialectIsPostgres ?? isPostgres()) || !PLAIN_IDENTIFIER.test(table)) {
45
48
  return false;
49
+ }
46
50
  const client = injectedClient ?? getDbExec();
47
51
  try {
48
52
  const { rows } = await client.execute({
@@ -95,9 +99,11 @@ export async function pgColumnExists(table, column, injectedClient) {
95
99
  * connection this can hang just like a `CREATE`/`ALTER` would; checking first
96
100
  * skips the lock on the already-migrated hot path.
97
101
  */
98
- export async function pgIndexExists(indexName, injectedClient) {
99
- if (!isPostgres() || !PLAIN_IDENTIFIER.test(indexName))
102
+ export async function pgIndexExists(indexName, injectedClient, dialectIsPostgres) {
103
+ if (!(dialectIsPostgres ?? isPostgres()) ||
104
+ !PLAIN_IDENTIFIER.test(indexName)) {
100
105
  return false;
106
+ }
101
107
  const client = injectedClient ?? getDbExec();
102
108
  try {
103
109
  const { rows } = await client.execute({
@@ -138,10 +144,14 @@ export async function pgIndexExists(indexName, injectedClient) {
138
144
  * `false` only when it already existed up front (no DDL issued).
139
145
  */
140
146
  export async function ensureSchemaObject(options) {
141
- const { probe, ddl, label, lockTimeout, injectedClient } = options;
147
+ const { probe, ddl, label, lockTimeout, injectedClient, dialectIsPostgres } = options;
142
148
  if (await probe())
143
149
  return false;
144
- const ran = await runGuardedDdl(ddl, { lockTimeout, injectedClient });
150
+ const ran = await runGuardedDdl(ddl, {
151
+ lockTimeout,
152
+ injectedClient,
153
+ dialectIsPostgres,
154
+ });
145
155
  if (ran)
146
156
  return true;
147
157
  // The DDL was swallowed by a lock-timeout. The object is virtually always
@@ -163,11 +173,12 @@ export async function ensureSchemaObject(options) {
163
173
  */
164
174
  export async function ensureTableExists(table, createSql, options = {}) {
165
175
  return ensureSchemaObject({
166
- probe: () => pgTableExists(table, options.injectedClient),
176
+ probe: () => pgTableExists(table, options.injectedClient, options.dialectIsPostgres),
167
177
  ddl: createSql,
168
178
  label: `table ${table}`,
169
179
  lockTimeout: options.lockTimeout,
170
180
  injectedClient: options.injectedClient,
181
+ dialectIsPostgres: options.dialectIsPostgres,
171
182
  });
172
183
  }
173
184
  /**
@@ -189,11 +200,12 @@ export async function ensureColumnExists(table, column, addColumnSql, options =
189
200
  */
190
201
  export async function ensureIndexExists(indexName, createIndexSql, options = {}) {
191
202
  return ensureSchemaObject({
192
- probe: () => pgIndexExists(indexName, options.injectedClient),
203
+ probe: () => pgIndexExists(indexName, options.injectedClient, options.dialectIsPostgres),
193
204
  ddl: createIndexSql,
194
205
  label: `index ${indexName}`,
195
206
  lockTimeout: options.lockTimeout,
196
207
  injectedClient: options.injectedClient,
208
+ dialectIsPostgres: options.dialectIsPostgres,
197
209
  });
198
210
  }
199
211
  /** True when an error looks like a Postgres `lock_timeout` (SQLSTATE 55P03). */
@@ -226,7 +238,7 @@ export function isLockTimeoutError(err) {
226
238
  */
227
239
  export async function runGuardedDdl(ddl, options = {}) {
228
240
  const client = options.injectedClient ?? getDbExec();
229
- if (!isPostgres()) {
241
+ if (!(options.dialectIsPostgres ?? isPostgres())) {
230
242
  await client.execute(ddl);
231
243
  return true;
232
244
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ddl-guard.js","sourceRoot":"","sources":["../../src/db/ddl-guard.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Guards for on-demand `ensureTable()` DDL so the common already-migrated path\n * takes NO `ACCESS EXCLUSIVE` lock on Postgres.\n *\n * Lives in its own module (like `./widen-columns.js`) so stores can import it\n * without every `vi.mock(\"../db/client.js\")` test needing to stub it: the\n * helpers resolve `isPostgres()` / `getDbExec()` through `client.js`, so a test\n * that mocks the client to SQLite (`isPostgres: () => false`) makes the\n * Postgres-only existence checks no-ops automatically.\n *\n * ## Why\n *\n * `ensureTable()` runs once per process on first DB touch. In a long-lived Node\n * server the cost is paid once and is invisible. In a Netlify `-background`\n * function the process is fresh, so this is the FIRST touch — and the\n * `CREATE TABLE`/`ALTER TABLE ... ADD COLUMN` DDL it issues takes an\n * `ACCESS EXCLUSIVE` lock on the shared Neon Postgres database. Behind a\n * concurrent connection that already holds a conflicting lock, that DDL can\n * block ~indefinitely (observed >16s hangs in the bg worker vs ~1s inline).\n *\n * The tables/columns are essentially always already present in production, so\n * the DDL is redundant in the hot path. These helpers let a store cheaply check\n * `information_schema` first and only issue DDL when something is actually\n * missing — and when DDL must run, wrap it in a short `lock_timeout` so a\n * contended lock fails fast instead of hanging.\n *\n * All of this is Postgres-only behaviour gated on `isPostgres()`. On SQLite\n * (local dev) there is no such lock problem, so callers keep their existing\n * behaviour there.\n */\n\nimport { isPostgres, getDbExec, type DbExec } from \"./client.js\";\n\nconst PLAIN_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * True when running against Postgres AND the given table already exists in the\n * `public` schema. Returns `false` on SQLite (callers gate their own behaviour\n * there), for invalid identifiers, or when `information_schema` is unreadable —\n * the conservative answer \"not known to exist\" makes the caller fall through to\n * its idempotent `CREATE TABLE IF NOT EXISTS`, preserving today's behaviour.\n *\n * This is a plain read (no lock), so it never blocks on an `ACCESS EXCLUSIVE`\n * lock the way `CREATE`/`ALTER` would.\n */\nexport async function pgTableExists(\n table: string,\n injectedClient?: DbExec,\n): Promise<boolean> {\n if (!isPostgres() || !PLAIN_IDENTIFIER.test(table)) return false;\n const client = injectedClient ?? getDbExec();\n try {\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM information_schema.tables\n WHERE table_schema = 'public' AND table_name = ? LIMIT 1`,\n args: [table],\n });\n return rows.length > 0;\n } catch {\n // information_schema unreadable (permissions / non-standard backend) —\n // report \"unknown\" so the caller falls back to IF NOT EXISTS.\n return false;\n }\n}\n\n/**\n * True when running against Postgres AND the given column already exists on the\n * given table in the `public` schema. Returns `false` on SQLite, for invalid\n * identifiers, or when `information_schema` is unreadable.\n *\n * Plain read — no lock taken.\n */\nexport async function pgColumnExists(\n table: string,\n column: string,\n injectedClient?: DbExec,\n): Promise<boolean> {\n if (!isPostgres()) return false;\n if (!PLAIN_IDENTIFIER.test(table) || !PLAIN_IDENTIFIER.test(column)) {\n return false;\n }\n const client = injectedClient ?? getDbExec();\n try {\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM information_schema.columns\n WHERE table_schema = 'public' AND table_name = ? AND column_name = ?\n LIMIT 1`,\n args: [table, column],\n });\n return rows.length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * True when running against Postgres AND an index with the given name already\n * exists in the `public` schema. Returns `false` on SQLite, for invalid\n * identifiers, or when `pg_indexes` is unreadable.\n *\n * `CREATE INDEX` (without CONCURRENTLY) takes a `SHARE` lock that blocks\n * writes, so on a fresh background-worker process behind a concurrent\n * connection this can hang just like a `CREATE`/`ALTER` would; checking first\n * skips the lock on the already-migrated hot path.\n */\nexport async function pgIndexExists(\n indexName: string,\n injectedClient?: DbExec,\n): Promise<boolean> {\n if (!isPostgres() || !PLAIN_IDENTIFIER.test(indexName)) return false;\n const client = injectedClient ?? getDbExec();\n try {\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM pg_indexes\n WHERE schemaname = 'public' AND indexname = ? LIMIT 1`,\n args: [indexName],\n });\n return rows.length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Probe → guarded-DDL → re-probe sequence for one piece of on-demand schema.\n *\n * This is the single safe primitive every `ensureTable()` should use so a\n * swallowed `lock_timeout` can NEVER poison a store's init memo with missing\n * schema. The flow:\n *\n * 1. `probe()` — cheap, lock-free existence check (pgTableExists / etc.).\n * Already present → return `false` (nothing to do; the hot path takes NO\n * lock).\n * 2. Missing → run `ddl` through `runGuardedDdl` (bounded `lock_timeout`).\n * - DDL completed → return `true`.\n * - DDL was swallowed by a lock-timeout (`runGuardedDdl` returned `false`)\n * → RE-PROBE. A concurrent connection virtually always created the\n * object meanwhile, so the re-probe usually now reports it present →\n * return `true`. But if it is STILL missing, the required schema does\n * NOT exist and we must NOT let the caller memoize success — so THROW.\n * The caller's `_initPromise` rejects and the next call retries instead\n * of running forever against absent schema.\n *\n * On SQLite the probe is always `false` (helpers no-op there) and the SQLite\n * branch of each store keeps its own create-then-catch behaviour, so this is a\n * Postgres-path primitive in practice. Returns `true` when the object exists\n * after this call (either pre-existing-after-timeout-race or freshly created),\n * `false` only when it already existed up front (no DDL issued).\n */\nexport async function ensureSchemaObject(options: {\n /** Lock-free existence check; `true` ⇒ already present, skip DDL. */\n probe: () => Promise<boolean>;\n /** DDL to run only when `probe()` reports the object missing. */\n ddl: string;\n /** Human-readable name of what's being ensured, for the error message. */\n label: string;\n /** Forwarded to `runGuardedDdl`. */\n lockTimeout?: string;\n /** Injectable client for tests. */\n injectedClient?: DbExec;\n}): Promise<boolean> {\n const { probe, ddl, label, lockTimeout, injectedClient } = options;\n if (await probe()) return false;\n const ran = await runGuardedDdl(ddl, { lockTimeout, injectedClient });\n if (ran) return true;\n // The DDL was swallowed by a lock-timeout. The object is virtually always\n // already correct by the time a contended boot retries (a concurrent\n // connection created it), so re-probe before giving up.\n if (await probe()) return true;\n // Still missing after a swallowed timeout: do NOT memoize success with absent\n // schema. Throw so the caller's init promise rejects and the next call\n // retries, rather than leaving ensureTable \"initialized\" against a table/\n // column/index that does not exist.\n throw new Error(\n `ensureSchemaObject: required schema \"${label}\" is still missing after a ` +\n `lock-timed-out DDL; refusing to memoize init success. The next call will retry.`,\n );\n}\n\n/**\n * Convenience wrapper: ensure a TABLE exists (probe via `pgTableExists`).\n * No-op-returns `false` on SQLite (probe is always false there) — callers run\n * the SQLite create on their own branch, so this is used on the Postgres path.\n */\nexport async function ensureTableExists(\n table: string,\n createSql: string,\n options: { lockTimeout?: string; injectedClient?: DbExec } = {},\n): Promise<boolean> {\n return ensureSchemaObject({\n probe: () => pgTableExists(table, options.injectedClient),\n ddl: createSql,\n label: `table ${table}`,\n lockTimeout: options.lockTimeout,\n injectedClient: options.injectedClient,\n });\n}\n\n/**\n * Convenience wrapper: ensure a COLUMN exists (probe via `pgColumnExists`).\n * `addColumnSql` should be the full `ALTER TABLE … ADD COLUMN IF NOT EXISTS …`.\n */\nexport async function ensureColumnExists(\n table: string,\n column: string,\n addColumnSql: string,\n options: { lockTimeout?: string; injectedClient?: DbExec } = {},\n): Promise<boolean> {\n return ensureSchemaObject({\n probe: () => pgColumnExists(table, column, options.injectedClient),\n ddl: addColumnSql,\n label: `column ${table}.${column}`,\n lockTimeout: options.lockTimeout,\n injectedClient: options.injectedClient,\n });\n}\n\n/**\n * Convenience wrapper: ensure an INDEX exists (probe via `pgIndexExists`).\n * `createIndexSql` should be the full `CREATE INDEX IF NOT EXISTS <name> …`.\n */\nexport async function ensureIndexExists(\n indexName: string,\n createIndexSql: string,\n options: { lockTimeout?: string; injectedClient?: DbExec } = {},\n): Promise<boolean> {\n return ensureSchemaObject({\n probe: () => pgIndexExists(indexName, options.injectedClient),\n ddl: createIndexSql,\n label: `index ${indexName}`,\n lockTimeout: options.lockTimeout,\n injectedClient: options.injectedClient,\n });\n}\n\n/** True when an error looks like a Postgres `lock_timeout` (SQLSTATE 55P03). */\nexport function isLockTimeoutError(err: unknown): boolean {\n const anyErr = err as { code?: unknown; message?: unknown } | null;\n if (anyErr?.code === \"55P03\") return true;\n const msg = String(anyErr?.message ?? anyErr ?? \"\");\n return /lock[_ ]?timeout|canceling statement due to lock timeout/i.test(msg);\n}\n\n/**\n * Run a DDL statement that MUST execute (the schema is actually missing), with\n * a short `lock_timeout` so a contended `ACCESS EXCLUSIVE` lock fails fast\n * instead of hanging the whole process.\n *\n * Postgres path: wrap the DDL in an explicit transaction and set\n * `SET LOCAL lock_timeout` so the timeout is scoped to THIS transaction only\n * and reset automatically on COMMIT/ROLLBACK — it never leaks onto the pooled\n * (session-reused) connection the way a bare `SET lock_timeout` would. If the\n * DbExec has no `transaction` (shouldn't happen for Postgres, but defensively),\n * fall back to a session `SET` + `RESET` in a finally. A lock-timeout error is\n * swallowed: the table/column is virtually always already correct by the time a\n * contended boot retries, and the caller's memoization should still resolve so\n * the path isn't retried in a tight loop. Any non-lock-timeout error rethrows.\n *\n * SQLite path: no lock problem — just run the DDL directly.\n *\n * @returns `true` if the DDL ran to completion, `false` if it was skipped due to\n * a lock-timeout (so the caller can decide whether to log).\n */\nexport async function runGuardedDdl(\n ddl: string,\n options: { lockTimeout?: string; injectedClient?: DbExec } = {},\n): Promise<boolean> {\n const client = options.injectedClient ?? getDbExec();\n if (!isPostgres()) {\n await client.execute(ddl);\n return true;\n }\n\n const lockTimeout = options.lockTimeout ?? \"3s\";\n try {\n if (typeof client.transaction === \"function\") {\n await client.transaction(async (tx) => {\n // SET LOCAL is transaction-scoped: it reverts on COMMIT/ROLLBACK and\n // never persists on the pooled connection.\n await tx.execute(`SET LOCAL lock_timeout = '${lockTimeout}'`);\n await tx.execute(ddl);\n });\n } else {\n // Defensive fallback: no transaction support. Use a session SET and\n // guarantee a RESET so the timeout never leaks onto a reused connection.\n try {\n await client.execute(`SET lock_timeout = '${lockTimeout}'`);\n await client.execute(ddl);\n } finally {\n await client.execute(`RESET lock_timeout`).catch(() => {});\n }\n }\n return true;\n } catch (err) {\n if (isLockTimeoutError(err)) {\n // Contended lock — the schema is virtually always already correct by now.\n // Proceed; the caller's memoization still resolves so we don't loop.\n return false;\n }\n throw err;\n }\n}\n"]}
1
+ {"version":3,"file":"ddl-guard.js","sourceRoot":"","sources":["../../src/db/ddl-guard.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Guards for on-demand `ensureTable()` DDL so the common already-migrated path\n * takes NO `ACCESS EXCLUSIVE` lock on Postgres.\n *\n * Lives in its own module (like `./widen-columns.js`) so stores can import it\n * without every `vi.mock(\"../db/client.js\")` test needing to stub it: the\n * helpers resolve `isPostgres()` / `getDbExec()` through `client.js`, so a test\n * that mocks the client to SQLite (`isPostgres: () => false`) makes the\n * Postgres-only existence checks no-ops automatically.\n *\n * ## Why\n *\n * `ensureTable()` runs once per process on first DB touch. In a long-lived Node\n * server the cost is paid once and is invisible. In a Netlify `-background`\n * function the process is fresh, so this is the FIRST touch — and the\n * `CREATE TABLE`/`ALTER TABLE ... ADD COLUMN` DDL it issues takes an\n * `ACCESS EXCLUSIVE` lock on the shared Neon Postgres database. Behind a\n * concurrent connection that already holds a conflicting lock, that DDL can\n * block ~indefinitely (observed >16s hangs in the bg worker vs ~1s inline).\n *\n * The tables/columns are essentially always already present in production, so\n * the DDL is redundant in the hot path. These helpers let a store cheaply check\n * `information_schema` first and only issue DDL when something is actually\n * missing — and when DDL must run, wrap it in a short `lock_timeout` so a\n * contended lock fails fast instead of hanging.\n *\n * All of this is Postgres-only behaviour gated on `isPostgres()`. On SQLite\n * (local dev) there is no such lock problem, so callers keep their existing\n * behaviour there.\n */\n\nimport { isPostgres, getDbExec, type DbExec } from \"./client.js\";\n\nconst PLAIN_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * True when running against Postgres AND the given table already exists in the\n * `public` schema. Returns `false` on SQLite (callers gate their own behaviour\n * there), for invalid identifiers, or when `information_schema` is unreadable —\n * the conservative answer \"not known to exist\" makes the caller fall through to\n * its idempotent `CREATE TABLE IF NOT EXISTS`, preserving today's behaviour.\n *\n * This is a plain read (no lock), so it never blocks on an `ACCESS EXCLUSIVE`\n * lock the way `CREATE`/`ALTER` would.\n */\nexport async function pgTableExists(\n table: string,\n injectedClient?: DbExec,\n dialectIsPostgres?: boolean,\n): Promise<boolean> {\n // The dialect override travels with an injected client: a multi-app process\n // (the hosted Realtime Gateway) probes a per-app Postgres DB even when its\n // own process-global DB is absent or SQLite.\n if (!(dialectIsPostgres ?? isPostgres()) || !PLAIN_IDENTIFIER.test(table)) {\n return false;\n }\n const client = injectedClient ?? getDbExec();\n try {\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM information_schema.tables\n WHERE table_schema = 'public' AND table_name = ? LIMIT 1`,\n args: [table],\n });\n return rows.length > 0;\n } catch {\n // information_schema unreadable (permissions / non-standard backend) —\n // report \"unknown\" so the caller falls back to IF NOT EXISTS.\n return false;\n }\n}\n\n/**\n * True when running against Postgres AND the given column already exists on the\n * given table in the `public` schema. Returns `false` on SQLite, for invalid\n * identifiers, or when `information_schema` is unreadable.\n *\n * Plain read — no lock taken.\n */\nexport async function pgColumnExists(\n table: string,\n column: string,\n injectedClient?: DbExec,\n): Promise<boolean> {\n if (!isPostgres()) return false;\n if (!PLAIN_IDENTIFIER.test(table) || !PLAIN_IDENTIFIER.test(column)) {\n return false;\n }\n const client = injectedClient ?? getDbExec();\n try {\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM information_schema.columns\n WHERE table_schema = 'public' AND table_name = ? AND column_name = ?\n LIMIT 1`,\n args: [table, column],\n });\n return rows.length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * True when running against Postgres AND an index with the given name already\n * exists in the `public` schema. Returns `false` on SQLite, for invalid\n * identifiers, or when `pg_indexes` is unreadable.\n *\n * `CREATE INDEX` (without CONCURRENTLY) takes a `SHARE` lock that blocks\n * writes, so on a fresh background-worker process behind a concurrent\n * connection this can hang just like a `CREATE`/`ALTER` would; checking first\n * skips the lock on the already-migrated hot path.\n */\nexport async function pgIndexExists(\n indexName: string,\n injectedClient?: DbExec,\n dialectIsPostgres?: boolean,\n): Promise<boolean> {\n if (\n !(dialectIsPostgres ?? isPostgres()) ||\n !PLAIN_IDENTIFIER.test(indexName)\n ) {\n return false;\n }\n const client = injectedClient ?? getDbExec();\n try {\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM pg_indexes\n WHERE schemaname = 'public' AND indexname = ? LIMIT 1`,\n args: [indexName],\n });\n return rows.length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Probe → guarded-DDL → re-probe sequence for one piece of on-demand schema.\n *\n * This is the single safe primitive every `ensureTable()` should use so a\n * swallowed `lock_timeout` can NEVER poison a store's init memo with missing\n * schema. The flow:\n *\n * 1. `probe()` — cheap, lock-free existence check (pgTableExists / etc.).\n * Already present → return `false` (nothing to do; the hot path takes NO\n * lock).\n * 2. Missing → run `ddl` through `runGuardedDdl` (bounded `lock_timeout`).\n * - DDL completed → return `true`.\n * - DDL was swallowed by a lock-timeout (`runGuardedDdl` returned `false`)\n * → RE-PROBE. A concurrent connection virtually always created the\n * object meanwhile, so the re-probe usually now reports it present →\n * return `true`. But if it is STILL missing, the required schema does\n * NOT exist and we must NOT let the caller memoize success — so THROW.\n * The caller's `_initPromise` rejects and the next call retries instead\n * of running forever against absent schema.\n *\n * On SQLite the probe is always `false` (helpers no-op there) and the SQLite\n * branch of each store keeps its own create-then-catch behaviour, so this is a\n * Postgres-path primitive in practice. Returns `true` when the object exists\n * after this call (either pre-existing-after-timeout-race or freshly created),\n * `false` only when it already existed up front (no DDL issued).\n */\nexport async function ensureSchemaObject(options: {\n /** Lock-free existence check; `true` ⇒ already present, skip DDL. */\n probe: () => Promise<boolean>;\n /** DDL to run only when `probe()` reports the object missing. */\n ddl: string;\n /** Human-readable name of what's being ensured, for the error message. */\n label: string;\n /** Forwarded to `runGuardedDdl`. */\n lockTimeout?: string;\n /** Injectable client for tests. */\n injectedClient?: DbExec;\n /** Dialect override for injected per-app clients; defaults to the global. */\n dialectIsPostgres?: boolean;\n}): Promise<boolean> {\n const { probe, ddl, label, lockTimeout, injectedClient, dialectIsPostgres } =\n options;\n if (await probe()) return false;\n const ran = await runGuardedDdl(ddl, {\n lockTimeout,\n injectedClient,\n dialectIsPostgres,\n });\n if (ran) return true;\n // The DDL was swallowed by a lock-timeout. The object is virtually always\n // already correct by the time a contended boot retries (a concurrent\n // connection created it), so re-probe before giving up.\n if (await probe()) return true;\n // Still missing after a swallowed timeout: do NOT memoize success with absent\n // schema. Throw so the caller's init promise rejects and the next call\n // retries, rather than leaving ensureTable \"initialized\" against a table/\n // column/index that does not exist.\n throw new Error(\n `ensureSchemaObject: required schema \"${label}\" is still missing after a ` +\n `lock-timed-out DDL; refusing to memoize init success. The next call will retry.`,\n );\n}\n\n/**\n * Convenience wrapper: ensure a TABLE exists (probe via `pgTableExists`).\n * No-op-returns `false` on SQLite (probe is always false there) — callers run\n * the SQLite create on their own branch, so this is used on the Postgres path.\n */\nexport async function ensureTableExists(\n table: string,\n createSql: string,\n options: {\n lockTimeout?: string;\n injectedClient?: DbExec;\n dialectIsPostgres?: boolean;\n } = {},\n): Promise<boolean> {\n return ensureSchemaObject({\n probe: () =>\n pgTableExists(table, options.injectedClient, options.dialectIsPostgres),\n ddl: createSql,\n label: `table ${table}`,\n lockTimeout: options.lockTimeout,\n injectedClient: options.injectedClient,\n dialectIsPostgres: options.dialectIsPostgres,\n });\n}\n\n/**\n * Convenience wrapper: ensure a COLUMN exists (probe via `pgColumnExists`).\n * `addColumnSql` should be the full `ALTER TABLE … ADD COLUMN IF NOT EXISTS …`.\n */\nexport async function ensureColumnExists(\n table: string,\n column: string,\n addColumnSql: string,\n options: { lockTimeout?: string; injectedClient?: DbExec } = {},\n): Promise<boolean> {\n return ensureSchemaObject({\n probe: () => pgColumnExists(table, column, options.injectedClient),\n ddl: addColumnSql,\n label: `column ${table}.${column}`,\n lockTimeout: options.lockTimeout,\n injectedClient: options.injectedClient,\n });\n}\n\n/**\n * Convenience wrapper: ensure an INDEX exists (probe via `pgIndexExists`).\n * `createIndexSql` should be the full `CREATE INDEX IF NOT EXISTS <name> …`.\n */\nexport async function ensureIndexExists(\n indexName: string,\n createIndexSql: string,\n options: {\n lockTimeout?: string;\n injectedClient?: DbExec;\n dialectIsPostgres?: boolean;\n } = {},\n): Promise<boolean> {\n return ensureSchemaObject({\n probe: () =>\n pgIndexExists(\n indexName,\n options.injectedClient,\n options.dialectIsPostgres,\n ),\n ddl: createIndexSql,\n label: `index ${indexName}`,\n lockTimeout: options.lockTimeout,\n injectedClient: options.injectedClient,\n dialectIsPostgres: options.dialectIsPostgres,\n });\n}\n\n/** True when an error looks like a Postgres `lock_timeout` (SQLSTATE 55P03). */\nexport function isLockTimeoutError(err: unknown): boolean {\n const anyErr = err as { code?: unknown; message?: unknown } | null;\n if (anyErr?.code === \"55P03\") return true;\n const msg = String(anyErr?.message ?? anyErr ?? \"\");\n return /lock[_ ]?timeout|canceling statement due to lock timeout/i.test(msg);\n}\n\n/**\n * Run a DDL statement that MUST execute (the schema is actually missing), with\n * a short `lock_timeout` so a contended `ACCESS EXCLUSIVE` lock fails fast\n * instead of hanging the whole process.\n *\n * Postgres path: wrap the DDL in an explicit transaction and set\n * `SET LOCAL lock_timeout` so the timeout is scoped to THIS transaction only\n * and reset automatically on COMMIT/ROLLBACK — it never leaks onto the pooled\n * (session-reused) connection the way a bare `SET lock_timeout` would. If the\n * DbExec has no `transaction` (shouldn't happen for Postgres, but defensively),\n * fall back to a session `SET` + `RESET` in a finally. A lock-timeout error is\n * swallowed: the table/column is virtually always already correct by the time a\n * contended boot retries, and the caller's memoization should still resolve so\n * the path isn't retried in a tight loop. Any non-lock-timeout error rethrows.\n *\n * SQLite path: no lock problem — just run the DDL directly.\n *\n * @returns `true` if the DDL ran to completion, `false` if it was skipped due to\n * a lock-timeout (so the caller can decide whether to log).\n */\nexport async function runGuardedDdl(\n ddl: string,\n options: {\n lockTimeout?: string;\n injectedClient?: DbExec;\n dialectIsPostgres?: boolean;\n } = {},\n): Promise<boolean> {\n const client = options.injectedClient ?? getDbExec();\n if (!(options.dialectIsPostgres ?? isPostgres())) {\n await client.execute(ddl);\n return true;\n }\n\n const lockTimeout = options.lockTimeout ?? \"3s\";\n try {\n if (typeof client.transaction === \"function\") {\n await client.transaction(async (tx) => {\n // SET LOCAL is transaction-scoped: it reverts on COMMIT/ROLLBACK and\n // never persists on the pooled connection.\n await tx.execute(`SET LOCAL lock_timeout = '${lockTimeout}'`);\n await tx.execute(ddl);\n });\n } else {\n // Defensive fallback: no transaction support. Use a session SET and\n // guarantee a RESET so the timeout never leaks onto a reused connection.\n try {\n await client.execute(`SET lock_timeout = '${lockTimeout}'`);\n await client.execute(ddl);\n } finally {\n await client.execute(`RESET lock_timeout`).catch(() => {});\n }\n }\n return true;\n } catch (err) {\n if (isLockTimeoutError(err)) {\n // Contended lock — the schema is virtually always already correct by now.\n // Proceed; the caller's memoization still resolves so we don't loop.\n return false;\n }\n throw err;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/deploy/build.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/deploy/build.ts"],"names":[],"mappings":"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"}