dirsql 0.3.89__tar.gz → 0.3.90__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 (137) hide show
  1. {dirsql-0.3.89 → dirsql-0.3.90}/Cargo.lock +1 -1
  2. {dirsql-0.3.89 → dirsql-0.3.90}/PKG-INFO +1 -1
  3. {dirsql-0.3.89/packages/rust → dirsql-0.3.90}/docs/howto/persist.md +20 -9
  4. {dirsql-0.3.89/packages/rust → dirsql-0.3.90}/docs/reference/cli.md +5 -0
  5. {dirsql-0.3.89/packages/rust → dirsql-0.3.90}/docs/reference/config.md +5 -6
  6. {dirsql-0.3.89 → dirsql-0.3.90}/docs/reference/sdk.md +4 -5
  7. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/Cargo.toml +1 -1
  8. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/howto/persist.md +20 -9
  9. {dirsql-0.3.89 → dirsql-0.3.90/packages/python}/docs/reference/cli.md +5 -0
  10. {dirsql-0.3.89 → dirsql-0.3.90/packages/python}/docs/reference/config.md +5 -6
  11. {dirsql-0.3.89/packages/rust → dirsql-0.3.90/packages/python}/docs/reference/sdk.md +4 -5
  12. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/e2e-attestation.json +2 -2
  13. {dirsql-0.3.89 → dirsql-0.3.90/packages/rust}/docs/howto/persist.md +20 -9
  14. {dirsql-0.3.89/packages/python → dirsql-0.3.90/packages/rust}/docs/reference/cli.md +5 -0
  15. {dirsql-0.3.89/packages/python → dirsql-0.3.90/packages/rust}/docs/reference/config.md +5 -6
  16. {dirsql-0.3.89/packages/python → dirsql-0.3.90/packages/rust}/docs/reference/sdk.md +4 -5
  17. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/bin/dirsql.rs +61 -12
  18. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/config.rs +14 -110
  19. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/lib.rs +2 -16
  20. {dirsql-0.3.89 → dirsql-0.3.90}/Cargo.toml +0 -0
  21. {dirsql-0.3.89 → dirsql-0.3.90}/README.md +0 -0
  22. {dirsql-0.3.89 → dirsql-0.3.90}/dirsql/__init__.py +0 -0
  23. {dirsql-0.3.89 → dirsql-0.3.90}/dirsql/_async.py +0 -0
  24. {dirsql-0.3.89 → dirsql-0.3.90}/dirsql/_dirsql.pyi +0 -0
  25. {dirsql-0.3.89 → dirsql-0.3.90}/dirsql/cli/__init__.py +0 -0
  26. {dirsql-0.3.89 → dirsql-0.3.90}/dirsql/cli/binary_path.py +0 -0
  27. {dirsql-0.3.89 → dirsql-0.3.90}/dirsql/cli/is_windows.py +0 -0
  28. {dirsql-0.3.89 → dirsql-0.3.90}/dirsql/cli/main.py +0 -0
  29. {dirsql-0.3.89 → dirsql-0.3.90}/dirsql/cli/resolve_config_extensions.py +0 -0
  30. {dirsql-0.3.89 → dirsql-0.3.90}/dirsql/py.typed +0 -0
  31. {dirsql-0.3.89 → dirsql-0.3.90}/dirsql/resolve_config_extensions.py +0 -0
  32. {dirsql-0.3.89 → dirsql-0.3.90}/dirsql/resolve_extension.py +0 -0
  33. {dirsql-0.3.89 → dirsql-0.3.90}/docs/.claude/CLAUDE.md +0 -0
  34. {dirsql-0.3.89 → dirsql-0.3.90}/docs/.vitepress/config.ts +0 -0
  35. {dirsql-0.3.89 → dirsql-0.3.90}/docs/.vitepress/theme/index.ts +0 -0
  36. {dirsql-0.3.89 → dirsql-0.3.90}/docs/.vitepress/theme/lang.ts +0 -0
  37. {dirsql-0.3.89 → dirsql-0.3.90}/docs/AGENTS.md +0 -0
  38. {dirsql-0.3.89 → dirsql-0.3.90}/docs/explanation.md +0 -0
  39. {dirsql-0.3.89 → dirsql-0.3.90}/docs/getting-started.md +0 -0
  40. {dirsql-0.3.89 → dirsql-0.3.90}/docs/howto/columns-from-paths.md +0 -0
  41. {dirsql-0.3.89 → dirsql-0.3.90}/docs/howto/define-tables.md +0 -0
  42. {dirsql-0.3.89 → dirsql-0.3.90}/docs/howto/embed.md +0 -0
  43. {dirsql-0.3.89 → dirsql-0.3.90}/docs/howto/extract-from-contents.md +0 -0
  44. {dirsql-0.3.89 → dirsql-0.3.90}/docs/howto/load-extension.md +0 -0
  45. {dirsql-0.3.89 → dirsql-0.3.90}/docs/howto/react-to-changes.md +0 -0
  46. {dirsql-0.3.89 → dirsql-0.3.90}/docs/howto/search-by-meaning.md +0 -0
  47. {dirsql-0.3.89 → dirsql-0.3.90}/docs/howto/skip-files.md +0 -0
  48. {dirsql-0.3.89 → dirsql-0.3.90}/docs/index.md +0 -0
  49. {dirsql-0.3.89 → dirsql-0.3.90}/docs/migrations.md +0 -0
  50. {dirsql-0.3.89 → dirsql-0.3.90}/docs/package.json +0 -0
  51. {dirsql-0.3.89 → dirsql-0.3.90}/docs/playwright.config.ts +0 -0
  52. {dirsql-0.3.89 → dirsql-0.3.90}/docs/pnpm-lock.yaml +0 -0
  53. {dirsql-0.3.89 → dirsql-0.3.90}/docs/pnpm-workspace.yaml +0 -0
  54. {dirsql-0.3.89 → dirsql-0.3.90}/docs/reference/columns.md +0 -0
  55. {dirsql-0.3.89 → dirsql-0.3.90}/docs/reference/hooks.md +0 -0
  56. {dirsql-0.3.89 → dirsql-0.3.90}/docs/reference/http-api.md +0 -0
  57. {dirsql-0.3.89 → dirsql-0.3.90}/docs/tests/integration/home.spec.ts +0 -0
  58. {dirsql-0.3.89 → dirsql-0.3.90}/docs/tests/integration/language-flag.spec.ts +0 -0
  59. {dirsql-0.3.89 → dirsql-0.3.90}/docs/tests/integration/sidebar.spec.ts +0 -0
  60. {dirsql-0.3.89 → dirsql-0.3.90}/docs/tests/unit/config.test.ts +0 -0
  61. {dirsql-0.3.89 → dirsql-0.3.90}/docs/tests/unit/lang.test.ts +0 -0
  62. {dirsql-0.3.89 → dirsql-0.3.90}/docs/vitest.config.ts +0 -0
  63. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/README.md +0 -0
  64. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/conftest.py +0 -0
  65. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/.claude/CLAUDE.md +0 -0
  66. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/.vitepress/config.ts +0 -0
  67. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  68. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  69. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/AGENTS.md +0 -0
  70. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/explanation.md +0 -0
  71. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/getting-started.md +0 -0
  72. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/howto/columns-from-paths.md +0 -0
  73. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/howto/define-tables.md +0 -0
  74. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/howto/embed.md +0 -0
  75. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/howto/extract-from-contents.md +0 -0
  76. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/howto/load-extension.md +0 -0
  77. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/howto/react-to-changes.md +0 -0
  78. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/howto/search-by-meaning.md +0 -0
  79. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/howto/skip-files.md +0 -0
  80. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/index.md +0 -0
  81. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/migrations.md +0 -0
  82. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/package.json +0 -0
  83. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/playwright.config.ts +0 -0
  84. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/pnpm-lock.yaml +0 -0
  85. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/pnpm-workspace.yaml +0 -0
  86. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/reference/columns.md +0 -0
  87. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/reference/hooks.md +0 -0
  88. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/reference/http-api.md +0 -0
  89. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/tests/integration/home.spec.ts +0 -0
  90. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
  91. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/tests/integration/sidebar.spec.ts +0 -0
  92. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/tests/unit/config.test.ts +0 -0
  93. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/tests/unit/lang.test.ts +0 -0
  94. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/docs/vitest.config.ts +0 -0
  95. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/src/lib.rs +0 -0
  96. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/tests/__init__.py +0 -0
  97. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/tests/conftest.py +0 -0
  98. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/tests/e2e/__init__.py +0 -0
  99. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/tests/integration/__init__.py +0 -0
  100. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/tests/integration/binding/__init__.py +0 -0
  101. {dirsql-0.3.89 → dirsql-0.3.90}/packages/python/tests/integration/hermetic/__init__.py +0 -0
  102. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/Cargo.toml +0 -0
  103. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/README.md +0 -0
  104. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/benches/db_bench.rs +0 -0
  105. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/benches/differ_bench.rs +0 -0
  106. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/benches/matcher_bench.rs +0 -0
  107. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/benches/scanner_bench.rs +0 -0
  108. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/explanation.md +0 -0
  109. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/getting-started.md +0 -0
  110. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/howto/columns-from-paths.md +0 -0
  111. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/howto/define-tables.md +0 -0
  112. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/howto/embed.md +0 -0
  113. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/howto/extract-from-contents.md +0 -0
  114. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/howto/load-extension.md +0 -0
  115. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/howto/react-to-changes.md +0 -0
  116. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/howto/search-by-meaning.md +0 -0
  117. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/howto/skip-files.md +0 -0
  118. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/index.md +0 -0
  119. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/migrations.md +0 -0
  120. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/reference/columns.md +0 -0
  121. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/reference/hooks.md +0 -0
  122. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/docs/reference/http-api.md +0 -0
  123. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/cli/execute.rs +0 -0
  124. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/cli/init.rs +0 -0
  125. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/cli/mod.rs +0 -0
  126. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/cli/router.rs +0 -0
  127. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/cli/serialize.rs +0 -0
  128. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/cli/server.rs +0 -0
  129. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/command.rs +0 -0
  130. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/db.rs +0 -0
  131. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/default_config.toml +0 -0
  132. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/differ.rs +0 -0
  133. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/matcher.rs +0 -0
  134. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/persist.rs +0 -0
  135. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/scanner.rs +0 -0
  136. {dirsql-0.3.89 → dirsql-0.3.90}/packages/rust/src/watcher.rs +0 -0
  137. {dirsql-0.3.89 → dirsql-0.3.90}/pyproject.toml +0 -0
@@ -500,7 +500,7 @@ dependencies = [
500
500
 
501
501
  [[package]]
502
502
  name = "dirsql-py-ext"
503
- version = "0.3.89"
503
+ version = "0.3.90"
504
504
  dependencies = [
505
505
  "dirsql",
506
506
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.3.89
3
+ Version: 0.3.90
4
4
  Summary: Ephemeral SQL index over a local directory
5
5
  Keywords: sql,filesystem,directory,sqlite,index
6
6
  Author: Kevin Scott
@@ -1,23 +1,34 @@
1
1
  # Keep the index across restarts
2
2
 
3
3
  By default the database is ephemeral: rebuilt from your files on every
4
- startup and discarded on exit. [`persist`](../reference/config.md#dirsql-keys) keeps the
5
- SQLite index on disk instead, so a restart only re-parses files that
6
- actually changed — the difference between seconds and milliseconds on large
7
- trees, and between re-running and skipping expensive
4
+ startup and discarded on exit. The [`--persist [PATH]`](../reference/cli.md#server-mode)
5
+ flag keeps the SQLite index on disk instead, so a restart only re-parses
6
+ files that actually changed — the difference between seconds and
7
+ milliseconds on large trees, and between re-running and skipping expensive
8
8
  [`on-file`](./extract-from-contents.md) commands.
9
9
 
10
+ Whether and where to cache is a machine-local operational choice — it
11
+ belongs to the command you run, not to the shared `.dirsql.toml`. That is
12
+ why it is a CLI flag, not a config key.
13
+
10
14
  ## 1. Turn it on
11
15
 
12
- ```toml
13
- [dirsql]
14
- persist = true
16
+ ```bash
17
+ dirsql --persist
15
18
  ```
16
19
 
17
20
  That's the whole change. On the next run the cache is written to
18
21
  `.dirsql/cache.db` under the root; runs after that start from it. To put
19
- the cache elsewhere (a CI cache dir, a tmpfs), set
20
- [`persist_path`](../reference/config.md#dirsql-keys).
22
+ the cache elsewhere (a CI cache dir, a tmpfs), pass a path:
23
+
24
+ ```bash
25
+ dirsql --persist /var/cache/dirsql.db
26
+ ```
27
+
28
+ The same flag works on [`dirsql query`](../reference/cli.md#dirsql-query);
29
+ put a bare `--persist` after the SQL there so it does not consume the query
30
+ argument. Embedding `dirsql`? The SDK constructors expose the same switch —
31
+ see [_Embedding `dirsql`?_](#embedding-dirsql) below.
21
32
 
22
33
  ## 2. Keep the cache out of git
23
34
 
@@ -48,6 +48,7 @@ requests, closes open `/events` streams, and exits.
48
48
  | `-c, --config <path>` | `./.dirsql.toml` | Path to the [config file](./config.md). The index is rooted at the directory containing this file (unless the config sets `[dirsql].root`). When the file does not exist, the server runs in [zero-config mode](#zero-config-mode). |
49
49
  | `--host <addr>` | `localhost` | Bind address. |
50
50
  | `--port <n>` | `7117` | TCP port to bind. |
51
+ | `--persist [<path>]` | off | Keep the SQLite index on disk between runs so a restart only re-parses files that actually changed. Bare `--persist` caches at `<root>/.dirsql/cache.db`; `--persist <path>` caches at `<path>`. Off by default (the index is ephemeral). Global — also honored by [`dirsql query`](#dirsql-query). See [Keep the index across restarts](../howto/persist.md). |
51
52
  | `--extension <path>` | none | Load a SQLite extension by literal path, overriding the config's `[[dirsql.extension]]` entries. Repeatable. Format: `<path>` or `<path>::<entrypoint>`. Internal plumbing for the pip/npm launchers, which resolve package-name extensions and pass the resolved paths here — not intended for direct use. When any `--extension` is present, the config file's own extension entries are not loaded. |
52
53
  | `--version` | | Print the version and exit. |
53
54
  | `--help` | | Print usage and exit. |
@@ -119,6 +120,10 @@ uses**, so behavior is identical to `POST /query` by construction:
119
120
  - **Config discovery** honors `--config` (default `./.dirsql.toml`),
120
121
  [zero-config mode](#zero-config-mode), and `--extension` overrides,
121
122
  exactly as server mode does.
123
+ - **`--persist [<path>]`** is honored, so a repeated `dirsql query` reuses the
124
+ on-disk cache. Because its value is optional, place a bare `--persist` after
125
+ the SQL (`dirsql query "SELECT …" --persist`) or use the `=` form
126
+ (`--persist=/path`) so it does not swallow the SQL argument.
122
127
  - **Hooks** ([`pre-query`](./hooks.md#pre-query) /
123
128
  [`post-query`](./hooks.md#post-query)) and the
124
129
  [`[dirsql].hook-timeout`](./config.md#dirsql-keys) apply identically.
@@ -11,7 +11,7 @@ The [CLI](./cli.md) loads `./.dirsql.toml` by default (`--config <path>`
11
11
  overrides). The [SDKs](./sdk.md) load a config via the `config` constructor
12
12
  parameter.
13
13
 
14
- **Path resolution.** Relative paths in the config (`root`, `persist_path`,
14
+ **Path resolution.** Relative paths in the config (`root`,
15
15
  `[[dirsql.extension]]` `path`) resolve against the config file's parent
16
16
  directory. The scan root defaults to that same directory when `root` is not
17
17
  set.
@@ -22,8 +22,6 @@ set.
22
22
  |---|---|---|---|
23
23
  | `root` | string | config file's parent directory | Directory to index. Relative values resolve against the config file's parent. An explicit `root` passed to an SDK constructor overrides this (a warning is emitted on stderr). |
24
24
  | `ignore` | array of strings | `[]` | Glob patterns matched against root-relative paths. Matched files are skipped entirely — excluded from the initial scan and from watch events. |
25
- | `persist` | boolean | `false` | Keep the SQLite index on disk between runs. When `false`, the index is ephemeral: rebuilt from your files on every startup and discarded on exit. |
26
- | `persist_path` | string | `<root>/.dirsql/cache.db` | Location of the on-disk cache. Relative values resolve against the config file's parent. Ignored unless `persist = true`. |
27
25
  | `pre-query` | string | none | Server-wide command hook: the raw `POST /query` request body is passed to this command as `{args}`, and the plain-text SQL it prints is executed instead of parsing the body as `{"sql": …}`. CLI server only; the SDKs ignore it. Must be non-empty. See [Command hooks](./hooks.md#pre-query). |
28
26
  | `post-query` | string | none | Server-wide command hook: each successful `POST /query` result set is handed to this command (as a JSON array on stdin, and as `{args}` up to 96 KiB), and the JSON body it prints is returned instead of the bare row array. CLI server only; the SDKs ignore it. Must be non-empty. See [Command hooks](./hooks.md#post-query). |
29
27
  | `hook-timeout` | integer (seconds) | `30` | One global per-run timeout for every command hook — `on-file`, `pre-query`, and `post-query` alike. Positive whole seconds; zero and negative values are a config error. See [Command hooks](./hooks.md#timeout). |
@@ -38,11 +36,13 @@ directory.
38
36
  [dirsql]
39
37
  root = "../data"
40
38
  ignore = ["node_modules/**", ".git/**"]
41
- persist = true
42
- persist_path = ".dirsql/cache.db" # the default; shown for illustration
43
39
  hook-timeout = 300
44
40
  ```
45
41
 
42
+ Persistence is not a config key. Keep the SQLite index on disk between runs
43
+ with the [`--persist [PATH]` CLI flag](./cli.md#server-mode) — a machine-local
44
+ operational choice that belongs to the runner, not to shareable config.
45
+
46
46
  ## `[[dirsql.extension]]`
47
47
 
48
48
  Each entry declares a SQLite extension to load at startup. Extensions are
@@ -150,7 +150,6 @@ SDKs raise/reject) when:
150
150
  ```toml
151
151
  [dirsql]
152
152
  ignore = ["node_modules/**", ".git/**", "dist/**"]
153
- persist = true
154
153
  pre-query = "uv run python to_sql.py {args}"
155
154
  post-query = "jq -c '{results: .}'"
156
155
  hook-timeout = 120
@@ -111,11 +111,10 @@ directory" error.
111
111
  - `persist` — Keep the SQLite index on disk between runs (default off:
112
112
  ephemeral, rebuilt every startup). The cache lives at
113
113
  `<root>/.dirsql/cache.db` by default; on restart, only files whose stat
114
- changed are re-parsed. `persist = true` in the config also enables it.
115
- - `persist_path` / `persistPath` Override the cache location. An
116
- explicit value wins over the config's `persist_path`. Ignored when
117
- persistence is off. (Config-file values resolve relative to the config's
118
- parent directory; constructor values are used as given.)
114
+ changed are re-parsed. (The CLI exposes the same switch as the
115
+ [`--persist [PATH]`](./cli.md#server-mode) flag; it is not a config key.)
116
+ - `persist_path` / `persistPath` — Override the cache location. Ignored when
117
+ persistence is off. Constructor values are used as given.
119
118
  - `extensions` — SQLite extensions to load at startup, before any table
120
119
  DDL (enable → load → disable, so SQL `load_extension()` is never
121
120
  exposed). Each entry pairs a shared-library `path` with an optional
@@ -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.3.89"
7
+ version = "0.3.90"
8
8
  # abi3 note: the `abi3-py311` pyo3 feature (below) builds ONE stable-ABI
9
9
  # `cp311-abi3` wheel per platform that loads on every CPython >= 3.11
10
10
  # (matching `pyproject.toml`'s `requires-python = ">=3.11"`), instead of
@@ -1,23 +1,34 @@
1
1
  # Keep the index across restarts
2
2
 
3
3
  By default the database is ephemeral: rebuilt from your files on every
4
- startup and discarded on exit. [`persist`](../reference/config.md#dirsql-keys) keeps the
5
- SQLite index on disk instead, so a restart only re-parses files that
6
- actually changed — the difference between seconds and milliseconds on large
7
- trees, and between re-running and skipping expensive
4
+ startup and discarded on exit. The [`--persist [PATH]`](../reference/cli.md#server-mode)
5
+ flag keeps the SQLite index on disk instead, so a restart only re-parses
6
+ files that actually changed — the difference between seconds and
7
+ milliseconds on large trees, and between re-running and skipping expensive
8
8
  [`on-file`](./extract-from-contents.md) commands.
9
9
 
10
+ Whether and where to cache is a machine-local operational choice — it
11
+ belongs to the command you run, not to the shared `.dirsql.toml`. That is
12
+ why it is a CLI flag, not a config key.
13
+
10
14
  ## 1. Turn it on
11
15
 
12
- ```toml
13
- [dirsql]
14
- persist = true
16
+ ```bash
17
+ dirsql --persist
15
18
  ```
16
19
 
17
20
  That's the whole change. On the next run the cache is written to
18
21
  `.dirsql/cache.db` under the root; runs after that start from it. To put
19
- the cache elsewhere (a CI cache dir, a tmpfs), set
20
- [`persist_path`](../reference/config.md#dirsql-keys).
22
+ the cache elsewhere (a CI cache dir, a tmpfs), pass a path:
23
+
24
+ ```bash
25
+ dirsql --persist /var/cache/dirsql.db
26
+ ```
27
+
28
+ The same flag works on [`dirsql query`](../reference/cli.md#dirsql-query);
29
+ put a bare `--persist` after the SQL there so it does not consume the query
30
+ argument. Embedding `dirsql`? The SDK constructors expose the same switch —
31
+ see [_Embedding `dirsql`?_](#embedding-dirsql) below.
21
32
 
22
33
  ## 2. Keep the cache out of git
23
34
 
@@ -48,6 +48,7 @@ requests, closes open `/events` streams, and exits.
48
48
  | `-c, --config <path>` | `./.dirsql.toml` | Path to the [config file](./config.md). The index is rooted at the directory containing this file (unless the config sets `[dirsql].root`). When the file does not exist, the server runs in [zero-config mode](#zero-config-mode). |
49
49
  | `--host <addr>` | `localhost` | Bind address. |
50
50
  | `--port <n>` | `7117` | TCP port to bind. |
51
+ | `--persist [<path>]` | off | Keep the SQLite index on disk between runs so a restart only re-parses files that actually changed. Bare `--persist` caches at `<root>/.dirsql/cache.db`; `--persist <path>` caches at `<path>`. Off by default (the index is ephemeral). Global — also honored by [`dirsql query`](#dirsql-query). See [Keep the index across restarts](../howto/persist.md). |
51
52
  | `--extension <path>` | none | Load a SQLite extension by literal path, overriding the config's `[[dirsql.extension]]` entries. Repeatable. Format: `<path>` or `<path>::<entrypoint>`. Internal plumbing for the pip/npm launchers, which resolve package-name extensions and pass the resolved paths here — not intended for direct use. When any `--extension` is present, the config file's own extension entries are not loaded. |
52
53
  | `--version` | | Print the version and exit. |
53
54
  | `--help` | | Print usage and exit. |
@@ -119,6 +120,10 @@ uses**, so behavior is identical to `POST /query` by construction:
119
120
  - **Config discovery** honors `--config` (default `./.dirsql.toml`),
120
121
  [zero-config mode](#zero-config-mode), and `--extension` overrides,
121
122
  exactly as server mode does.
123
+ - **`--persist [<path>]`** is honored, so a repeated `dirsql query` reuses the
124
+ on-disk cache. Because its value is optional, place a bare `--persist` after
125
+ the SQL (`dirsql query "SELECT …" --persist`) or use the `=` form
126
+ (`--persist=/path`) so it does not swallow the SQL argument.
122
127
  - **Hooks** ([`pre-query`](./hooks.md#pre-query) /
123
128
  [`post-query`](./hooks.md#post-query)) and the
124
129
  [`[dirsql].hook-timeout`](./config.md#dirsql-keys) apply identically.
@@ -11,7 +11,7 @@ The [CLI](./cli.md) loads `./.dirsql.toml` by default (`--config <path>`
11
11
  overrides). The [SDKs](./sdk.md) load a config via the `config` constructor
12
12
  parameter.
13
13
 
14
- **Path resolution.** Relative paths in the config (`root`, `persist_path`,
14
+ **Path resolution.** Relative paths in the config (`root`,
15
15
  `[[dirsql.extension]]` `path`) resolve against the config file's parent
16
16
  directory. The scan root defaults to that same directory when `root` is not
17
17
  set.
@@ -22,8 +22,6 @@ set.
22
22
  |---|---|---|---|
23
23
  | `root` | string | config file's parent directory | Directory to index. Relative values resolve against the config file's parent. An explicit `root` passed to an SDK constructor overrides this (a warning is emitted on stderr). |
24
24
  | `ignore` | array of strings | `[]` | Glob patterns matched against root-relative paths. Matched files are skipped entirely — excluded from the initial scan and from watch events. |
25
- | `persist` | boolean | `false` | Keep the SQLite index on disk between runs. When `false`, the index is ephemeral: rebuilt from your files on every startup and discarded on exit. |
26
- | `persist_path` | string | `<root>/.dirsql/cache.db` | Location of the on-disk cache. Relative values resolve against the config file's parent. Ignored unless `persist = true`. |
27
25
  | `pre-query` | string | none | Server-wide command hook: the raw `POST /query` request body is passed to this command as `{args}`, and the plain-text SQL it prints is executed instead of parsing the body as `{"sql": …}`. CLI server only; the SDKs ignore it. Must be non-empty. See [Command hooks](./hooks.md#pre-query). |
28
26
  | `post-query` | string | none | Server-wide command hook: each successful `POST /query` result set is handed to this command (as a JSON array on stdin, and as `{args}` up to 96 KiB), and the JSON body it prints is returned instead of the bare row array. CLI server only; the SDKs ignore it. Must be non-empty. See [Command hooks](./hooks.md#post-query). |
29
27
  | `hook-timeout` | integer (seconds) | `30` | One global per-run timeout for every command hook — `on-file`, `pre-query`, and `post-query` alike. Positive whole seconds; zero and negative values are a config error. See [Command hooks](./hooks.md#timeout). |
@@ -38,11 +36,13 @@ directory.
38
36
  [dirsql]
39
37
  root = "../data"
40
38
  ignore = ["node_modules/**", ".git/**"]
41
- persist = true
42
- persist_path = ".dirsql/cache.db" # the default; shown for illustration
43
39
  hook-timeout = 300
44
40
  ```
45
41
 
42
+ Persistence is not a config key. Keep the SQLite index on disk between runs
43
+ with the [`--persist [PATH]` CLI flag](./cli.md#server-mode) — a machine-local
44
+ operational choice that belongs to the runner, not to shareable config.
45
+
46
46
  ## `[[dirsql.extension]]`
47
47
 
48
48
  Each entry declares a SQLite extension to load at startup. Extensions are
@@ -150,7 +150,6 @@ SDKs raise/reject) when:
150
150
  ```toml
151
151
  [dirsql]
152
152
  ignore = ["node_modules/**", ".git/**", "dist/**"]
153
- persist = true
154
153
  pre-query = "uv run python to_sql.py {args}"
155
154
  post-query = "jq -c '{results: .}'"
156
155
  hook-timeout = 120
@@ -111,11 +111,10 @@ directory" error.
111
111
  - `persist` — Keep the SQLite index on disk between runs (default off:
112
112
  ephemeral, rebuilt every startup). The cache lives at
113
113
  `<root>/.dirsql/cache.db` by default; on restart, only files whose stat
114
- changed are re-parsed. `persist = true` in the config also enables it.
115
- - `persist_path` / `persistPath` Override the cache location. An
116
- explicit value wins over the config's `persist_path`. Ignored when
117
- persistence is off. (Config-file values resolve relative to the config's
118
- parent directory; constructor values are used as given.)
114
+ changed are re-parsed. (The CLI exposes the same switch as the
115
+ [`--persist [PATH]`](./cli.md#server-mode) flag; it is not a config key.)
116
+ - `persist_path` / `persistPath` — Override the cache location. Ignored when
117
+ persistence is off. Constructor values are used as given.
119
118
  - `extensions` — SQLite extensions to load at startup, before any table
120
119
  DDL (enable → load → disable, so SQL `load_extension()` is never
121
120
  exposed). Each entry pairs a shared-library `path` with an optional
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "command": "uv run python -m pytest tests/e2e/ -x -q",
3
- "ran_at": 1783636465,
3
+ "ran_at": 1783637710,
4
4
  "exit_code": 0,
5
- "commit": "3b275ebe12b28777822a7dbcc9d8c155697106d7"
5
+ "commit": "c7c626ca03beb1b23e26ab96febf3c9df5d4a67f"
6
6
  }
@@ -1,23 +1,34 @@
1
1
  # Keep the index across restarts
2
2
 
3
3
  By default the database is ephemeral: rebuilt from your files on every
4
- startup and discarded on exit. [`persist`](../reference/config.md#dirsql-keys) keeps the
5
- SQLite index on disk instead, so a restart only re-parses files that
6
- actually changed — the difference between seconds and milliseconds on large
7
- trees, and between re-running and skipping expensive
4
+ startup and discarded on exit. The [`--persist [PATH]`](../reference/cli.md#server-mode)
5
+ flag keeps the SQLite index on disk instead, so a restart only re-parses
6
+ files that actually changed — the difference between seconds and
7
+ milliseconds on large trees, and between re-running and skipping expensive
8
8
  [`on-file`](./extract-from-contents.md) commands.
9
9
 
10
+ Whether and where to cache is a machine-local operational choice — it
11
+ belongs to the command you run, not to the shared `.dirsql.toml`. That is
12
+ why it is a CLI flag, not a config key.
13
+
10
14
  ## 1. Turn it on
11
15
 
12
- ```toml
13
- [dirsql]
14
- persist = true
16
+ ```bash
17
+ dirsql --persist
15
18
  ```
16
19
 
17
20
  That's the whole change. On the next run the cache is written to
18
21
  `.dirsql/cache.db` under the root; runs after that start from it. To put
19
- the cache elsewhere (a CI cache dir, a tmpfs), set
20
- [`persist_path`](../reference/config.md#dirsql-keys).
22
+ the cache elsewhere (a CI cache dir, a tmpfs), pass a path:
23
+
24
+ ```bash
25
+ dirsql --persist /var/cache/dirsql.db
26
+ ```
27
+
28
+ The same flag works on [`dirsql query`](../reference/cli.md#dirsql-query);
29
+ put a bare `--persist` after the SQL there so it does not consume the query
30
+ argument. Embedding `dirsql`? The SDK constructors expose the same switch —
31
+ see [_Embedding `dirsql`?_](#embedding-dirsql) below.
21
32
 
22
33
  ## 2. Keep the cache out of git
23
34
 
@@ -48,6 +48,7 @@ requests, closes open `/events` streams, and exits.
48
48
  | `-c, --config <path>` | `./.dirsql.toml` | Path to the [config file](./config.md). The index is rooted at the directory containing this file (unless the config sets `[dirsql].root`). When the file does not exist, the server runs in [zero-config mode](#zero-config-mode). |
49
49
  | `--host <addr>` | `localhost` | Bind address. |
50
50
  | `--port <n>` | `7117` | TCP port to bind. |
51
+ | `--persist [<path>]` | off | Keep the SQLite index on disk between runs so a restart only re-parses files that actually changed. Bare `--persist` caches at `<root>/.dirsql/cache.db`; `--persist <path>` caches at `<path>`. Off by default (the index is ephemeral). Global — also honored by [`dirsql query`](#dirsql-query). See [Keep the index across restarts](../howto/persist.md). |
51
52
  | `--extension <path>` | none | Load a SQLite extension by literal path, overriding the config's `[[dirsql.extension]]` entries. Repeatable. Format: `<path>` or `<path>::<entrypoint>`. Internal plumbing for the pip/npm launchers, which resolve package-name extensions and pass the resolved paths here — not intended for direct use. When any `--extension` is present, the config file's own extension entries are not loaded. |
52
53
  | `--version` | | Print the version and exit. |
53
54
  | `--help` | | Print usage and exit. |
@@ -119,6 +120,10 @@ uses**, so behavior is identical to `POST /query` by construction:
119
120
  - **Config discovery** honors `--config` (default `./.dirsql.toml`),
120
121
  [zero-config mode](#zero-config-mode), and `--extension` overrides,
121
122
  exactly as server mode does.
123
+ - **`--persist [<path>]`** is honored, so a repeated `dirsql query` reuses the
124
+ on-disk cache. Because its value is optional, place a bare `--persist` after
125
+ the SQL (`dirsql query "SELECT …" --persist`) or use the `=` form
126
+ (`--persist=/path`) so it does not swallow the SQL argument.
122
127
  - **Hooks** ([`pre-query`](./hooks.md#pre-query) /
123
128
  [`post-query`](./hooks.md#post-query)) and the
124
129
  [`[dirsql].hook-timeout`](./config.md#dirsql-keys) apply identically.
@@ -11,7 +11,7 @@ The [CLI](./cli.md) loads `./.dirsql.toml` by default (`--config <path>`
11
11
  overrides). The [SDKs](./sdk.md) load a config via the `config` constructor
12
12
  parameter.
13
13
 
14
- **Path resolution.** Relative paths in the config (`root`, `persist_path`,
14
+ **Path resolution.** Relative paths in the config (`root`,
15
15
  `[[dirsql.extension]]` `path`) resolve against the config file's parent
16
16
  directory. The scan root defaults to that same directory when `root` is not
17
17
  set.
@@ -22,8 +22,6 @@ set.
22
22
  |---|---|---|---|
23
23
  | `root` | string | config file's parent directory | Directory to index. Relative values resolve against the config file's parent. An explicit `root` passed to an SDK constructor overrides this (a warning is emitted on stderr). |
24
24
  | `ignore` | array of strings | `[]` | Glob patterns matched against root-relative paths. Matched files are skipped entirely — excluded from the initial scan and from watch events. |
25
- | `persist` | boolean | `false` | Keep the SQLite index on disk between runs. When `false`, the index is ephemeral: rebuilt from your files on every startup and discarded on exit. |
26
- | `persist_path` | string | `<root>/.dirsql/cache.db` | Location of the on-disk cache. Relative values resolve against the config file's parent. Ignored unless `persist = true`. |
27
25
  | `pre-query` | string | none | Server-wide command hook: the raw `POST /query` request body is passed to this command as `{args}`, and the plain-text SQL it prints is executed instead of parsing the body as `{"sql": …}`. CLI server only; the SDKs ignore it. Must be non-empty. See [Command hooks](./hooks.md#pre-query). |
28
26
  | `post-query` | string | none | Server-wide command hook: each successful `POST /query` result set is handed to this command (as a JSON array on stdin, and as `{args}` up to 96 KiB), and the JSON body it prints is returned instead of the bare row array. CLI server only; the SDKs ignore it. Must be non-empty. See [Command hooks](./hooks.md#post-query). |
29
27
  | `hook-timeout` | integer (seconds) | `30` | One global per-run timeout for every command hook — `on-file`, `pre-query`, and `post-query` alike. Positive whole seconds; zero and negative values are a config error. See [Command hooks](./hooks.md#timeout). |
@@ -38,11 +36,13 @@ directory.
38
36
  [dirsql]
39
37
  root = "../data"
40
38
  ignore = ["node_modules/**", ".git/**"]
41
- persist = true
42
- persist_path = ".dirsql/cache.db" # the default; shown for illustration
43
39
  hook-timeout = 300
44
40
  ```
45
41
 
42
+ Persistence is not a config key. Keep the SQLite index on disk between runs
43
+ with the [`--persist [PATH]` CLI flag](./cli.md#server-mode) — a machine-local
44
+ operational choice that belongs to the runner, not to shareable config.
45
+
46
46
  ## `[[dirsql.extension]]`
47
47
 
48
48
  Each entry declares a SQLite extension to load at startup. Extensions are
@@ -150,7 +150,6 @@ SDKs raise/reject) when:
150
150
  ```toml
151
151
  [dirsql]
152
152
  ignore = ["node_modules/**", ".git/**", "dist/**"]
153
- persist = true
154
153
  pre-query = "uv run python to_sql.py {args}"
155
154
  post-query = "jq -c '{results: .}'"
156
155
  hook-timeout = 120
@@ -111,11 +111,10 @@ directory" error.
111
111
  - `persist` — Keep the SQLite index on disk between runs (default off:
112
112
  ephemeral, rebuilt every startup). The cache lives at
113
113
  `<root>/.dirsql/cache.db` by default; on restart, only files whose stat
114
- changed are re-parsed. `persist = true` in the config also enables it.
115
- - `persist_path` / `persistPath` Override the cache location. An
116
- explicit value wins over the config's `persist_path`. Ignored when
117
- persistence is off. (Config-file values resolve relative to the config's
118
- parent directory; constructor values are used as given.)
114
+ changed are re-parsed. (The CLI exposes the same switch as the
115
+ [`--persist [PATH]`](./cli.md#server-mode) flag; it is not a config key.)
116
+ - `persist_path` / `persistPath` — Override the cache location. Ignored when
117
+ persistence is off. Constructor values are used as given.
119
118
  - `extensions` — SQLite extensions to load at startup, before any table
120
119
  DDL (enable → load → disable, so SQL `load_extension()` is never
121
120
  exposed). Each entry pairs a shared-library `path` with an optional
@@ -61,6 +61,28 @@ struct Cli {
61
61
  /// subcommand.
62
62
  #[arg(long = "extension", global = true)]
63
63
  extension: Vec<String>,
64
+
65
+ /// Keep the SQLite index on disk between runs so a restart only re-parses
66
+ /// files that actually changed. Bare `--persist` caches at the default
67
+ /// location (`<root>/.dirsql/cache.db`); `--persist <path>` caches there.
68
+ /// Off by default (ephemeral index). Used by server mode and `query`.
69
+ #[arg(long, num_args = 0..=1, global = true)]
70
+ persist: Option<Option<PathBuf>>,
71
+ }
72
+
73
+ impl Cli {
74
+ /// Apply the `--persist [PATH]` flag to a builder. Absent → no change;
75
+ /// bare `--persist` → persist at the default location; `--persist <path>`
76
+ /// → persist at `<path>`.
77
+ fn apply_persist(&self, mut builder: dirsql::DirSQLBuilder) -> dirsql::DirSQLBuilder {
78
+ if let Some(path) = &self.persist {
79
+ builder = builder.persist(true);
80
+ if let Some(path) = path {
81
+ builder = builder.persist_path(path);
82
+ }
83
+ }
84
+ builder
85
+ }
64
86
  }
65
87
 
66
88
  #[derive(Debug, Subcommand)]
@@ -216,7 +238,7 @@ fn load_state(cli: &Cli) -> AppState {
216
238
  if !config_path.exists() {
217
239
  // No config: serve a default `files` table so dirsql is queryable
218
240
  // out of the box. A config file, when present, fully overrules this.
219
- return load_default_state(config_path);
241
+ return load_default_state(cli, config_path);
220
242
  }
221
243
 
222
244
  // Canonicalize so the root (derived from the config's parent) is
@@ -237,16 +259,14 @@ fn load_state(cli: &Cli) -> AppState {
237
259
  // resolved them (including package names the compiled binary can't
238
260
  // resolve), so suppress the config's extension loading and supply the
239
261
  // resolved literal paths instead.
240
- let build = if cli.extension.is_empty() {
241
- DirSQL::from_config_path(&resolved)
242
- } else {
243
- DirSQL::builder()
244
- .config(&resolved)
262
+ let mut builder = DirSQL::builder().config(&resolved);
263
+ if !cli.extension.is_empty() {
264
+ builder = builder
245
265
  .extensions(parse_extension_specs(&cli.extension))
246
- .suppress_config_extensions(true)
247
- .build()
248
- };
249
- match build {
266
+ .suppress_config_extensions(true);
267
+ }
268
+ builder = cli.apply_persist(builder);
269
+ match builder.build() {
250
270
  Ok(db) => AppState::Ready(db),
251
271
  Err(err) => AppState::Unavailable(format!("failed to load config: {err}")),
252
272
  }
@@ -324,7 +344,7 @@ fn load_post_query(cli: &Cli) -> Option<PostQuery> {
324
344
  /// table — one row per file, columns drawn entirely from filesystem facts —
325
345
  /// so `SELECT * FROM files` works immediately. A config file, when present,
326
346
  /// fully overrules this default.
327
- fn load_default_state(config_path: &Path) -> AppState {
347
+ fn load_default_state(cli: &Cli, config_path: &Path) -> AppState {
328
348
  let dir = config_path
329
349
  .parent()
330
350
  .filter(|p| !p.as_os_str().is_empty())
@@ -340,7 +360,8 @@ fn load_default_state(config_path: &Path) -> AppState {
340
360
  }
341
361
  };
342
362
 
343
- match DirSQL::new(root, vec![default_files_table()]) {
363
+ let builder = cli.apply_persist(DirSQL::builder().root(root).table(default_files_table()));
364
+ match builder.build() {
344
365
  Ok(db) => AppState::Ready(db),
345
366
  Err(err) => AppState::Unavailable(format!("failed to build default index: {err}")),
346
367
  }
@@ -406,6 +427,34 @@ mod tests {
406
427
  assert_eq!(query_body(" "), r#"{"sql":" "}"#);
407
428
  }
408
429
 
430
+ #[test]
431
+ fn persist_flag_absent_is_none() {
432
+ let cli = Cli::parse_from(["dirsql"]);
433
+ assert_eq!(cli.persist, None);
434
+ }
435
+
436
+ #[test]
437
+ fn persist_flag_bare_enables_default_location() {
438
+ // Bare `--persist` (no value) → `Some(None)`: persist at the default
439
+ // `<root>/.dirsql/cache.db`, no override path.
440
+ let cli = Cli::parse_from(["dirsql", "--persist"]);
441
+ assert_eq!(cli.persist, Some(None));
442
+ }
443
+
444
+ #[test]
445
+ fn persist_flag_with_path_carries_the_value() {
446
+ let cli = Cli::parse_from(["dirsql", "--persist", "/var/cache/x.db"]);
447
+ assert_eq!(cli.persist, Some(Some(PathBuf::from("/var/cache/x.db"))));
448
+ }
449
+
450
+ #[test]
451
+ fn persist_flag_is_global_on_the_query_subcommand() {
452
+ // `--persist` is global, so it attaches to `query` too; the flag sits
453
+ // after the positional SQL to avoid the num_args(0..=1) greedy grab.
454
+ let cli = Cli::parse_from(["dirsql", "query", "SELECT 1", "--persist"]);
455
+ assert_eq!(cli.persist, Some(None));
456
+ }
457
+
409
458
  #[test]
410
459
  fn parse_extension_specs_handles_bare_path_and_entrypoint() {
411
460
  let specs = vec![