@xmachines/play-router 6.0.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +79 -23
  2. package/dist/base-route-map.js +240 -270
  3. package/dist/base-route-map.js.map +1 -1
  4. package/dist/build-tree.js +84 -96
  5. package/dist/build-tree.js.map +1 -1
  6. package/dist/create-route-map-from-tree.js +50 -47
  7. package/dist/create-route-map-from-tree.js.map +1 -1
  8. package/dist/errors.js +124 -133
  9. package/dist/errors.js.map +1 -1
  10. package/dist/find-route.js +88 -105
  11. package/dist/find-route.js.map +1 -1
  12. package/dist/index.js +10 -28
  13. package/dist/provider-lifecycle.js +109 -143
  14. package/dist/provider-lifecycle.js.map +1 -1
  15. package/dist/router-bridge-base.d.ts +5 -5
  16. package/dist/router-bridge-base.d.ts.map +1 -1
  17. package/dist/router-bridge-base.js +1026 -1601
  18. package/dist/router-bridge-base.js.map +1 -1
  19. package/dist/router-sync.js +107 -139
  20. package/dist/router-sync.js.map +1 -1
  21. package/dist/types.d.ts +17 -17
  22. package/dist/types.d.ts.map +1 -1
  23. package/dist/validate-routes.js +58 -66
  24. package/dist/validate-routes.js.map +1 -1
  25. package/dist/xstate/create-route-map.js +36 -35
  26. package/dist/xstate/create-route-map.js.map +1 -1
  27. package/dist/xstate/extract-routes.js +66 -83
  28. package/dist/xstate/extract-routes.js.map +1 -1
  29. package/dist/xstate/index.js +5 -33
  30. package/dist/xstate/machine-to-graph.js +98 -109
  31. package/dist/xstate/machine-to-graph.js.map +1 -1
  32. package/dist/xstate/query.js +190 -212
  33. package/dist/xstate/query.js.map +1 -1
  34. package/package.json +16 -16
  35. package/dist/index.js.map +0 -1
  36. package/dist/types.js +0 -2
  37. package/dist/types.js.map +0 -1
  38. package/dist/xstate/index.js.map +0 -1
  39. package/dist/xstate/machine-types.js +0 -2
  40. package/dist/xstate/machine-types.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"router-bridge-base.js","sourceRoot":"","sources":["../src/router-bridge-base.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EACN,YAAY,EACZ,gBAAgB,EAChB,aAAa,EAEb,uBAAuB,EACvB,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,aAAa,GAGb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE9F,OAAO,EACN,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,aAAa,GACb,MAAM,kBAAkB,CAAC;AA8B1B;;;;;;;;;;GAUG;AACH,MAAM,aAAa,GAAG,IAAI,OAAO,EAA4B,CAAC;AAe9D,MAAM,OAAgB,gBAAgB;IA8HjB;IACA;IA9HpB,mEAAmE;IACzD,WAAW,GAAY,KAAK,CAAC;IAC7B,gBAAgB,GAAY,KAAK,CAAC;IAC5C;;;;;;;;;;;;;;;OAeG;IACO,cAAc,GAAkB,IAAI,CAAC;IAC/C;;;;;;;;;;OAUG;IACO,cAAc,GAAkB,IAAI,CAAC;IAC/C;;;;;;;;;;OAUG;IACO,sBAAsB,GAAY,KAAK,CAAC;IAClD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACK,UAAU,GAAe,aAAa,CAAC;IAC/C;;;;;;;;;;;;;OAaG;IACK,yBAAyB,GAAY,KAAK,CAAC;IACzC,YAAY,GAA8B,IAAI,CAAC;IACzD;;;;;;;;;;;;;;OAcG;IACK,KAAK,GAAqB,YAAY,CAAC;IAC/C;;;;;OAKG;IACgB,cAAc,CAAiB;IAElD;;;;;;;;OAQG;IACH,YACoB,KAAoB,EACpB,QAGlB,EACD,OAA6B;QALV,UAAK,GAAL,KAAK,CAAe;QACpB,aAAQ,GAAR,QAAQ,CAG1B;QAGD,iFAAiF;QACjF,0EAA0E;QAC1E,iFAAiF;QACjF,iDAAiD;QACjD,IAAI,CAAC,cAAc,GAAG;YACrB,YAAY,EAAE,OAAO,EAAE,YAAY;YACnC,aAAa,EAAE,OAAO,EAAE,aAAa;SACrC,CAAC;QACF,gFAAgF;QAChF,mEAAmE;QACnE,IAAI,CAAC,KAAK,GAAG,eAAe,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;QACzE,iFAAiF;QACjF,gFAAgF;QAChF,oDAAoD;QACpD,EAAE;QACF,mFAAmF;QACnF,mFAAmF;QACnF,4EAA4E;QAC5E,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC;IAC7D,CAAC;IAED;;;OAGG;IACH,IAAI,QAAQ;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACxB,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,IAAI,cAAc;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACH,WAAW,CACV,QAAsC,EACtC,cAAkD;QAElD,MAAM,IAAI,GAAG,eAAe,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QAE/C,mFAAmF;QACnF,kFAAkF;QAClF,oFAAoF;QACpF,kFAAkF;QAClF,6EAA6E;QAC7E,wDAAwD;QACxD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAElB,kFAAkF;QAClF,+EAA+E;QAC/E,IAAI,QAAQ;YAAE,OAAO;QAErB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,8EAA8E;YAC9E,8EAA8E;YAC9E,wDAAwD;YACxD,EAAE;YACF,gFAAgF;YAChF,4EAA4E;YAC5E,wEAAwE;YACxE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;YAC3B,8EAA8E;YAC9E,kFAAkF;YAClF,mFAAmF;YACnF,4EAA4E;YAC5E,mFAAmF;YACnF,kFAAkF;YAClF,gFAAgF;YAChF,gEAAgE;YAChE,IAAI,CAAC,UAAU,GAAG,aAAa,CAAC;YAChC,8EAA8E;YAC9E,8EAA8E;YAC9E,+EAA+E;YAC/E,iFAAiF;YACjF,IAAI,CAAC,yBAAyB,GAAG,KAAK,CAAC;YACvC,6EAA6E;YAC7E,gFAAgF;YAChF,+EAA+E;YAC/E,8EAA8E;YAC9E,kFAAkF;YAClF,6EAA6E;YAC7E,uEAAuE;YACvE,IAAI,CAAC,0BAA0B,EAAE,CAAC;QACnC,CAAC;IACF,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACK,0BAA0B;QACjC,MAAM,UAAU,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC/C,oFAAoF;QACpF,kFAAkF;QAClF,kFAAkF;QAClF,+EAA+E;QAC/E,sEAAsE;QACtE,MAAM,SAAS,GAAG,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAEvF,6EAA6E;QAC7E,wEAAwE;QACxE,4EAA4E;QAC5E,mCAAmC;QACnC,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YAC1D,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;YACpC,OAAO;QACR,CAAC;QAED,MAAM,WAAW,GAAG,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9D,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YAC1B,gFAAgF;YAChF,8EAA8E;YAC9E,mEAAmE;YACnE,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;YACpC,OAAO;QACR,CAAC;QAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;QACxD,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACxB,8EAA8E;YAC9E,gFAAgF;YAChF,yCAAyC;YACzC,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,OAAO;QACR,CAAC;QAED,kFAAkF;QAClF,4EAA4E;QAC5E,kFAAkF;QAClF,mFAAmF;QACnF,gFAAgF;QAChF,uEAAuE;QACvE,EAAE;QACF,gFAAgF;QAChF,6CAA6C;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,qBAAqB,CAAC,iBAAiB,CAAC,CAAC;QAC/D,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,UAAU,GAAG,mBAAmB,CAAC;YACtC,OAAO;QACR,CAAC;QAED,4EAA4E;QAC5E,IAAI,CAAC,UAAU,GAAG,aAAa,CAAC;QAChC,mFAAmF;QACnF,2EAA2E;QAC3E,oFAAoF;QACpF,mFAAmF;QACnF,mFAAmF;QACnF,qCAAqC;QACrC,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC;QACtC,mFAAmF;QACnF,iFAAiF;QACjF,yCAAyC;QACzC,IAAI,CAAC,qBAAqB,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC;IACzD,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACK,qBAAqB,CAAC,KAAa,EAAE,QAAwB;QACpE,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC;QACnC,IAAI,CAAC;YACJ,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACzC,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,sBAAsB,GAAG,KAAK,CAAC;QACrC,CAAC;IACF,CAAC;IAED,sFAAsF;IAEtF;;;;;;;;;;;;;;;OAeG;IACH,OAAO;QACN,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,OAAO;QACR,CAAC;QAED,iFAAiF;QACjF,mFAAmF;QACnF,kFAAkF;QAClF,SAAS;QACT,MAAM,cAAc,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrD,IAAI,cAAc,KAAK,SAAS,IAAI,cAAc,KAAK,IAAI,EAAE,CAAC;YAC7D,MAAM,IAAI,oBAAoB,EAAE,CAAC;QAClC,CAAC;QACD,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAEpC,kFAAkF;QAClF,+EAA+E;QAC/E,oFAAoF;QACpF,gFAAgF;QAChF,iFAAiF;QACjF,oFAAoF;QACpF,4CAA4C;QAC5C,EAAE;QACF,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC;QAE5D,kFAAkF;QAClF,kFAAkF;QAClF,mFAAmF;QACnF,oFAAoF;QACpF,+EAA+E;QAC/E,0EAA0E;QAC1E,sEAAsE;QACtE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAE3B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAE7B,iFAAiF;QACjF,0CAA0C;QAC1C,8DAA8D;QAC9D,iFAAiF;QACjF,mFAAmF;QACnF,oFAAoF;QACpF,uBAAuB;QACvB,gFAAgF;QAChF,mFAAmF;QACnF,mBAAmB;QACnB,EAAE;QACF,+EAA+E;QAC/E,oBAAoB;QACpB,EAAE;QACF,+EAA+E;QAC/E,6DAA6D;QAC7D,iFAAiF;QACjF,0CAA0C;QAC1C,oEAAoE;QACpE,iFAAiF;QACjF,gFAAgF;QAChF,+EAA+E;QAC/E,aAAa;QACb,IAAI,CAAC;YACJ,kEAAkE;YAClE,IAAI,CAAC,YAAY,GAAG,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE;gBACzE,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;YACjC,CAAC,CAAC,CAAC;YAEH,4EAA4E;YAC5E,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAE1B,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,gFAAgF;YAChF,gFAAgF;YAChF,gFAAgF;YAChF,gFAAgF;YAChF,6EAA6E;YAC7E,iCAAiC;YACjC,IAAI,CAAC;gBACJ,IAAI,CAAC,UAAU,EAAE,CAAC;YACnB,CAAC;YAAC,MAAM,CAAC;gBACR,wEAAwE;gBACxE,yEAAyE;gBACzE,uBAAuB;YACxB,CAAC;YACD,MAAM,KAAK,CAAC;QACb,CAAC;IACF,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACK,kBAAkB,CAAC,iBAA4C;QACtE,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;QACxD,+EAA+E;QAC/E,6EAA6E;QAC7E,+DAA+D;QAC/D,IAAI,MAA0B,CAAC;QAC/B,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,MAAM,YAAY,GAAG,GAAuB,EAAE;YAC7C,IAAI,CAAC,UAAU,EAAE,CAAC;gBACjB,MAAM,GAAG,IAAI,CAAC,sBAAsB,EAAE,CAAC;gBACvC,UAAU,GAAG,IAAI,CAAC;YACnB,CAAC;YACD,OAAO,MAAM,CAAC;QACf,CAAC,CAAC;QAEF,IAAI,OAAO,iBAAiB,KAAK,QAAQ,EAAE,CAAC;YAC3C,8EAA8E;YAC9E,wEAAwE;YACxE,0EAA0E;YAC1E,yEAAyE;YACzE,6EAA6E;YAC7E,sEAAsE;YACtE,MAAM,SAAS,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;YACtD,IAAI,SAAS,KAAK,IAAI;gBAAE,OAAO;YAE/B,8EAA8E;YAC9E,gFAAgF;YAChF,+EAA+E;YAC/E,6EAA6E;YAC7E,+CAA+C;YAC/C,MAAM,WAAW,GAAG,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC9D,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;gBAC1B,IAAI,CAAC,WAAW,EAAE,CAAC;gBACnB,OAAO;YACR,CAAC;YACD,6EAA6E;YAC7E,6EAA6E;YAC7E,sEAAsE;YACtE,2EAA2E;YAC3E,wEAAwE;YACxE,gDAAgD;YAChD,gFAAgF;YAChF,+EAA+E;YAC/E,+EAA+E;YAC/E,iBAAiB;YACjB,IAAI,CAAC,UAAU,GAAG,aAAa,CAAC;YAChC,kFAAkF;YAClF,gDAAgD;YAChD,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC;YAEtC,+EAA+E;YAC/E,kFAAkF;YAClF,8EAA8E;YAC9E,8EAA8E;YAC9E,6BAA6B;YAC7B,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;YAC7E,MAAM,4BAA4B,GACjC,iBAAiB;gBACjB,iBAAiB,IAAI,IAAI;gBACzB,SAAS,CAAC,iBAAiB,EAAE,YAAY,EAAE,CAAC,CAAC;YAE9C,iFAAiF;YACjF,gFAAgF;YAChF,8EAA8E;YAC9E,8EAA8E;YAC9E,4EAA4E;YAC5E,6CAA6C;YAC7C,EAAE;YACF,mFAAmF;YACnF,8EAA8E;YAC9E,iFAAiF;YACjF,8EAA8E;YAC9E,YAAY;YACZ,IAAI,4BAA4B,EAAE,CAAC;gBAClC,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,WAAW,EAAE,YAAY,EAAE,CAAC,CAAC;YAC/D,CAAC;iBAAM,CAAC;gBACP,oFAAoF;gBACpF,mBAAmB;gBACnB,EAAE;gBACF,qFAAqF;gBACrF,oBAAoB;gBACpB,+EAA+E;gBAC/E,EAAE;gBACF,8EAA8E;gBAC9E,2CAA2C;gBAC3C,0EAA0E;gBAC1E,EAAE;gBACF,oFAAoF;gBACpF,yDAAyD;gBACzD,EAAE;gBACF,gFAAgF;gBAChF,gFAAgF;gBAChF,6EAA6E;gBAC7E,yEAAyE;gBACzE,+DAA+D;gBAC/D,EAAE;gBACF,gFAAgF;gBAChF,8EAA8E;gBAC9E,iFAAiF;gBACjF,kFAAkF;gBAClF,4EAA4E;gBAC5E,4EAA4E;gBAC5E,4EAA4E;gBAC5E,8EAA8E;gBAC9E,+DAA+D;gBAC/D,IACC,iBAAiB;oBACjB,WAAW,KAAK,IAAI,CAAC,KAAK,CAAC,YAAY;oBACvC,iBAAiB,KAAK,IAAI,CAAC,KAAK,CAAC,YAAY;oBAC7C,CAAC,CAAC,iBAAiB,IAAI,kBAAkB,CAAC,iBAAiB,CAAC,CAAC,EAC5D,CAAC;oBACF,IAAI,CAAC,qBAAqB,CAAC,iBAAiB,CAAC,CAAC;gBAC/C,CAAC;qBAAM,CAAC;oBACP,8EAA8E;oBAC9E,2EAA2E;oBAC3E,yBAAyB;oBACzB,IAAI,CAAC,mBAAmB,CAAC,iBAAiB,EAAE,YAAY,EAAE,CAAC,CAAC;gBAC7D,CAAC;YACF,CAAC;QACF,CAAC;aAAM,IAAI,iBAAiB,IAAI,iBAAiB,KAAK,IAAI,EAAE,CAAC;YAC5D,iFAAiF;YACjF,oCAAoC;YACpC,oFAAoF;YACpF,YAAY;YACZ,IAAI,CAAC,qBAAqB,CAAC,iBAAiB,CAAC,CAAC;YAC9C,+EAA+E;YAC/E,gFAAgF;YAChF,kFAAkF;YAClF,kFAAkF;YAClF,mFAAmF;YACnF,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC;QACvC,CAAC;aAAM,IAAI,iBAAiB,IAAI,iBAAiB,KAAK,IAAI,CAAC,cAAc,EAAE,CAAC;YAC3E,8FAA8F;YAC9F,IAAI,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,CAAC;YAC5C,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC;QACvC,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACH,UAAU;QACT,MAAM,eAAe,GAAG,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC;QAEnD,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,IAAI,CAAC;gBACJ,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;YAC7B,CAAC;YAAC,MAAM,CAAC;gBACR,kFAAkF;YACnF,CAAC;QACF,CAAC;QACD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAEzB,iFAAiF;QACjF,+EAA+E;QAC/E,iFAAiF;QACjF,0EAA0E;QAC1E,kFAAkF;QAClF,2EAA2E;QAC3E,4DAA4D;QAC5D,IAAI,CAAC;YACJ,IAAI,IAAI,CAAC,WAAW,IAAI,eAAe,EAAE,CAAC;gBACzC,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC7B,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,+EAA+E;YAC/E,4EAA4E;YAC5E,4EAA4E;YAC5E,8EAA8E;YAC9E,6CAA6C;QAC9C,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,sBAAsB,GAAG,KAAK,CAAC;YACpC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YACzB,gFAAgF;YAChF,iFAAiF;YACjF,6DAA6D;YAC7D,IAAI,CAAC,UAAU,GAAG,aAAa,CAAC;YAChC,IAAI,CAAC,yBAAyB,GAAG,KAAK,CAAC;YAEvC,2EAA2E;YAC3E,8EAA8E;YAC9E,uEAAuE;YACvE,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC5C,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClC,CAAC;QACF,CAAC;IACF,CAAC;IAED,gGAAgG;IAEhG;;;;;;;;;;;;;;;;;;;OAmBG;IACO,mBAAmB,CAAC,KAA8B;QAC3D,IAAI,IAAI,CAAC,gBAAgB,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QACvD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO;QAChD,2EAA2E;QAC3E,+EAA+E;QAC/E,gFAAgF;QAChF,8EAA8E;QAC9E,yEAAyE;QACzE,EAAE;QACF,8EAA8E;QAC9E,2EAA2E;QAC3E,6EAA6E;QAC7E,gFAAgF;QAChF,sBAAsB;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAE5C,IAAI,IAAI,CAAC,UAAU,KAAK,aAAa,EAAE,CAAC;YACvC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC;YAChE,OAAO;QACR,CAAC;QAED,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QAExB,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;;OAMG;IACK,WAAW;QAClB,IAAI,IAAI,CAAC,UAAU,KAAK,aAAa;YAAE,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IACpE,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACK,oBAAoB,CAAC,WAAmB,EAAE,MAA0B;QAC3E,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC;QACjC,IAAI,QAAQ,KAAK,aAAa;YAAE,OAAO,KAAK,CAAC;QAC7C,oFAAoF;QACpF,mFAAmF;QACnF,gDAAgD;QAChD,IAAI,CAAC,UAAU,GAAG,aAAa,CAAC;QAEhC,uEAAuE;QACvE,kFAAkF;QAClF,gFAAgF;QAChF,gFAAgF;QAChF,gFAAgF;QAChF,kFAAkF;QAClF,yCAAyC;QACzC,EAAE;QACF,gFAAgF;QAChF,gFAAgF;QAChF,kFAAkF;QAClF,0EAA0E;QAC1E,4CAA4C;QAC5C,MAAM,aAAa,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC;QACtD,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC;QACtC,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;QACpD,8EAA8E;QAC9E,kFAAkF;QAClF,uEAAuE;QACvE,oFAAoF;QACpF,oFAAoF;QACpF,kFAAkF;QAClF,kCAAkC;QAClC,EAAE;QACF,kFAAkF;QAClF,gFAAgF;QAChF,mFAAmF;QACnF,8EAA8E;QAC9E,MAAM,WAAW,GAAG,aAAa,IAAI,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;QACpF,MAAM,eAAe,GACpB,WAAW,IAAI,aAAa,IAAI,IAAI,IAAI,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QAC1E,oFAAoF;QACpF,mFAAmF;QACnF,oFAAoF;QACpF,gFAAgF;QAChF,8EAA8E;QAC9E,oFAAoF;QACpF,8DAA8D;QAC9D,IACC,aAAa;YACb,aAAa;YACb,WAAW,KAAK,IAAI,CAAC,KAAK,CAAC,YAAY;YACvC,aAAa,KAAK,IAAI,CAAC,KAAK,CAAC,YAAY;YACzC,CAAC,eAAe;YAChB,CAAC,CAAC,WAAW,IAAI,kBAAkB,CAAC,aAAa,CAAC,CAAC,EAClD,CAAC;YACF,OAAO,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,aAAa,EAAE,CAAC;YACnB,6EAA6E;YAC7E,8EAA8E;YAC9E,gFAAgF;YAChF,4BAA4B;YAC5B,EAAE;YACF,8EAA8E;YAC9E,gFAAgF;YAChF,qCAAqC;YACrC,EAAE;YACF,iFAAiF;YACjF,4EAA4E;YAC5E,gFAAgF;YAChF,kFAAkF;YAClF,kFAAkF;YAClF,iFAAiF;YACjF,kFAAkF;YAClF,+CAA+C;YAC/C,IAAI,eAAe,EAAE,CAAC;gBACrB,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;gBACtD,OAAO,IAAI,CAAC;YACb,CAAC;YAED,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;YAC3B,OAAO,KAAK,CAAC;QACd,CAAC;QAED,IAAI,QAAQ,KAAK,mBAAmB;YAAE,OAAO,KAAK,CAAC;QAEnD,mFAAmF;QACnF,wEAAwE;QACxE,EAAE;QACF,8EAA8E;QAC9E,mFAAmF;QACnF,iFAAiF;QACjF,gFAAgF;QAChF,iEAAiE;QACjE,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;QAE9D,iEAAiE;QACjE,4EAA4E;QAC5E,8BAA8B;QAC9B,EAAE;QACF,gFAAgF;QAChF,oFAAoF;QACpF,iFAAiF;QACjF,iFAAiF;QACjF,mDAAmD;QACnD,IAAI,CAAC,aAAa;YAAE,OAAO,KAAK,CAAC;QACjC,OAAO,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACK,cAAc,CAAC,KAAa;QACnC,IAAI,IAAI,CAAC,sBAAsB;YAAE,OAAO,IAAI,CAAC;QAE7C,gFAAgF;QAChF,qFAAqF;QACrF,oFAAoF;QACpF,mFAAmF;QACnF,oFAAoF;QACpF,oFAAoF;QACpF,oEAAoE;QACpE,EAAE;QACF,mFAAmF;QACnF,+EAA+E;QAC/E,qCAAqC;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACnD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACvB,6EAA6E;YAC7E,gFAAgF;YAChF,qEAAqE;YACrE,8EAA8E;YAC9E,iFAAiF;YACjF,+EAA+E;YAC/E,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;YAC3B,OAAO,KAAK,CAAC;QACd,CAAC;QAED,IAAI,CAAC,qBAAqB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACK,kBAAkB,CAAC,KAAa;QACvC,iFAAiF;QACjF,gFAAgF;QAChF,6EAA6E;QAC7E,gEAAgE;QAChE,IAAI,KAAK,KAAK,IAAI,CAAC,cAAc;YAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAE3D,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC;QACrC,gFAAgF;QAChF,2CAA2C;QAC3C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC;QACvE,CAAC;QAED,mFAAmF;QACnF,uDAAuD;QACvD,IAAI,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC;YAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAE3D,mFAAmF;QACnF,8EAA8E;QAC9E,mFAAmF;QACnF,iFAAiF;QACjF,uEAAuE;QACvE,EAAE;QACF,iFAAiF;QACjF,iFAAiF;QACjF,yCAAyC;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACnD,IAAI,QAAQ,KAAK,IAAI,IAAI,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC;YAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACnF,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IACpC,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACK,iBAAiB,CACxB,KAAa,EACb,WAA0B,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC;QAE3D,oFAAoF;QACpF,sDAAsD;QACtD,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;QAC5B,oFAAoF;QACpF,iFAAiF;QACjF,kFAAkF;QAClF,kDAAkD;QAClD,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO;QAC9B,IAAI,CAAC,cAAc,GAAG,QAAQ,CAAC;QAC/B,4EAA4E;QAC5E,iFAAiF;QACjF,kFAAkF;QAClF,+EAA+E;QAC/E,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACO,mBAAmB,CAAC,QAAgB,EAAE,MAAe;QAC9D,IAAI,IAAI,CAAC,gBAAgB,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QACvD,IAAI,OAAO,QAAQ,KAAK,QAAQ;YAAE,OAAO;QAEzC,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,SAAS,KAAK,IAAI;YAAE,OAAO,CAAC,kCAAkC;QAElE,gFAAgF;QAChF,iFAAiF;QACjF,kFAAkF;QAClF,uDAAuD;QACvD,MAAM,WAAW,GAAG,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9D,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YAC1B,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,OAAO;QACR,CAAC;QAED,8EAA8E;QAC9E,kFAAkF;QAClF,0EAA0E;QAC1E,IAAI,IAAI,CAAC,oBAAoB,CAAC,WAAW,EAAE,MAAM,CAAC;YAAE,OAAO;QAE3D,kFAAkF;QAClF,8EAA8E;QAC9E,oFAAoF;QACpF,mFAAmF;QACnF,gFAAgF;QAChF,mFAAmF;QACnF,oFAAoF;QACpF,kFAAkF;QAClF,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC;QAEtC,IAAI,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,MAAM,CAAC;YAAE,OAAO;QACvD,IAAI,IAAI,CAAC,sBAAsB;YAAE,OAAO;QAExC,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC;QAEnC,IAAI,CAAC;YACJ,MAAM,SAAS,GAAG,mBAAmB,CAAC;gBACrC,QAAQ,EAAE,WAAW;gBACrB,MAAM;gBACN,KAAK,EAAE,CAAC,YAAY,EAAE,EAAE,CACvB,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,gBAAgB,EAAE,OAAO,EAAE,EAAE,CACxE,IAAI,CAAC,aAAa,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAC7C;aACF,CAAC,CAAC;YAEH,IAAI,CAAC,SAAS,EAAE,CAAC;gBAChB,6DAA6D;gBAC7D,oFAAoF;gBACpF,sFAAsF;gBACtF,8EAA8E;gBAC9E,EAAE;gBACF,iFAAiF;gBACjF,qFAAqF;gBACrF,oFAAoF;gBACpF,oFAAoF;gBACpF,oDAAoD;gBACpD,oFAAoF;gBACpF,SAAS;gBACT,EAAE;gBACF,kFAAkF;gBAClF,8EAA8E;gBAC9E,0DAA0D;gBAC1D,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;gBACxD,IAAI,iBAAiB,EAAE,CAAC;oBACvB,IAAI,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;gBAC3C,CAAC;gBACD,OAAO;YACR,CAAC;YAED,qEAAqE;YACrE,EAAE;YACF,0EAA0E;YAC1E,4EAA4E;YAC5E,0DAA0D;YAC1D,EAAE;YACF,4EAA4E;YAC5E,yEAAyE;YACzE,4EAA4E;YAC5E,0EAA0E;YAC1E,wBAAwB;YACxB,yEAAyE;YACzE,0EAA0E;YAC1E,sBAAsB;YACtB,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC7D,8EAA8E;YAC9E,iFAAiF;YACjF,4EAA4E;YAC5E,8EAA8E;YAC9E,kFAAkF;YAClF,wDAAwD;YACxD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;YAC3B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,+EAA+E;YAC/E,kFAAkF;YAClF,kFAAkF;YAClF,6BAA6B;YAC7B,IAAI,KAAK,YAAY,wBAAwB,EAAE,CAAC;gBAC/C,MAAM,KAAK,CAAC;YACb,CAAC;YACD,MAAM,IAAI,eAAe,CAAC,kDAAkD,EAAE;gBAC7E,KAAK,EAAE,KAAK;aACZ,CAAC,CAAC;QACJ,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,sBAAsB,GAAG,KAAK,CAAC;QACrC,CAAC;IACF,CAAC;IAED,4GAA4G;IAE5G;;;;;;;;;;;OAWG;IACO,aAAa,CAAC,QAAgB,EAAE,OAAe;QACxD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACxD,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QACxB,OAAO,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IACnE,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACO,qBAAqB,CAAC,KAAa;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC9D,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC/B,gFAAgF;QAChF,0EAA0E;QAC1E,6EAA6E;QAC7E,qFAAqF;QACrF,qFAAqF;QACrF,yDAAyD;QACzD,EAAE;QACF,mFAAmF;QACnF,aAAa;QACb,EAAE;QACF,oFAAoF;QACpF,kCAAkC;QAClC,uEAAuE;QACvE,mFAAmF;QACnF,mFAAmF;QACnF,iFAAiF;QACjF,8EAA8E;QAC9E,6DAA6D;QAC7D,EAAE;QACF,oFAAoF;QACpF,qFAAqF;QACrF,oFAAoF;QACpF,kFAAkF;QAClF,mFAAmF;QACnF,mFAAmF;QACnF,mFAAmF;QACnF,SAAS;QACT,IAAI,MAAM,IAAI,IAAI;YAAE,OAAO,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,WAAW,CAAC;QAC/E,6EAA6E;QAC7E,kFAAkF;QAClF,qFAAqF;QACrF,mFAAmF;QACnF,kFAAkF;QAClF,gFAAgF;QAChF,qFAAqF;QACrF,kFAAkF;QAClF,mFAAmF;QACnF,mCAAmC;QACnC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACjD,oFAAoF;QACpF,wBAAwB;QACxB,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,WAAW,CAAC;YACxE,4EAA4E;YAC5E,mFAAmF;YACnF,8EAA8E;YAC9E,yEAAyE;YACzE,mFAAmF;YACnF,mFAAmF;YACnF,8EAA8E;YAC9E,wEAAwE;YACxE,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC;QACxD,CAAC;QACD,kFAAkF;QAClF,oFAAoF;QACpF,kFAAkF;QAClF,2EAA2E;QAC3E,IAAI,uBAAuB,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QACnD,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2CG;IACK,aAAa,CAAC,WAAmB,EAAE,UAAqC;QAC/E,IAAI,UAAU,IAAI,IAAI;YAAE,OAAO,KAAK,CAAC;QACrC,wCAAwC;QACxC,EAAE;QACF,uEAAuE;QACvE,iFAAiF;QACjF,mFAAmF;QACnF,qFAAqF;QACrF,oFAAoF;QACpF,kFAAkF;QAClF,kEAAkE;QAClE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;QAC1D,mFAAmF;QACnF,oFAAoF;QACpF,iFAAiF;QACjF,qFAAqF;QACrF,kFAAkF;QAClF,gFAAgF;QAChF,2DAA2D;QAC3D,MAAM,WAAW,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC3C,CAAC,CAAC,YAAY,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,WAAW;YAC1D,CAAC,CAAC,IAAI,CAAC;QACR,oFAAoF;QACpF,iFAAiF;QACjF,gFAAgF;QAChF,mFAAmF;QACnF,uCAAuC;QACvC,IAAI,WAAW,KAAK,CAAC,WAAW,IAAI,SAAS,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5D,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;QACpE,IAAI,eAAe,KAAK,IAAI,IAAI,eAAe,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC5E,IAAI,eAAe,KAAK,UAAU;YAAE,OAAO,IAAI,CAAC;QAChD,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAC7C,MAAM,gBAAgB,GAAG,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC;QAC9D,OAAO,CACN,gBAAgB,IAAI,IAAI;YACxB,gBAAgB,KAAK,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,CAC9D,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACK,mBAAmB,CAAC,KAAa;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACrD,IAAI,MAAM,IAAI,IAAI;YAAE,OAAO,MAAM,CAAC;QAClC,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC;QACvE,OAAO,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAClD,CAAC;IAED;;;;;OAKG;IACO,YAAY,CAAC,MAAc;QACpC,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACK,gBAAgB,CAAC,SAAiB,EAAE,MAA0B;QACrE,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC;QACjC,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QAChC,IAAI,SAAS,KAAK,IAAI,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC;QAE7D,+EAA+E;QAC/E,gFAAgF;QAChF,gFAAgF;QAChF,mEAAmE;QACnE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACrE,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACzC,OAAO,WAAW,CAAC,SAAS,CAAC,KAAK,WAAW,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IAiBD;;;;;;;;;;;;;;;OAeG;IACO,YAAY,CAAC,QAAgB;QACtC,OAAO,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IA6BD;;;;;;;;;;;;;;;;;;OAkBG;IACO,oBAAoB;QAC7B,OAAO,SAAS,CAAC;IAClB,CAAC;IAED;;;;;;;;;;;;;OAaG;IACO,sBAAsB;QAC/B,OAAO,SAAS,CAAC;IAClB,CAAC;CACD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAS,SAAS,CAAC,UAAkB,EAAE,MAA0B;IAChE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IAClE,oFAAoF;IACpF,uFAAuF;IACvF,qFAAqF;IACrF,qFAAqF;IACrF,sFAAsF;IACtF,8BAA8B;IAC9B,IAAI,QAAQ,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAClC,OAAO,WAAW,CAAC,UAAU,CAAC,KAAK,WAAW,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,kBAAkB,CAAC,UAAkB;IAC7C,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IACtD,OAAO,KAAK,KAAK,EAAE,IAAI,QAAQ,KAAK,EAAE,CAAC;AACxC,CAAC;AAED,yFAAyF;AACzF,SAAS,UAAU,CAAC,QAAgB,EAAE,MAA0B;IAC/D,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAChD,OAAO,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,KAAK,EAAE,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAS,WAAW,CAAC,MAAc;IAClC,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAC3D,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,kBAAkB,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAC3E,CAAC;IACF,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,WAAW,KAAI,CAAC;AAEzB;;;;;;;;;GASG;AACH,SAAS,kBAAkB,CAC1B,IAAiC,EACjC,OAAuC;IAEvC,IAAI,OAAO,GAAe,WAAW,CAAC;IACtC,MAAM,OAAO,GAAuB;QACnC,KAAK,CAAC,QAAqC;YAC1C,8EAA8E;YAC9E,kFAAkF;YAClF,2EAA2E;YAC3E,+EAA+E;YAC/E,OAAO,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,GAAG,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;QACD,OAAO;YACN,MAAM,OAAO,GAAG,OAAO,CAAC;YACxB,OAAO,GAAG,WAAW,CAAC;YACtB,OAAO,EAAE,CAAC;QACX,CAAC;KACD,CAAC;IAEF,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpB,OAAO,OAAO,CAAC;AAChB,CAAC;AAUD;;;;;;;;GAQG;AACH,SAAS,YAAY,CAAC,CAAS,EAAE,CAAS;IACzC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IACjF,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IACjF,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAClC,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,WAAW,CAAC,MAAM,CAAC,KAAK,WAAW,CAAC,MAAM,CAAC,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"router-bridge-base.js","names":[],"sources":["../src/router-bridge-base.ts"],"sourcesContent":["/**\n * RouterBridgeBase — the abstract base class of every framework router adapter\n *\n * The class holds the logic that each router bridge shares, and that is 90% of the\n * code of every bridge:\n * - The atom watcher of the direction from the actor to the router\n * - `lastSyncedPath`, for the echo suppression in the direction from the actor to the router\n * - The `isProcessingNavigation` flag, which stops a loop of a guard redirect in `syncActorFromRouter` only\n * - `syncRouterFromActor` and `syncActorFromRouter`, with the read of each URL parameter\n * - The connect and disconnect lifecycle of the RouterBridge protocol\n *\n * A subclass implements the 3 abstract methods of its framework only:\n * - navigateRouter(path): how the bridge tells the framework router to navigate\n * - watchRouterChanges(): how the bridge subscribes to each location change of the router\n * - unwatchRouterChanges(): how the bridge cancels that subscription\n *\n * @example\n * ```typescript\n * export class MyRouterBridge extends RouterBridgeBase {\n * private unsubscribe: (() => void) | null = null;\n *\n * constructor(private router: MyRouter, actor: RoutableActor, routeMap: { getStateIdByPath(path: string): string | undefined; getPathByStateId(id: string): string | undefined; }) {\n * super(actor, routeMap);\n * }\n *\n * protected navigateRouter(path: string): void {\n * this.router.navigate(path);\n * }\n *\n * protected watchRouterChanges(): void {\n * this.unsubscribe = this.router.subscribe((state) => {\n * this.syncActorFromRouter(state.location.pathname, state.location.search);\n * });\n * }\n *\n * protected unwatchRouterChanges(): void {\n * this.unsubscribe?.();\n * this.unsubscribe = null;\n * }\n * }\n * ```\n *\n * @see [Play RFC](../../docs/rfc/play.md) - invariant INV-04\n */\n\nimport {\n\tNO_BASE_PATH,\n\tsanitizePathname,\n\tsplitLocation,\n\ttype CompileOptions,\n\tholdsUnsubstitutedParam,\n\tjoinBasePath,\n\tparsePattern,\n\tresolveBasePath,\n\tstripBasePath,\n\ttype BasePathOptions,\n\ttype ResolvedBasePath,\n} from \"@xmachines/play-url\";\nimport { watchAtom } from \"@xmachines/play-atom\";\nimport type { ReadonlyAtom } from \"@xmachines/play-atom\";\nimport { DuplicateBridgeError, RouterSyncError, InvalidRoutePatternError } from \"./errors.js\";\nimport type { MountableRouterBridge, RoutableActor } from \"./types.js\";\nimport {\n\tbuildPlayRouteEvent,\n\textractQuery,\n\textractRouteParams,\n\tmatchRouteMap,\n} from \"./router-sync.js\";\n\n/**\n * What a bridge takes beside the actor and the route map.\n *\n * It is {@link BasePathOptions} with the caches of {@link CompileOptions} added: the parse\n * cache AND the compiled cache. A base path and a cache are two different questions, and\n * this type joins them at the ONE place that asks both: the constructor of a bridge.\n */\nexport interface RouterBridgeOptions extends BasePathOptions, CompileOptions {}\n\n/**\n * The narrow interface of the atom watcher. `RouterBridgeBase` uses it to\n * observe each change of `actor.currentRoute`.\n *\n * The interface hides the whole subscription surface of the atom. It exposes the\n * two operations that `RouterBridgeBase` needs:\n * - `watch(atom)` — it starts the watch of one atom\n * - `unwatch()` — it stops the watch and frees the resources\n *\n * A framework adapter subclass touches this handle never. `RouterBridgeBase` makes\n * it and manages it internally.\n */\nexport interface RouteWatcherHandle {\n\t/** Arms the watcher on the given atom. */\n\twatch(atom: ReadonlyAtom<string | null>): void;\n\t/** Stops the observation and frees the watcher. */\n\tunwatch(): void;\n}\n\n/**\n * The registry of the actors with an active bridge connection, at the module level.\n *\n * The key is the actor instance, and the registry is a WeakMap, which holds no\n * reference. The class uses it to find and refuse a second `connect()` call of the\n * same actor. Such a call makes two `play.route` events for one navigation, and it\n * makes two `lastSyncedPath` values of the echo suppression that contradict each\n * other.\n *\n * @internal\n */\nconst activeBridges = new WeakMap<object, RouterBridgeBase>();\n\n/**\n * The abstract base class of every router adapter bridge of `@xmachines`.\n *\n * The class implements the RouterBridge protocol, and it holds every part of the\n * bridge logic that the adapters share. A subclass implements the 3 abstract methods\n * that are different in each framework, and it implements nothing more.\n */\n/**\n * Who owns the address bar of a mounted bridge. See\n * {@link RouterBridgeBase.mountState} for what each value means.\n */\ntype MountState = \"under-mount\" | \"outside\" | \"outside-push-owed\";\n\nexport abstract class RouterBridgeBase implements MountableRouterBridge {\n\t// ── The common state. It is identical in each of the 4 bridges ──\n\tprotected isConnected: boolean = false;\n\tprotected hasConnectedOnce: boolean = false;\n\t/**\n\t * The machine-side location that the router holds, as far as this bridge knows. It\n\t * carries the query and the fragment, and it carries NO `basePath`.\n\t *\n\t * Only code that saw a location, or that wrote one, writes this field:\n\t * `performInitialSync` when it finds the two sides in step, `takeReturnUnderMount`\n\t * for the same test under a mount, `pushResolvedRoute` after it resolves a route to\n\t * a concrete path, and `syncActorFromRouter` for the location that the router\n\t * reports. `null` means that this bridge placed no location yet, so it knows nothing\n\t * about the address bar, and every test against this field must then answer \"no\".\n\t *\n\t * The field held the route of the actor as well, from the seed of the constructor\n\t * and from a route that resolves to no URL. Those two values describe no location\n\t * that a router ever showed, and each reader of this field asks about a location, so\n\t * each of them read them wrongly. {@link lastActorRoute} holds them now.\n\t */\n\tprotected lastSyncedPath: string | null = null;\n\t/**\n\t * The raw value of `actor.currentRoute` that this bridge accounted for already.\n\t *\n\t * The field answers ONE question: \"did the bridge see this value of the atom\n\t * before?\". It is therefore a string, and not a location — a route that resolves to\n\t * no URL, such as an unknown stateId or a parameterized pattern, still stops a\n\t * second attempt at the same value.\n\t *\n\t * The constructor and `connect()` seed it, so that the first fire of the watcher of\n\t * the atom pushes nothing for a route that moved nowhere.\n\t */\n\tprotected lastActorRoute: string | null = null;\n\t/**\n\t * The flag guards `syncActorFromRouter` against a re-entrant call from a guard\n\t * redirect of the actor itself. Such a call has this sequence: the bridge sends to\n\t * the actor, the atom fires, the bridge pushes to the router, and a second\n\t * `syncActorFromRouter` call starts before the first one returns.\n\t *\n\t * The flag is NOT the echo suppression of the direction from the actor to the\n\t * router. `lastSyncedPath` does that work alone: the bridge writes it before the\n\t * `navigateRouter()` call. Therefore each router callback of the same location stops\n\t * at the {@link isEchoOfLastSync} test in `syncActorFromRouter`.\n\t */\n\tprotected isProcessingNavigation: boolean = false;\n\t/**\n\t * Who owns the address bar, and what this bridge owes it.\n\t *\n\t * - `\"under-mount\"` — the location lies under the mount, so this bridge owns the\n\t * address bar and it writes the location for every route of its actor.\n\t * - `\"outside\"` — the location lies outside the mount, so the HOST owns it. The\n\t * bridge sends no event and it corrects no URL, which is what lets the routes of\n\t * the host and the routes of the machine share one router.\n\t * - `\"outside-push-owed\"` — outside, AND the route of the actor changed while the\n\t * host owned the address bar. The push waits here instead of going to the router,\n\t * and {@link RouterBridgeBase.takeReturnUnderMount} spends it when the location\n\t * comes back. The machine therefore keeps its place while the user reads a page of\n\t * the host.\n\t *\n\t * ONE value, and not a latch plus a memory. The memory only ever means something\n\t * while the latch is up, so two fields could hold a fourth combination that says\n\t * \"the bridge owns the address bar AND a push waits for a return\" — a state with no\n\t * meaning, which a return would spend at a location the router already holds. Two\n\t * writes also cannot happen together: every site that lowered the latch wrote the\n\t * memory on the NEXT line, and the value between the two lines was that fourth\n\t * combination. `takeReturnUnderMount` reads the value it replaces instead.\n\t *\n\t * `syncActorFromRouter` is the one place that sees a location, so it is the one\n\t * place that decides between `\"under-mount\"` and `\"outside\"`.\n\t * `performInitialSync` decides it for the location that the page loaded with.\n\t *\n\t * A bridge with no mount never leaves its mount: `stripBasePath` returns the path\n\t * unchanged for an empty prefix, and never `null`. The value therefore stays\n\t * `\"under-mount\"` for every bridge that takes no `basePath`.\n\t */\n\tprivate mountState: MountState = \"under-mount\";\n\t/**\n\t * True once the first synchronization ran against a location INSIDE the mount.\n\t *\n\t * `performInitialSync` separates a deep link from a restore, and it can make that\n\t * decision only for a location that lies under the mount. A bridge that connects\n\t * while the host owns the address bar therefore reaches no decision at all: it\n\t * raises the latch and returns. The flag says that the decision is still owed, and\n\t * `syncActorFromRouter` pays it at the first location that IS under the mount —\n\t * through {@link RouterBridgeBase.takeReturnUnderMount} for a RETURN, which needs the\n\t * decision itself, and directly for every other location under the mount.\n\t *\n\t * A bridge with no mount never leaves its mount, so `performInitialSync` writes this\n\t * flag on the first `connect()` and nothing reads it again.\n\t */\n\tprivate hasSynchronizedUnderMount: boolean = false;\n\tprotected routeWatcher: RouteWatcherHandle | null = null;\n\t/**\n\t * The mount point of the machine inside the host router: the resolved prefix, and\n\t * the values of each `:param` of that prefix.\n\t *\n\t * The prefix lives here, and NOT in the route map, for two reasons. A route map is\n\t * static, it is shared, and it holds an LRU cache inside, while a mount point is\n\t * dynamic. One route map therefore serves every `machineId` without a rebuild. And\n\t * the bridge is the boundary between a location of the host router and a path of\n\t * the machine already, which is exactly what the prefix separates.\n\t *\n\t * `lastSyncedPath` stays MACHINE-side, and it carries no prefix. The code adds the\n\t * prefix at the one `navigateRouter()` call in `pushResolvedRoute`, and it removes\n\t * the prefix at the one inbound entry point `syncActorFromRouter`. Every test of\n\t * the echo suppression therefore keeps comparing like with like.\n\t */\n\tprivate mount: ResolvedBasePath = NO_BASE_PATH;\n\t/**\n\t * The caches that every parse and every compilation of this bridge reads.\n\t *\n\t * `resolveNavigationPath` and `isActorAtPath` parse a CONCRETE location, and an\n\t * application writes one for each value that it puts in a param.\n\t */\n\tprotected readonly compileOptions: CompileOptions;\n\n\t/**\n\t * @param actor - A `RoutableActor`, with `currentRoute`, `initialRoute`, and `send`.\n\t * @param routeMap - The route map of both directions, for the resolution between a\n\t * `stateId` and a `path`. Give `getStateIdByPath` and `getPathByStateId`. A\n\t * framework adapter usually wraps the result of `createRouteMap(machine)`, or an\n\t * equivalent value. The key of `getPathByStateId` is the form `\"#stateId\"` or the\n\t * bare form `\"stateId\"`. The bridge tries both forms. Therefore an implementation\n\t * of your own, for example a plain test object, handles one form only.\n\t */\n\tconstructor(\n\t\tprotected readonly actor: RoutableActor,\n\t\tprotected readonly routeMap: {\n\t\t\tgetStateIdByPath(path: string): string | null | undefined;\n\t\t\tgetPathByStateId(id: string): string | null | undefined;\n\t\t},\n\t\toptions?: RouterBridgeOptions,\n\t) {\n\t\t// The caches of this bridge, or the shared defaults. `resolveNavigationPath` and\n\t\t// `isActorAtPath` parse a CONCRETE location — one for each value that the\n\t\t// application puts in a param — so a server that holds a bridge for each request\n\t\t// passes caches that the request owns and drops.\n\t\tthis.compileOptions = {\n\t\t\tpatternCache: options?.patternCache,\n\t\t\tcompiledCache: options?.compiledCache,\n\t\t};\n\t\t// Resolve the mount BEFORE the seed of lastSyncedPath: an invalid basePath must\n\t\t// fail here, at the construction, and not on the first navigation.\n\t\tthis.mount = resolveBasePath(options?.basePath, options?.basePathParams);\n\t\t// Record the route of the actor as accounted for. This stops a loop in the first\n\t\t// synchronization: the watcher of the atom fires once when `connect()` arms it,\n\t\t// and a route that moved nowhere must push nothing.\n\t\t//\n\t\t// The seed goes to `lastActorRoute`, and NOT to `lastSyncedPath`. The route of the\n\t\t// actor describes no location that the router ever showed — the bridge has written\n\t\t// nothing yet — and every reader of `lastSyncedPath` asks about a location.\n\t\tthis.lastActorRoute = this.actor.currentRoute.get() ?? null;\n\t}\n\n\t/**\n\t * The resolved URL prefix that the machine of this bridge is mounted under, or `\"\"`\n\t * when the machine owns the complete router.\n\t */\n\tget basePath(): string {\n\t\treturn this.mount.path;\n\t}\n\n\t/**\n\t * The resolved values of the `:param` segments of the mount, or `{}` for a prefix\n\t * without a param.\n\t *\n\t * These params belong to the HOST: the host wrote the prefix and resolved the\n\t * values, and they describe the route of the host and not the route of the machine.\n\t * They therefore travel in NO `play.route` event. The machine is authoritative over\n\t * its own params, and `event.params` holds what the pattern of the machine\n\t * declares, and nothing else.\n\t *\n\t * They live here instead, because a value that reached the actor on a navigation\n\t * ALONE would go stale: `setBasePath()` can move the mount while the machine stays\n\t * on the same route, no event goes out, and `event.params` would then contradict\n\t * `basePath`. A read of this accessor cannot go stale.\n\t *\n\t * A machine that needs the identity of its host — a `machineId`, a tenant — takes\n\t * it through the `input` of the actor, where it belongs: that identity decides\n\t * WHICH machine runs, and it is not a param of a route inside the machine.\n\t *\n\t * @example\n\t * ```typescript\n\t * bridge.basePath; // \"/abc123/play\"\n\t * bridge.basePathParams; // { machineId: \"abc123\" }\n\t * ```\n\t */\n\tget basePathParams(): Readonly<Record<string, string>> {\n\t\treturn this.mount.params;\n\t}\n\n\t/**\n\t * Moves the machine to a different mount point, while it stays connected.\n\t *\n\t * This is the \"load and unload\" half of a shared router. It moves WHERE an actor is\n\t * mounted, and never WHICH actor is mounted: the actor, the route map, and its LRU\n\t * cache all stay, and nothing goes away.\n\t *\n\t * An actor never changes identity. A prefix that IDENTIFIES the actor — a\n\t * `machineId` that names the document it runs — therefore never moves through this\n\t * method: a new identity is a new actor, and a new actor takes a new bridge, because\n\t * `connect()` permits one bridge for each actor. The segments that move here are the\n\t * ones that LOCATE: a region, a locale, a tenant, a workspace slug.\n\t *\n\t * The call then brings the location in step with the NEW prefix. A location inside\n\t * the new mount runs the same first-synchronization decision as `connect()`: it\n\t * drives the actor, and a restore keeps the route of the actor. A location OUTSIDE\n\t * the new mount is the old mount in practice, so the bridge writes the new one\n\t * itself, and it keeps the route of the actor while it does so — nothing else moves\n\t * the address bar. A call that resolves to the current prefix changes no location,\n\t * and it therefore reconciles nothing.\n\t *\n\t * @param basePath - The new prefix, as a pattern or as a concrete path. An absent\n\t * value, `\"\"`, and `\"/\"` all remove the prefix, and they give the machine the\n\t * complete router again.\n\t * @param basePathParams - The values of the `:param` segments of `basePath`.\n\t * @throws {InvalidBasePathError} For a prefix that resolves to one concrete path never.\n\t * @throws {MissingBasePathParamError} When a `:param` of the prefix has no value.\n\t *\n\t * @example\n\t * ```typescript\n\t * // The host moved this actor to another place in its URL space. `machineId` names\n\t * // WHICH document the actor runs, and it does not change: a new identity is a new\n\t * // actor, and therefore a new bridge, because one actor takes one bridge.\n\t * bridge.setBasePath(\"/:region/:machineId/play\", { region: \"us\", machineId });\n\t * ```\n\t */\n\tsetBasePath(\n\t\tbasePath?: BasePathOptions[\"basePath\"],\n\t\tbasePathParams?: BasePathOptions[\"basePathParams\"],\n\t): void {\n\t\tconst next = resolveBasePath(basePath, basePathParams);\n\t\tconst samePath = next.path === this.mount.path;\n\n\t\t// Take the new mount in every case, also when the prefix STRING does not change: a\n\t\t// caller can move between a literal that holds the value already (\"/abc123/play\")\n\t\t// and the equal pattern (\"/:machineId/play\" with `{ machineId }`). The two give the\n\t\t// same path and different params, and `basePathParams` reports those params. They\n\t\t// travel in NO `play.route` event: the machine is authoritative over its own\n\t\t// params, and a param of the mount belongs to the host.\n\t\tthis.mount = next;\n\n\t\t// The same prefix means that no location changed, so there is nothing to bring in\n\t\t// step, and `lastSyncedPath` still describes a location that is still current.\n\t\tif (samePath) return;\n\n\t\tif (this.isConnected) {\n\t\t\t// The machine was at no location under the NEW prefix yet, and lastSyncedPath\n\t\t\t// describes the old one. A clear of that field lets the first synchronization\n\t\t\t// below push or send without a stop at the dedup guard.\n\t\t\t//\n\t\t\t// The clear stays INSIDE this guard, with the three records below: each of them\n\t\t\t// is a statement about a connection, and a bridge that is not connected has\n\t\t\t// none to correct. `connect()` clears the same record for the next one.\n\t\t\tthis.lastSyncedPath = null;\n\t\t\t// Both records of the OLD mount go with it. The latch says \"the location lies\n\t\t\t// outside the mount\", and the memory says \"the actor is not where the address bar\n\t\t\t// says\" — each of them is a statement about the prefix that this call replaces, so\n\t\t\t// neither survives it. `reconcileLocationWithMount` writes both again for a\n\t\t\t// location that it can read; for a location that it CANNOT read — an adapter whose\n\t\t\t// `getInitialRouterPath()` returns `null` or `undefined` at this moment — a stale\n\t\t\t// latch silenced the direction from the actor to the router for the rest of the\n\t\t\t// session, at a mount that may well cover the location already.\n\t\t\tthis.mountState = \"under-mount\";\n\t\t\t// The first synchronization of the NEW prefix is owed again. The one that ran\n\t\t\t// against the old prefix says nothing about this one: a location that the old\n\t\t\t// mount covered can lie outside the new mount, and the decision between a deep\n\t\t\t// link and a restore therefore has to run again for the prefix that replaces it.\n\t\t\tthis.hasSynchronizedUnderMount = false;\n\t\t\t// No rollback here, unlike in `connect()`. A throw there escapes a call that\n\t\t\t// returned never, so the caller holds nothing for a cleanup. Here the bridge is\n\t\t\t// connected already, and the caller holds its `disconnect` already. A teardown\n\t\t\t// on a failed move of the mount would make that handle a silent no-op, and it\n\t\t\t// would stop a bridge that the caller still believes in. A throw from here leaves\n\t\t\t// the NEW mount in place — `resolveBasePath` above refuses an invalid prefix\n\t\t\t// before the assignment — so the caller sees the throw and it decides.\n\t\t\tthis.reconcileLocationWithMount();\n\t\t}\n\t}\n\n\t/**\n\t * Brings the location of the router in step with a mount that just moved.\n\t *\n\t * **The location lies under the new mount:** run the same first-synchronization\n\t * decision as `connect()`, so a location inside the mount drives the actor and a\n\t * restore keeps the route of the actor.\n\t *\n\t * **The location lies outside it:** the bridge writes the new mount ITSELF, and it\n\t * keeps the route of the actor while it does so. A host that re-points a mount\n\t * changes no location by itself, and nothing else moves the address bar. Without\n\t * this push the machine sits at a mount that no location is under: it hears no\n\t * `play.route`, `lastSyncedPath` is clear already, and the stale prefix stays in the\n\t * URL until the route of the actor happens to change — which then jumps the URL with\n\t * no reason that the history of the user explains.\n\t *\n\t * `pushResolvedRoute` writes `lastSyncedPath` before it navigates, so the callback\n\t * of the router for this push stops at the echo suppression, and it sends no event.\n\t */\n\tprivate reconcileLocationWithMount(): void {\n\t\tconst routerPath = this.getInitialRouterPath();\n\t\t// `sanitizePathname` refuses a path of more than 2048 characters, and the RAW value\n\t\t// is no substitute for the clean one: it still carries a query, a fragment, and a\n\t\t// duplicate slash, so a compare against the mount reads a location of the host as\n\t\t// \"inside the mount\", or the opposite. Such a location is one that this bridge\n\t\t// cannot place, exactly like a location that the adapter cannot read.\n\t\tconst sanitized = typeof routerPath === \"string\" ? sanitizePathname(routerPath) : null;\n\n\t\t// A location that reads as `null` because the adapter cannot read its router\n\t\t// synchronously is NOT a location outside the mount. Leave that case to\n\t\t// `performInitialSync`, which handles an absent location already, and which\n\t\t// refuses a path that is too long.\n\t\tif (typeof routerPath !== \"string\" || sanitized === null) {\n\t\t\tthis.performInitialSync(routerPath);\n\t\t\treturn;\n\t\t}\n\n\t\tconst machinePath = stripBasePath(sanitized, this.mount.path);\n\t\tif (machinePath !== null) {\n\t\t\t// Hand the location on, so that the adapter is asked for it ONE time: a read of\n\t\t\t// `getInitialRouterPath()` belongs to the adapter, it can walk the state of a\n\t\t\t// router, and a second read can answer differently from the first.\n\t\t\tthis.performInitialSync(routerPath);\n\t\t\treturn;\n\t\t}\n\n\t\tconst currentActorRoute = this.actor.currentRoute.get();\n\t\tif (!currentActorRoute) {\n\t\t\t// Nothing to write yet. The location still belongs to the HOST, and recording\n\t\t\t// that is the whole job of this branch: without it the first route of the actor\n\t\t\t// would write over the page of the host.\n\t\t\tthis.markOutside();\n\t\t\treturn;\n\t\t}\n\n\t\t// The route of the actor has to resolve to a concrete path BEFORE the latch comes\n\t\t// down. `pushResolvedRoute` writes nothing for an unknown stateId and for a\n\t\t// parameterized pattern, and it reports nothing either: a lowered latch then said\n\t\t// that the bridge owns an address bar that still holds a location of the HOST, and\n\t\t// the next route change of the actor — an `after` timer, a guard that settles —\n\t\t// wrote over the page of the host with no URL event that asked for it.\n\t\t//\n\t\t// Keep the latch UP for that case, and remember the push, exactly as the branch\n\t\t// above does for an actor with no route yet.\n\t\tconst resolved = this.resolveNavigationPath(currentActorRoute);\n\t\tif (resolved === null) {\n\t\t\tthis.mountState = \"outside-push-owed\";\n\t\t\treturn;\n\t\t}\n\n\t\t// The bridge owns the address bar again the moment it writes the new mount.\n\t\tthis.mountState = \"under-mount\";\n\t\t// The push below IS the first synchronization of this mount: the actor won it, and\n\t\t// the bridge wrote the location. A debt that stayed said the opposite, and\n\t\t// `takeReturnUnderMount` paid it at the first LATER return under the mount: it read\n\t\t// the route of the actor as a restore and pushed it, so a deliberate navigation of\n\t\t// the host to the mount point landed the user somewhere else, with no `play.route`\n\t\t// event for the location they chose.\n\t\tthis.hasSynchronizedUnderMount = true;\n\t\t// The resolution above travels with the route, so `resolveNavigationPath` runs one\n\t\t// time: the method is `protected`, a subclass can override it, and a second call\n\t\t// can answer differently from the first.\n\t\tthis.pushHoldingNavigation(currentActorRoute, resolved);\n\t}\n\n\t/**\n\t * Pushes a route to the router, and it holds {@link isProcessingNavigation} across\n\t * the write.\n\t *\n\t * Every corrective push of the bridge needs that flag: `navigateRouter` makes the\n\t * router call `syncActorFromRouter` again inside it, and a router that NORMALIZES the\n\t * location it receives — a `validateSearch` that injects a default, a trailing slash\n\t * that a history adds — reports a value that the echo test misses. The bridge would\n\t * then answer its own write with a `play.route` event, and on a press of BACK that\n\t * answer is a history entry that FORWARD cannot return to.\n\t *\n\t * The one push that does NOT come through here is `syncRouterFromActor`: that push\n\t * follows the actor, and the echo suppression of `lastSyncedPath` is what stops it.\n\t *\n\t * @param route - The raw value of the actor route.\n\t * @param resolved - The concrete path of that route, when the caller resolved it\n\t * already. `undefined` resolves it in `pushResolvedRoute`.\n\t */\n\tprivate pushHoldingNavigation(route: string, resolved?: string | null): void {\n\t\tthis.isProcessingNavigation = true;\n\t\ttry {\n\t\t\tthis.pushResolvedRoute(route, resolved);\n\t\t} finally {\n\t\t\tthis.isProcessingNavigation = false;\n\t\t}\n\t}\n\n\t// ── The RouterBridge protocol. It is final: a subclass must override nothing here ──\n\n\t/**\n\t * Connects the router bridge to the Actor.\n\t *\n\t * The method installs the atom watcher of the direction from the actor to the\n\t * router. It then starts the watch of the router changes, which each framework does\n\t * in its own way.\n\t *\n\t * The order of these steps is part of the contract of the bridge:\n\t * - The constructor seeds `lastActorRoute` from `actor.currentRoute`, and it records no location\n\t * - The method installs the actor watcher before the router subscriptions of the adapter\n\t * - The first synchronization then separates a deep link from a restore, with `actor.initialRoute`\n\t *\n\t * An adapter that needs a different behavior of the first synchronization overrides\n\t * `getInitialRouterPath()`. It does not change the order of the steps of\n\t * `connect()`.\n\t */\n\tconnect(): void {\n\t\tif (this.isConnected) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Permit one bridge for each actor: two connected bridges of the same actor both\n\t\t// receive every navigation event of the router. They therefore send two play.route\n\t\t// events, and their lastSyncedPath values of the echo suppression contradict each\n\t\t// other.\n\t\tconst existingBridge = activeBridges.get(this.actor);\n\t\tif (existingBridge !== undefined && existingBridge !== this) {\n\t\t\tthrow new DuplicateBridgeError();\n\t\t}\n\t\tactiveBridges.set(this.actor, this);\n\n\t\t// Seed the dedup again, exactly as the constructor does, and for the same reason:\n\t\t// the field must describe the route of the actor at the moment this connection\n\t\t// starts. `disconnect()` leaves the value of the PREVIOUS connection there, and the\n\t\t// actor can move while nothing watches it — a provider that unmounts and mounts\n\t\t// again does that, and so does the pair of `<StrictMode>`. The write also covers\n\t\t// the gap between the constructor and the first `connect()`, which the Vue provider\n\t\t// reaches through `await router.isReady()`.\n\t\t//\n\t\tthis.lastActorRoute = this.actor.currentRoute.get() ?? null;\n\n\t\t// This connection has observed NO location yet: `performInitialSync` below is the\n\t\t// first read of the router. Say so, rather than keep the location of the previous\n\t\t// connection — `disconnect()` leaves that value behind, and `setBasePath` can move\n\t\t// the prefix while nothing is connected, so the value can describe a location under\n\t\t// a prefix that is gone. `isEchoOfLastSync` then read the real location of the\n\t\t// router as an echo of this bridge, and the first synchronization sent no\n\t\t// `play.route` for it: the URL and the actor separated with no error.\n\t\tthis.lastSyncedPath = null;\n\n\t\tthis.isConnected = true;\n\t\tthis.hasConnectedOnce = true;\n\n\t\t// From here on, connect() gives all or nothing. THREE steps below can throw, and\n\t\t// the rollback has to cover each of them:\n\t\t// - `createRouteWatcher`, on an atom that refuses a watcher\n\t\t// - `watchRouterChanges()`, which belongs to the adapter: `afterNavigate()` of\n\t\t// SvelteKit raises `lifecycle_outside_component` outside the initialization of\n\t\t// a component, and a host reaches that by calling `connectRouter` from a module\n\t\t// scope or a store\n\t\t// - `performInitialSync()`, with an InvalidRoutePatternError for a route that\n\t\t// URLPattern refuses, and a RouterSyncError for each other failure of the send\n\t\t// to the actor\n\t\t//\n\t\t// The throw needs an action of the caller, and it therefore goes to the caller\n\t\t// without a change.\n\t\t//\n\t\t// connect() then returns never, and the caller holds therefore NO handle for a\n\t\t// cleanup. Every part above must go away again, or it leaks:\n\t\t// - the router listener keeps firing, and it drives the actor through a bridge\n\t\t// that the caller believes to be dead\n\t\t// - the atom watcher keeps pushing each actor route to the router\n\t\t// - the activeBridges entry keeps the actor, so the NEXT connect() call of the\n\t\t// caller for this actor throws a DuplicateBridgeError. A repair of the real\n\t\t// cause, for example a correction of the route pattern, rescues that actor\n\t\t// never.\n\t\ttry {\n\t\t\t// The atom watcher of the direction from the actor to the router.\n\t\t\tthis.routeWatcher = createRouteWatcher(this.actor.currentRoute, (route) => {\n\t\t\t\tthis.syncRouterFromActor(route);\n\t\t\t});\n\n\t\t\t// The watch of the router changes. Each framework does this in its own way.\n\t\t\tthis.watchRouterChanges();\n\n\t\t\tthis.performInitialSync(this.getInitialRouterPath());\n\t\t} catch (error) {\n\t\t\t// disconnect() is exactly this teardown, and it is idempotent. It runs inside a\n\t\t\t// guard of its own, because `unwatchRouterChanges()` belongs to the adapter and\n\t\t\t// it can throw: a Vue scope that stops, a Solid disposer, a SvelteKit teardown.\n\t\t\t// That error must NOT replace the one the rollback exists to surface — a caller\n\t\t\t// that receives it learns neither which route pattern URLPattern refused nor\n\t\t\t// which event the actor refused.\n\t\t\ttry {\n\t\t\t\tthis.disconnect();\n\t\t\t} catch {\n\t\t\t\t// The teardown failed, so the slot of the actor may still be taken. The\n\t\t\t\t// original error still reaches the caller, which is the one that names a\n\t\t\t\t// cause it can repair.\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Brings the actor and the router in step one time, and decides which side wins.\n\t *\n\t * The method is separate from `connect()`, so that `connect()` can roll the whole\n\t * first synchronization back when it throws. See the `try` block there.\n\t *\n\t * The first synchronization: the direction from the router to the actor has the\n\t * priority over the other direction.\n\t *\n\t * The page can load on a URL that is not the initial state of the actor. For\n\t * example, the user types \"/about\", or the user follows a deep link. The router\n\t * holds the correct path then, and the actor is still at its initial state (\"/\").\n\t * The code must therefore drive the actor to the URL, and it must not write the\n\t * default of the actor to the URL.\n\t *\n\t * router.subscribe() fires on a *later* navigation event only. It does NOT fire for\n\t * the location that the page loaded. A subclass that can read the current location\n\t * of its router synchronously overrides getInitialRouterPath().\n\t *\n\t * @param initialRouterPath - The location of the router. Every caller reads it and\n\t * passes it, and this parameter carries NO default: a default fires for an\n\t * explicit `undefined` too, so `reconcileLocationWithMount` asked the adapter a\n\t * SECOND time for exactly the value that says \"the adapter does the first\n\t * synchronization itself\" — and a read belongs to the adapter, which can walk the\n\t * state of a router and answer differently on the second call.\n\t */\n\tprivate performInitialSync(initialRouterPath: string | null | undefined): void {\n\t\tconst initialActorRoute = this.actor.currentRoute.get();\n\t\t// ONE read of the query of the router, and a LAZY one. The read belongs to the\n\t\t// adapter and it can walk the state of a router, so a second call can answer\n\t\t// differently — and most branches below reach no query at all.\n\t\tlet search: string | undefined;\n\t\tlet searchRead = false;\n\t\tconst routerSearch = (): string | undefined => {\n\t\t\tif (!searchRead) {\n\t\t\t\tsearch = this.getInitialRouterSearch();\n\t\t\t\tsearchRead = true;\n\t\t\t}\n\t\t\treturn search;\n\t\t};\n\n\t\tif (typeof initialRouterPath === \"string\") {\n\t\t\t// `sanitizePathname` refuses a path of more than 2048 characters, and the RAW\n\t\t\t// value is no substitute for the clean one: it still carries a query, a\n\t\t\t// fragment, and a duplicate slash, so a compare against the mount reads a\n\t\t\t// location of the host as \"inside the mount\", or the opposite. The route\n\t\t\t// resolution refuses such a path anyway — `syncActorFromRouter` sanitizes it\n\t\t\t// again and returns — so touch no latch for it and reconcile nothing.\n\t\t\tconst sanitized = sanitizePathname(initialRouterPath);\n\t\t\tif (sanitized === null) return;\n\n\t\t\t// Take the machine half of the location of the router. A location OUTSIDE the\n\t\t\t// mount belongs to the host, so the bridge stays out of it completely: it sends\n\t\t\t// no event, and — this part makes a shared router work — it runs no corrective\n\t\t\t// navigation either. A correction here would drag the user off a page of the\n\t\t\t// host at the moment when the bridge connects.\n\t\t\tconst machinePath = stripBasePath(sanitized, this.mount.path);\n\t\t\tif (machinePath === null) {\n\t\t\t\tthis.markOutside();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// The symmetric write, and the reason this method has to make it: a location\n\t\t\t// that lies INSIDE the mount means the bridge owns the address bar, and this\n\t\t\t// method is reached without a router event — by `connect()`, and by a\n\t\t\t// `setBasePath()` that moves the mount to where the location already is. A\n\t\t\t// latch that only ever went up silenced the machine for the rest of the\n\t\t\t// session, because nothing else here lowers it.\n\t\t\t// The synchronization below brings the two sides in step, so a push that waited\n\t\t\t// for the address bar to come back is spent with the same write. A memory that\n\t\t\t// survived would replay that push on the next return, at a location the router\n\t\t\t// already holds.\n\t\t\tthis.mountState = \"under-mount\";\n\t\t\t// The decision below IS the first synchronization, and it runs against a location\n\t\t\t// under the mount, so nothing is owed any more.\n\t\t\tthis.hasSynchronizedUnderMount = true;\n\n\t\t\t// Test if the actor is at the current location of the router already. The PATH\n\t\t\t// and the QUERY are two answers of ONE lookup: the path decides whether a restore\n\t\t\t// is possible at all, and the query decides whether the two sides are in step\n\t\t\t// there. `routerSearch` runs behind the `&&`, so a branch that answers on the\n\t\t\t// path alone reads no query.\n\t\t\tconst actorAtRouterPath = this.isActorAtPath(machinePath, initialActorRoute);\n\t\t\tconst actorAlreadyAtRouterLocation =\n\t\t\t\tactorAtRouterPath &&\n\t\t\t\tinitialActorRoute != null &&\n\t\t\t\tsameQuery(initialActorRoute, routerSearch());\n\n\t\t\t// The two are in step already, so nothing is synchronized — but the location has\n\t\t\t// to be RECORDED, or the echo suppression starts disarmed. `setBasePath` clears\n\t\t\t// `lastSyncedPath` on a move of the prefix, and this branch left it null: the\n\t\t\t// next callback of the router for that same location then read as a real move\n\t\t\t// and sent a redundant `play.route`, and the next route change of the actor\n\t\t\t// pushed a location the router already held.\n\t\t\t//\n\t\t\t// The write is UNCONDITIONAL, and a test for a null field is wrong here: the field\n\t\t\t// must describe the location that this method just found in step, whatever it\n\t\t\t// held before. `setBasePath` can re-point the mount between two synchronizations\n\t\t\t// of one connection, and the record of the old prefix says nothing about this\n\t\t\t// location.\n\t\t\tif (actorAlreadyAtRouterLocation) {\n\t\t\t\tthis.lastSyncedPath = withSearch(machinePath, routerSearch());\n\t\t\t} else {\n\t\t\t\t// The path of the router is different from the actor route. Is this a deep link, or\n\t\t\t\t// is it a restore?\n\t\t\t\t//\n\t\t\t\t// A deep link: the router is at a URL that is not the initial one, and the actor saw\n\t\t\t\t// that URL not yet.\n\t\t\t\t// → the router wins: syncActorFromRouter. The guards then decide the access.\n\t\t\t\t//\n\t\t\t\t// A restore: the browser is at the initial URL of the machine, and a snapshot\n\t\t\t\t// restored the actor to a different route.\n\t\t\t\t// → the actor wins: push the restored route of the actor to the router.\n\t\t\t\t//\n\t\t\t\t// The detection: the URL of the router is the initial route of the machine, AND the\n\t\t\t\t// actor is at a different route. This is then a restore.\n\t\t\t\t//\n\t\t\t\t// The branch reads the LOCATION test above, through the `else` that carries it,\n\t\t\t\t// and not the path test alone: a fragment that the actor carries and the router\n\t\t\t\t// reports never — `sanitizePathname` cuts it off — is a difference that only\n\t\t\t\t// this push can place in the address bar. A query of the ACTOR is such a\n\t\t\t\t// difference too.\n\t\t\t\t//\n\t\t\t\t// \"The initial URL\" is the path AND the query of `initialRoute`, see\n\t\t\t\t// {@link isAtInitialLocation}. A router at `/?other=1` is not at the initial URL\n\t\t\t\t// `/`, so the user opened a deep link and the router wins (issue #18).\n\t\t\t\t//\n\t\t\t\t// `carriesNoOwnSuffix` is what keeps the two apart. An actor that stands on the\n\t\t\t\t// PATH of the router and carries no query and no fragment of its own restores\n\t\t\t\t// NOTHING: the address bar already shows its place, and a push only rewrites the\n\t\t\t\t// location that the user opened. The raw comparison against `initialRoute` cannot\n\t\t\t\t// see that, because a stateId is never string-equal to a path — an actor at\n\t\t\t\t// `\"#home\"` against a router at `\"/?ref=nav\"` read as a move, so the bridge\n\t\t\t\t// pushed `\"/\"` over the query the user opened and sent no `play.route` that\n\t\t\t\t// carried it. The router keeps the priority for such a location, which is the\n\t\t\t\t// answer that the same bridge gives for the actor route `\"/\"`.\n\t\t\t\tif (\n\t\t\t\t\tinitialActorRoute &&\n\t\t\t\t\tisAtInitialLocation(this.actor.initialRoute, machinePath, routerSearch()) &&\n\t\t\t\t\tinitialActorRoute !== this.actor.initialRoute &&\n\t\t\t\t\t!(actorAtRouterPath && carriesNoOwnSuffix(initialActorRoute))\n\t\t\t\t) {\n\t\t\t\t\tthis.pushHoldingNavigation(initialActorRoute);\n\t\t\t\t} else {\n\t\t\t\t\t// The search is read through `routerSearch`, which reads it one time and only\n\t\t\t\t\t// when a branch needs it: the read belongs to the adapter, and it can walk\n\t\t\t\t\t// the state of a router.\n\t\t\t\t\tthis.syncActorFromRouter(initialRouterPath, routerSearch());\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (initialActorRoute && initialRouterPath === null) {\n\t\t\t// An explicit null: no path of the router is available, from a restore or from a\n\t\t\t// bootstrap → push the actor route.\n\t\t\t// The value undefined means that the adapter does the first synchronization itself.\n\t\t\t// Continue.\n\t\t\tthis.pushHoldingNavigation(initialActorRoute);\n\t\t\t// The push IS the first synchronization of this mount, and the actor won it. A\n\t\t\t// debt that stayed said the opposite, and `takeReturnUnderMount` paid it at the\n\t\t\t// first LATER return under the mount: it read the route of the actor as a restore\n\t\t\t// and pushed it, so a deliberate navigation of the host to the mount point landed\n\t\t\t// the user somewhere else, with no `play.route` event for the location they chose.\n\t\t\tthis.hasSynchronizedUnderMount = true;\n\t\t} else if (initialActorRoute && initialActorRoute !== this.lastActorRoute) {\n\t\t\t// No path of the router replaces the actor route → write the router from the actor, as before\n\t\t\tthis.syncRouterFromActor(initialActorRoute);\n\t\t\tthis.hasSynchronizedUnderMount = true;\n\t\t}\n\t}\n\n\t/**\n\t * Disconnects the router bridge from the Actor.\n\t *\n\t * The method stops the watch of the atom, and it removes the router listener of the\n\t * framework.\n\t */\n\tdisconnect(): void {\n\t\tconst hadRouteWatcher = this.routeWatcher !== null;\n\n\t\tif (this.routeWatcher) {\n\t\t\ttry {\n\t\t\t\tthis.routeWatcher.unwatch();\n\t\t\t} catch {\n\t\t\t\t// Ignore an error of a detached watcher. disconnect() therefore stays idempotent.\n\t\t\t}\n\t\t}\n\t\tthis.routeWatcher = null;\n\n\t\t// `unwatchRouterChanges()` belongs to the adapter, and it can throw: a Vue scope\n\t\t// that stops, a disposed Solid root, a SvelteKit teardown. Everything below it\n\t\t// still has to run — a `finally`, and not a plain sequence. Without it the state\n\t\t// stayed connected and the slot of the actor stayed taken, so every later\n\t\t// `connect()` for that actor threw a DuplicateBridgeError, which is the one error\n\t\t// that naming the real cause cannot repair. That also makes `disconnect()`\n\t\t// idempotent, which the rollback of `connect()` depends on.\n\t\ttry {\n\t\t\tif (this.isConnected || hadRouteWatcher) {\n\t\t\t\tthis.unwatchRouterChanges();\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore an error of the teardown of the adapter, exactly as the watcher above\n\t\t\t// ignores one. `disconnect()` is what a provider calls on an unmount, and a\n\t\t\t// failure to release one subscription must not break that unmount — nor the\n\t\t\t// rollback of `connect()`, which calls this method and needs the error of the\n\t\t\t// first synchronization to reach the caller.\n\t\t} finally {\n\t\t\tthis.isProcessingNavigation = false;\n\t\t\tthis.isConnected = false;\n\t\t\t// A push that waited for the address bar to come back dies with the connection,\n\t\t\t// and so does the record of where the location lay. A later `connect()` runs its\n\t\t\t// own first synchronization, and it decides all three again.\n\t\t\tthis.mountState = \"under-mount\";\n\t\t\tthis.hasSynchronizedUnderMount = false;\n\n\t\t\t// Free the slot of the actor, so that a new bridge can connect to the same\n\t\t\t// actor. Clear the slot only when this bridge is the registered one: a second\n\t\t\t// bridge that connect() refused must not remove the legitimate bridge.\n\t\t\tif (activeBridges.get(this.actor) === this) {\n\t\t\t\tactiveBridges.delete(this.actor);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── The sync methods. They are protected, and a subclass overrides one for its own behavior ──\n\n\t/**\n\t * Writes the location of the router when the route atom of the actor changes.\n\t *\n\t * The method resolves the actor route to a concrete URL path, then it calls\n\t * navigateRouter() for the navigation of the framework. When it cannot resolve the\n\t * route, which happens for an unknown stateId and for a parameterized pattern\n\t * without concrete values, it skips the push completely: `navigateRouter` receives a\n\t * raw stateId or a pattern never.\n\t *\n\t * `lastSyncedPath` does the complete echo suppression, which stops the callback of\n\t * the router from a send to the actor: the method writes the resolved path to\n\t * `lastSyncedPath` before the `navigateRouter()` call. Therefore each\n\t * `syncActorFromRouter` call of the same path stops at the\n\t * `sanitized === lastSyncedPath` test, and it sends no event. This is correct for a\n\t * synchronous callback and for an asynchronous callback.\n\t *\n\t * The method does NOT set `isProcessingNavigation`. That flag lives inside\n\t * `syncActorFromRouter`, and it guards against a re-entrant loop of a guard\n\t * redirect.\n\t */\n\tprotected syncRouterFromActor(route: string | null | unknown): void {\n\t\tif (this.hasConnectedOnce && !this.isConnected) return;\n\t\tif (!route || typeof route !== \"string\") return;\n\t\t// The host owns the address bar, so this bridge writes no URL. The inbound\n\t\t// direction stays silent for the same location, and the two directions have to\n\t\t// agree: a route that changes for a reason that is NOT a URL event — an `after`\n\t\t// timer, an async guard that settles, a restore of a snapshot, a sibling that\n\t\t// sends an event — would otherwise drag the user off a page of the host.\n\t\t//\n\t\t// The memory holds ONE fact: the actor is not where the address bar says. The\n\t\t// test therefore runs on each change, and an echo CLEARS it — a route that\n\t\t// wanders away and back leaves the URL correct, and a memory that stayed set\n\t\t// would push a location the router already holds, which is a history entry that\n\t\t// BACK cannot escape.\n\t\tconst echo = this.testEchoOfLastPush(route);\n\n\t\tif (this.mountState !== \"under-mount\") {\n\t\t\tthis.mountState = echo.isEcho ? \"outside\" : \"outside-push-owed\";\n\t\t\treturn;\n\t\t}\n\n\t\tif (echo.isEcho) return;\n\n\t\tthis.pushResolvedRoute(route, echo.resolved);\n\t}\n\n\t/**\n\t * Records that the location lies outside the mount, so the HOST owns the address bar.\n\t *\n\t * A bridge that is outside ALREADY keeps the value it holds: `\"outside-push-owed\"`\n\t * says that a push waits for the address bar to come back, and a second location of\n\t * the host is no reason to forget it.\n\t */\n\tprivate markOutside(): void {\n\t\tif (this.mountState === \"under-mount\") this.mountState = \"outside\";\n\t}\n\n\t/**\n\t * Takes the return of the address bar under the mount.\n\t *\n\t * The actor wins that return when it moved while the host owned the location AND the\n\t * address bar came back to the location it still held: the method pushes the route of\n\t * the actor, and the caller sends no `play.route` event. A return to any other route\n\t * of the mount belongs to the host, so the memory goes and the caller drives the\n\t * actor from the location instead.\n\t *\n\t * The method also pays the first synchronization that the mount still owes. A bridge\n\t * that connected while the host owned the address bar reached no decision between a\n\t * deep link and a restore: `performInitialSync` can decide only for a location under\n\t * the mount, and it found none. This is the first such location, so the decision runs\n\t * here, with the same rule.\n\t *\n\t * The method holds `isProcessingNavigation` across each push, because\n\t * `navigateRouter` makes the router call `syncActorFromRouter` again.\n\t *\n\t * @returns `true` when the method handled the location, so the caller returns.\n\t */\n\tprivate takeReturnUnderMount(machinePath: string, search: string | undefined): boolean {\n\t\tconst previous = this.mountState;\n\t\tif (previous === \"under-mount\") return false;\n\t\t// The bridge owns the address bar again from here. The memory of a push that waited\n\t\t// travels in `previous`, and the write below spends it in the SAME step: no branch\n\t\t// has to clear it, and no branch can forget to.\n\t\tthis.mountState = \"under-mount\";\n\n\t\t// The first synchronization of this mount never ran: `connect()`, or a\n\t\t// `setBasePath()` that moved the mount, found the location outside the prefix, so\n\t\t// `performInitialSync` raised the latch and returned before it could separate a\n\t\t// deep link from a restore. Make that decision HERE, at the first location that\n\t\t// lies under the mount, with the rule of `performInitialSync`: the router is at\n\t\t// the initial route of the machine AND the actor is at another one, so a snapshot\n\t\t// restored the actor and the actor wins.\n\t\t//\n\t\t// Without this the restore was lost the moment the host showed the machine: the\n\t\t// location drove the actor back to the route of the mount point, while the SAME\n\t\t// bridge connected at the SAME location kept the restored route. Which of the two\n\t\t// happened depended on where the one shared router stood at the moment of\n\t\t// `connect()`, which is the arbitrary part.\n\t\tconst owesFirstSync = !this.hasSynchronizedUnderMount;\n\t\tthis.hasSynchronizedUnderMount = true;\n\t\tconst restoredRoute = this.actor.currentRoute.get();\n\t\t// `isActorAtPath` is the test that `performInitialSync` makes BEFORE it looks\n\t\t// for a restore, and both branches of the owed synchronization below need it. The\n\t\t// actor route can be a stateId, and a stateId is never string-equal to\n\t\t// `initialRoute`, which is a path: an actor that sits at \"#home\" of the mount point\n\t\t// read as a restore, so the bridge pushed \"/abc123/play\" over the location the HOST\n\t\t// chose, dropped its query, and returned `true` — so no `play.route` carried that\n\t\t// location to the machine either.\n\t\t//\n\t\t// ONE call, because `getStateIdByPath` and `getPathByStateId` belong to the route\n\t\t// map: a map of a consumer can answer differently on a second call, and the two\n\t\t// branches would then disagree about the same location. The PATH and the QUERY are\n\t\t// two answers of that one lookup, exactly as `performInitialSync` reads them.\n\t\tconst actorAtPath = owesFirstSync && this.isActorAtPath(machinePath, restoredRoute);\n\t\tconst actorAtLocation =\n\t\t\tactorAtPath && restoredRoute != null && sameQuery(restoredRoute, search);\n\t\t// The RESTORE reads the LOCATION test, exactly as `performInitialSync` does, and it\n\t\t// refuses the same empty restore: an actor that stands on the PATH of the location\n\t\t// and carries no query and no fragment of its own has nothing to put in the address\n\t\t// bar. Without that test an actor at `\"#home\"` met a host that came back to the\n\t\t// mount point with a query, and the bridge pushed the mount point over it and\n\t\t// returned `true` — so no `play.route` carried that location to the machine either,\n\t\t// which is the fault that the comment above this block names.\n\t\tif (\n\t\t\towesFirstSync &&\n\t\t\trestoredRoute &&\n\t\t\tisAtInitialLocation(this.actor.initialRoute, machinePath, search) &&\n\t\t\trestoredRoute !== this.actor.initialRoute &&\n\t\t\t!actorAtLocation &&\n\t\t\t!(actorAtPath && carriesNoOwnSuffix(restoredRoute))\n\t\t) {\n\t\t\treturn this.pushUnderMount(restoredRoute);\n\t\t}\n\n\t\tif (owesFirstSync) {\n\t\t\t// The restore does not apply, and the bridge held the address bar under this\n\t\t\t// mount NEVER: `performInitialSync` found the location outside the prefix and\n\t\t\t// returned before it could record one. This location is therefore the first one\n\t\t\t// that the mount can place.\n\t\t\t//\n\t\t\t// The memory is dropped already, so hand the location back to the caller. The\n\t\t\t// caller then drives the actor from it, which is the deep-link half of the rule\n\t\t\t// that `performInitialSync` applies.\n\t\t\t//\n\t\t\t// The FIRST half of that rule, and this branch owes it too: `performInitialSync`\n\t\t\t// asks whether the actor stands at the location already, and it records the\n\t\t\t// location and sends NOTHING when it does. Without the same test here, the same\n\t\t\t// two situations answered differently — the bridge that connected under the mount\n\t\t\t// stayed quiet, and the bridge that connected outside it re-entered the state the\n\t\t\t// actor was already in, which runs every entry action of that state again. Which\n\t\t\t// of the two happened depended on where the one shared router stood at the moment\n\t\t\t// of `connect()`, which is the arbitrary part.\n\t\t\tif (actorAtLocation) {\n\t\t\t\tthis.lastSyncedPath = withSearch(machinePath, search);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tthis.lastSyncedPath = null;\n\t\t\treturn false;\n\t\t}\n\n\t\tif (previous !== \"outside-push-owed\") return false;\n\n\t\t// The actor wins a return to the location that the address bar STILL HELD. That is\n\t\t// the machine keeping its place while the user read a page of the host.\n\t\t//\n\t\t// It must not win a return to another route of the mount. A location that the\n\t\t// address bar did not hold is a choice of the host — a link that the user pressed,\n\t\t// a deep link they opened — and a push of the route of the actor would land them\n\t\t// on a page they did not ask for, with no explanation. Drop the memory instead,\n\t\t// and let the caller send the event for the location they chose.\n\t\tif (!this.isEchoOfLastSync(machinePath, search)) return false;\n\n\t\t// The route that the block above read already. One read, because\n\t\t// `actor.currentRoute` belongs to the consumer and a second read can answer\n\t\t// differently from the first.\n\t\t//\n\t\t// A falsy route has nothing to write, and the bridge owns the address bar again\n\t\t// from the top of this method: `syncRouterFromActor` pushes the first real route of\n\t\t// the actor by itself, so no replay is needed. A memory kept here could be spent\n\t\t// only by a LATER return, at which point it pushes a location the router already\n\t\t// holds — a history entry that BACK cannot escape.\n\t\tif (!restoredRoute) return false;\n\t\treturn this.pushUnderMount(restoredRoute);\n\t}\n\n\t/**\n\t * Writes the route of the actor to a router that just came under the mount.\n\t *\n\t * The method holds `isProcessingNavigation` across the push, because\n\t * `navigateRouter` makes the router call `syncActorFromRouter` again inside it.\n\t *\n\t * A re-entrant call pushes nothing inside another push, and the memory of a\n\t * suppressed push DOES survive that case: the outer push is still in flight, and a\n\t * clear here would throw the remembered route away with nothing left to replay it.\n\t *\n\t * @param route - The raw value of the actor route.\n\t * @returns `true` when the method wrote the location, so that the caller returns and\n\t * sends no event. `false` when the route resolves to no concrete path, so that the\n\t * caller drives the actor from the location instead.\n\t */\n\tprivate pushUnderMount(route: string): boolean {\n\t\tif (this.isProcessingNavigation) return true;\n\n\t\t// The route has to resolve to a concrete path BEFORE the method reports that it\n\t\t// handled the location. `pushResolvedRoute` navigates nowhere for an unknown stateId\n\t\t// and for a parameterized pattern, and it records the RAW value in `lastActorRoute`\n\t\t// alone: a `true` return then left the location in neither side — the URL kept the\n\t\t// page of the host, and the actor heard no `play.route`. The record of the location\n\t\t// that the address bar holds says nothing about it either, so no later return could\n\t\t// be recognized. `reconcileLocationWithMount` guards the same case.\n\t\t//\n\t\t// The resolution travels with the route, so `resolveNavigationPath` runs one time:\n\t\t// the method is `protected`, a subclass can override it, and a second call can\n\t\t// answer differently from the first.\n\t\tconst resolved = this.resolveNavigationPath(route);\n\t\tif (resolved === null) {\n\t\t\t// Clear the record of the location, or the `false` return reaches the caller\n\t\t\t// and changes nothing. The caller hands the location back for the SAME location\n\t\t\t// that `lastSyncedPath` describes — the suppressed-push branch tests\n\t\t\t// `isEchoOfLastSync` before it calls this method — so the caller read its own\n\t\t\t// record as an echo and returned. The actor then stayed on a route that resolves\n\t\t\t// to no URL, the location it was given reached it never, and no error said so.\n\t\t\tthis.lastSyncedPath = null;\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.pushHoldingNavigation(route, resolved);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Tests a route of the actor against the last synchronized location.\n\t *\n\t * The exact string test covers a stateId, for example `\"#home\"`, which\n\t * `pushResolvedRoute` writes to the field when it cannot resolve a path. The query\n\t * test covers a location that carries a query: the order of the keys of a query is\n\t * not significant, so `?b=2&a=1` and `?a=1&b=2` are the same location, and the\n\t * bridge must push neither of them back.\n\t *\n\t * The FRAGMENT counts in this direction, and the inbound test ignores it. The two\n\t * are different questions. `navigateRouter` writes the whole value that the actor\n\t * gave — `buildRouteUrl` appends `context.hash` — so `\"/docs\"` and `\"/docs#intro\"`\n\t * are two locations to write, and a test that read them as one left the fragment out\n\t * of the address bar for ever. `isEchoOfLastSync` compares against what the ROUTER\n\t * reports, and `sanitizePathname` has removed the fragment from that already.\n\t */\n\tprivate testEchoOfLastPush(route: string): EchoTest {\n\t\t// The FIRST question: did the bridge account for this value of the atom already?\n\t\t// The test is an exact string compare, because it asks about the VALUE. A route\n\t\t// that resolves to no URL — an unknown stateId, a parameterized pattern — is\n\t\t// therefore covered too, and the bridge tries it one time only.\n\t\tif (route === this.lastActorRoute) return { isEcho: true };\n\n\t\tconst location = this.lastSyncedPath;\n\t\t// The bridge placed no location yet, so it knows nothing about the address bar.\n\t\t// Resolve for the caller, and let it push.\n\t\tif (location === null) {\n\t\t\treturn { isEcho: false, resolved: this.resolveNavigationPath(route) };\n\t\t}\n\n\t\t// The SECOND question: does the router hold the location that this route describes\n\t\t// already? A route that IS a path answers it directly.\n\t\tif (sameLocation(route, location)) return { isEcho: true };\n\n\t\t// A route that is a stateId answers it only after the resolution. The machine that\n\t\t// mirrors an inbound `play.route` back into its route atom reaches this line:\n\t\t// the bridge wrote \"/about\" and the actor answers \"#about\", which is ONE location.\n\t\t// A compare of the raw value alone missed, and the bridge pushed a location that\n\t\t// the router already held — one history entry that BACK cannot escape.\n\t\t//\n\t\t// The resolution travels back to the caller, so `resolveNavigationPath` runs one\n\t\t// time: the method is `protected`, a subclass can override it, and a second call\n\t\t// can answer differently from the first.\n\t\tconst resolved = this.resolveNavigationPath(route);\n\t\tif (resolved !== null && sameLocation(resolved, location)) return { isEcho: true };\n\t\treturn { isEcho: false, resolved };\n\t}\n\n\t/**\n\t * Resolves an actor route to its concrete URL path, and pushes that path to the\n\t * router.\n\t *\n\t * lastSyncedPath must hold the concrete path, because the callback of the router\n\t * fires with the concrete path. The two values therefore match, and\n\t * syncActorFromRouter stops. A raw stateId in that field, for example \"#home\", does\n\t * not match the sanitized path (\"/\") in the watcher.\n\t *\n\t * When resolveNavigationPath returns null, which happens for a parameterized\n\t * pattern, for a wildcard pattern, and for an unknown id, the method skips the push\n\t * completely: a push of the raw value writes a stateId, a `:param` pattern, or a\n\t * literal `*` into the browser URL. The method still writes the raw route to\n\t * lastActorRoute, and it leaves lastSyncedPath as it stands, because that field\n\t * records a LOCATION and no location changed. Therefore the dedup guard fires\n\t * correctly on the next identical value of the atom.\n\t *\n\t * @param route - The raw value of the actor route.\n\t * @param resolved - The concrete path of that route. The default resolves it here. A\n\t * caller that resolved it already passes it, so that `resolveNavigationPath` runs\n\t * one time: the method is `protected`, and a subclass can override it.\n\t */\n\tprivate pushResolvedRoute(\n\t\troute: string,\n\t\tresolved: string | null = this.resolveNavigationPath(route),\n\t): void {\n\t\t// The VALUE is accounted for either way, so the dedup stops the next identical fire\n\t\t// of the atom even when the route resolves to no URL.\n\t\tthis.lastActorRoute = route;\n\t\t// The LOCATION record takes the concrete path only. A raw stateId here described no\n\t\t// location that a router ever showed, and `isEchoOfLastSync` compares this field\n\t\t// against the location that the router reports: it read a real move as an echo of\n\t\t// this bridge, and the actor heard nothing of it.\n\t\tif (resolved === null) return;\n\t\tthis.lastSyncedPath = resolved;\n\t\t// This is the ONE place that adds the base path. `lastSyncedPath` keeps the\n\t\t// machine-side path above, with no prefix, so it still compares directly against\n\t\t// an actor route in `syncRouterFromActor`; `navigateRouter` receives the location\n\t\t// of the host router, because that is the location the framework navigates to.\n\t\tthis.navigateRouter(joinBasePath(this.mount.path, resolved));\n\t}\n\n\t/**\n\t * Writes the actor state when the location of the router changes.\n\t *\n\t * **A known path:** the method sends a `play.route` event to the actor, with the\n\t * stateId of the match, the params, and the query. The `isProcessingNavigation` flag\n\t * stops a circular update.\n\t *\n\t * **An unknown path, or a path with no entry in the map:** the method sends NO\n\t * `play.route` event, and the actor state stays as it is. It corrects the browser URL\n\t * instead, with a `navigateRouter(actor.currentRoute.get())` call. The URL therefore\n\t * follows the actor state, also when the user types an invalid path in the address\n\t * bar, or when the code pushes such a path during a session. The method writes the\n\t * resolved concrete path to `lastSyncedPath` before the `navigateRouter` call.\n\t * Therefore the callback of the router for that navigation stops at the guard of the\n\t * echo suppression, and it sends no false event.\n\t */\n\tprotected syncActorFromRouter(pathname: string, search?: string): void {\n\t\tif (this.hasConnectedOnce && !this.isConnected) return;\n\t\tif (typeof pathname !== \"string\") return;\n\n\t\tconst sanitized = sanitizePathname(pathname);\n\t\tif (sanitized === null) return; // The path is too long. Refuse it\n\n\t\t// Take the machine half of the location. A `null` value means that the location\n\t\t// lies OUTSIDE the mount, so it belongs to the host: return without a send of an\n\t\t// event, and without a correction of the URL. That silence lets the routes of the\n\t\t// host and the routes of the machine share one router.\n\t\tconst machinePath = stripBasePath(sanitized, this.mount.path);\n\t\tif (machinePath === null) {\n\t\t\tthis.markOutside();\n\t\t\treturn;\n\t\t}\n\n\t\t// The test runs BEFORE the echo suppression. The location of a return matches\n\t\t// `lastSyncedPath` in the common case — the host comes back to the mount point it\n\t\t// left — and an early stop there would drop the suppressed push for good.\n\t\tif (this.takeReturnUnderMount(machinePath, search)) return;\n\n\t\t// The location lies under the mount, and this bridge is about to place it, so the\n\t\t// first synchronization of this mount is owed no more. `takeReturnUnderMount`\n\t\t// records it for a RETURN, and it returns at its first line for a bridge that never\n\t\t// left, so the debt survived a first synchronization that reached no decision — an\n\t\t// adapter whose `getInitialRouterPath()` gave `undefined`, or gave `null` to an\n\t\t// actor with no route yet. The first later excursion of the host then came back to\n\t\t// the mount point and read that stale debt as a restore: it pushed the route of the\n\t\t// actor over the location that the user chose, with no `play.route` event for it.\n\t\tthis.hasSynchronizedUnderMount = true;\n\n\t\tif (this.isEchoOfLastSync(machinePath, search)) return;\n\t\tif (this.isProcessingNavigation) return;\n\n\t\tthis.isProcessingNavigation = true;\n\n\t\ttry {\n\t\t\tconst nextRoute = buildPlayRouteEvent({\n\t\t\t\tpathname: machinePath,\n\t\t\t\tsearch,\n\t\t\t\tmatch: (nextPathname) =>\n\t\t\t\t\tmatchRouteMap(nextPathname, this.routeMap, (resolvedPathname, stateId) =>\n\t\t\t\t\t\tthis.extractParams(resolvedPathname, stateId),\n\t\t\t\t\t),\n\t\t\t});\n\n\t\t\tif (!nextRoute) {\n\t\t\t\t// The path is unknown, because the map holds no route of it.\n\t\t\t\t// Push the current route of the actor back to the router. The browser URL therefore\n\t\t\t\t// follows the actor state, and this corrects an invalid entry in the address bar on a\n\t\t\t\t// cold load, and also a navigation to a path that is absent during a session.\n\t\t\t\t//\n\t\t\t\t// The code calls pushResolvedRoute() directly, and not syncRouterFromActor(). It\n\t\t\t\t// therefore passes around the dedup guard, because lastSyncedPath holds the route of\n\t\t\t\t// the actor already. pushResolvedRoute sets lastSyncedPath to the resolved concrete\n\t\t\t\t// path before the push. Therefore the callback of the router for this navigation, a\n\t\t\t\t// hashchange event or a history event, stops at the\n\t\t\t\t// sanitized === lastSyncedPath guard, and it sends no false play.route event to the\n\t\t\t\t// actor.\n\t\t\t\t//\n\t\t\t\t// isProcessingNavigation stays true here. The finally block below clears it after\n\t\t\t\t// navigateRouter returns. That block therefore guards synchronously against a\n\t\t\t\t// re-entrance of each router callback that fires at once.\n\t\t\t\tconst currentActorRoute = this.actor.currentRoute.get();\n\t\t\t\tif (currentActorRoute) {\n\t\t\t\t\tthis.pushResolvedRoute(currentActorRoute);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Record the location BEFORE the send, and record it with its query.\n\t\t\t//\n\t\t\t// The route atom of the actor notifies the watcher of the other direction\n\t\t\t// for this same move, and `syncRouterFromActor` then tests the route of the\n\t\t\t// actor against this field. Two details decide that test:\n\t\t\t//\n\t\t\t// - The write must happen first. A write after the send leaves the previous\n\t\t\t// path in the field while the watcher runs, so the test misses and the\n\t\t\t// bridge pushes the location that the router already holds. On a press on\n\t\t\t// BACK that push is a new history entry, and it replaces the entry that\n\t\t\t// FORWARD returns to.\n\t\t\t// - The write must carry the query. A machine that republishes the query\n\t\t\t// holds `/second?id=1` as its route, and a pathname alone in this field\n\t\t\t// never matches it.\n\t\t\tthis.lastSyncedPath = withSearch(nextRoute.pathname, search);\n\t\t\t// The router moved, and the actor has not answered yet, so the value that the\n\t\t\t// bridge accounted for describes the agreement of the two sides NO MORE. A value\n\t\t\t// that stayed read the answer of the actor as a repeat of the atom: a guard\n\t\t\t// redirect BACK to the route the actor came from — the route that the seed of\n\t\t\t// `connect()` recorded — stopped at the dedup, the bridge pushed nothing, and the\n\t\t\t// address bar kept the location that the guard refused.\n\t\t\tthis.lastActorRoute = null;\n\t\t\tthis.actor.send(nextRoute.event);\n\t\t} catch (error) {\n\t\t\t// An InvalidRoutePatternError needs an action of the DEVELOPER: a route of the\n\t\t\t// machine declares a pattern that URLPattern refuses, and no location can fix it.\n\t\t\t// It goes to the caller without a change, so that the caller can find it and read\n\t\t\t// the pattern that it names.\n\t\t\tif (error instanceof InvalidRoutePatternError) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrow new RouterSyncError(\"Failed to sync actor state from router location.\", {\n\t\t\t\tcause: error,\n\t\t\t});\n\t\t} finally {\n\t\t\tthis.isProcessingNavigation = false;\n\t\t}\n\t}\n\n\t// ── The utilities. They are protected, and a subclass overrides one to read the params of its framework ──\n\n\t/**\n\t * Reads the path parameters of a URL, with the URLPattern API.\n\t *\n\t * The API is always present: `@xmachines/play-url` uses the native URLPattern of the\n\t * runtime, and it falls back to `urlpattern-polyfill`, which it carries as an ordinary\n\t * dependency. A consumer therefore loads nothing, whatever the runtime.\n\t *\n\t * @param pathname - The real URL path, for example '/profile/john'\n\t * @param stateId - The stateId of the match, for the lookup of the route pattern\n\t * @returns The path parameters of the read, or an empty object when nothing matches\n\t * @throws {InvalidRoutePatternError} When URLPattern cannot compile the route pattern\n\t */\n\tprotected extractParams(pathname: string, stateId: string): Record<string, string> {\n\t\tconst pattern = this.routeMap.getPathByStateId(stateId);\n\t\tif (!pattern) return {};\n\t\treturn extractRouteParams(pathname, pattern, this.compileOptions);\n\t}\n\n\t/**\n\t * Resolves a value of an actor route to a concrete URL path for a navigation.\n\t *\n\t * A bridge that receives a raw `actor.currentRoute` value in `navigateRouter` calls\n\t * this method to convert a stateId, with a `#` prefix or without one, into a path.\n\t * The method returns `null` when a navigation is not possible:\n\t * - an unknown stateId, with no entry in the route map\n\t * - a parameterized pattern or a wildcard pattern, for example `/profile/:id` or\n\t * `/docs/*`, because no concrete value is available for a real URL\n\t * - a string that is no path and no known stateId\n\t *\n\t * A LITERAL route of the map comes back with each escape resolved: `/tags/c\\+\\+` gives\n\t * the path `/tags/c++`, which is the path that the route matches.\n\t *\n\t * A lookup of a stateId tries the form `\"#stateId\"` and the bare form `\"stateId\"`,\n\t * through `lookupPathByStateId`. Therefore a route map of your own that holds one\n\t * form only still resolves the path.\n\t *\n\t * The result is MACHINE-side: it carries no `basePath`, because it is also the\n\t * value that `lastSyncedPath` stores and that the code compares against an actor\n\t * route. The code adds the prefix one time, at the `navigateRouter()` call in\n\t * `pushResolvedRoute`.\n\t *\n\t * @param route - The raw value of the actor route: a stateId, a stateId with a `#`, or a concrete path\n\t * @returns The concrete machine-side URL path, or `null` when the bridge must skip the navigation\n\t */\n\tprotected resolveNavigationPath(route: string): string | null {\n\t\tconst mapped = this.lookupPathByStateId(route);\n\t\tconst path = mapped ?? (route.startsWith(\"/\") ? route : null);\n\t\tif (path === null) return null;\n\t\t// A LITERAL route resolves to the one path that it matches, and the source of a\n\t\t// pattern is not always that path: `/tags/c\\+\\+` matches `/tags/c++`, and\n\t\t// `/i18n/{en}` matches `/i18n/en`. A bridge that pushed the source wrote the\n\t\t// backslashes and the braces into the address bar, where `getStateIdByPath` answered\n\t\t// that location never — so the URL stopped following the actor, and its next move to\n\t\t// the same route was suppressed as an echo of that push.\n\t\t//\n\t\t// The two branches above resolve DIFFERENT amounts, and the difference is what the\n\t\t// string is:\n\t\t//\n\t\t// - A route of the MAP is a pattern source, so every construct in it is grammar and\n\t\t// the parse resolves all of it.\n\t\t// - A route that the ACTOR built is a path already. `buildRouteUrl` of\n\t\t// `@xmachines/play-xstate` reads the whole grammar and resolves every escape, so\n\t\t// a route that IT built carries no backslash. An actor of another kind can still\n\t\t// write one, and a BACKSLASH is the one construct that means grammar in such a\n\t\t// path — and nothing else does. A brace in such a path is a real brace, and\n\t\t// `/i18n/{en}` from an actor is the location `/i18n/{en}`.\n\t\t//\n\t\t// A route of the map is a pattern source WHOLE, and its `?` is a modifier and never\n\t\t// a query. The parse therefore reads all of it, and its answer is complete: a `null`\n\t\t// literal path says that the pattern describes a SET of paths, so the bridge pushes\n\t\t// nothing. A fall to the concrete-path test below answered that question with the\n\t\t// characters of the string instead, and it let every pattern whose grammar carries\n\t\t// no \":\" and no \"*\" through — `/docs/rfc(2119)`, `/books{/v2}?` and `/files/(\\d+)`\n\t\t// all reached the address bar as they stand, where `getStateIdByPath` answers them\n\t\t// never.\n\t\tif (mapped != null) return parsePattern(path, this.compileOptions).literalPath;\n\t\t// A route of the ACTOR carries its query, because `context.query` travels in\n\t\t// `currentRoute`, and the PATHNAME half alone reaches the grammar for it. A query\n\t\t// holds a \":\" and a \"*\" legally — a time in \"?at=10:30\", a URL in \"?next=https://x\",\n\t\t// a glob in \"?q=a*\" — and it holds the \"?\" that the grammar reads as a modifier. A\n\t\t// parse of the whole string called such a route malformed and resolved nothing of\n\t\t// it, so `/tags/c\\+\\+?ref=nav` went to the address bar with both backslashes; a\n\t\t// PATTERN test of the whole string called it a pattern, so `pushResolvedRoute` wrote\n\t\t// `lastSyncedPath` and navigated nowhere, and the dedup guard then suppressed the\n\t\t// next attempt at the same route as an echo. The suffix carries no grammar, and it\n\t\t// travels to the router unchanged.\n\t\tconst { pathname, suffix } = splitLocation(path);\n\t\t// A BACKSLASH is the one construct that reaches an actor route unresolved, and this\n\t\t// is where it resolves.\n\t\tif (pathname.includes(\"\\\\\")) {\n\t\t\tconst literal = parsePattern(pathname, this.compileOptions).literalPath;\n\t\t\t// The resolution FAILED, so this route resolves to no concrete path and the\n\t\t\t// bridge pushes nothing. A fall to the test below refused none of it — a backslash\n\t\t\t// is neither a \":\" nor a \"*\" — so the SOURCE went to the address bar with its\n\t\t\t// backslashes, where `getStateIdByPath` answers that location never, and\n\t\t\t// `pushResolvedRoute` then recorded it and suppressed the next attempt at the same\n\t\t\t// route as an echo. `encodeURIComponent` leaves \"(\", \")\", \"!\", \"'\" and \"~\" as they\n\t\t\t// stand, so a param value of `f(x)` under a template that carries an escape —\n\t\t\t// `/files/c\\+\\+/:name` — reaches this line and derives no literal path.\n\t\t\treturn literal === null ? null : `${literal}${suffix}`;\n\t\t}\n\t\t// The test is `holdsUnsubstitutedParam` and NOT `isParameterizedPattern`, because\n\t\t// this string is a concrete path and not a pattern. Its own documentation gives the\n\t\t// reason: a real param value carries a character of the grammar often enough, and\n\t\t// the grammar reads a modifier in \"/tags/c++\" and a group in \"/i18n/{en}\".\n\t\tif (holdsUnsubstitutedParam(pathname)) return null;\n\t\treturn path;\n\t}\n\n\t/**\n\t * Tells you whether the actor already stands on the PATH of a location of the router.\n\t *\n\t * The `currentRoute` of the actor can be a stateId, for example `\"#app.home\"`, while a\n\t * location of the router is a URL path, for example `\"/home\"`. Therefore the method\n\t * resolves the path to a stateId before it compares. Without this step, a false \"the\n\t * two are different\" starts a synchronization that nothing needs — and, in\n\t * `takeReturnUnderMount`, it reads a location that the HOST chose as a restore of the\n\t * actor and writes over it.\n\t *\n\t * The actor is on the path in three cases: the stateId of the match IS the actor\n\t * route, the path of the match IS the actor route, or the actor route is a stateId\n\t * that resolves to the same registered path as the stateId of the match. Each lookup\n\t * goes through {@link lookupPathByStateId}, which tries the form `\"#stateId\"` and also\n\t * the bare form `\"stateId\"`. Therefore a route map with `\"#about\"` recognizes an actor\n\t * route of `\"about\"`, and the opposite also works, and this is correct for a\n\t * structural map of your own that holds one form.\n\t *\n\t * An actor route with a concrete path, which starts with `\"/\"`, goes to a direct\n\t * comparison of the pathname against `machinePath`. That comparison runs BEFORE the\n\t * map, because a location the map does not hold is still a location the actor can\n\t * stand on.\n\t *\n\t * The QUERY is a question of its own, and {@link sameQuery} answers it. Each caller\n\t * asks BOTH, and the two answers do different work. \"The two sides are in step\" needs\n\t * the path AND the query, so a router that carries the only query is a deep link that\n\t * the actor has to follow — for a stateId route as much as for a path route. The\n\t * RESTORE needs the path, beside `carriesNoOwnSuffix`: an actor that stands here and\n\t * carries no query and no fragment of its own has nothing to put in the address bar,\n\t * and a push would only rewrite the location the user opened.\n\t *\n\t * A restore also needs the router at the initial URL, and {@link isAtInitialLocation}\n\t * compares the query there too. An actor at `/?ref=nav` therefore loses against a\n\t * router at `/?other=1`: the user opened a URL that the machine does not start at,\n\t * and that is a deep link (issue #18).\n\t *\n\t * Each lookup runs ONE time, and a caller that needs both answers calls this method\n\t * one time too. `getPathByStateId` belongs to the route map: a map of a consumer can\n\t * answer differently on a second call, and `RouteMap` pays a cache lookup for each of\n\t * them.\n\t *\n\t * @param machinePath - The machine half of the location of the router, with no query.\n\t * @param actorRoute - The raw value of the actor route. It CAN carry a query.\n\t */\n\tprivate isActorAtPath(machinePath: string, actorRoute: string | null | undefined): boolean {\n\t\tif (actorRoute == null) return false;\n\t\t// The PATHNAME half of the actor route.\n\t\t//\n\t\t// An actor route carries its QUERY, because `context.query` travels in\n\t\t// `currentRoute`, and `machinePath` carries none — `sanitizePathname` cut it off\n\t\t// before the caller stripped the prefix. The two are therefore never the identical\n\t\t// string for a route with a query, and a comparison of the whole strings reported an\n\t\t// actor standing exactly at this location as \"somewhere else\": `performInitialSync`\n\t\t// read that as a deep link and sent a `play.route` for the state the actor was in\n\t\t// already, so every entry action of that state ran a second time.\n\t\tconst { pathname: actorPath } = splitLocation(actorRoute);\n\t\t// An ESCAPED literal route resolves on that same half, and its two forms are never\n\t\t// the identical string: the actor carries `/tags/c\\+\\+`, which is what the template\n\t\t// writes, and the router stands at `/tags/c++`, which is the path that the route\n\t\t// matches and the path that `resolveNavigationPath` pushes. The resolution reads the\n\t\t// PATHNAME, and not the whole string, for two reasons: a `?` is a modifier of the\n\t\t// grammar that makes `/tags/c\\+\\+?ref=nav` malformed, and a query would key the\n\t\t// bounded parse cache on a string that no route map holds.\n\t\tconst literalPath = actorPath.includes(\"\\\\\")\n\t\t\t? parsePattern(actorPath, this.compileOptions).literalPath\n\t\t\t: null;\n\t\t// The pathname test answers with no map at all, and it has to run FIRST: the lookup\n\t\t// below returns nothing for a location that the map does not hold, and the early\n\t\t// return then reported an actor standing exactly at that location as \"somewhere\n\t\t// else\". The seed of the constructor hid this for as long as one field carried the\n\t\t// route of the actor AND the location.\n\t\tif (machinePath === (literalPath ?? actorPath)) return true;\n\t\tconst resolvedStateId = this.routeMap.getStateIdByPath(machinePath);\n\t\tif (resolvedStateId === null || resolvedStateId === undefined) return false;\n\t\tif (resolvedStateId === actorRoute) return true;\n\t\tif (actorRoute.startsWith(\"/\")) return false;\n\t\tconst actorStateIdPath = this.lookupPathByStateId(actorRoute);\n\t\treturn (\n\t\t\tactorStateIdPath != null &&\n\t\t\tactorStateIdPath === this.lookupPathByStateId(resolvedStateId)\n\t\t);\n\t}\n\n\t/**\n\t * Returns the path of the route map for a value of an actor route, and it tries both\n\t * forms of a stateId.\n\t *\n\t * The actor can emit `\"#stateId\"` while a route map of your own holds the bare form\n\t * `\"stateId\"`, and also the opposite. `RouteMap` makes the canonical form\n\t * internally, but the constructor accepts every structural\n\t * `{ getStateIdByPath, getPathByStateId }` object: a plain test object and a map of\n\t * one adapter are legal, and the documentation says so. Therefore this method tries\n\t * both forms: the raw form first, then the form without the `#`, then the form with\n\t * the `#`.\n\t *\n\t * @param route - The raw value of the actor route: a stateId, with a `#` prefix or without one\n\t * @returns The path of the map, or `null` or `undefined` when no form is registered\n\t */\n\tprivate lookupPathByStateId(route: string): string | null | undefined {\n\t\tconst direct = this.routeMap.getPathByStateId(route);\n\t\tif (direct != null) return direct;\n\t\tconst alternate = route.startsWith(\"#\") ? route.slice(1) : `#${route}`;\n\t\treturn this.routeMap.getPathByStateId(alternate);\n\t}\n\n\t/**\n\t * Reads the query parameters of a URL search string.\n\t *\n\t * @param search - The URL search string, for example '?tab=security&page=1'\n\t * @returns The query parameters of the read, or an empty object\n\t */\n\tprotected extractQuery(search: string): Record<string, string> {\n\t\treturn extractQuery(search);\n\t}\n\n\t/**\n\t * True when a location of the router is the location that this bridge pushed.\n\t *\n\t * `lastSyncedPath` holds the value of `actor.currentRoute`, and that value carries\n\t * the query string of `context.query`, for example `\"/dates?trip=one-way\"`. A router\n\t * callback reports the pathname and the search SEPARATELY, and `sanitizePathname`\n\t * removes the query. A comparison of the pathname alone therefore misses for every\n\t * route with a query, and the push of this bridge comes back as a `play.route` event\n\t * of its own navigation. That echo is not only wasted work: the bridge sends a\n\t * `play.route` for a location that the actor stands on already, so every entry action\n\t * of that state runs a second time, and a guard that refuses the repeat moves the\n\t * actor to a state that no user asked for.\n\t *\n\t * The query is compared as a MAP, and not as a string: a framework can re-serialize\n\t * a search string in another order, and `URLSearchParams` also accepts the form with\n\t * a leading `?` and the form without one.\n\t *\n\t * @param sanitized - The clean pathname of the router callback, with no query.\n\t * @param search - The search string of the router callback, when it has one.\n\t * @returns True when the location is the echo of the last push of this bridge.\n\t */\n\tprivate isEchoOfLastSync(sanitized: string, search: string | undefined): boolean {\n\t\tconst last = this.lastSyncedPath;\n\t\tif (last === null) return false;\n\t\tif (sanitized === last && (search ?? \"\") === \"\") return true;\n\n\t\t// Compare the query of BOTH sides. A pathname test alone reports an echo for a\n\t\t// move that keeps the path and changes the query, which a filter link does, and\n\t\t// which a `validateSearch` of a route does when it injects a default. The actor\n\t\t// then never hears of the move, and the URL and the actor diverge.\n\t\tconst { pathname: lastPath, query: lastQuery } = splitLocation(last);\n\t\tif (lastPath !== sanitized) return false;\n\t\treturn sortedQuery(lastQuery) === sortedQuery(search ?? \"\");\n\t}\n\n\t// ── The abstract methods. Each framework is different, and a subclass must implement them ──\n\n\t/**\n\t * The method must start the navigation of the framework router, for example with a\n\t * router.navigate(path) call.\n\t *\n\t * The contract: `path` is always a RESOLVED concrete URL path, for example\n\t * `\"/home\"`. The base class resolves each value of an actor route, which includes a\n\t * stateId such as `\"#home\"` and a parameterized pattern, with\n\t * `resolveNavigationPath()` before this call. It skips the push completely when the\n\t * resolution fails. Therefore an implementation must NOT resolve the value again,\n\t * and it pushes the value without a change.\n\t */\n\tprotected abstract navigateRouter(path: string): void;\n\n\t/**\n\t * Cleans a raw URL pathname of the router, and checks it.\n\t *\n\t * The method applies the limit of the path length, which is 2048 characters. It\n\t * removes a query string and a fragment from the segment of the pathname, and it\n\t * normalizes each sequence of slashes.\n\t *\n\t * An implementation that passes around `syncActorFromRouter()`, for example with a\n\t * reactive watcher of its framework that receives a route object of a parse before,\n\t * MUST call this method before it gives the path to a lookup in the route map.\n\t * `syncActorFromRouter()` calls the method internally. Therefore a bridge that uses\n\t * that method calls `sanitizePath` never itself.\n\t *\n\t * @param pathname - The raw URL pathname of the framework router.\n\t * @returns The clean pathname, or `null` when the path is invalid or too long.\n\t */\n\tprotected sanitizePath(pathname: string): string | null {\n\t\treturn sanitizePathname(pathname);\n\t}\n\n\t/**\n\t * Starts the watch of the location changes of the router.\n\t *\n\t * `connect()` calls this method. The method installs the subscription of the\n\t * framework for each location change, and it calls syncActorFromRouter() on each\n\t * change.\n\t *\n\t * **An implementation that calls `syncActorFromRouter`** receives the clean path\n\t * automatically, and it needs no more work.\n\t *\n\t * **An implementation that passes around `syncActorFromRouter`**, for example\n\t * `VueRouterBridge`, which builds the `play.route` event from the route params of its\n\t * framework, MUST call `this.sanitizePath(path)` and return at once when that\n\t * method returns `null`. Without this call, a path that is too long or malformed\n\t * passes the guards of the length and of the contents, and those guards protect the\n\t * lookup in the route map.\n\t */\n\tprotected abstract watchRouterChanges(): void;\n\n\t/**\n\t * Stops the watch of the location changes of the router.\n\t *\n\t * `disconnect()` calls this method. The method removes the subscription of the\n\t * framework.\n\t */\n\tprotected abstract unwatchRouterChanges(): void;\n\n\t/**\n\t * Returns the current pathname of the router at the moment of `connect()`.\n\t *\n\t * `connect()` calls this method one time, for the first synchronization from the URL\n\t * to the actor. router.subscribe() fires on a *later* navigation event only. It does\n\t * not repeat the location that the router loaded before. A subclass that can read the\n\t * current location of its router synchronously, for example with\n\t * `router.state.location.pathname`, overrides this method. A deep link and a direct\n\t * URL then drive the actor to the correct state, and they leave it not at the default\n\t * of its machine.\n\t *\n\t * The meaning of each return value:\n\t * - `string` → the router has a current path, and the base `connect()` writes the actor state from the router\n\t * - `null` → the router is active, but it has no current path yet, and the base `connect()` writes the router from the actor\n\t * - `undefined` → the adapter does the first synchronization itself, and the base `connect()` does nothing\n\t *\n\t * The default value is `undefined`. Therefore a bridge without this hook keeps its\n\t * earlier behavior.\n\t */\n\tprotected getInitialRouterPath(): string | null | undefined {\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Returns the current search string of the router at the moment of `connect()`.\n\t *\n\t * This method is the pair of `getInitialRouterPath()`. `connect()` calls it one time,\n\t * to give the query string of the first URL to `syncActorFromRouter()`. It returns\n\t * `undefined` when the current URL of the router has no search string, and also when\n\t * the subclass overrides the method not. `syncActorFromRouter` then makes an empty\n\t * `query: {}` in the event.\n\t *\n\t * A subclass that overrides `getInitialRouterPath()` and has a query string\n\t * overrides this method too.\n\t *\n\t * @returns The URL search string, for example `\"?tab=security\"`, or `undefined` when it is not available.\n\t */\n\tprotected getInitialRouterSearch(): string | undefined {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * True when the query of an actor route and the query of the router are the same query.\n *\n * `isActorAtPath` answers where the actor stands, and this function answers whether the\n * two sides agree about the query there. The order of the keys is not significant, which\n * is what `isEchoOfLastSync` says for the same pair of values.\n *\n * A query that DIFFERS answers `false`, and it must: the first synchronization gives the\n * router the priority, so a location with another query is a deep link that the actor has\n * to follow. An ABSENT query of the actor against a query of the router is such a\n * difference — without this, a deep link of `/settings?ref=nav` reached an actor that\n * stood at `/settings` never, and `context.query` stayed empty for the session while\n * `lastSyncedPath` recorded the query and suppressed every later report of it. A stateId\n * carries no query of its own, so it stands at a location with one never.\n *\n * @param actorRoute - The raw value of the actor route. It CAN carry a query.\n * @param search - The query of the router, or `undefined` when it has none.\n */\nfunction sameQuery(actorRoute: string, search: string | undefined): boolean {\n\tconst { query: actorQuery, fragment } = splitLocation(actorRoute);\n\t// A FRAGMENT is a difference, always. `sanitizePathname` cuts it off, so the router\n\t// reports one never and this comparison can confirm one never — and \"unknown\" must not\n\t// read as \"the same\". An actor that carries `/#intro` against a router at `/` is the\n\t// restore that puts the fragment in the address bar, and a `true` here swallowed it:\n\t// the bridge recorded the location, sent nothing, and the fragment reached the URL on\n\t// no later navigation either.\n\tif (fragment !== \"\") return false;\n\treturn sortedQuery(actorQuery) === sortedQuery(search ?? \"\");\n}\n\n/**\n * True when an actor route carries no query and no fragment of its own.\n *\n * The RESTORE branch asks this beside {@link RouterBridgeBase.isActorAtPath}. An actor\n * that stands on the path of the router and answers `true` here restores nothing: the\n * address bar already shows its place, and a push writes the same path back and drops\n * whatever the router carried beside it.\n *\n * A stateId answers `true`, because a stateId carries no query and no fragment. The\n * leading `#` of `\"#home\"` is no fragment, which is what {@link splitLocation} says at\n * index 0.\n *\n * @param actorRoute - The raw value of the actor route.\n */\nfunction carriesNoOwnSuffix(actorRoute: string): boolean {\n\tconst { query, fragment } = splitLocation(actorRoute);\n\treturn query === \"\" && fragment === \"\";\n}\n\n/**\n * True when the router stands at the initial URL of the machine: at the path of\n * `initialRoute` AND at its query.\n *\n * The restore test of `performInitialSync` and of `takeReturnUnderMount` asks this. It\n * compared the path alone, so a router at `/?other=1` read as \"at the initial URL `/`\"\n * and a restored actor at `/?ref=nav` won: the bridge pushed the query of the actor over\n * the query that the user opened, and no `play.route` carried it (issue #18). A query\n * that the machine did not put there is what a deep link looks like, so the router wins.\n *\n * The cost of the rule: after a reload, a query that the host or the browser added to the\n * initial URL is a deep link too, and the actor loses its restored route to it.\n *\n * @param initialRoute - The initial route of the actor. `null` is no initial route.\n * @param machinePath - The machine half of the location of the router, with no query.\n * @param search - The query of the router.\n */\nfunction isAtInitialLocation(\n\tinitialRoute: string | null,\n\tmachinePath: string,\n\tsearch: string | undefined,\n): boolean {\n\t// The type says `string | null`, and an actor of plain JavaScript can leave the field\n\t// out. Neither is an initial URL.\n\tif (typeof initialRoute !== \"string\") return false;\n\treturn splitLocation(initialRoute).pathname === machinePath && sameQuery(initialRoute, search);\n}\n\n/** Joins a pathname and a query into one location value. An empty query adds nothing. */\nfunction withSearch(pathname: string, search: string | undefined): string {\n\tconst query = (search ?? \"\").replace(/^\\?/, \"\");\n\treturn query === \"\" ? pathname : `${pathname}?${query}`;\n}\n\n/**\n * The canonical form of a query string, for a comparison that ignores the order:\n * each `key=value` pair, sorted, joined with `&`. An empty search gives `\"\"`.\n *\n * Each name AND each value are escaped, because a raw join ALIASES: with \"=\" and \"&\"\n * as literal separators, `?a=1&b=2` and `?a=1%26b%3D2` gave one canonical form for two\n * different locations. `isEchoOfLastSync` then read a real navigation as the echo of\n * its own push, so the actor never heard of the move and the URL and the actor state\n * diverged. `mountKey` of `provider-lifecycle.ts` escapes for the same reason.\n *\n * `toSorted()` and not `sort()`: an in-place sort is safe here, because the array is a\n * fresh one of `map()` and the mutation escapes nowhere. It is still the second spelling\n * of one operation. `mountKey` of `provider-lifecycle.ts` sorts this same shared source\n * with `toSorted()`, so this site uses it too.\n *\n * The copy costs nothing that a profile can find. `resolveViewMeta` of play-xstate holds\n * the measurement for the same question.\n */\nfunction sortedQuery(search: string): string {\n\tconst pairs = [...new URLSearchParams(search).entries()].map(\n\t\t([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`,\n\t);\n\treturn pairs.toSorted().join(\"&\");\n}\n\nfunction noopCleanup() {}\n\n/**\n * Watches the route atom of the actor.\n *\n * The bridge wraps the read in NOTHING. A subscription evaluates a computed atom inside\n * the `set` of the actor state, so a throw from the derivation would unwind into\n * `actor.send` and break the transition for every other observer — and `currentRoute`\n * therefore contains its own failures, reports them through `onError` of the actor, and\n * keeps its last route. Every subscriber benefits from that rule, and not this bridge\n * alone.\n */\nfunction createRouteWatcher(\n\tatom: ReadonlyAtom<string | null>,\n\tonRoute: (route: string | null) => void,\n): RouteWatcherHandle {\n\tlet cleanup: () => void = noopCleanup;\n\tconst watcher: RouteWatcherHandle = {\n\t\twatch(nextAtom: ReadonlyAtom<string | null>) {\n\t\t\t// Release the watch that this handle holds already. `RouteWatcherHandle` is a\n\t\t\t// published type, so a bridge of a consumer can repoint the handle at the atom of\n\t\t\t// a new actor — and an assignment alone dropped the reference to the first\n\t\t\t// cleanup, which left the first subscription live for the life of the process.\n\t\t\twatcher.unwatch();\n\t\t\tcleanup = watchAtom(nextAtom, onRoute);\n\t\t},\n\t\tunwatch() {\n\t\t\tconst release = cleanup;\n\t\t\tcleanup = noopCleanup;\n\t\t\trelease();\n\t\t},\n\t};\n\n\twatcher.watch(atom);\n\treturn watcher;\n}\n\n/**\n * The answer of {@link RouterBridgeBase.testEchoOfLastPush}.\n *\n * The resolution travels with a `false`, so the caller pushes without a second call to\n * `resolveNavigationPath`. An `true` carries none: the caller pushes nothing.\n */\ntype EchoTest = { isEcho: true } | { isEcho: false; resolved: string | null };\n\n/**\n * True when two machine-side locations are the same place.\n *\n * The order of the keys of a query is not significant, so `?b=2&a=1` and `?a=1&b=2` are\n * one location. The FRAGMENT counts: `navigateRouter` writes the whole value that the\n * actor gave — `buildRouteUrl` appends `context.hash` — so `/docs` and `/docs#intro` are\n * two locations to write, and a test that read them as one left the fragment out of the\n * address bar for ever.\n */\nfunction sameLocation(a: string, b: string): boolean {\n\tconst { pathname: aPath, query: aQuery, fragment: aFragment } = splitLocation(a);\n\tconst { pathname: bPath, query: bQuery, fragment: bFragment } = splitLocation(b);\n\tif (aPath !== bPath) return false;\n\tif (aFragment !== bFragment) return false;\n\treturn sortedQuery(aQuery) === sortedQuery(bQuery);\n}\n"],"mappings":"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"}
@@ -1,152 +1,120 @@
1
- import { getCompiledPattern, parsePattern, sanitizePathname, } from "@xmachines/play-url";
1
+ import { getCompiledPattern, parsePattern, sanitizePathname } from "@xmachines/play-url";
2
2
  import { createNullRecord } from "@xmachines/core/utils";
3
+ //#region packages/play-router/src/router-sync.ts
3
4
  /**
4
- * Matches a clean pathname against a route map, and reads each param of the match.
5
- *
6
- * The low-level sync path and the framework sync path share this function.
7
- * Therefore the semantics of the match in the route map stay identical.
8
- */
9
- export function matchRouteMap(pathname, routeMap, extractParams) {
10
- const stateId = routeMap.getStateIdByPath(pathname);
11
- if (!stateId) {
12
- return { to: null, params: {} };
13
- }
14
- return {
15
- to: stateId,
16
- params: extractParams(pathname, stateId),
17
- };
5
+ * Matches a clean pathname against a route map, and reads each param of the match.
6
+ *
7
+ * The low-level sync path and the framework sync path share this function.
8
+ * Therefore the semantics of the match in the route map stay identical.
9
+ */
10
+ function matchRouteMap(pathname, routeMap, extractParams) {
11
+ const stateId = routeMap.getStateIdByPath(pathname);
12
+ if (!stateId) return {
13
+ to: null,
14
+ params: {}
15
+ };
16
+ return {
17
+ to: stateId,
18
+ params: extractParams(pathname, stateId)
19
+ };
18
20
  }
19
21
  /**
20
- * Reads the named path parameters of a URL, with the URLPattern API.
21
- *
22
- * The function takes the string of the pattern directly. Use it when you know the
23
- * pattern already. For a read through the lookup of a state ID, see
24
- * `RouterBridgeBase.extractParams`.
25
- *
26
- * The object of the return holds no undefined value. Such a value comes from an
27
- * optional segment without a match, for example `/settings` against
28
- * `/settings/:section?`.
29
- *
30
- * The function normalizes the pattern with the rules of the match in `RouteMap`: a
31
- * name with a hyphen, such as `:cat-id`, becomes `:cat_id` for URLPattern. It then
32
- * maps each group name back to the original param name. Therefore `/docs/123`
33
- * against `/docs/:cat-id` gives `{ "cat-id": "123" }`.
34
- *
35
- * The function decodes each percent sequence of a value (`john%20doe` → `john doe`),
36
- * as a framework router does, for example vue-router. It keeps a malformed sequence
37
- * raw.
38
- *
39
- * @param pathname - The concrete URL pathname, for example `/profile/alice`
40
- * @param pattern - The template of the URL pattern, for example `/profile/:username`
41
- * @returns The record of the parameter values of the read, or `{}` for a static pattern.
42
- * @throws {InvalidRoutePatternError} When URLPattern cannot compile the pattern.
43
- *
44
- * @example
45
- * ```typescript
46
- * import { extractRouteParams } from "@xmachines/play-router";
47
- *
48
- * extractRouteParams("/profile/alice", "/profile/:username")
49
- * // → { username: "alice" }
50
- *
51
- * extractRouteParams("/settings", "/settings/:section?")
52
- * // → {} — the optional param is absent, and the object holds it not
53
- * ```
54
- */
55
- export function extractRouteParams(pathname, pattern, options) {
56
- // ONE parse answers both questions of this function: does the pattern need a match,
57
- // and which name does each URLPattern group carry.
58
- const parsed = parsePattern(pattern, options);
59
- if (!parsed.parameterized)
60
- return {};
61
- // Compile the pattern with the SAME normalization as the match in RouteMap.
62
- // Without it, URLPattern reads ":cat-id" as the group ":cat" and the literal "-id",
63
- // and the read of the params then contradicts the map that matched the path.
64
- const urlPattern = getCompiledPattern(pattern, options);
65
- const match = urlPattern.exec({ pathname });
66
- if (!match?.pathname.groups)
67
- return {};
68
- // Map each normalized group name (":cat_id") back to its original param name ("cat-id").
69
- const nameMap = parsed.declaredNames;
70
- // A null prototype for the accumulator, exactly as `cleanFrameworkParams` uses: a plain
71
- // `{}` turns `params["__proto__"] = value` into a write of the PROTOTYPE setter, which
72
- // drops the value in silence. A machine that declares `/:__proto__` then received `{}`
73
- // from this extraction while the parse of Vue Router or SolidJS Router carried the
74
- // value, so the two sources of the same params disagreed.
75
- const params = createNullRecord();
76
- for (const [key, value] of Object.entries(match.pathname.groups)) {
77
- if (value !== undefined) {
78
- params[nameMap.get(key) ?? key] = decodeParamValue(value); // nosemgrep: gitlab.eslint.detect-object-injection
79
- }
80
- }
81
- // Spread at the boundary: `event.params` reaches a machine of the user, and a plain
82
- // object is what it received before. A spread creates OWN properties, so a param named
83
- // "__proto__" stays a key while the value keeps a prototype.
84
- return { ...params };
22
+ * Reads the named path parameters of a URL, with the URLPattern API.
23
+ *
24
+ * The function takes the string of the pattern directly. Use it when you know the
25
+ * pattern already. For a read through the lookup of a state ID, see
26
+ * `RouterBridgeBase.extractParams`.
27
+ *
28
+ * The object of the return holds no undefined value. Such a value comes from an
29
+ * optional segment without a match, for example `/settings` against
30
+ * `/settings/:section?`.
31
+ *
32
+ * The function normalizes the pattern with the rules of the match in `RouteMap`: a
33
+ * name with a hyphen, such as `:cat-id`, becomes `:cat_id` for URLPattern. It then
34
+ * maps each group name back to the original param name. Therefore `/docs/123`
35
+ * against `/docs/:cat-id` gives `{ "cat-id": "123" }`.
36
+ *
37
+ * The function decodes each percent sequence of a value (`john%20doe` → `john doe`),
38
+ * as a framework router does, for example vue-router. It keeps a malformed sequence
39
+ * raw.
40
+ *
41
+ * @param pathname - The concrete URL pathname, for example `/profile/alice`
42
+ * @param pattern - The template of the URL pattern, for example `/profile/:username`
43
+ * @returns The record of the parameter values of the read, or `{}` for a static pattern.
44
+ * @throws {InvalidRoutePatternError} When URLPattern cannot compile the pattern.
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * import { extractRouteParams } from "@xmachines/play-router";
49
+ *
50
+ * extractRouteParams("/profile/alice", "/profile/:username")
51
+ * // → { username: "alice" }
52
+ *
53
+ * extractRouteParams("/settings", "/settings/:section?")
54
+ * // → {} — the optional param is absent, and the object holds it not
55
+ * ```
56
+ */
57
+ function extractRouteParams(pathname, pattern, options) {
58
+ const parsed = parsePattern(pattern, options);
59
+ if (!parsed.parameterized) return {};
60
+ const match = getCompiledPattern(pattern, options).exec({ pathname });
61
+ if (!match?.pathname.groups) return {};
62
+ const nameMap = parsed.declaredNames;
63
+ const params = createNullRecord();
64
+ for (const [key, value] of Object.entries(match.pathname.groups)) if (value !== void 0) params[nameMap.get(key) ?? key] = decodeParamValue(value);
65
+ return { ...params };
85
66
  }
86
67
  /**
87
- * Decodes each percent sequence of a URLPattern group value. It keeps the raw value
88
- * when the sequence is malformed, because `decodeURIComponent` then throws a
89
- * `URIError`.
90
- */
68
+ * Decodes each percent sequence of a URLPattern group value. It keeps the raw value
69
+ * when the sequence is malformed, because `decodeURIComponent` then throws a
70
+ * `URIError`.
71
+ */
91
72
  function decodeParamValue(value) {
92
- try {
93
- return decodeURIComponent(value);
94
- }
95
- catch {
96
- return value;
97
- }
73
+ try {
74
+ return decodeURIComponent(value);
75
+ } catch {
76
+ return value;
77
+ }
98
78
  }
99
79
  /**
100
- * Parses a URL search string into the plain object that a `play.route` event
101
- * needs.
102
- */
103
- export function extractQuery(search) {
104
- try {
105
- // A null prototype for the accumulator, and a spread at the boundary: the same two
106
- // steps as `extractRouteParams` above, and for the same reason. A plain `{}` turns
107
- // `params["__proto__"] = value` into a write of the PROTOTYPE setter, which drops
108
- // the value in silence — so a location of `?__proto__=nav` reached the machine with
109
- // an EMPTY query, while the same name in the path reached it as a param. The two
110
- // halves of one `play.route` event answered one name differently.
111
- const params = createNullRecord();
112
- new URLSearchParams(search).forEach((value, key) => {
113
- params[key] = value; // nosemgrep: gitlab.eslint.detect-object-injection
114
- });
115
- return { ...params };
116
- }
117
- catch {
118
- return {};
119
- }
80
+ * Parses a URL search string into the plain object that a `play.route` event
81
+ * needs.
82
+ */
83
+ function extractQuery(search) {
84
+ try {
85
+ const params = createNullRecord();
86
+ new URLSearchParams(search).forEach((value, key) => {
87
+ params[key] = value;
88
+ });
89
+ return { ...params };
90
+ } catch {
91
+ return {};
92
+ }
120
93
  }
121
94
  /**
122
- * Builds a normalized `play.route` event from the raw input of a router or of a
123
- * browser.
124
- *
125
- * `connectRouter()` of `@xmachines/play-dom-router` and `RouterBridgeBase` both use
126
- * this helper. Therefore a low-level adapter and a framework adapter clean each
127
- * pathname, match each route, and read each query in the same way.
128
- */
129
- export function buildPlayRouteEvent(options) {
130
- const pathname = sanitizePathname(options.pathname);
131
- if (pathname === null)
132
- return null;
133
- const match = options.match(pathname);
134
- if (!match.to)
135
- return null;
136
- // The one place of the canonical form of the emitted event: a `play.route` event
137
- // always carries a target with a '#' prefix. A route map can register a bare
138
- // stateId, and it can return one, because RouteMap keeps the registered form.
139
- // Therefore this code formats the target of the event, and no other code does
140
- // it.
141
- const to = match.to.startsWith("#") ? match.to : `#${match.to}`;
142
- return {
143
- pathname,
144
- event: {
145
- type: "play.route",
146
- to,
147
- params: match.params ?? {},
148
- query: extractQuery(options.search ?? ""),
149
- },
150
- };
95
+ * Builds a normalized `play.route` event from the raw input of a router or of a
96
+ * browser.
97
+ *
98
+ * `connectRouter()` of `@xmachines/play-dom-router` and `RouterBridgeBase` both use
99
+ * this helper. Therefore a low-level adapter and a framework adapter clean each
100
+ * pathname, match each route, and read each query in the same way.
101
+ */
102
+ function buildPlayRouteEvent(options) {
103
+ const pathname = sanitizePathname(options.pathname);
104
+ if (pathname === null) return null;
105
+ const match = options.match(pathname);
106
+ if (!match.to) return null;
107
+ return {
108
+ pathname,
109
+ event: {
110
+ type: "play.route",
111
+ to: match.to.startsWith("#") ? match.to : `#${match.to}`,
112
+ params: match.params ?? {},
113
+ query: extractQuery(options.search ?? "")
114
+ }
115
+ };
151
116
  }
117
+ //#endregion
118
+ export { buildPlayRouteEvent, extractQuery, extractRouteParams, matchRouteMap };
119
+
152
120
  //# sourceMappingURL=router-sync.js.map