recce-nightly 0.59.0.20250325__tar.gz → 0.60.0.20250327__tar.gz

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.

Potentially problematic release.


This version of recce-nightly might be problematic. Click here for more details.

Files changed (162) hide show
  1. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/PKG-INFO +1 -1
  2. recce_nightly-0.60.0.20250327/recce/VERSION +1 -0
  3. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/adapter/base.py +1 -1
  4. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/adapter/dbt_adapter/__init__.py +203 -135
  5. recce_nightly-0.60.0.20250327/recce/data/404.html +1 -0
  6. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/269-9a6f1447f09759ef.js +65 -0
  7. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/29e3cc0d-b9131230fdb267e7.js → recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/29e3cc0d-6e3b0cf904a6874d.js +1 -1
  8. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/36e1c10d-800596efe83e74a6.js +1 -0
  9. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/523b8219-cd28a6efac8e1cf1.js → recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/38610ee6-24a85e2dfb424ddb.js +1 -1
  10. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/3998a672-18454f939c052b39.js → recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/3998a672-63fc54b2b517ee2b.js +1 -1
  11. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/450c323b-ff537c106edf3f59.js → recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/450c323b-b36e745822edebe2.js +1 -1
  12. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/47d8844f-cac2989cb39ba350.js → recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/47d8844f-0516cea2ef568380.js +1 -1
  13. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/6dc81886-9871b611a22bd997.js +1 -0
  14. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/783-784ce10322400a48.js +2 -0
  15. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/7a8a3e83-277b00caf162a419.js → recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/7a8a3e83-dd26fafa0a7421e5.js +1 -1
  16. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/7f27ae6c-9c6bdf82ccd2f646.js → recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/7f27ae6c-4ddea120696db611.js +1 -1
  17. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/9746af58-344daabf5df08121.js → recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/9746af58-527fd8cbd2d17eee.js +1 -1
  18. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/a30376cd-0e2abcaded0e4cfc.js → recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/a30376cd-1ad5a3f522da896c.js +1 -1
  19. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/app/_not-found/page-76f691066176b63a.js +1 -0
  20. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/app/layout-fdacc83a3bf974a3.js +1 -0
  21. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/app/page-139ac00bab13bd3c.js +1 -0
  22. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/b63b1b3f-fbe2ac2c00b4eb2f.js → recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/b63b1b3f-fe714039749c1250.js +1 -1
  23. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/c132bf7d-4cb4c4a123e27340.js +1 -0
  24. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/c1ceaa8b-64142a5184f1b8b2.js +1 -0
  25. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/cd9f8d63-ba9b342f3d5e92ac.js → recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/cd9f8d63-543677b0ef49da87.js +3 -3
  26. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/ce84277d-4bca7695ec1ada94.js +1 -0
  27. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/e24bf851-5db5db8459b0b291.js +1 -0
  28. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/fee69bc6-4490eb47d2aaaa72.js → recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/fee69bc6-1df8306af9d11293.js +1 -1
  29. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/main-7183c49fe89a1f9b.js +1 -0
  30. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/main-app-cfd795a5fe58bf37.js +1 -0
  31. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/pages/_app-9f1049bbd7b90455.js +1 -0
  32. recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/pages/_error-71ed60313c005df1.js +1 -0
  33. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/webpack-5ab3237140d2c0f8.js → recce_nightly-0.60.0.20250327/recce/data/_next/static/chunks/webpack-8a8709fe9d22c323.js +1 -1
  34. recce_nightly-0.60.0.20250327/recce/data/_next/static/toQz_Vr_ujCxxim6p2pBl/_buildManifest.js +1 -0
  35. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/index.html +2 -2
  36. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/index.txt +5 -5
  37. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/server.py +24 -2
  38. recce_nightly-0.60.0.20250327/recce/util/lineage.py +34 -0
  39. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce_nightly.egg-info/PKG-INFO +1 -1
  40. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce_nightly.egg-info/SOURCES.txt +31 -30
  41. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/adapter/dbt_adapter/dbt_test_helper.py +3 -1
  42. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/adapter/dbt_adapter/test_dbt_cll.py +11 -9
  43. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/tasks/test_schema.py +0 -3
  44. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/test_dbt.py +1 -5
  45. recce_nightly-0.59.0.20250325/recce/VERSION +0 -1
  46. recce_nightly-0.59.0.20250325/recce/data/404.html +0 -1
  47. recce_nightly-0.59.0.20250325/recce/data/_next/static/H9OYwyR7yb7CjKNEdDwCX/_buildManifest.js +0 -1
  48. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/36e1c10d-28bb82b9a3d959e7.js +0 -1
  49. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/6dc81886-d45acadb4b6674c7.js +0 -1
  50. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/727-81ddfe5839f762d5.js +0 -65
  51. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/921-4158ef2dc23e4a01.js +0 -2
  52. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/app/_not-found/page-8fb2dd95da4548ae.js +0 -1
  53. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/app/layout-2ba8d6f3664176aa.js +0 -1
  54. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/app/page-db98e82acba724d7.js +0 -1
  55. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/c132bf7d-3375ff4277eceb81.js +0 -1
  56. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/c1ceaa8b-c4f0232ef5c2c9ba.js +0 -1
  57. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/ce84277d-3aa717f588901c96.js +0 -1
  58. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/e24bf851-38fffe87a7b8784a.js +0 -1
  59. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/main-535eacaa53931854.js +0 -1
  60. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/main-app-6f45568a6a7cd7b0.js +0 -1
  61. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/pages/_app-bbd1966052cb1e5d.js +0 -1
  62. recce_nightly-0.59.0.20250325/recce/data/_next/static/chunks/pages/_error-2456b9a2e0600a52.js +0 -1
  63. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/LICENSE +0 -0
  64. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/README.md +0 -0
  65. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/__init__.py +0 -0
  66. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/adapter/__init__.py +0 -0
  67. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/adapter/dbt_adapter/dbt_version.py +0 -0
  68. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/adapter/sqlmesh_adapter.py +0 -0
  69. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/apis/__init__.py +0 -0
  70. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/apis/check_api.py +0 -0
  71. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/apis/check_func.py +0 -0
  72. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/apis/run_api.py +0 -0
  73. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/apis/run_func.py +0 -0
  74. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/artifact.py +0 -0
  75. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/cli.py +0 -0
  76. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/config.py +0 -0
  77. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/core.py +0 -0
  78. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/_next/static/chunks/framework-ded83d71b51ce901.js +0 -0
  79. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -0
  80. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/_next/static/css/c9ecb46a4b21c126.css +0 -0
  81. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/_next/static/media/montserrat-cyrillic-800-normal.22628180.woff2 +0 -0
  82. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/_next/static/media/montserrat-cyrillic-800-normal.31d693bb.woff +0 -0
  83. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/_next/static/media/montserrat-cyrillic-ext-800-normal.7e2c1e62.woff +0 -0
  84. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/_next/static/media/montserrat-cyrillic-ext-800-normal.94a63aea.woff2 +0 -0
  85. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/_next/static/media/montserrat-latin-800-normal.6f8fa298.woff2 +0 -0
  86. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/_next/static/media/montserrat-latin-800-normal.97e20d5e.woff +0 -0
  87. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/_next/static/media/montserrat-latin-ext-800-normal.013b84f9.woff2 +0 -0
  88. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/_next/static/media/montserrat-latin-ext-800-normal.aff52ab0.woff +0 -0
  89. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/_next/static/media/montserrat-vietnamese-800-normal.5f21869b.woff +0 -0
  90. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/_next/static/media/montserrat-vietnamese-800-normal.c0035377.woff2 +0 -0
  91. {recce_nightly-0.59.0.20250325/recce/data/_next/static/H9OYwyR7yb7CjKNEdDwCX → recce_nightly-0.60.0.20250327/recce/data/_next/static/toQz_Vr_ujCxxim6p2pBl}/_ssgManifest.js +0 -0
  92. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/favicon.ico +0 -0
  93. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/imgs/feedback/thumbs-down.png +0 -0
  94. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/imgs/feedback/thumbs-up.png +0 -0
  95. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/data/logo/recce-logo-white.png +0 -0
  96. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/diff.py +0 -0
  97. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/event/CONFIG +0 -0
  98. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/event/SENTRY_DNS +0 -0
  99. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/event/__init__.py +0 -0
  100. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/event/collector.py +0 -0
  101. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/event/track.py +0 -0
  102. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/exceptions.py +0 -0
  103. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/git.py +0 -0
  104. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/github.py +0 -0
  105. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/models/__init__.py +0 -0
  106. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/models/check.py +0 -0
  107. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/models/run.py +0 -0
  108. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/models/types.py +0 -0
  109. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/pull_request.py +0 -0
  110. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/run.py +0 -0
  111. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/state.py +0 -0
  112. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/summary.py +0 -0
  113. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/tasks/__init__.py +0 -0
  114. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/tasks/core.py +0 -0
  115. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/tasks/dataframe.py +0 -0
  116. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/tasks/histogram.py +0 -0
  117. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/tasks/lineage.py +0 -0
  118. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/tasks/profile.py +0 -0
  119. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/tasks/query.py +0 -0
  120. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/tasks/rowcount.py +0 -0
  121. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/tasks/schema.py +0 -0
  122. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/tasks/top_k.py +0 -0
  123. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/tasks/valuediff.py +0 -0
  124. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/util/__init__.py +0 -0
  125. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/util/breaking.py +0 -0
  126. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/util/cache.py +0 -0
  127. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/util/cll.py +0 -0
  128. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/util/io.py +0 -0
  129. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/util/logger.py +0 -0
  130. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/util/pydantic_model.py +0 -0
  131. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/util/recce_cloud.py +0 -0
  132. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/util/singleton.py +0 -0
  133. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce/yaml/__init__.py +0 -0
  134. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce_nightly.egg-info/dependency_links.txt +0 -0
  135. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce_nightly.egg-info/entry_points.txt +0 -0
  136. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce_nightly.egg-info/requires.txt +0 -0
  137. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/recce_nightly.egg-info/top_level.txt +0 -0
  138. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/setup.cfg +0 -0
  139. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/setup.py +0 -0
  140. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/__init__.py +0 -0
  141. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/adapter/__init__.py +0 -0
  142. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/adapter/dbt_adapter/__init__.py +0 -0
  143. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/adapter/dbt_adapter/conftest.py +0 -0
  144. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/adapter/dbt_adapter/test_dbt_adapter.py +0 -0
  145. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/adapter/dbt_adapter/test_selector.py +0 -0
  146. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/tasks/__init__.py +0 -0
  147. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/tasks/conftest.py +0 -0
  148. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/tasks/test_histogram.py +0 -0
  149. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/tasks/test_lineage.py +0 -0
  150. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/tasks/test_preset_checks.py +0 -0
  151. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/tasks/test_profile.py +0 -0
  152. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/tasks/test_query.py +0 -0
  153. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/tasks/test_row_count.py +0 -0
  154. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/tasks/test_top_k.py +0 -0
  155. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/tasks/test_valuediff.py +0 -0
  156. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/test_cli.py +0 -0
  157. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/test_config.py +0 -0
  158. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/test_core.py +0 -0
  159. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/test_pull_request.py +0 -0
  160. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/test_server.py +0 -0
  161. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/test_state.py +0 -0
  162. {recce_nightly-0.59.0.20250325 → recce_nightly-0.60.0.20250327}/tests/test_summary.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: recce-nightly
3
- Version: 0.59.0.20250325
3
+ Version: 0.60.0.20250327
4
4
  Summary: Environment diff tool for dbt
5
5
  Home-page: https://github.com/InfuseAI/recce
6
6
  Author: InfuseAI Dev Team
@@ -0,0 +1 @@
1
+ 0.60.0.20250327
@@ -42,7 +42,7 @@ class BaseAdapter(ABC):
42
42
 
43
43
  diff[key] = NodeDiff(change_status='modified', change_category='breaking')
44
44
  elif base_node:
45
- diff[key] = NodeDiff(chnage_status='removed')
45
+ diff[key] = NodeDiff(change_status='removed')
46
46
  elif curr_node:
47
47
  diff[key] = NodeDiff(change_status='added')
48
48
  return LineageDiff(
@@ -3,6 +3,7 @@ import logging
3
3
  import os
4
4
  import uuid
5
5
  from contextlib import contextmanager
6
+ from copy import deepcopy
6
7
  from dataclasses import dataclass, fields
7
8
  from errno import ENOENT
8
9
  from functools import lru_cache
@@ -13,6 +14,7 @@ from recce.event import log_performance
13
14
  from recce.exceptions import RecceException
14
15
  from recce.util.cll import cll, CLLPerformanceTracking
15
16
  from ...tasks.profile import ProfileTask
17
+ from recce.util.lineage import find_upstream, find_downstream
16
18
 
17
19
  try:
18
20
  import agate
@@ -571,22 +573,12 @@ class DbtAdapter(BaseAdapter):
571
573
 
572
574
  return parent_map
573
575
 
574
- @lru_cache(maxsize=2)
575
- def build_table_map(self, base: Optional[bool] = False) -> Dict[str, str]:
576
+ def build_parent_list_per_node(self, node_id: str, base: Optional[bool] = False) -> List[str]:
576
577
  manifest = self.curr_manifest if base is False else self.base_manifest
577
578
  manifest_dict = manifest.to_dict()
578
579
 
579
- table_map = {}
580
- for node in manifest_dict['nodes'].values():
581
- resource_type = node['resource_type']
582
- if resource_type not in ['model', 'seed', 'exposure', 'snapshot']:
583
- continue
584
- table_map[node['unique_id']] = node.get('alias')
585
-
586
- for source in manifest_dict['sources'].values():
587
- table_map[source['unique_id']] = source.get('identifier')
588
-
589
- return table_map
580
+ if node_id in manifest_dict['parent_map']:
581
+ return manifest_dict['parent_map'][node_id]
590
582
 
591
583
  def get_lineage(self, base: Optional[bool] = False):
592
584
  manifest = self.curr_manifest if base is False else self.base_manifest
@@ -687,24 +679,16 @@ class DbtAdapter(BaseAdapter):
687
679
  'resource_type': source['resource_type'],
688
680
  'package_name': source['package_name'],
689
681
  'config': source['config'],
690
- 'columns': {
691
- col.get('name'): {
692
- 'name': col.get('name'),
693
- 'type': col.get('data_type')
694
- }
695
- for col in source.get('columns', {}).values()
696
- }
697
682
  }
698
683
 
699
684
  if catalog is not None and unique_id in catalog.sources:
700
- columns = {
685
+ nodes[unique_id]['columns'] = {
701
686
  col_name: {
702
687
  'name': col_name,
703
688
  'type': col_metadata.type
704
689
  }
705
690
  for col_name, col_metadata in catalog.sources[unique_id].columns.items()
706
691
  }
707
- nodes[unique_id]['columns'].update(columns)
708
692
 
709
693
  for exposure in manifest_dict['exposures'].values():
710
694
  nodes[exposure['unique_id']] = {
@@ -735,16 +719,10 @@ class DbtAdapter(BaseAdapter):
735
719
 
736
720
  parent_map = self.build_parent_map(nodes, base)
737
721
 
738
- # Handle column-level lineage, only enable if env var is set to true
739
- if os.getenv('RECCE_CLL_ENABLED') != 'false':
740
- if base is False:
741
- cll_tracker.start_column_lineage()
742
- self.append_column_lineage(nodes, parent_map, base)
743
- cll_tracker.end_column_lineage()
744
-
745
722
  if base is False:
746
723
  cll_tracker.end_lineage()
747
- log_performance("column level lineage", cll_tracker.to_dict())
724
+ cll_tracker.set_total_nodes(len(nodes))
725
+ log_performance('model lineage', cll_tracker.to_dict())
748
726
  cll_tracker.reset()
749
727
 
750
728
  return dict(
@@ -754,111 +732,6 @@ class DbtAdapter(BaseAdapter):
754
732
  catalog_metadata=catalog_metadata,
755
733
  )
756
734
 
757
- def append_column_lineage(self, nodes: Dict, parent_map: Dict, base: Optional[bool] = False):
758
- def _apply_all_columns(node, trans_type, depends_on):
759
- for col in node.get('columns', {}).values():
760
- col['transformation_type'] = trans_type
761
- col['depends_on'] = depends_on
762
-
763
- def _depend_node_to_id(column_lineage, nodes):
764
- for cl in column_lineage.values():
765
- for depend_on in cl.depends_on:
766
- if depend_on.node.startswith('__'):
767
- for n in nodes.values():
768
- if n.get('resource_type') != 'source':
769
- continue
770
- # __source__table -> source.table
771
- source_table = depend_on.node.lstrip("_").replace("__", ".", 1).lower()
772
- if source_table in n.get('id'):
773
- depend_on.node = n.get('id')
774
- break
775
- else:
776
- for n in nodes.values():
777
- if n.get('name') == depend_on.node.lower():
778
- depend_on.node = n.get('id')
779
- break
780
-
781
- cll_tracker = CLLPerformanceTracking()
782
- cll_tracker.set_total_nodes(len(nodes))
783
- manifest = as_manifest(self.get_manifest(base))
784
- for node in nodes.values():
785
- resource_type = node.get('resource_type')
786
- if resource_type not in {'model', 'seed', 'source', 'snapshot'}:
787
- continue
788
-
789
- if resource_type == 'source' or resource_type == 'seed':
790
- _apply_all_columns(node, 'source', [])
791
- continue
792
-
793
- if node.get('raw_code') is None or self.is_python_model(node.get('id'), base=base):
794
- _apply_all_columns(node, 'unknown', [])
795
- continue
796
-
797
- # dbt <= 1.8, MetricFlow expects the time spine table to be named metricflow_time_spine
798
- if node.get('name') == 'metricflow_time_spine':
799
- _apply_all_columns(node, 'source', [])
800
- continue
801
-
802
- if not node.get('columns', {}):
803
- # no catalog
804
- continue
805
-
806
- def ref_func(*args):
807
- if len(args) == 1:
808
- node = args[0]
809
- elif len(args) > 1:
810
- node = args[1]
811
- else:
812
- return None
813
- return node
814
-
815
- def source_func(source_name, table_name):
816
- return f"__{source_name}__{table_name}"
817
-
818
- raw_code = node.get('raw_code')
819
- jinja_context = dict(
820
- ref=ref_func,
821
- source=source_func,
822
- )
823
-
824
- schema = {}
825
- for parent_id in parent_map[node.get('id')]:
826
- parent_node = nodes.get(parent_id)
827
- if parent_node is None:
828
- continue
829
- columns = parent_node.get('columns') or {}
830
- name = parent_node.get('name')
831
- if parent_node.get('resource_type') == 'source':
832
- parts = parent_id.split('.')
833
- source = parts[2]
834
- table = parts[3]
835
- name = f"__{source}__{table}"
836
- schema[name] = {
837
- name: column.get('type') for name, column in columns.items()
838
- }
839
-
840
- try:
841
- # provide a manifest to speedup and not pollute the manifest
842
- compiled_sql = self.generate_sql(raw_code, base=base, context=jinja_context, provided_manifest=manifest)
843
- dialect = self.adapter.type()
844
- column_lineage = cll(compiled_sql, schema=schema, dialect=dialect)
845
- except RecceException:
846
- # TODO: provide parsing error message if needed
847
- _apply_all_columns(node, 'unknown', [])
848
- cll_tracker.increment_sqlglot_error_nodes()
849
- continue
850
- except Exception:
851
- _apply_all_columns(node, 'unknown', [])
852
- cll_tracker.increment_other_error_nodes()
853
- continue
854
-
855
- _depend_node_to_id(column_lineage, nodes)
856
-
857
- for name, column in node.get('columns', {}).items():
858
- if name in column_lineage:
859
- column['depends_on'] = column_lineage[name].depends_on
860
- column['transformation_type'] = column_lineage[name].type
861
-
862
735
  @lru_cache(maxsize=1)
863
736
  def _get_lineage_diff_cached(self, cache_key) -> LineageDiff:
864
737
  base = self.get_lineage(base=True)
@@ -902,6 +775,198 @@ class DbtAdapter(BaseAdapter):
902
775
  diff=diff,
903
776
  )
904
777
 
778
+ def get_cll_by_node_id(self, node_id: str, base: Optional[bool] = False):
779
+ cll_tracker = CLLPerformanceTracking()
780
+ cll_tracker.start_column_lineage()
781
+
782
+ manifest = self.curr_manifest if base is False else self.base_manifest
783
+ manifest_dict = manifest.to_dict()
784
+
785
+ parent_ids = find_upstream(node_id, manifest_dict.get('parent_map'))
786
+ child_ids = find_downstream(node_id, manifest_dict.get('child_map'))
787
+ cll_node_ids = parent_ids.union(child_ids)
788
+ cll_node_ids.add(node_id)
789
+
790
+ node_manifest = self.get_lineage_nodes_metadata(base=base)
791
+ nodes = {}
792
+ for node_id in cll_node_ids:
793
+ if node_id not in node_manifest:
794
+ continue
795
+ nodes[node_id] = self.get_cll_cached(node_id, base=base)
796
+
797
+ cll_tracker.end_column_lineage()
798
+ cll_tracker.set_total_nodes(len(nodes))
799
+ log_performance('column level lineage', cll_tracker.to_dict())
800
+ cll_tracker.reset()
801
+
802
+ return dict(nodes=nodes)
803
+
804
+ @lru_cache(maxsize=128)
805
+ def get_cll_cached(self, node_id: str, base: Optional[bool] = False):
806
+ nodes = self.get_lineage_nodes_metadata(base=base)
807
+
808
+ manifest = self.curr_manifest if base is False else self.base_manifest
809
+ manifest_dict = manifest.to_dict()
810
+ parent_list = []
811
+ if node_id in manifest_dict['parent_map']:
812
+ parent_list = manifest_dict['parent_map'][node_id]
813
+
814
+ node = deepcopy(nodes[node_id])
815
+ self.append_column_lineage(node, parent_list, base)
816
+ return node
817
+
818
+ def append_column_lineage(self, node: Dict, parent_list: List, base: Optional[bool] = False):
819
+ def _apply_all_columns(node, trans_type, depends_on):
820
+ for col in node.get('columns', {}).values():
821
+ col['transformation_type'] = trans_type
822
+ col['depends_on'] = depends_on
823
+
824
+ def _depend_node_to_id(column_lineage, nodes):
825
+ for cl in column_lineage.values():
826
+ for depend_on in cl.depends_on:
827
+ if depend_on.node.startswith('__'):
828
+ for n in nodes.values():
829
+ if n.get('resource_type') != 'source':
830
+ continue
831
+ # __source__table -> source.table
832
+ source_table = depend_on.node.lstrip("_").replace("__", ".", 1).lower()
833
+ if source_table in n.get('id'):
834
+ depend_on.node = n.get('id')
835
+ break
836
+ else:
837
+ for n in nodes.values():
838
+ if n.get('name') == depend_on.node.lower():
839
+ depend_on.node = n.get('id')
840
+ break
841
+
842
+ cll_tracker = CLLPerformanceTracking()
843
+ nodes = self.get_lineage_nodes_metadata(base=base)
844
+ manifest = as_manifest(self.get_manifest(base))
845
+ resource_type = node.get('resource_type')
846
+ if resource_type not in {'model', 'seed', 'source', 'snapshot'}:
847
+ return
848
+
849
+ if resource_type == 'source' or resource_type == 'seed':
850
+ _apply_all_columns(node, 'source', [])
851
+ return
852
+
853
+ if node.get('raw_code') is None or self.is_python_model(node.get('id'), base=base):
854
+ _apply_all_columns(node, 'unknown', [])
855
+ return
856
+
857
+ # dbt <= 1.8, MetricFlow expects the time spine table to be named metricflow_time_spine
858
+ if node.get('name') == 'metricflow_time_spine':
859
+ _apply_all_columns(node, 'source', [])
860
+ return
861
+
862
+ if not node.get('columns', {}):
863
+ # no catalog
864
+ return
865
+
866
+ def ref_func(*args):
867
+ if len(args) == 1:
868
+ node = args[0]
869
+ elif len(args) > 1:
870
+ node = args[1]
871
+ else:
872
+ return None
873
+ return node
874
+
875
+ def source_func(source_name, table_name):
876
+ return f"__{source_name}__{table_name}"
877
+
878
+ raw_code = node.get('raw_code')
879
+ jinja_context = dict(
880
+ ref=ref_func,
881
+ source=source_func,
882
+ )
883
+
884
+ schema = {}
885
+ for parent_id in parent_list:
886
+ parent_node = nodes.get(parent_id)
887
+ if parent_node is None:
888
+ continue
889
+ columns = parent_node.get('columns') or {}
890
+ name = parent_node.get('name')
891
+ if parent_node.get('resource_type') == 'source':
892
+ parts = parent_id.split('.')
893
+ source = parts[2]
894
+ table = parts[3]
895
+ name = f"__{source}__{table}"
896
+ schema[name] = {
897
+ name: column.get('type') for name, column in columns.items()
898
+ }
899
+
900
+ try:
901
+ # provide a manifest to speedup and not pollute the manifest
902
+ compiled_sql = self.generate_sql(raw_code, base=base, context=jinja_context, provided_manifest=manifest)
903
+ dialect = self.adapter.type()
904
+ column_lineage = cll(compiled_sql, schema=schema, dialect=dialect)
905
+ except RecceException:
906
+ # TODO: provide parsing error message if needed
907
+ _apply_all_columns(node, 'unknown', [])
908
+ cll_tracker.increment_sqlglot_error_nodes()
909
+ return
910
+ except Exception:
911
+ _apply_all_columns(node, 'unknown', [])
912
+ cll_tracker.increment_other_error_nodes()
913
+ return
914
+
915
+ _depend_node_to_id(column_lineage, nodes)
916
+
917
+ for name, column in node.get('columns', {}).items():
918
+ if name in column_lineage:
919
+ column['depends_on'] = column_lineage[name].depends_on
920
+ column['transformation_type'] = column_lineage[name].type
921
+
922
+ @lru_cache(maxsize=2)
923
+ def get_lineage_nodes_metadata(self, base: Optional[bool] = False):
924
+ manifest = self.curr_manifest if base is False else self.base_manifest
925
+ catalog = self.curr_catalog if base is False else self.base_catalog
926
+ manifest_dict = manifest.to_dict()
927
+
928
+ nodes = {}
929
+ for node in manifest_dict['nodes'].values():
930
+ unique_id = node['unique_id']
931
+ resource_type = node['resource_type']
932
+
933
+ if resource_type not in ['model', 'seed', 'exposure', 'snapshot']:
934
+ continue
935
+
936
+ nodes[unique_id] = {
937
+ 'id': node['unique_id'],
938
+ 'name': node['name'],
939
+ 'resource_type': node['resource_type'],
940
+ 'raw_code': node['raw_code'],
941
+ }
942
+
943
+ if catalog is not None and unique_id in catalog.nodes:
944
+ columns = {}
945
+ for col_name, col_metadata in catalog.nodes[unique_id].columns.items():
946
+ col = dict(name=col_name, type=col_metadata.type)
947
+ columns[col_name] = col
948
+ nodes[unique_id]['columns'] = columns
949
+
950
+ for source in manifest_dict['sources'].values():
951
+ unique_id = source['unique_id']
952
+
953
+ nodes[unique_id] = {
954
+ 'id': source['unique_id'],
955
+ 'name': source['name'],
956
+ 'resource_type': source['resource_type'],
957
+ }
958
+
959
+ if catalog is not None and unique_id in catalog.sources:
960
+ nodes[unique_id]['columns'] = {
961
+ col_name: {
962
+ 'name': col_name,
963
+ 'type': col_metadata.type
964
+ }
965
+ for col_name, col_metadata in catalog.sources[unique_id].columns.items()
966
+ }
967
+
968
+ return nodes
969
+
905
970
  def get_manifests_by_id(self, unique_id: str):
906
971
  curr_manifest = self.get_manifest(base=False)
907
972
  base_manifest = self.get_manifest(base=True)
@@ -997,8 +1062,11 @@ class DbtAdapter(BaseAdapter):
997
1062
  if refresh_file_path.endswith('manifest.json'):
998
1063
  self.curr_manifest = load_manifest(path=refresh_file_path)
999
1064
  self.manifest = as_manifest(self.curr_manifest)
1065
+ self.get_cll_cached.cache_clear()
1066
+ self.get_lineage_nodes_metadata.cache_clear()
1000
1067
  elif refresh_file_path.endswith('catalog.json'):
1001
1068
  self.curr_catalog = load_catalog(path=refresh_file_path)
1069
+ self.get_lineage_nodes_metadata.cache_clear()
1002
1070
  elif self.base_path and target_type == os.path.basename(self.base_path):
1003
1071
  if refresh_file_path.endswith('manifest.json'):
1004
1072
  self.base_manifest = load_manifest(path=refresh_file_path)
@@ -0,0 +1 @@
1
+ <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-8a8709fe9d22c323.js"/><script src="/_next/static/chunks/38610ee6-24a85e2dfb424ddb.js" async=""></script><script src="/_next/static/chunks/783-784ce10322400a48.js" async=""></script><script src="/_next/static/chunks/main-app-cfd795a5fe58bf37.js" async=""></script><meta name="robots" content="noindex"/><title>404: This page could not be found.</title><title>recce</title><meta name="description" content="Recce: Data validation toolkit for comprehensive PR review"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><script src="/_next/static/chunks/webpack-8a8709fe9d22c323.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:I[83798,[],\"\"]\n3:I[26375,[],\"\"]\n4:I[22587,[],\"\"]\na:I[114,[],\"\"]\n5:{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"}\n6:{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"}\n7:{\"display\":\"inline-block\"}\n8:{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0}\nb:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L1\",null,{\"buildId\":\"toQz_Vr_ujCxxim6p2pBl\",\"assetPrefix\":\"\",\"urlParts\":[\"\",\"_not-found\"],\"initialTree\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{},[[\"$L2\",[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null],null],null]},[null,[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"/_not-found\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[[null,[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"suppressHydrationWarning\":true,\"children\":[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":\"$5\",\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":\"$6\",\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":\"$7\",\"children\":[\"$\",\"h2\",null,{\"style\":\"$8\",\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]}]}]],null],null],\"couldBeIntercepted\":false,\"initialHead\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],\"$L9\"],\"globalErrorComponent\":\"$a\",\"missingSlots\":\"$Wb\"}]\n"])</script><script>self.__next_f.push([1,"9:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"recce\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"Recce: Data validation toolkit for comprehensive PR review\"}]]\n2:null\n"])</script></body></html>