dirsql 0.4.69__tar.gz → 0.4.71__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.
Files changed (309) hide show
  1. {dirsql-0.4.69 → dirsql-0.4.71}/Cargo.lock +2 -2
  2. {dirsql-0.4.69 → dirsql-0.4.71}/PKG-INFO +1 -1
  3. {dirsql-0.4.69/packages/rust → dirsql-0.4.71}/docs/reference/path-tables.md +9 -0
  4. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/Cargo.toml +1 -1
  5. dirsql-0.4.71/packages/python/changelog.d/2026-10-05-path-table-node-modules-named.md +6 -0
  6. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/reference/path-tables.md +9 -0
  7. dirsql-0.4.71/packages/python/e2e-attestations/claude-1267-posix-classes.json +7 -0
  8. dirsql-0.4.71/packages/python/e2e-attestations/claude-1271-node-modules-named.json +7 -0
  9. dirsql-0.4.71/packages/python/migrations.d/2026-10-05-path-table-node-modules-named.md +37 -0
  10. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/Cargo.toml +1 -1
  11. {dirsql-0.4.69 → dirsql-0.4.71/packages/rust}/docs/reference/path-tables.md +9 -0
  12. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/db.rs +13 -28
  13. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/lib.rs +1 -0
  14. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/matcher.rs +11 -1
  15. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/path_table.rs +43 -2
  16. dirsql-0.4.71/packages/rust/src/posix_class.rs +313 -0
  17. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/scanner.rs +2 -0
  18. {dirsql-0.4.69 → dirsql-0.4.71}/Cargo.toml +0 -0
  19. {dirsql-0.4.69 → dirsql-0.4.71}/README.md +0 -0
  20. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/__init__.py +0 -0
  21. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/_async.py +0 -0
  22. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/_dirsql.pyi +0 -0
  23. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/cli/__init__.py +0 -0
  24. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/cli/discover_plugins/__init__.py +0 -0
  25. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
  26. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
  27. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
  28. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/cli/discover_plugins/user_passed_config.py +0 -0
  29. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
  30. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/cli/main.py +0 -0
  31. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/cli/resolve_config_extensions.py +0 -0
  32. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/plan_entry_path.py +0 -0
  33. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/py.typed +0 -0
  34. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/read_config.py +0 -0
  35. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/resolve_configs_extension_specs.py +0 -0
  36. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/resolve_extension.py +0 -0
  37. {dirsql-0.4.69 → dirsql-0.4.71}/dirsql/resolve_package.py +0 -0
  38. {dirsql-0.4.69 → dirsql-0.4.71}/docs/.claude/CLAUDE.md +0 -0
  39. {dirsql-0.4.69 → dirsql-0.4.71}/docs/.vitepress/config.ts +0 -0
  40. {dirsql-0.4.69 → dirsql-0.4.71}/docs/.vitepress/theme/index.ts +0 -0
  41. {dirsql-0.4.69 → dirsql-0.4.71}/docs/.vitepress/theme/lang.ts +0 -0
  42. {dirsql-0.4.69 → dirsql-0.4.71}/docs/AGENTS.md +0 -0
  43. {dirsql-0.4.69 → dirsql-0.4.71}/docs/context/.gitattributes +0 -0
  44. {dirsql-0.4.69 → dirsql-0.4.71}/docs/context/recipes.md +0 -0
  45. {dirsql-0.4.69 → dirsql-0.4.71}/docs/context/troubleshooting.md +0 -0
  46. {dirsql-0.4.69 → dirsql-0.4.71}/docs/context/usage.md +0 -0
  47. {dirsql-0.4.69 → dirsql-0.4.71}/docs/explanation.md +0 -0
  48. {dirsql-0.4.69 → dirsql-0.4.71}/docs/getting-started.md +0 -0
  49. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/columns-from-paths.md +0 -0
  50. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/define-tables.md +0 -0
  51. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/embed.md +0 -0
  52. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/extract-from-contents.md +0 -0
  53. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/load-extension.md +0 -0
  54. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/parse-files-into-columns.md +0 -0
  55. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/persist.md +0 -0
  56. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/query-json.md +0 -0
  57. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/query-without-config.md +0 -0
  58. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/react-to-changes.md +0 -0
  59. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/search-by-meaning.md +0 -0
  60. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/search-indexes.md +0 -0
  61. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/skip-files.md +0 -0
  62. {dirsql-0.4.69 → dirsql-0.4.71}/docs/howto/write-a-plugin.md +0 -0
  63. {dirsql-0.4.69 → dirsql-0.4.71}/docs/index.md +0 -0
  64. {dirsql-0.4.69 → dirsql-0.4.71}/docs/package.json +0 -0
  65. {dirsql-0.4.69 → dirsql-0.4.71}/docs/plugins.md +0 -0
  66. {dirsql-0.4.69 → dirsql-0.4.71}/docs/pnpm-lock.yaml +0 -0
  67. {dirsql-0.4.69 → dirsql-0.4.71}/docs/pnpm-workspace.yaml +0 -0
  68. {dirsql-0.4.69 → dirsql-0.4.71}/docs/reference/cli.md +0 -0
  69. {dirsql-0.4.69 → dirsql-0.4.71}/docs/reference/columns.md +0 -0
  70. {dirsql-0.4.69 → dirsql-0.4.71}/docs/reference/config.md +0 -0
  71. {dirsql-0.4.69 → dirsql-0.4.71}/docs/reference/hooks.md +0 -0
  72. {dirsql-0.4.69 → dirsql-0.4.71}/docs/reference/http-api.md +0 -0
  73. {dirsql-0.4.69 → dirsql-0.4.71}/docs/reference/sdk.md +0 -0
  74. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/CHANGELOG.md +0 -0
  75. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/MIGRATIONS.md +0 -0
  76. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/README.md +0 -0
  77. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  78. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
  79. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  80. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
  81. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  82. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
  83. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
  84. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
  85. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  86. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
  87. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
  88. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
  89. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
  90. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
  91. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
  92. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
  93. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-04-cli-in-process.md +0 -0
  94. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-20-curtaincall-dev-dependency.md +0 -0
  95. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-20-declared-table-name.md +0 -0
  96. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-04-config-paths-from-argv-module.md +0 -0
  97. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-05-config-extension-loader-split.md +0 -0
  98. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-05-resolve-config-extensions-split.md +0 -0
  99. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-05-resolve-extension-split.md +0 -0
  100. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-09-launcher-config-path-scan.md +0 -0
  101. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-17-extension-resolution-via-core.md +0 -0
  102. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-18-export-scan-failure.md +0 -0
  103. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-18-flatten-row-event-in-core.md +0 -0
  104. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-18-programmatic-extension-plan.md +0 -0
  105. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
  106. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-24-query-rows-in-projection-order.md +0 -0
  107. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-02-readme-docs-host.md +0 -0
  108. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-03-one-glob-rule-for-config-globs.md +0 -0
  109. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-03-path-table-directory-one-level.md +0 -0
  110. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-04-path-table-dotfiles-hidden.md +0 -0
  111. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-05-cli-context.md +0 -0
  112. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-05-gitignore-requires-repo.md +0 -0
  113. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-05-path-table-symlinks.md +0 -0
  114. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-05-path-table-trailing-slash.md +0 -0
  115. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/changelog.d/README.md +0 -0
  116. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/conftest.py +0 -0
  117. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/.claude/CLAUDE.md +0 -0
  118. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/.vitepress/config.ts +0 -0
  119. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  120. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  121. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/AGENTS.md +0 -0
  122. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/context/.gitattributes +0 -0
  123. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/context/recipes.md +0 -0
  124. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/context/troubleshooting.md +0 -0
  125. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/context/usage.md +0 -0
  126. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/explanation.md +0 -0
  127. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/getting-started.md +0 -0
  128. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/columns-from-paths.md +0 -0
  129. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/define-tables.md +0 -0
  130. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/embed.md +0 -0
  131. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/extract-from-contents.md +0 -0
  132. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/load-extension.md +0 -0
  133. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/parse-files-into-columns.md +0 -0
  134. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/persist.md +0 -0
  135. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/query-json.md +0 -0
  136. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/query-without-config.md +0 -0
  137. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/react-to-changes.md +0 -0
  138. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/search-by-meaning.md +0 -0
  139. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/search-indexes.md +0 -0
  140. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/skip-files.md +0 -0
  141. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/howto/write-a-plugin.md +0 -0
  142. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/index.md +0 -0
  143. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/package.json +0 -0
  144. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/plugins.md +0 -0
  145. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/pnpm-lock.yaml +0 -0
  146. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/pnpm-workspace.yaml +0 -0
  147. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/reference/cli.md +0 -0
  148. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/reference/columns.md +0 -0
  149. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/reference/config.md +0 -0
  150. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/reference/hooks.md +0 -0
  151. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/reference/http-api.md +0 -0
  152. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/docs/reference/sdk.md +0 -0
  153. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/cc-keen-knuth-f11gyq.json +0 -0
  154. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1056-py-ofpf9.json +0 -0
  155. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1066-p1-resolve-extension.json +0 -0
  156. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1078-argv-config-paths-core.json +0 -0
  157. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1079-p2-resolve-config-extensions.json +0 -0
  158. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1080-share-vtab-scaffolding.json +0 -0
  159. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1081-indicatif-progress.json +0 -0
  160. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1096-watch-moved-dir.json +0 -0
  161. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1109-watch-moved-out.json +0 -0
  162. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1119-ext-plan.json +0 -0
  163. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1120-py-ext.json +0 -0
  164. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1122-ext-cleanup.json +0 -0
  165. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1130-codec-crates.json +0 -0
  166. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1131-flatten-row-event.json +0 -0
  167. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1139-sse-flatten.json +0 -0
  168. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1140-scan-failure-export.json +0 -0
  169. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1148-clippy-all-targets.json +0 -0
  170. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1164-sigint-fatal.json +0 -0
  171. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1170-projection-order.json +0 -0
  172. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1172-on-file-path.json +0 -0
  173. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1173-py-projection-order.json +0 -0
  174. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1184-win-rel-paths.json +0 -0
  175. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1185-win-abs-table-names.json +0 -0
  176. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1186-win-test-harness.json +0 -0
  177. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1196-join-index.json +0 -0
  178. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1199-glob-prefix.json +0 -0
  179. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1201-single-walk.json +0 -0
  180. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1204-embed-batch.json +0 -0
  181. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1207-win-argv-quote.json +0 -0
  182. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1208-win-nonverbatim-paths.json +0 -0
  183. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1215-on-file-batch.json +0 -0
  184. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1216-on-file-cleanup.json +0 -0
  185. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1223-config-glob-rule.json +0 -0
  186. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1223-dir-one-level.json +0 -0
  187. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1226-stale-docs.json +0 -0
  188. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1238-parallel-walk.json +0 -0
  189. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1241-content-prefetch.json +0 -0
  190. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1243-ingest-perf.json +0 -0
  191. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1249-dotfiles.json +0 -0
  192. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1256-symlinks.json +0 -0
  193. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1257-trailing-slash.json +0 -0
  194. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1264-context.json +0 -0
  195. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1269-utf8-chars.json +0 -0
  196. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1272-ancestor-gitignore.json +0 -0
  197. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1273-walk-prune.json +0 -0
  198. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1285-ingest-fast.json +0 -0
  199. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-819-oneshot-timeout.json +0 -0
  200. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-827-unquote-doubled-quotes.json +0 -0
  201. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-epic-953-slice2-reedline.json +0 -0
  202. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-epic-953-slice3-format.json +0 -0
  203. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-epic-953-stacked-prs-x59wm3.json +0 -0
  204. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-github-issue-825-e4mhhp.json +0 -0
  205. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-issue-951-0eease.json +0 -0
  206. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-issue-962-0jjrfv.json +0 -0
  207. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-issue-986-red-test-e7sjrb.json +0 -0
  208. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-tackle-1034-cachesplit.json +0 -0
  209. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-tackle-1042-py12.json +0 -0
  210. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-tackle-956-xzen74.json +0 -0
  211. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6-worker-calls.json +0 -0
  212. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6.json +0 -0
  213. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-tackle-975-n8mc2h.json +0 -0
  214. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  215. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  216. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  217. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
  218. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
  219. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
  220. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  221. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
  222. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-08-04-cli-in-process.md +0 -0
  223. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-08-20-declared-table-name.md +0 -0
  224. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
  225. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-09-24-query-rows-in-projection-order.md +0 -0
  226. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-02-readme-docs-host.md +0 -0
  227. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-03-one-glob-rule-for-config-globs.md +0 -0
  228. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-03-path-table-directory-one-level.md +0 -0
  229. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-04-path-table-dotfiles-hidden.md +0 -0
  230. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-05-cli-context.md +0 -0
  231. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-05-gitignore-requires-repo.md +0 -0
  232. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-05-path-table-symlinks.md +0 -0
  233. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-05-path-table-trailing-slash.md +0 -0
  234. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/migrations.d/README.md +0 -0
  235. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/src/lib.rs +0 -0
  236. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/testing-conventions.toml +0 -0
  237. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/tests/__init__.py +0 -0
  238. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/tests/conftest.py +0 -0
  239. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/tests/e2e/__init__.py +0 -0
  240. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
  241. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +0 -0
  242. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/tests/e2e/speed_of_light.py +0 -0
  243. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/tests/integration/__init__.py +0 -0
  244. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/tests/integration/binding/__init__.py +0 -0
  245. {dirsql-0.4.69 → dirsql-0.4.71}/packages/python/tests/integration/hermetic/__init__.py +0 -0
  246. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/CHANGELOG.md +0 -0
  247. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/MIGRATIONS.md +0 -0
  248. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/README.md +0 -0
  249. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/benches/db_bench.rs +0 -0
  250. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/benches/differ_bench.rs +0 -0
  251. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/benches/matcher_bench.rs +0 -0
  252. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/benches/scanner_bench.rs +0 -0
  253. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/context/.gitattributes +0 -0
  254. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/context/recipes.md +0 -0
  255. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/context/troubleshooting.md +0 -0
  256. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/context/usage.md +0 -0
  257. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/explanation.md +0 -0
  258. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/getting-started.md +0 -0
  259. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/columns-from-paths.md +0 -0
  260. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/define-tables.md +0 -0
  261. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/embed.md +0 -0
  262. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/extract-from-contents.md +0 -0
  263. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/load-extension.md +0 -0
  264. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/parse-files-into-columns.md +0 -0
  265. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/persist.md +0 -0
  266. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/query-json.md +0 -0
  267. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/query-without-config.md +0 -0
  268. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/react-to-changes.md +0 -0
  269. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/search-by-meaning.md +0 -0
  270. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/search-indexes.md +0 -0
  271. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/skip-files.md +0 -0
  272. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/howto/write-a-plugin.md +0 -0
  273. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/index.md +0 -0
  274. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/plugins.md +0 -0
  275. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/reference/cli.md +0 -0
  276. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/reference/columns.md +0 -0
  277. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/reference/config.md +0 -0
  278. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/reference/hooks.md +0 -0
  279. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/reference/http-api.md +0 -0
  280. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/docs/reference/sdk.md +0 -0
  281. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/bin/dirsql.rs +0 -0
  282. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/cli/context.rs +0 -0
  283. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/cli/execute.rs +0 -0
  284. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/cli/init.rs +0 -0
  285. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/cli/mod.rs +0 -0
  286. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/cli/repl.rs +0 -0
  287. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/cli/router.rs +0 -0
  288. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/cli/run.rs +0 -0
  289. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/cli/serialize.rs +0 -0
  290. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/cli/server.rs +0 -0
  291. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/cli/table.rs +0 -0
  292. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/command.rs +0 -0
  293. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/config.rs +0 -0
  294. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/default_config.toml +0 -0
  295. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/differ.rs +0 -0
  296. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/extension_resolution.rs +0 -0
  297. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/functions.rs +0 -0
  298. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/infer.rs +0 -0
  299. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/launcher.rs +0 -0
  300. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/on_file.rs +0 -0
  301. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/parsed_vtab.rs +0 -0
  302. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/persist.rs +0 -0
  303. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/progress.rs +0 -0
  304. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/row_event_flat.rs +0 -0
  305. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/sql_literal.rs +0 -0
  306. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/vtab.rs +0 -0
  307. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/vtab_scaffold.rs +0 -0
  308. {dirsql-0.4.69 → dirsql-0.4.71}/packages/rust/src/watcher.rs +0 -0
  309. {dirsql-0.4.69 → dirsql-0.4.71}/pyproject.toml +0 -0
@@ -552,7 +552,7 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
552
552
 
553
553
  [[package]]
554
554
  name = "dirsql"
555
- version = "0.4.69"
555
+ version = "0.4.71"
556
556
  dependencies = [
557
557
  "assert_cmd",
558
558
  "axum",
@@ -601,7 +601,7 @@ dependencies = [
601
601
 
602
602
  [[package]]
603
603
  name = "dirsql-py-ext"
604
- version = "0.4.69"
604
+ version = "0.4.71"
605
605
  dependencies = [
606
606
  "dirsql",
607
607
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.4.69
3
+ Version: 0.4.71
4
4
  Requires-Dist: bin-shim>=0.1
5
5
  Summary: Ephemeral SQL index over a local directory
6
6
  Keywords: sql,filesystem,directory,sqlite,index
@@ -295,6 +295,15 @@ SELECT path FROM './dist'; -- scans dist/ even when gitignored
295
295
  The `.gitignore` files in force there, including those above it, still
296
296
  filter what lies beneath it.
297
297
 
298
+ A `node_modules` component after a glob names it too, at any depth:
299
+
300
+ ```sql
301
+ SELECT path FROM './**/node_modules/*/package.json'; -- every node_modules
302
+ SELECT path FROM './*/node_modules/**'; -- one level down
303
+ ```
304
+
305
+ Only the literal name counts; `'./**/*.js'` still skips every `node_modules`.
306
+
298
307
  ### Hidden files
299
308
 
300
309
  A path component starting with `.` is skipped unless the path spells it, the
@@ -4,7 +4,7 @@ name = "dirsql-py-ext"
4
4
  # pypi/maturin handler can rewrite it via `write-version` before
5
5
  # `maturin build`. `pyproject.toml` declares `dynamic = ["version"]`
6
6
  # and maturin reads this field. Mirrors `packages/rust/Cargo.toml`.
7
- version = "0.4.69"
7
+ version = "0.4.71"
8
8
  # abi3 note: the `abi3-py310` pyo3 feature (below) builds ONE stable-ABI
9
9
  # `cp310-abi3` wheel per platform that loads on every CPython >= 3.10
10
10
  # (matching `pyproject.toml`'s `requires-python = ">=3.10"`), instead of
@@ -0,0 +1,6 @@
1
+ **Fixed** A path-table pattern that names `node_modules` after a glob
2
+ component now scans it: `'./**/node_modules/**'` lists every `node_modules`
3
+ in the tree and `'./*/node_modules/*/*'` the ones one level down. Only a
4
+ `node_modules` in the literal prefix used to count, so these returned nothing.
5
+ A pattern that does not name `node_modules`, such as `'./**'` or
6
+ `'./**/*.js'`, still skips it.
@@ -295,6 +295,15 @@ SELECT path FROM './dist'; -- scans dist/ even when gitignored
295
295
  The `.gitignore` files in force there, including those above it, still
296
296
  filter what lies beneath it.
297
297
 
298
+ A `node_modules` component after a glob names it too, at any depth:
299
+
300
+ ```sql
301
+ SELECT path FROM './**/node_modules/*/package.json'; -- every node_modules
302
+ SELECT path FROM './*/node_modules/**'; -- one level down
303
+ ```
304
+
305
+ Only the literal name counts; `'./**/*.js'` still skips every `node_modules`.
306
+
298
307
  ### Hidden files
299
308
 
300
309
  A path component starting with `.` is skipped unless the path spells it, the
@@ -0,0 +1,7 @@
1
+ {
2
+ "command": "just test-e2e",
3
+ "ran_at": 1791224868,
4
+ "exit_code": 0,
5
+ "commit": "ca8ccbb55b7f51f9373b8f774665b925ba10ac21",
6
+ "branch": "claude/1267-posix-classes"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "command": "just test-e2e",
3
+ "ran_at": 1791224899,
4
+ "exit_code": 0,
5
+ "commit": "e48a79c9fc67e5c2d69ec6c77116e5d2730684bb",
6
+ "branch": "claude/1271-node-modules-named"
7
+ }
@@ -0,0 +1,37 @@
1
+ ### Core: `node_modules` named after a glob is scanned
2
+
3
+ #### Summary
4
+
5
+ A path-table pattern with a literal `node_modules` component after a glob,
6
+ such as `'./**/node_modules/**'`, now scans the `node_modules` directories it
7
+ names; it used to return nothing. Every surface that resolves a path-table is
8
+ affected: the CLI, the REPL, the HTTP server, the SDKs, and the `--on-file`
9
+ form. Nothing breaks; such a query can return more rows. A pattern that does
10
+ not name `node_modules` still skips it, and declared `[[table]]` globs are
11
+ unchanged.
12
+
13
+ #### Required changes
14
+
15
+ _None._
16
+
17
+ #### Deprecations removed
18
+
19
+ _None._
20
+
21
+ #### Behavior changes without code changes
22
+
23
+ - `'./**/node_modules/**'` lists files under every `node_modules`.
24
+ - `'./*/node_modules/*/*'` lists files two levels into each `node_modules`
25
+ one level down.
26
+
27
+ #### Verification
28
+
29
+ ```bash
30
+ cd "$(mktemp -d)"
31
+ mkdir -p node_modules/pkg pkg/node_modules/dep
32
+ touch node_modules/pkg/index.js pkg/node_modules/dep/i.js
33
+ dirsql query "SELECT path FROM './**/node_modules/*/*' ORDER BY path"
34
+ # expected: [{"path":"node_modules/pkg/index.js"},{"path":"pkg/node_modules/dep/i.js"}]
35
+ dirsql query "SELECT path FROM './**/*.js'"
36
+ # expected: []
37
+ ```
@@ -6,7 +6,7 @@ description = "Ephemeral SQL index over a local directory"
6
6
  # version stays for the binding crates (publish = false). The literal
7
7
  # intentionally lags the published version: putitoutthere rewrites it to
8
8
  # the planned release version at build time and never commits it back.
9
- version = "0.4.69"
9
+ version = "0.4.71"
10
10
  edition.workspace = true
11
11
  # Literal `license` (not `license.workspace = true`) because
12
12
  # putitoutthere's preflight check reads `[package].license` directly
@@ -295,6 +295,15 @@ SELECT path FROM './dist'; -- scans dist/ even when gitignored
295
295
  The `.gitignore` files in force there, including those above it, still
296
296
  filter what lies beneath it.
297
297
 
298
+ A `node_modules` component after a glob names it too, at any depth:
299
+
300
+ ```sql
301
+ SELECT path FROM './**/node_modules/*/package.json'; -- every node_modules
302
+ SELECT path FROM './*/node_modules/**'; -- one level down
303
+ ```
304
+
305
+ Only the literal name counts; `'./**/*.js'` still skips every `node_modules`.
306
+
298
307
  ### Hidden files
299
308
 
300
309
  A path component starting with `.` is skipped unless the path spells it, the
@@ -315,7 +315,8 @@ pub struct Db {
315
315
  /// exactly where `files` used to exist implicitly. A user who declared
316
316
  /// tables and forgot `files` gets the plain SQLite error.
317
317
  hint_legacy_files_table: bool,
318
- /// Skip rules a path-table scan applies, seeded with the built-in defaults.
318
+ /// Configured skip rules a path-table scan applies on top of the table's
319
+ /// [`PathTable::default_ignores`].
319
320
  path_table_ignore: Vec<String>,
320
321
  /// Whether a path-table scan respects `.gitignore` files. On by default;
321
322
  /// the CLI's `--no-ignore` turns it off.
@@ -351,14 +352,6 @@ fn worker_error(message: String) -> DbError {
351
352
  DbError::Sqlite(rusqlite::Error::UserFunctionError(message.into()))
352
353
  }
353
354
 
354
- /// The skip rules a fresh `Db` starts with.
355
- fn default_path_table_ignore() -> Vec<String> {
356
- path_table::DEFAULT_IGNORES
357
- .iter()
358
- .map(|s| (*s).to_string())
359
- .collect()
360
- }
361
-
362
355
  impl Db {
363
356
  /// Open the default, ephemeral `Db`: an **anonymous disk-backed temp
364
357
  /// database** (`Connection::open("")`), not `:memory:`.
@@ -382,7 +375,7 @@ impl Db {
382
375
  scope,
383
376
  path_table_root: None,
384
377
  hint_legacy_files_table: false,
385
- path_table_ignore: default_path_table_ignore(),
378
+ path_table_ignore: Vec::new(),
386
379
  path_table_gitignore: true,
387
380
  path_table_parser: None,
388
381
  calls: CallReporter::new(),
@@ -407,7 +400,7 @@ impl Db {
407
400
  scope,
408
401
  path_table_root: None,
409
402
  hint_legacy_files_table: false,
410
- path_table_ignore: default_path_table_ignore(),
403
+ path_table_ignore: Vec::new(),
411
404
  path_table_gitignore: true,
412
405
  path_table_parser: None,
413
406
  calls: CallReporter::new(),
@@ -1170,10 +1163,15 @@ impl Db {
1170
1163
  .path_table_root
1171
1164
  .as_deref()
1172
1165
  .expect("a path-table resolves only under an index root");
1166
+ let ignore: Vec<String> = table
1167
+ .default_ignores()
1168
+ .map(str::to_string)
1169
+ .chain(self.path_table_ignore.iter().cloned())
1170
+ .collect();
1173
1171
  self.conn.execute_batch(&path_table_ddl(
1174
1172
  name,
1175
1173
  table,
1176
- &self.path_table_ignore,
1174
+ &ignore,
1177
1175
  self.path_table_gitignore,
1178
1176
  self.path_table_parser.as_deref(),
1179
1177
  index_root,
@@ -3083,14 +3081,6 @@ mod tests {
3083
3081
  );
3084
3082
  }
3085
3083
 
3086
- #[test]
3087
- fn default_path_table_ignore_carries_the_documented_defaults() {
3088
- assert_eq!(
3089
- default_path_table_ignore(),
3090
- vec!["**/node_modules/**".to_string(), "**/.git/**".to_string()]
3091
- );
3092
- }
3093
-
3094
3084
  fn docs_path_table() -> PathTable {
3095
3085
  PathTable {
3096
3086
  root: PathBuf::from("/root"),
@@ -3304,17 +3294,12 @@ mod tests {
3304
3294
  }
3305
3295
 
3306
3296
  #[test]
3307
- fn add_path_table_ignore_extends_the_defaults() {
3297
+ fn add_path_table_ignore_accumulates_configured_rules() {
3308
3298
  let mut db = Db::new().unwrap();
3309
3299
  db.add_path_table_ignore(vec!["*.tmp".to_string()]);
3300
+ db.add_path_table_ignore(vec!["*.bak".to_string()]);
3310
3301
 
3311
- assert_eq!(db.path_table_ignore.last(), Some(&"*.tmp".to_string()));
3312
- assert!(
3313
- db.path_table_ignore
3314
- .contains(&"**/node_modules/**".to_string()),
3315
- "the built-in defaults must survive: {:?}",
3316
- db.path_table_ignore
3317
- );
3302
+ assert_eq!(db.path_table_ignore, ["*.tmp", "*.bak"]);
3318
3303
  }
3319
3304
 
3320
3305
  #[test]
@@ -33,6 +33,7 @@ pub mod parsed_vtab;
33
33
  pub mod path_table;
34
34
  #[doc(hidden)]
35
35
  pub mod persist;
36
+ mod posix_class;
36
37
  #[doc(hidden)]
37
38
  pub mod progress;
38
39
  #[doc(hidden)]
@@ -1,3 +1,4 @@
1
+ use crate::posix_class::expand_posix_classes;
1
2
  use globset::GlobBuilder;
2
3
  use regex::{Regex, RegexBuilder};
3
4
  use std::path::Path;
@@ -20,7 +21,9 @@ pub(crate) struct Pattern(Regex);
20
21
  impl Pattern {
21
22
  /// `*` and `?` stop at `/`; `**` crosses it.
22
23
  pub(crate) fn new(glob: &str) -> Result<Self, GlobError> {
23
- let glob = GlobBuilder::new(glob).literal_separator(true).build()?;
24
+ let glob = GlobBuilder::new(&expand_posix_classes(glob))
25
+ .literal_separator(true)
26
+ .build()?;
24
27
  let regex = RegexBuilder::new(&unicode_regex(glob.regex()))
25
28
  .dot_matches_new_line(true)
26
29
  .build()?;
@@ -586,4 +589,11 @@ mod tests {
586
589
  .is_empty()
587
590
  );
588
591
  }
592
+
593
+ #[test]
594
+ fn a_posix_class_in_a_table_glob_matches_its_characters() {
595
+ let matcher = TableMatcher::new(&[("notes/[[:digit:]].md", "n")], &[]).unwrap();
596
+ assert_eq!(names(&matcher, "notes/7.md"), vec!["n"]);
597
+ assert!(matcher.match_all(Path::new("notes/x.md")).is_empty());
598
+ }
589
599
  }
@@ -16,8 +16,11 @@ use typed_path::{
16
16
 
17
17
  /// Directories a path-table scan skips, at any depth. They are skipped only
18
18
  /// *beneath* the literal part of the path you write, so naming one explicitly
19
- /// still scans it.
20
- pub const DEFAULT_IGNORES: [&str; 2] = ["**/node_modules/**", "**/.git/**"];
19
+ /// still scans it. See [`PathTable::default_ignores`] for `node_modules`
20
+ /// named after a glob.
21
+ pub const DEFAULT_IGNORES: [&str; 2] = [NODE_MODULES_IGNORE, "**/.git/**"];
22
+
23
+ const NODE_MODULES_IGNORE: &str = "**/node_modules/**";
21
24
 
22
25
  /// The glob a directory expands to: one level, like `ls`. Any depth is
23
26
  /// spelled explicitly as `**`.
@@ -36,6 +39,18 @@ pub struct PathTable {
36
39
  pub path_prefix: String,
37
40
  }
38
41
 
42
+ impl PathTable {
43
+ /// The [`DEFAULT_IGNORES`] this table applies. A glob with a
44
+ /// `node_modules` component names that directory wherever the component
45
+ /// sits, so its skip rule is dropped.
46
+ pub fn default_ignores(&self) -> impl Iterator<Item = &'static str> {
47
+ let names_node_modules = self.glob.split('/').any(|c| c == "node_modules");
48
+ DEFAULT_IGNORES
49
+ .into_iter()
50
+ .filter(move |rule| !(names_node_modules && *rule == NODE_MODULES_IGNORE))
51
+ }
52
+ }
53
+
39
54
  /// What a name SQLite could not find turns out to be.
40
55
  #[derive(Debug, PartialEq)]
41
56
  pub enum Resolution {
@@ -315,6 +330,32 @@ mod tests {
315
330
  assert_eq!(DEFAULT_IGNORES, ["**/node_modules/**", "**/.git/**"]);
316
331
  }
317
332
 
333
+ fn table_globbing(glob: &str) -> PathTable {
334
+ PathTable {
335
+ root: PathBuf::from("/r"),
336
+ glob: glob.to_string(),
337
+ path_prefix: String::new(),
338
+ }
339
+ }
340
+
341
+ fn default_ignores_of(glob: &str) -> Vec<&'static str> {
342
+ table_globbing(glob).default_ignores().collect()
343
+ }
344
+
345
+ #[test]
346
+ fn a_glob_not_naming_node_modules_keeps_every_default_ignore() {
347
+ assert_eq!(default_ignores_of("**"), DEFAULT_IGNORES);
348
+ assert_eq!(default_ignores_of("**/*.js"), DEFAULT_IGNORES);
349
+ assert_eq!(default_ignores_of("node_modules*/*"), DEFAULT_IGNORES);
350
+ }
351
+
352
+ #[test]
353
+ fn a_glob_naming_node_modules_anywhere_drops_its_ignore() {
354
+ assert_eq!(default_ignores_of("**/node_modules/**"), ["**/.git/**"]);
355
+ assert_eq!(default_ignores_of("*/node_modules/*/*"), ["**/.git/**"]);
356
+ assert_eq!(default_ignores_of("node_modules"), ["**/.git/**"]);
357
+ }
358
+
318
359
  #[test]
319
360
  fn has_glob_metacharacter_spots_each_metacharacter() {
320
361
  assert!(has_glob_metacharacter("a*"));
@@ -0,0 +1,313 @@
1
+ //! POSIX character classes (`[[:digit:]]`) for globset, which has no
2
+ //! `[:name:]` syntax: each bracket expression holding one is rewritten into an
3
+ //! equivalent bracket of plain ranges before the glob is compiled.
4
+
5
+ /// Rewrite every bracket expression in `pattern` that holds a `[:name:]`
6
+ /// class into plain ranges. Brackets without one, unclosed brackets and
7
+ /// backslash-escaped characters are left exactly as written.
8
+ pub(crate) fn expand_posix_classes(pattern: &str) -> String {
9
+ let chars: Vec<char> = pattern.chars().collect();
10
+ let mut out = String::with_capacity(pattern.len());
11
+ let mut i = 0;
12
+ while i < chars.len() {
13
+ match chars[i] {
14
+ '\\' => {
15
+ let end = (i + 2).min(chars.len());
16
+ out.extend(&chars[i..end]);
17
+ i = end;
18
+ }
19
+ '[' => match bracket(&chars, i) {
20
+ Some((end, Some(rewritten))) => {
21
+ out.push_str(&rewritten);
22
+ i = end;
23
+ }
24
+ Some((end, None)) => {
25
+ out.extend(&chars[i..end]);
26
+ i = end;
27
+ }
28
+ None => {
29
+ out.push('[');
30
+ i += 1;
31
+ }
32
+ },
33
+ c => {
34
+ out.push(c);
35
+ i += 1;
36
+ }
37
+ }
38
+ }
39
+ out
40
+ }
41
+
42
+ /// Parse the bracket expression opening at `start`. `None` when it never
43
+ /// closes; otherwise the index past its `]`, plus its rewrite when it holds a
44
+ /// class.
45
+ fn bracket(chars: &[char], start: usize) -> Option<(usize, Option<String>)> {
46
+ let mut j = start + 1;
47
+ let negated = matches!(chars.get(j), Some('!' | '^'));
48
+ if negated {
49
+ j += 1;
50
+ }
51
+ let body_start = j;
52
+ let mut ranges: Vec<(char, char)> = Vec::new();
53
+ let mut has_class = false;
54
+ // bash matches nothing at all when a range ends in a class, negated or not.
55
+ let mut range_into_class = false;
56
+ loop {
57
+ let c = *chars.get(j)?;
58
+ if c == ']' && j > body_start {
59
+ j += 1;
60
+ break;
61
+ }
62
+ if let Some((name, next)) = class_at(chars, j) {
63
+ ranges.extend_from_slice(class_ranges(&name));
64
+ has_class = true;
65
+ j = next;
66
+ continue;
67
+ }
68
+ match (chars.get(j + 1), chars.get(j + 2)) {
69
+ (Some('-'), Some(&end)) if end != ']' => {
70
+ if let Some((_, next)) = class_at(chars, j + 2) {
71
+ has_class = true;
72
+ range_into_class = true;
73
+ j = next;
74
+ continue;
75
+ }
76
+ if c <= end {
77
+ ranges.push((c, end));
78
+ }
79
+ j += 3;
80
+ }
81
+ _ => {
82
+ ranges.push((c, c));
83
+ j += 1;
84
+ }
85
+ }
86
+ }
87
+ if !has_class {
88
+ return Some((j, None));
89
+ }
90
+ let rewritten = if range_into_class {
91
+ "[\0]".to_string()
92
+ } else {
93
+ render(negated, ranges)
94
+ };
95
+ Some((j, Some(rewritten)))
96
+ }
97
+
98
+ /// The class `[:name:]` starting at `j`, with the index past its `:]`. A `]`
99
+ /// before the `:]` means the `[` is an ordinary character, as in bash.
100
+ fn class_at(chars: &[char], j: usize) -> Option<(String, usize)> {
101
+ if chars.get(j) != Some(&'[') || chars.get(j + 1) != Some(&':') {
102
+ return None;
103
+ }
104
+ let mut k = j + 2;
105
+ loop {
106
+ match (chars.get(k)?, chars.get(k + 1)) {
107
+ (':', Some(']')) => return Some((chars[j + 2..k].iter().collect(), k + 2)),
108
+ (']', _) => return None,
109
+ _ => k += 1,
110
+ }
111
+ }
112
+ }
113
+
114
+ /// The ASCII members of a class; an unknown name has none, as in bash.
115
+ fn class_ranges(name: &str) -> &'static [(char, char)] {
116
+ match name {
117
+ "alnum" => &[('0', '9'), ('A', 'Z'), ('a', 'z')],
118
+ "alpha" => &[('A', 'Z'), ('a', 'z')],
119
+ "ascii" => &[('\0', '\x7f')],
120
+ "blank" => &[(' ', ' '), ('\t', '\t')],
121
+ "cntrl" => &[('\0', '\x1f'), ('\x7f', '\x7f')],
122
+ "digit" => &[('0', '9')],
123
+ "graph" => &[('!', '~')],
124
+ "lower" => &[('a', 'z')],
125
+ "print" => &[(' ', '~')],
126
+ "punct" => &[('!', '/'), (':', '@'), ('[', '`'), ('{', '~')],
127
+ "space" => &[('\t', '\r'), (' ', ' ')],
128
+ "upper" => &[('A', 'Z')],
129
+ "word" => &[('0', '9'), ('A', 'Z'), ('a', 'z'), ('_', '_')],
130
+ "xdigit" => &[('0', '9'), ('A', 'F'), ('a', 'f')],
131
+ _ => &[],
132
+ }
133
+ }
134
+
135
+ /// Characters globset reads as syntax at some position inside a bracket, so
136
+ /// they are pulled out of ranges and placed where they are literal.
137
+ const POSITIONAL: [char; 4] = [']', '-', '!', '^'];
138
+
139
+ /// A globset bracket matching exactly `ranges` (or everything else when
140
+ /// `negated`). A bracket never matches the separator, so `/` is dropped from a
141
+ /// positive set and added to a negated one. NUL, which no path holds, stands
142
+ /// in when nothing else may open the bracket.
143
+ fn render(negated: bool, ranges: Vec<(char, char)>) -> String {
144
+ let mut set: Vec<(u32, u32)> = ranges
145
+ .into_iter()
146
+ .map(|(a, b)| (a as u32, b as u32))
147
+ .collect();
148
+ set = without(set, '/');
149
+ if negated {
150
+ set.push(('/' as u32, '/' as u32));
151
+ }
152
+ let present = |c: char| set.iter().any(|&(a, b)| a <= c as u32 && c as u32 <= b);
153
+ let specials: Vec<char> = POSITIONAL.into_iter().filter(|&c| present(c)).collect();
154
+ let mut plain = POSITIONAL.into_iter().fold(set.clone(), without);
155
+ plain.sort_unstable();
156
+ if plain.is_empty() && !specials.contains(&']') {
157
+ plain.push((0, 0));
158
+ }
159
+
160
+ let mut out = String::from("[");
161
+ if negated {
162
+ out.push('!');
163
+ }
164
+ if specials.contains(&']') {
165
+ out.push(']');
166
+ }
167
+ for (a, b) in plain {
168
+ out.extend(char::from_u32(a));
169
+ if a != b {
170
+ out.push('-');
171
+ out.extend(char::from_u32(b));
172
+ }
173
+ }
174
+ for c in ['!', '^', '-'] {
175
+ if specials.contains(&c) {
176
+ out.push(c);
177
+ }
178
+ }
179
+ out.push(']');
180
+ out
181
+ }
182
+
183
+ /// `set` with `c` removed, splitting any range that spans it. `c` is never
184
+ /// NUL, so `c - 1` cannot underflow.
185
+ fn without(set: Vec<(u32, u32)>, c: char) -> Vec<(u32, u32)> {
186
+ let c = c as u32;
187
+ set.into_iter()
188
+ .flat_map(|(a, b)| [(a, b.min(c - 1)), (a.max(c + 1), b)])
189
+ .filter(|&(a, b)| a <= b)
190
+ .collect()
191
+ }
192
+
193
+ #[cfg(test)]
194
+ mod tests {
195
+ use super::*;
196
+
197
+ #[test]
198
+ fn a_class_becomes_its_ranges() {
199
+ assert_eq!(expand_posix_classes("[[:digit:]].md"), "[0-9].md");
200
+ assert_eq!(expand_posix_classes("[[:upper:]]*"), "[A-Z]*");
201
+ }
202
+
203
+ #[test]
204
+ fn every_named_class_expands() {
205
+ let cases = [
206
+ ("alnum", "[0-9A-Za-z]"),
207
+ ("alpha", "[A-Za-z]"),
208
+ ("ascii", "[]\0- \"-,.0-\\_-\x7f!^-]"),
209
+ ("blank", "[\t ]"),
210
+ ("cntrl", "[\0-\x1f\x7f]"),
211
+ ("digit", "[0-9]"),
212
+ ("graph", "[]\"-,.0-\\_-~!^-]"),
213
+ ("lower", "[a-z]"),
214
+ ("print", "[] \"-,.0-\\_-~!^-]"),
215
+ ("punct", "[]\"-,.:-@[-\\_-`{-~!^-]"),
216
+ ("space", "[\t-\r ]"),
217
+ ("upper", "[A-Z]"),
218
+ ("word", "[0-9A-Z_a-z]"),
219
+ ("xdigit", "[0-9A-Fa-f]"),
220
+ ];
221
+ for (name, want) in cases {
222
+ assert_eq!(
223
+ expand_posix_classes(&format!("[[:{name}:]]")),
224
+ want,
225
+ "{name}"
226
+ );
227
+ }
228
+ }
229
+
230
+ #[test]
231
+ fn a_negated_class_also_excludes_the_separator() {
232
+ assert_eq!(expand_posix_classes("[![:digit:]]"), "[!/0-9]");
233
+ assert_eq!(expand_posix_classes("[^[:digit:]]"), "[!/0-9]");
234
+ }
235
+
236
+ #[test]
237
+ fn classes_and_characters_combine() {
238
+ assert_eq!(expand_posix_classes("[a[:digit:]]"), "[0-9a]");
239
+ assert_eq!(expand_posix_classes("[[:digit:][:upper:]]"), "[0-9A-Z]");
240
+ assert_eq!(expand_posix_classes("[[:digit:]x-z]"), "[0-9x-z]");
241
+ }
242
+
243
+ #[test]
244
+ fn a_dash_after_a_class_is_literal() {
245
+ assert_eq!(expand_posix_classes("[[:digit:]-z]"), "[0-9z-]");
246
+ assert_eq!(expand_posix_classes("[[:digit:]-]"), "[0-9-]");
247
+ assert_eq!(expand_posix_classes("[[:digit:]x-]"), "[0-9x-]");
248
+ }
249
+
250
+ #[test]
251
+ fn a_leading_close_bracket_is_a_member() {
252
+ assert_eq!(expand_posix_classes("[][:digit:]]"), "[]0-9]");
253
+ }
254
+
255
+ #[test]
256
+ fn an_unknown_class_contributes_nothing() {
257
+ assert_eq!(expand_posix_classes("[[:foo:]a]"), "[a]");
258
+ assert_eq!(expand_posix_classes("[[:DIGIT:]]"), "[\0]");
259
+ assert_eq!(expand_posix_classes("[![:foo:]]"), "[!/]");
260
+ }
261
+
262
+ #[test]
263
+ fn a_lone_bang_is_kept_off_the_negation_position() {
264
+ assert_eq!(expand_posix_classes("[[:foo:]!]"), "[\0!]");
265
+ assert_eq!(expand_posix_classes("[[:foo:]^]"), "[\0^]");
266
+ }
267
+
268
+ #[test]
269
+ fn a_range_ending_in_a_class_matches_nothing() {
270
+ assert_eq!(expand_posix_classes("[x-[:digit:]]"), "[\0]");
271
+ assert_eq!(expand_posix_classes("[!x-[:digit:]]"), "[\0]");
272
+ }
273
+
274
+ #[test]
275
+ fn a_reversed_range_beside_a_class_is_empty() {
276
+ assert_eq!(expand_posix_classes("[z-a[:digit:]]"), "[0-9]");
277
+ }
278
+
279
+ #[test]
280
+ fn a_bracket_without_a_class_is_untouched() {
281
+ for pattern in [
282
+ "[a-c]*",
283
+ "[!x].md",
284
+ "[]]",
285
+ "[[:a]",
286
+ "{a,b}/[xy]",
287
+ "[a:digit:]]",
288
+ "[[:a][:digit:]]",
289
+ ] {
290
+ assert_eq!(expand_posix_classes(pattern), pattern);
291
+ }
292
+ }
293
+
294
+ #[test]
295
+ fn an_unclosed_bracket_is_untouched() {
296
+ assert_eq!(expand_posix_classes("[[:digit:]"), "[[:digit:]");
297
+ assert_eq!(expand_posix_classes("a["), "a[");
298
+ }
299
+
300
+ #[test]
301
+ fn an_escaped_bracket_is_untouched() {
302
+ assert_eq!(expand_posix_classes("\\[[:digit:]]"), "\\[[:digit:]]");
303
+ assert_eq!(expand_posix_classes("a\\"), "a\\");
304
+ }
305
+
306
+ #[test]
307
+ fn text_around_brackets_is_kept() {
308
+ assert_eq!(
309
+ expand_posix_classes("**/v[[:digit:]]/*.md"),
310
+ "**/v[0-9]/*.md"
311
+ );
312
+ }
313
+ }