dirsql 0.3.40__tar.gz → 0.3.42__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.40 → dirsql-0.3.42}/Cargo.lock +1 -1
  2. {dirsql-0.3.40 → dirsql-0.3.42}/PKG-INFO +1 -1
  3. {dirsql-0.3.40 → dirsql-0.3.42}/dirsql/_async.py +4 -35
  4. dirsql-0.3.42/dirsql/cli/interpret/__init__.py +14 -0
  5. {dirsql-0.3.40 → dirsql-0.3.42}/dirsql/cli/main.py +2 -12
  6. {dirsql-0.3.40/packages/python → dirsql-0.3.42}/docs/.vitepress/config.ts +10 -13
  7. {dirsql-0.3.40/packages/rust → dirsql-0.3.42}/docs/api/index.md +3 -2
  8. {dirsql-0.3.40/packages/python → dirsql-0.3.42}/docs/cli/config.md +12 -7
  9. {dirsql-0.3.40/packages/rust → dirsql-0.3.42}/docs/cli/index.md +1 -1
  10. dirsql-0.3.42/docs/tests/integration/sidebar.spec.ts +46 -0
  11. dirsql-0.3.42/docs/tests/unit/config.test.ts +49 -0
  12. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/Cargo.toml +1 -1
  13. {dirsql-0.3.40 → dirsql-0.3.42/packages/python}/docs/.vitepress/config.ts +10 -13
  14. {dirsql-0.3.40 → dirsql-0.3.42/packages/python}/docs/api/index.md +3 -2
  15. {dirsql-0.3.40 → dirsql-0.3.42/packages/python}/docs/cli/config.md +12 -7
  16. {dirsql-0.3.40 → dirsql-0.3.42/packages/python}/docs/cli/index.md +1 -1
  17. dirsql-0.3.42/packages/python/docs/tests/integration/sidebar.spec.ts +46 -0
  18. dirsql-0.3.42/packages/python/docs/tests/unit/config.test.ts +49 -0
  19. dirsql-0.3.42/packages/python/e2e-attestation.json +6 -0
  20. {dirsql-0.3.40/packages/python → dirsql-0.3.42/packages/rust}/docs/api/index.md +3 -2
  21. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/docs/cli/config.md +12 -7
  22. {dirsql-0.3.40/packages/python → dirsql-0.3.42/packages/rust}/docs/cli/index.md +1 -1
  23. {dirsql-0.3.40 → dirsql-0.3.42}/pyproject.toml +1 -1
  24. dirsql-0.3.40/dirsql/cli/interpret/__init__.py +0 -17
  25. dirsql-0.3.40/dirsql/cli/interpret/dispatch_extract.py +0 -41
  26. dirsql-0.3.40/dirsql/cli/interpret/load_app.py +0 -32
  27. dirsql-0.3.40/dirsql/cli/interpret/run.py +0 -66
  28. dirsql-0.3.40/dirsql/cli/interpret/write_message.py +0 -18
  29. dirsql-0.3.40/dirsql/resolve_config.py +0 -61
  30. dirsql-0.3.40/docs/tests/unit/config.test.ts +0 -22
  31. dirsql-0.3.40/packages/python/docs/tests/unit/config.test.ts +0 -22
  32. dirsql-0.3.40/packages/python/e2e-attestation.json +0 -6
  33. dirsql-0.3.40/packages/python/tests/e2e/__fixtures__/data/a/meta.json +0 -1
  34. dirsql-0.3.40/packages/python/tests/e2e/__fixtures__/data/b/meta.json +0 -1
  35. dirsql-0.3.40/packages/python/tests/e2e/__fixtures__/dirsql.config.py +0 -29
  36. dirsql-0.3.40/packages/python/tests/e2e/__fixtures__/interpret/data/a/meta.json +0 -1
  37. dirsql-0.3.40/packages/python/tests/e2e/__fixtures__/interpret/data/b/meta.json +0 -1
  38. dirsql-0.3.40/packages/python/tests/e2e/__fixtures__/interpret/dirsql.config.py +0 -27
  39. dirsql-0.3.40/packages/python/tests/e2e/__fixtures__/interpret/dirsql.config_no_app.py +0 -3
  40. dirsql-0.3.40/packages/python/tests/e2e/__fixtures__/interpret/dirsql.config_raises.py +0 -21
  41. dirsql-0.3.40/packages/python/tests/e2e/interpret_subprocess.py +0 -96
  42. {dirsql-0.3.40 → dirsql-0.3.42}/Cargo.toml +0 -0
  43. {dirsql-0.3.40 → dirsql-0.3.42}/README.md +0 -0
  44. {dirsql-0.3.40 → dirsql-0.3.42}/dirsql/__init__.py +0 -0
  45. {dirsql-0.3.40 → dirsql-0.3.42}/dirsql/_dirsql.pyi +0 -0
  46. {dirsql-0.3.40 → dirsql-0.3.42}/dirsql/cli/__init__.py +0 -0
  47. {dirsql-0.3.40 → dirsql-0.3.42}/dirsql/cli/binary_path.py +0 -0
  48. {dirsql-0.3.40 → dirsql-0.3.42}/dirsql/cli/is_windows.py +0 -0
  49. {dirsql-0.3.40 → dirsql-0.3.42}/dirsql/py.typed +0 -0
  50. {dirsql-0.3.40 → dirsql-0.3.42}/docs/.claude/CLAUDE.md +0 -0
  51. {dirsql-0.3.40 → dirsql-0.3.42}/docs/.vitepress/theme/index.ts +0 -0
  52. {dirsql-0.3.40 → dirsql-0.3.42}/docs/.vitepress/theme/lang.ts +0 -0
  53. {dirsql-0.3.40 → dirsql-0.3.42}/docs/AGENTS.md +0 -0
  54. {dirsql-0.3.40 → dirsql-0.3.42}/docs/cli/http-api.md +0 -0
  55. {dirsql-0.3.40 → dirsql-0.3.42}/docs/cli/init.md +0 -0
  56. {dirsql-0.3.40 → dirsql-0.3.42}/docs/cli/server.md +0 -0
  57. {dirsql-0.3.40 → dirsql-0.3.42}/docs/getting-started.md +0 -0
  58. {dirsql-0.3.40 → dirsql-0.3.42}/docs/guide/async.md +0 -0
  59. {dirsql-0.3.40 → dirsql-0.3.42}/docs/guide/crdt.md +0 -0
  60. {dirsql-0.3.40 → dirsql-0.3.42}/docs/guide/persistence.md +0 -0
  61. {dirsql-0.3.40 → dirsql-0.3.42}/docs/guide/querying.md +0 -0
  62. {dirsql-0.3.40 → dirsql-0.3.42}/docs/guide/tables.md +0 -0
  63. {dirsql-0.3.40 → dirsql-0.3.42}/docs/guide/watching.md +0 -0
  64. {dirsql-0.3.40 → dirsql-0.3.42}/docs/index.md +0 -0
  65. {dirsql-0.3.40 → dirsql-0.3.42}/docs/migrations.md +0 -0
  66. {dirsql-0.3.40 → dirsql-0.3.42}/docs/package.json +0 -0
  67. {dirsql-0.3.40 → dirsql-0.3.42}/docs/playwright.config.ts +0 -0
  68. {dirsql-0.3.40 → dirsql-0.3.42}/docs/pnpm-lock.yaml +0 -0
  69. {dirsql-0.3.40 → dirsql-0.3.42}/docs/pnpm-workspace.yaml +0 -0
  70. {dirsql-0.3.40 → dirsql-0.3.42}/docs/tests/integration/home.spec.ts +0 -0
  71. {dirsql-0.3.40 → dirsql-0.3.42}/docs/tests/integration/language-flag.spec.ts +0 -0
  72. {dirsql-0.3.40 → dirsql-0.3.42}/docs/tests/unit/lang.test.ts +0 -0
  73. {dirsql-0.3.40 → dirsql-0.3.42}/docs/vitest.config.ts +0 -0
  74. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/README.md +0 -0
  75. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/conftest.py +0 -0
  76. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/.claude/CLAUDE.md +0 -0
  77. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  78. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  79. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/AGENTS.md +0 -0
  80. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/cli/http-api.md +0 -0
  81. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/cli/init.md +0 -0
  82. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/cli/server.md +0 -0
  83. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/getting-started.md +0 -0
  84. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/guide/async.md +0 -0
  85. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/guide/crdt.md +0 -0
  86. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/guide/persistence.md +0 -0
  87. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/guide/querying.md +0 -0
  88. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/guide/tables.md +0 -0
  89. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/guide/watching.md +0 -0
  90. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/index.md +0 -0
  91. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/migrations.md +0 -0
  92. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/package.json +0 -0
  93. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/playwright.config.ts +0 -0
  94. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/pnpm-lock.yaml +0 -0
  95. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/pnpm-workspace.yaml +0 -0
  96. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/tests/integration/home.spec.ts +0 -0
  97. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
  98. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/tests/unit/lang.test.ts +0 -0
  99. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/docs/vitest.config.ts +0 -0
  100. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/src/lib.rs +0 -0
  101. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/tests/__init__.py +0 -0
  102. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/tests/conftest.py +0 -0
  103. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/tests/e2e/__init__.py +0 -0
  104. {dirsql-0.3.40 → dirsql-0.3.42}/packages/python/tests/integration/__init__.py +0 -0
  105. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/Cargo.toml +0 -0
  106. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/README.md +0 -0
  107. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/benches/db_bench.rs +0 -0
  108. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/benches/differ_bench.rs +0 -0
  109. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/benches/matcher_bench.rs +0 -0
  110. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/benches/scanner_bench.rs +0 -0
  111. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/docs/cli/http-api.md +0 -0
  112. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/docs/cli/init.md +0 -0
  113. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/docs/cli/server.md +0 -0
  114. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/docs/getting-started.md +0 -0
  115. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/docs/guide/async.md +0 -0
  116. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/docs/guide/crdt.md +0 -0
  117. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/docs/guide/persistence.md +0 -0
  118. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/docs/guide/querying.md +0 -0
  119. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/docs/guide/tables.md +0 -0
  120. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/docs/guide/watching.md +0 -0
  121. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/docs/index.md +0 -0
  122. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/docs/migrations.md +0 -0
  123. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/bin/dirsql.rs +0 -0
  124. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/cli/init.rs +0 -0
  125. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/cli/mod.rs +0 -0
  126. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/cli/native_config.rs +0 -0
  127. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/cli/router.rs +0 -0
  128. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/cli/serialize.rs +0 -0
  129. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/cli/server.rs +0 -0
  130. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/config.rs +0 -0
  131. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/db.rs +0 -0
  132. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/differ.rs +0 -0
  133. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/lib.rs +0 -0
  134. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/matcher.rs +0 -0
  135. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/persist.rs +0 -0
  136. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/scanner.rs +0 -0
  137. {dirsql-0.3.40 → dirsql-0.3.42}/packages/rust/src/watcher.rs +0 -0
@@ -499,7 +499,7 @@ dependencies = [
499
499
 
500
500
  [[package]]
501
501
  name = "dirsql-py-ext"
502
- version = "0.3.40"
502
+ version = "0.3.42"
503
503
  dependencies = [
504
504
  "dirsql",
505
505
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.3.40
3
+ Version: 0.3.42
4
4
  Requires-Dist: pytest>=8 ; extra == 'dev'
5
5
  Requires-Dist: pytest-describe>=2 ; extra == 'dev'
6
6
  Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
@@ -1,20 +1,8 @@
1
1
  """Async-by-default DirSQL wrapper."""
2
2
 
3
3
  import asyncio
4
- from typing import TYPE_CHECKING
5
4
 
6
5
  from dirsql._dirsql import DirSQL as _RustDirSQL
7
- from dirsql.resolve_config import resolve_config
8
-
9
- if TYPE_CHECKING:
10
- # `typing.override` is 3.12+; the package supports 3.11, so source it from
11
- # `typing_extensions` for the type checker only. At runtime `@override` is
12
- # a pure marker, so a no-op identity avoids the runtime dependency.
13
- from typing_extensions import override
14
- else:
15
-
16
- def override(func):
17
- return func
18
6
 
19
7
 
20
8
  class _WatchStream:
@@ -55,9 +43,10 @@ class DirSQL:
55
43
  async for event in db.watch():
56
44
  ...
57
45
 
58
- At least one of ``root`` or ``config`` must be supplied. When both are
59
- set, the explicit ``root`` wins over any ``[dirsql].root`` in the config
60
- file (a warning is emitted on stderr).
46
+ Supply a ``root``, a ``config`` path, or both. When both are set, the
47
+ explicit ``root`` wins over any ``[dirsql].root`` in the config file (a
48
+ warning is emitted on stderr). If neither is given, the initial scan
49
+ fails with a "no root directory" error raised by the core.
61
50
 
62
51
  Pass ``persist=True`` to keep an on-disk SQLite cache (default location:
63
52
  ``<root>/.dirsql/cache.db``). Override the location with ``persist_path``.
@@ -79,8 +68,6 @@ class DirSQL:
79
68
  persist_path=None,
80
69
  extensions=None,
81
70
  ):
82
- if root is None and config is None:
83
- raise TypeError("DirSQL requires either a root directory or a config= path")
84
71
  self._root = root
85
72
  self._tables = tables
86
73
  self._ignore = ignore
@@ -136,21 +123,3 @@ class DirSQL:
136
123
  def watch(self):
137
124
  """Start watching for file changes. Returns an async iterable of RowEvent."""
138
125
  return _WatchStream(self._db)
139
-
140
- @property
141
- @override
142
- def __dict__(self):
143
- """Resolved construction state as a JSON-serializable dict.
144
-
145
- Recomputed on each access; reads the ``.dirsql.toml`` if ``config=``
146
- was supplied. Works before ``await db.ready()``.
147
- """
148
- return resolve_config(
149
- self._root,
150
- self._tables,
151
- self._ignore,
152
- self._config,
153
- self._persist,
154
- self._persist_path,
155
- self._extensions,
156
- )
@@ -0,0 +1,14 @@
1
+ """Empty package shell — the native-config ``interpret`` helper was removed.
2
+
3
+ The ``dirsql interpret`` subcommand and its NDJSON ``extract`` loop (``run``,
4
+ ``load_app``, ``dispatch_extract``, ``write_message``) were removed in #321
5
+ (#323). The CLI now accepts only ``.dirsql.toml``; to run user-defined
6
+ ``extract`` callbacks, use the programmatic SDK (``DirSQL(...)`` with
7
+ in-process closures).
8
+
9
+ This ``__init__.py`` carries no logic and no re-exports. It remains only
10
+ because the colocated-test tooling cannot yet express *deleting* an exempt
11
+ package barrel (the co-change check flags a deleted source that has no
12
+ co-deleted colocated test, and a retained exempt for a deleted path is
13
+ rejected as stale). The directory is removed once that is resolved.
14
+ """
@@ -1,8 +1,6 @@
1
1
  """Console-script entry point. Execs the bundled binary on POSIX,
2
- subprocesses it on Windows. When ``argv[0] == "interpret"`` the
3
- in-process Python helper handles the subcommand directly so a Rust
4
- orchestrator can spawn this script for native-language configs (#196)
5
- without depending on the bundled Rust binary."""
2
+ subprocesses it on Windows. All argv is forwarded transparently to the
3
+ bundled Rust binary."""
6
4
 
7
5
  from __future__ import annotations
8
6
 
@@ -18,14 +16,6 @@ def main(argv: list[str] | None = None) -> int:
18
16
  if argv is None:
19
17
  argv = sys.argv[1:]
20
18
 
21
- if argv and argv[0] == "interpret":
22
- from .interpret.run import run
23
-
24
- try:
25
- return run(argv[1:])
26
- except KeyboardInterrupt:
27
- return 130
28
-
29
19
  try:
30
20
  binary = binary_path()
31
21
  except FileNotFoundError as exc:
@@ -19,19 +19,12 @@ export default defineConfig({
19
19
  { text: 'GitHub', link: 'https://github.com/thekevinscott/dirsql' }
20
20
  ],
21
21
 
22
+ // A single global sidebar shown on every page. The CLI section is a
23
+ // self-contained group with all its subpages -- but it never *replaces*
24
+ // the rest of the nav. There is intentionally no path-scoped (`/cli/`)
25
+ // key: a path-scoped sidebar swaps the whole tree out, which deletes the
26
+ // other sections when you enter CLI (see #301). Keep one sidebar.
22
27
  sidebar: {
23
- '/cli/': [
24
- {
25
- text: 'CLI',
26
- items: [
27
- { text: 'Overview & Installation', link: '/cli/' },
28
- { text: 'Running the Server', link: '/cli/server' },
29
- { text: 'Generating a Config (`init`)', link: '/cli/init' },
30
- { text: 'Configuration File', link: '/cli/config' },
31
- { text: 'HTTP API', link: '/cli/http-api' }
32
- ]
33
- }
34
- ],
35
28
  '/': [
36
29
  {
37
30
  text: 'Tutorials',
@@ -53,7 +46,11 @@ export default defineConfig({
53
46
  {
54
47
  text: 'CLI',
55
48
  items: [
56
- { text: 'Using `dirsql` from the CLI', link: '/cli/' }
49
+ { text: 'Overview & Installation', link: '/cli/' },
50
+ { text: 'Running the Server', link: '/cli/server' },
51
+ { text: 'Generating a Config (`init`)', link: '/cli/init' },
52
+ { text: 'Configuration File', link: '/cli/config' },
53
+ { text: 'HTTP API', link: '/cli/http-api' }
57
54
  ]
58
55
  },
59
56
  {
@@ -59,6 +59,7 @@ new DirSQL({
59
59
  tables?: TableDef[],
60
60
  ignore?: string[],
61
61
  config?: string,
62
+ extensions?: ExtensionSpec[], // [{ path: string, entrypoint?: string }]
62
63
  })
63
64
  ```
64
65
 
@@ -76,7 +77,7 @@ In Python, the constructor starts scanning in a background thread and returns im
76
77
  - `tables` -- List of `Table` definitions. Each defines a SQLite table, a glob pattern, and an extract function.
77
78
  - `ignore` -- Optional list of glob patterns. Files matching any ignore pattern are skipped regardless of table globs.
78
79
  - `config` -- Optional path to a `.dirsql.toml` config file. Its `[[table]]` entries are appended to any programmatic `tables`; its `[dirsql].ignore` patterns are appended to any explicit `ignore`; its optional `[dirsql].root` supplies the root directory when `root` is not passed explicitly; its `[[dirsql.extension]]` entries are appended to any programmatic `extensions`.
79
- - `extensions` -- Optional SQLite extensions to load onto the connection at startup, before any table DDL (enable → load → disable, so the SQL `load_extension()` function is never left exposed). Each entry pairs a shared-library `path` with an optional `entrypoint` init-symbol override (Python: `{ "path", "entrypoint"? }` dicts; Rust: `Extension { path, entrypoint }`). Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. Available in the Python and Rust SDKs; the TypeScript constructor parameter is tracked in [#230](https://github.com/thekevinscott/dirsql/issues/230). See [Loading extensions](../cli/config.md#loading-extensions).
80
+ - `extensions` -- Optional SQLite extensions to load onto the connection at startup, before any table DDL (enable → load → disable, so the SQL `load_extension()` function is never left exposed). Each entry pairs a shared-library `path` with an optional `entrypoint` init-symbol override (Python: `{ "path", "entrypoint"? }` dicts; Rust: `Extension { path, entrypoint }`; TypeScript: `{ path, entrypoint? }` objects). Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. Available in the Python, Rust, and TypeScript SDKs. See [Loading extensions](../cli/config.md#loading-extensions).
80
81
 
81
82
  ### Methods
82
83
 
@@ -162,7 +163,7 @@ JSON.stringify(db) // via db.toJSON()
162
163
 
163
164
  :::
164
165
 
165
- Returns the resolved construction state as a JSON-compatible value with fields `root`, `tables`, `ignore`, `persist`, `persist_path` (camelCase `persistPath` in TypeScript). Each table is `{ ddl, glob, strict }`. The Python and Rust snapshots also include `extensions` -- an array of `{ path, entrypoint }` (empty when none are configured); the TypeScript `toJSON` snapshot will gain it with [#230](https://github.com/thekevinscott/dirsql/issues/230). Excludes the original `config` path (already merged into `root` / `tables` / `ignore`), per-table `extract`, and per-table `name`. Available immediately after construction in Python and TypeScript; Rust's sync `build()` returns a ready instance.
166
+ Returns the resolved construction state as a JSON-compatible value with fields `root`, `tables`, `ignore`, `persist`, `persist_path` (camelCase `persistPath` in TypeScript). Each table is `{ ddl, glob, strict }`. All three SDK snapshots also include `extensions` -- an array of `{ path, entrypoint }` (empty when none are configured; `entrypoint` is `null` when no override was supplied). Excludes the original `config` path (already merged into `root` / `tables` / `ignore`), per-table `extract`, and per-table `name`. Available immediately after construction in Python and TypeScript; Rust's sync `build()` returns a ready instance.
166
167
 
167
168
  ---
168
169
 
@@ -239,7 +239,7 @@ def extract_meta(path):
239
239
 
240
240
  # Python must export a module-level `app`.
241
241
  app = DirSQL(
242
- root="papers", # required see "Set a root" below
242
+ root="papers", # optional; defaults to the current directory
243
243
  tables=[
244
244
  Table(
245
245
  ddl="CREATE TABLE papers (title TEXT, _path TEXT)",
@@ -269,7 +269,7 @@ import { readFileSync } from "node:fs";
269
269
  import { DirSQL } from "dirsql";
270
270
 
271
271
  export default new DirSQL({
272
- root: "papers", // required see "Set a root" below
272
+ root: "papers", // optional; defaults to the current directory
273
273
  tables: [
274
274
  {
275
275
  ddl: "CREATE TABLE papers (title TEXT, _path TEXT)",
@@ -285,7 +285,7 @@ const { readFileSync } = require("node:fs");
285
285
  const { DirSQL } = require("dirsql");
286
286
 
287
287
  module.exports = new DirSQL({
288
- root: "papers", // required see "Set a root" below
288
+ root: "papers", // optional; defaults to the current directory
289
289
  tables: [
290
290
  {
291
291
  ddl: "CREATE TABLE papers (title TEXT, _path TEXT)",
@@ -308,10 +308,15 @@ These apply to both the Python and JavaScript forms above.
308
308
  package) uses `module.exports = new DirSQL(...)`. Only the extension
309
309
  matters — the file can be named anything; `dirsql.config.{py,mjs,cjs}` is the
310
310
  suggested convention, not a requirement.
311
- - **Set a `root`.** Unlike TOML configs (which default the scan root to the
312
- config file's directory), native-language configs require an explicit `root`.
313
- Without one the Python launcher errors and the JavaScript launcher silently
314
- indexes nothing.
311
+ - **`root` defaults to the current directory.** A native-language config with
312
+ no `root` indexes the process's current working directory the directory you
313
+ ran `dirsql` from. (This differs from TOML configs, which default the scan
314
+ root to the config file's own directory.) Pass `root` explicitly to index
315
+ somewhere else.
316
+ - **No nested `config=`.** A native-language config builds its `DirSQL` from
317
+ `tables` and an optional `root`; it must not itself set `config=` to delegate
318
+ to another config file. `dirsql interpret` rejects such a config (a nested
319
+ config can't be represented in the handshake and would recurse).
315
320
  - **Install the launcher on your `PATH`.** To run your `extract`, the server
316
321
  spawns `dirsql interpret`, so the matching `dirsql` launcher must be installed
317
322
  and on your `PATH` — a global `pip`/`uv` install for `.py`, or `npm` for
@@ -20,7 +20,7 @@ Everything you need to run `dirsql` as a CLI lives in this section:
20
20
  - **[Configuration File](./config.md)** — the `.dirsql.toml` format and the
21
21
  `.py` / `.js` native-language alternative. Custom tables
22
22
  are defined through a config file; without one, the server runs in
23
- [zero-config mode](./server.md#zero-config-mode).
23
+ [zero-config mode](./server.md#defaults).
24
24
  - **[HTTP API](./http-api.md)** — the `POST /query` and `GET /events`
25
25
  endpoints, status codes, and event streaming.
26
26
 
@@ -0,0 +1,46 @@
1
+ import { expect, test } from '@playwright/test'
2
+
3
+ // Regression for #301: entering the CLI section must NOT replace the sidebar.
4
+ // The full set of sections has to stay visible on every page, including
5
+ // `/cli/*`, so navigation never loses its place.
6
+ const SECTIONS = ['Tutorials', 'How-to Guides', 'CLI', 'Reference']
7
+
8
+ async function sidebarSections(page: import('@playwright/test').Page) {
9
+ return await page.evaluate(() =>
10
+ Array.from(document.querySelectorAll('.VPSidebar .group .text'))
11
+ .map((el) => el.textContent?.trim())
12
+ .filter(Boolean)
13
+ )
14
+ }
15
+
16
+ for (const path of ['guide/tables.html', 'cli/index.html', 'cli/server.html']) {
17
+ test(`every section stays in the sidebar on ${path}`, async ({ page }) => {
18
+ await page.goto(`./${path}`)
19
+ await page.waitForSelector('.VPSidebar .group')
20
+ const sections = await sidebarSections(page)
21
+ for (const section of SECTIONS) {
22
+ expect(sections).toContain(section)
23
+ }
24
+ })
25
+ }
26
+
27
+ test('the CLI sidebar group lists all CLI subpages', async ({ page }) => {
28
+ await page.goto('./cli/index.html')
29
+ await page.waitForSelector('.VPSidebar .group')
30
+ const cliLinks = await page.evaluate(() => {
31
+ const groups = Array.from(document.querySelectorAll('.VPSidebar .group'))
32
+ const cli = groups.find(
33
+ (g) => g.querySelector('.text')?.textContent?.trim() === 'CLI'
34
+ )
35
+ return Array.from(cli?.querySelectorAll('a') ?? []).map(
36
+ (a) => new URL((a as HTMLAnchorElement).href).pathname
37
+ )
38
+ })
39
+ expect(cliLinks).toEqual([
40
+ '/dirsql/cli/',
41
+ '/dirsql/cli/server.html',
42
+ '/dirsql/cli/init.html',
43
+ '/dirsql/cli/config.html',
44
+ '/dirsql/cli/http-api.html'
45
+ ])
46
+ })
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import config from '../../.vitepress/config'
3
+
4
+ type SidebarItem = { text?: string; link?: string; items?: SidebarItem[] }
5
+
6
+ describe('vitepress config', () => {
7
+ it('has the expected site title and base path', () => {
8
+ expect(config.title).toBe('dirsql')
9
+ expect(config.base).toBe('/dirsql/')
10
+ })
11
+
12
+ // Regression guard: CLI docs live in their own `/cli/` section, not
13
+ // interleaved with the SDK how-to guides (see #179).
14
+ it('keeps CLI pages out of the How-to Guides group', () => {
15
+ const sidebar = config.themeConfig!.sidebar as Record<string, SidebarItem[]>
16
+ const howTo = sidebar['/'].find((group) => group.text === 'How-to Guides')
17
+ const links = (howTo!.items ?? []).map((item) => item.link)
18
+ expect(links).not.toContain('/guide/cli')
19
+ expect(links).not.toContain('/guide/init')
20
+ expect(links).not.toContain('/guide/config')
21
+ })
22
+
23
+ // Regression guard: the sidebar must never *replace* itself when entering a
24
+ // section. A path-scoped key (e.g. `/cli/`) swaps the whole tree out, which
25
+ // deletes the other sections and reads as the nav breaking (see #301). There
26
+ // must be exactly one global `/` sidebar.
27
+ it('has a single global sidebar with no replacing path-scoped keys', () => {
28
+ const sidebar = config.themeConfig!.sidebar as Record<string, SidebarItem[]>
29
+ expect(Object.keys(sidebar)).toEqual(['/'])
30
+ })
31
+
32
+ // The CLI section is self-contained: its group carries all CLI subpages,
33
+ // alongside (never instead of) the other sections (see #301).
34
+ it('shows every CLI subpage in a self-contained CLI group', () => {
35
+ const sidebar = config.themeConfig!.sidebar as Record<string, SidebarItem[]>
36
+ const groupTexts = sidebar['/'].map((group) => group.text)
37
+ expect(groupTexts).toEqual(['Tutorials', 'How-to Guides', 'CLI', 'Reference'])
38
+
39
+ const cli = sidebar['/'].find((group) => group.text === 'CLI')
40
+ const links = (cli!.items ?? []).map((item) => item.link)
41
+ expect(links).toEqual([
42
+ '/cli/',
43
+ '/cli/server',
44
+ '/cli/init',
45
+ '/cli/config',
46
+ '/cli/http-api'
47
+ ])
48
+ })
49
+ })
@@ -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.40"
7
+ version = "0.3.42"
8
8
  edition.workspace = true
9
9
  publish = false
10
10
  readme = "README.md"
@@ -19,19 +19,12 @@ export default defineConfig({
19
19
  { text: 'GitHub', link: 'https://github.com/thekevinscott/dirsql' }
20
20
  ],
21
21
 
22
+ // A single global sidebar shown on every page. The CLI section is a
23
+ // self-contained group with all its subpages -- but it never *replaces*
24
+ // the rest of the nav. There is intentionally no path-scoped (`/cli/`)
25
+ // key: a path-scoped sidebar swaps the whole tree out, which deletes the
26
+ // other sections when you enter CLI (see #301). Keep one sidebar.
22
27
  sidebar: {
23
- '/cli/': [
24
- {
25
- text: 'CLI',
26
- items: [
27
- { text: 'Overview & Installation', link: '/cli/' },
28
- { text: 'Running the Server', link: '/cli/server' },
29
- { text: 'Generating a Config (`init`)', link: '/cli/init' },
30
- { text: 'Configuration File', link: '/cli/config' },
31
- { text: 'HTTP API', link: '/cli/http-api' }
32
- ]
33
- }
34
- ],
35
28
  '/': [
36
29
  {
37
30
  text: 'Tutorials',
@@ -53,7 +46,11 @@ export default defineConfig({
53
46
  {
54
47
  text: 'CLI',
55
48
  items: [
56
- { text: 'Using `dirsql` from the CLI', link: '/cli/' }
49
+ { text: 'Overview & Installation', link: '/cli/' },
50
+ { text: 'Running the Server', link: '/cli/server' },
51
+ { text: 'Generating a Config (`init`)', link: '/cli/init' },
52
+ { text: 'Configuration File', link: '/cli/config' },
53
+ { text: 'HTTP API', link: '/cli/http-api' }
57
54
  ]
58
55
  },
59
56
  {
@@ -59,6 +59,7 @@ new DirSQL({
59
59
  tables?: TableDef[],
60
60
  ignore?: string[],
61
61
  config?: string,
62
+ extensions?: ExtensionSpec[], // [{ path: string, entrypoint?: string }]
62
63
  })
63
64
  ```
64
65
 
@@ -76,7 +77,7 @@ In Python, the constructor starts scanning in a background thread and returns im
76
77
  - `tables` -- List of `Table` definitions. Each defines a SQLite table, a glob pattern, and an extract function.
77
78
  - `ignore` -- Optional list of glob patterns. Files matching any ignore pattern are skipped regardless of table globs.
78
79
  - `config` -- Optional path to a `.dirsql.toml` config file. Its `[[table]]` entries are appended to any programmatic `tables`; its `[dirsql].ignore` patterns are appended to any explicit `ignore`; its optional `[dirsql].root` supplies the root directory when `root` is not passed explicitly; its `[[dirsql.extension]]` entries are appended to any programmatic `extensions`.
79
- - `extensions` -- Optional SQLite extensions to load onto the connection at startup, before any table DDL (enable → load → disable, so the SQL `load_extension()` function is never left exposed). Each entry pairs a shared-library `path` with an optional `entrypoint` init-symbol override (Python: `{ "path", "entrypoint"? }` dicts; Rust: `Extension { path, entrypoint }`). Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. Available in the Python and Rust SDKs; the TypeScript constructor parameter is tracked in [#230](https://github.com/thekevinscott/dirsql/issues/230). See [Loading extensions](../cli/config.md#loading-extensions).
80
+ - `extensions` -- Optional SQLite extensions to load onto the connection at startup, before any table DDL (enable → load → disable, so the SQL `load_extension()` function is never left exposed). Each entry pairs a shared-library `path` with an optional `entrypoint` init-symbol override (Python: `{ "path", "entrypoint"? }` dicts; Rust: `Extension { path, entrypoint }`; TypeScript: `{ path, entrypoint? }` objects). Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. Available in the Python, Rust, and TypeScript SDKs. See [Loading extensions](../cli/config.md#loading-extensions).
80
81
 
81
82
  ### Methods
82
83
 
@@ -162,7 +163,7 @@ JSON.stringify(db) // via db.toJSON()
162
163
 
163
164
  :::
164
165
 
165
- Returns the resolved construction state as a JSON-compatible value with fields `root`, `tables`, `ignore`, `persist`, `persist_path` (camelCase `persistPath` in TypeScript). Each table is `{ ddl, glob, strict }`. The Python and Rust snapshots also include `extensions` -- an array of `{ path, entrypoint }` (empty when none are configured); the TypeScript `toJSON` snapshot will gain it with [#230](https://github.com/thekevinscott/dirsql/issues/230). Excludes the original `config` path (already merged into `root` / `tables` / `ignore`), per-table `extract`, and per-table `name`. Available immediately after construction in Python and TypeScript; Rust's sync `build()` returns a ready instance.
166
+ Returns the resolved construction state as a JSON-compatible value with fields `root`, `tables`, `ignore`, `persist`, `persist_path` (camelCase `persistPath` in TypeScript). Each table is `{ ddl, glob, strict }`. All three SDK snapshots also include `extensions` -- an array of `{ path, entrypoint }` (empty when none are configured; `entrypoint` is `null` when no override was supplied). Excludes the original `config` path (already merged into `root` / `tables` / `ignore`), per-table `extract`, and per-table `name`. Available immediately after construction in Python and TypeScript; Rust's sync `build()` returns a ready instance.
166
167
 
167
168
  ---
168
169
 
@@ -239,7 +239,7 @@ def extract_meta(path):
239
239
 
240
240
  # Python must export a module-level `app`.
241
241
  app = DirSQL(
242
- root="papers", # required see "Set a root" below
242
+ root="papers", # optional; defaults to the current directory
243
243
  tables=[
244
244
  Table(
245
245
  ddl="CREATE TABLE papers (title TEXT, _path TEXT)",
@@ -269,7 +269,7 @@ import { readFileSync } from "node:fs";
269
269
  import { DirSQL } from "dirsql";
270
270
 
271
271
  export default new DirSQL({
272
- root: "papers", // required see "Set a root" below
272
+ root: "papers", // optional; defaults to the current directory
273
273
  tables: [
274
274
  {
275
275
  ddl: "CREATE TABLE papers (title TEXT, _path TEXT)",
@@ -285,7 +285,7 @@ const { readFileSync } = require("node:fs");
285
285
  const { DirSQL } = require("dirsql");
286
286
 
287
287
  module.exports = new DirSQL({
288
- root: "papers", // required see "Set a root" below
288
+ root: "papers", // optional; defaults to the current directory
289
289
  tables: [
290
290
  {
291
291
  ddl: "CREATE TABLE papers (title TEXT, _path TEXT)",
@@ -308,10 +308,15 @@ These apply to both the Python and JavaScript forms above.
308
308
  package) uses `module.exports = new DirSQL(...)`. Only the extension
309
309
  matters — the file can be named anything; `dirsql.config.{py,mjs,cjs}` is the
310
310
  suggested convention, not a requirement.
311
- - **Set a `root`.** Unlike TOML configs (which default the scan root to the
312
- config file's directory), native-language configs require an explicit `root`.
313
- Without one the Python launcher errors and the JavaScript launcher silently
314
- indexes nothing.
311
+ - **`root` defaults to the current directory.** A native-language config with
312
+ no `root` indexes the process's current working directory the directory you
313
+ ran `dirsql` from. (This differs from TOML configs, which default the scan
314
+ root to the config file's own directory.) Pass `root` explicitly to index
315
+ somewhere else.
316
+ - **No nested `config=`.** A native-language config builds its `DirSQL` from
317
+ `tables` and an optional `root`; it must not itself set `config=` to delegate
318
+ to another config file. `dirsql interpret` rejects such a config (a nested
319
+ config can't be represented in the handshake and would recurse).
315
320
  - **Install the launcher on your `PATH`.** To run your `extract`, the server
316
321
  spawns `dirsql interpret`, so the matching `dirsql` launcher must be installed
317
322
  and on your `PATH` — a global `pip`/`uv` install for `.py`, or `npm` for
@@ -20,7 +20,7 @@ Everything you need to run `dirsql` as a CLI lives in this section:
20
20
  - **[Configuration File](./config.md)** — the `.dirsql.toml` format and the
21
21
  `.py` / `.js` native-language alternative. Custom tables
22
22
  are defined through a config file; without one, the server runs in
23
- [zero-config mode](./server.md#zero-config-mode).
23
+ [zero-config mode](./server.md#defaults).
24
24
  - **[HTTP API](./http-api.md)** — the `POST /query` and `GET /events`
25
25
  endpoints, status codes, and event streaming.
26
26
 
@@ -0,0 +1,46 @@
1
+ import { expect, test } from '@playwright/test'
2
+
3
+ // Regression for #301: entering the CLI section must NOT replace the sidebar.
4
+ // The full set of sections has to stay visible on every page, including
5
+ // `/cli/*`, so navigation never loses its place.
6
+ const SECTIONS = ['Tutorials', 'How-to Guides', 'CLI', 'Reference']
7
+
8
+ async function sidebarSections(page: import('@playwright/test').Page) {
9
+ return await page.evaluate(() =>
10
+ Array.from(document.querySelectorAll('.VPSidebar .group .text'))
11
+ .map((el) => el.textContent?.trim())
12
+ .filter(Boolean)
13
+ )
14
+ }
15
+
16
+ for (const path of ['guide/tables.html', 'cli/index.html', 'cli/server.html']) {
17
+ test(`every section stays in the sidebar on ${path}`, async ({ page }) => {
18
+ await page.goto(`./${path}`)
19
+ await page.waitForSelector('.VPSidebar .group')
20
+ const sections = await sidebarSections(page)
21
+ for (const section of SECTIONS) {
22
+ expect(sections).toContain(section)
23
+ }
24
+ })
25
+ }
26
+
27
+ test('the CLI sidebar group lists all CLI subpages', async ({ page }) => {
28
+ await page.goto('./cli/index.html')
29
+ await page.waitForSelector('.VPSidebar .group')
30
+ const cliLinks = await page.evaluate(() => {
31
+ const groups = Array.from(document.querySelectorAll('.VPSidebar .group'))
32
+ const cli = groups.find(
33
+ (g) => g.querySelector('.text')?.textContent?.trim() === 'CLI'
34
+ )
35
+ return Array.from(cli?.querySelectorAll('a') ?? []).map(
36
+ (a) => new URL((a as HTMLAnchorElement).href).pathname
37
+ )
38
+ })
39
+ expect(cliLinks).toEqual([
40
+ '/dirsql/cli/',
41
+ '/dirsql/cli/server.html',
42
+ '/dirsql/cli/init.html',
43
+ '/dirsql/cli/config.html',
44
+ '/dirsql/cli/http-api.html'
45
+ ])
46
+ })
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import config from '../../.vitepress/config'
3
+
4
+ type SidebarItem = { text?: string; link?: string; items?: SidebarItem[] }
5
+
6
+ describe('vitepress config', () => {
7
+ it('has the expected site title and base path', () => {
8
+ expect(config.title).toBe('dirsql')
9
+ expect(config.base).toBe('/dirsql/')
10
+ })
11
+
12
+ // Regression guard: CLI docs live in their own `/cli/` section, not
13
+ // interleaved with the SDK how-to guides (see #179).
14
+ it('keeps CLI pages out of the How-to Guides group', () => {
15
+ const sidebar = config.themeConfig!.sidebar as Record<string, SidebarItem[]>
16
+ const howTo = sidebar['/'].find((group) => group.text === 'How-to Guides')
17
+ const links = (howTo!.items ?? []).map((item) => item.link)
18
+ expect(links).not.toContain('/guide/cli')
19
+ expect(links).not.toContain('/guide/init')
20
+ expect(links).not.toContain('/guide/config')
21
+ })
22
+
23
+ // Regression guard: the sidebar must never *replace* itself when entering a
24
+ // section. A path-scoped key (e.g. `/cli/`) swaps the whole tree out, which
25
+ // deletes the other sections and reads as the nav breaking (see #301). There
26
+ // must be exactly one global `/` sidebar.
27
+ it('has a single global sidebar with no replacing path-scoped keys', () => {
28
+ const sidebar = config.themeConfig!.sidebar as Record<string, SidebarItem[]>
29
+ expect(Object.keys(sidebar)).toEqual(['/'])
30
+ })
31
+
32
+ // The CLI section is self-contained: its group carries all CLI subpages,
33
+ // alongside (never instead of) the other sections (see #301).
34
+ it('shows every CLI subpage in a self-contained CLI group', () => {
35
+ const sidebar = config.themeConfig!.sidebar as Record<string, SidebarItem[]>
36
+ const groupTexts = sidebar['/'].map((group) => group.text)
37
+ expect(groupTexts).toEqual(['Tutorials', 'How-to Guides', 'CLI', 'Reference'])
38
+
39
+ const cli = sidebar['/'].find((group) => group.text === 'CLI')
40
+ const links = (cli!.items ?? []).map((item) => item.link)
41
+ expect(links).toEqual([
42
+ '/cli/',
43
+ '/cli/server',
44
+ '/cli/init',
45
+ '/cli/config',
46
+ '/cli/http-api'
47
+ ])
48
+ })
49
+ })
@@ -0,0 +1,6 @@
1
+ {
2
+ "command": "uv run python -m pytest tests/e2e/ -x -q",
3
+ "ran_at": 1782833107,
4
+ "exit_code": 0,
5
+ "commit": "07bd37825c22f9ad73153d70fc65c9e3c92f855e"
6
+ }
@@ -59,6 +59,7 @@ new DirSQL({
59
59
  tables?: TableDef[],
60
60
  ignore?: string[],
61
61
  config?: string,
62
+ extensions?: ExtensionSpec[], // [{ path: string, entrypoint?: string }]
62
63
  })
63
64
  ```
64
65
 
@@ -76,7 +77,7 @@ In Python, the constructor starts scanning in a background thread and returns im
76
77
  - `tables` -- List of `Table` definitions. Each defines a SQLite table, a glob pattern, and an extract function.
77
78
  - `ignore` -- Optional list of glob patterns. Files matching any ignore pattern are skipped regardless of table globs.
78
79
  - `config` -- Optional path to a `.dirsql.toml` config file. Its `[[table]]` entries are appended to any programmatic `tables`; its `[dirsql].ignore` patterns are appended to any explicit `ignore`; its optional `[dirsql].root` supplies the root directory when `root` is not passed explicitly; its `[[dirsql.extension]]` entries are appended to any programmatic `extensions`.
79
- - `extensions` -- Optional SQLite extensions to load onto the connection at startup, before any table DDL (enable → load → disable, so the SQL `load_extension()` function is never left exposed). Each entry pairs a shared-library `path` with an optional `entrypoint` init-symbol override (Python: `{ "path", "entrypoint"? }` dicts; Rust: `Extension { path, entrypoint }`). Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. Available in the Python and Rust SDKs; the TypeScript constructor parameter is tracked in [#230](https://github.com/thekevinscott/dirsql/issues/230). See [Loading extensions](../cli/config.md#loading-extensions).
80
+ - `extensions` -- Optional SQLite extensions to load onto the connection at startup, before any table DDL (enable → load → disable, so the SQL `load_extension()` function is never left exposed). Each entry pairs a shared-library `path` with an optional `entrypoint` init-symbol override (Python: `{ "path", "entrypoint"? }` dicts; Rust: `Extension { path, entrypoint }`; TypeScript: `{ path, entrypoint? }` objects). Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. Available in the Python, Rust, and TypeScript SDKs. See [Loading extensions](../cli/config.md#loading-extensions).
80
81
 
81
82
  ### Methods
82
83
 
@@ -162,7 +163,7 @@ JSON.stringify(db) // via db.toJSON()
162
163
 
163
164
  :::
164
165
 
165
- Returns the resolved construction state as a JSON-compatible value with fields `root`, `tables`, `ignore`, `persist`, `persist_path` (camelCase `persistPath` in TypeScript). Each table is `{ ddl, glob, strict }`. The Python and Rust snapshots also include `extensions` -- an array of `{ path, entrypoint }` (empty when none are configured); the TypeScript `toJSON` snapshot will gain it with [#230](https://github.com/thekevinscott/dirsql/issues/230). Excludes the original `config` path (already merged into `root` / `tables` / `ignore`), per-table `extract`, and per-table `name`. Available immediately after construction in Python and TypeScript; Rust's sync `build()` returns a ready instance.
166
+ Returns the resolved construction state as a JSON-compatible value with fields `root`, `tables`, `ignore`, `persist`, `persist_path` (camelCase `persistPath` in TypeScript). Each table is `{ ddl, glob, strict }`. All three SDK snapshots also include `extensions` -- an array of `{ path, entrypoint }` (empty when none are configured; `entrypoint` is `null` when no override was supplied). Excludes the original `config` path (already merged into `root` / `tables` / `ignore`), per-table `extract`, and per-table `name`. Available immediately after construction in Python and TypeScript; Rust's sync `build()` returns a ready instance.
166
167
 
167
168
  ---
168
169