@deepagents/text2sql 7.1.1 → 7.1.3

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 (48) hide show
  1. package/README.md +2 -4
  2. package/dist/index.js +385 -520
  3. package/dist/index.js.map +4 -4
  4. package/dist/lib/adapters/bigquery/constraint.bigquery.grounding.d.ts.map +1 -1
  5. package/dist/lib/adapters/bigquery/index.js +14 -17
  6. package/dist/lib/adapters/bigquery/index.js.map +2 -2
  7. package/dist/lib/adapters/bigquery/table.bigquery.grounding.d.ts +2 -2
  8. package/dist/lib/adapters/bigquery/table.bigquery.grounding.d.ts.map +1 -1
  9. package/dist/lib/adapters/clickhouse/clickhouse.sql-policy.d.ts.map +1 -1
  10. package/dist/lib/adapters/clickhouse/index.js +13 -9
  11. package/dist/lib/adapters/clickhouse/index.js.map +3 -3
  12. package/dist/lib/adapters/duckdb/duckdb.d.ts.map +1 -1
  13. package/dist/lib/adapters/duckdb/duckdb.sql-policy.d.ts.map +1 -1
  14. package/dist/lib/adapters/duckdb/index.js +12 -4
  15. package/dist/lib/adapters/duckdb/index.js.map +4 -4
  16. package/dist/lib/adapters/groundings/index.js +2 -2
  17. package/dist/lib/adapters/groundings/index.js.map +2 -2
  18. package/dist/lib/adapters/groundings/table.grounding.d.ts +2 -2
  19. package/dist/lib/adapters/groundings/table.grounding.d.ts.map +1 -1
  20. package/dist/lib/adapters/mysql/index.js +2 -2
  21. package/dist/lib/adapters/mysql/index.js.map +2 -2
  22. package/dist/lib/adapters/postgres/column-values.postgres.grounding.d.ts.map +1 -1
  23. package/dist/lib/adapters/postgres/index.js +17 -17
  24. package/dist/lib/adapters/postgres/index.js.map +2 -2
  25. package/dist/lib/adapters/spreadsheet/index.js +19 -20
  26. package/dist/lib/adapters/spreadsheet/index.js.map +2 -2
  27. package/dist/lib/adapters/spreadsheet/spreadsheet.d.ts.map +1 -1
  28. package/dist/lib/adapters/sqlite/index.js +2 -2
  29. package/dist/lib/adapters/sqlite/index.js.map +2 -2
  30. package/dist/lib/adapters/sqlserver/index.js +2 -2
  31. package/dist/lib/adapters/sqlserver/index.js.map +2 -2
  32. package/dist/lib/fs/mssql/mssql-fs.d.ts +3 -2
  33. package/dist/lib/fs/mssql/mssql-fs.d.ts.map +1 -1
  34. package/dist/lib/fs/mssql/sqlserver-schema-lock.d.ts +14 -0
  35. package/dist/lib/fs/mssql/sqlserver-schema-lock.d.ts.map +1 -0
  36. package/dist/lib/fs/postgres/postgres-fs.d.ts +3 -2
  37. package/dist/lib/fs/postgres/postgres-fs.d.ts.map +1 -1
  38. package/dist/lib/fs/sqlite/sqlite-fs.d.ts.map +1 -1
  39. package/dist/lib/synthesis/decorators/filtered-producer.d.ts.map +1 -1
  40. package/dist/lib/synthesis/index.js +3 -3
  41. package/dist/lib/synthesis/index.js.map +2 -2
  42. package/package.json +23 -9
  43. package/dist/eval/src/evals/models.d.ts +0 -12
  44. package/dist/eval/src/evals/models.d.ts.map +0 -1
  45. package/dist/eval/src/evals/sql-create-context/sql-create-context.eval.d.ts +0 -2
  46. package/dist/eval/src/evals/sql-create-context/sql-create-context.eval.d.ts.map +0 -1
  47. package/dist/scripts/scripts/bigquery-test.d.ts +0 -2
  48. package/dist/scripts/scripts/bigquery-test.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1545,6 +1545,7 @@ function hashConfig(config) {
1545
1545
  }
1546
1546
 
1547
1547
  // packages/text2sql/src/lib/fs/sqlite/sqlite-fs.ts
1548
+ import { transaction } from "@zukhruf/sqlite-transaction";
1548
1549
  import * as path2 from "node:path";
1549
1550
  import { DatabaseSync } from "node:sqlite";
1550
1551
  import z6 from "zod";
@@ -1583,7 +1584,7 @@ var chunkRow = z6.object({
1583
1584
  var dataRow = chunkRow.pick({ data: true });
1584
1585
  var SqliteFs = class {
1585
1586
  #db;
1586
- #statements = /* @__PURE__ */ new Map();
1587
+ #sql;
1587
1588
  #chunkSize;
1588
1589
  #root;
1589
1590
  constructor(options) {
@@ -1592,25 +1593,18 @@ var SqliteFs = class {
1592
1593
  this.#root = normalizedRoot === "/" ? "" : normalizedRoot;
1593
1594
  this.#db = new DatabaseSync(options.dbPath);
1594
1595
  this.#db.exec(ddl_sqlite_fs_default);
1595
- const rootSlashExists = this.#stmt(
1596
- "SELECT 1 FROM fs_entries WHERE path = ?"
1597
- ).get("/");
1596
+ this.#sql = this.#db.createTagStore();
1597
+ const rootSlashExists = this.#sql.get`SELECT 1 FROM fs_entries WHERE path = '/'`;
1598
1598
  if (!rootSlashExists) {
1599
- this.#stmt(
1600
- `INSERT INTO fs_entries (path, type, mode, size, mtime)
1601
- VALUES ('/', 'directory', 493, 0, ?)`
1602
- ).run(Date.now());
1599
+ this.#sql.run`INSERT INTO fs_entries (path, type, mode, size, mtime)
1600
+ VALUES ('/', 'directory', 493, 0, ${Date.now()})`;
1603
1601
  }
1604
1602
  if (this.#root) {
1605
1603
  this.#createParentDirs(this.#root);
1606
- const rootExists = this.#stmt(
1607
- "SELECT 1 FROM fs_entries WHERE path = ?"
1608
- ).get(this.#root);
1604
+ const rootExists = this.#sql.get`SELECT 1 FROM fs_entries WHERE path = ${this.#root}`;
1609
1605
  if (!rootExists) {
1610
- this.#stmt(
1611
- `INSERT INTO fs_entries (path, type, mode, size, mtime)
1612
- VALUES (?, 'directory', 493, 0, ?)`
1613
- ).run(this.#root, Date.now());
1606
+ this.#sql.run`INSERT INTO fs_entries (path, type, mode, size, mtime)
1607
+ VALUES (${this.#root}, 'directory', 493, 0, ${Date.now()})`;
1614
1608
  }
1615
1609
  }
1616
1610
  }
@@ -1623,25 +1617,13 @@ var SqliteFs = class {
1623
1617
  let currentPath = "/";
1624
1618
  for (let i = 0; i < segments.length - 1; i++) {
1625
1619
  currentPath = path2.posix.join(currentPath, segments[i]);
1626
- const exists = this.#stmt("SELECT 1 FROM fs_entries WHERE path = ?").get(
1627
- currentPath
1628
- );
1620
+ const exists = this.#sql.get`SELECT 1 FROM fs_entries WHERE path = ${currentPath}`;
1629
1621
  if (!exists) {
1630
- this.#stmt(
1631
- `INSERT INTO fs_entries (path, type, mode, size, mtime)
1632
- VALUES (?, 'directory', 493, 0, ?)`
1633
- ).run(currentPath, Date.now());
1622
+ this.#sql.run`INSERT INTO fs_entries (path, type, mode, size, mtime)
1623
+ VALUES (${currentPath}, 'directory', 493, 0, ${Date.now()})`;
1634
1624
  }
1635
1625
  }
1636
1626
  }
1637
- #stmt(sql) {
1638
- let stmt = this.#statements.get(sql);
1639
- if (!stmt) {
1640
- stmt = this.#db.prepare(sql);
1641
- this.#statements.set(sql, stmt);
1642
- }
1643
- return stmt;
1644
- }
1645
1627
  #normalizeRoot(root) {
1646
1628
  return path2.posix.resolve("/", root.trim());
1647
1629
  }
@@ -1666,20 +1648,6 @@ var SqliteFs = class {
1666
1648
  }
1667
1649
  return p;
1668
1650
  }
1669
- #useTransaction(fn) {
1670
- this.#db.exec("BEGIN TRANSACTION");
1671
- try {
1672
- const result = fn();
1673
- this.#db.exec("COMMIT");
1674
- return result;
1675
- } catch (error) {
1676
- try {
1677
- this.#db.exec("ROLLBACK");
1678
- } catch {
1679
- }
1680
- throw error;
1681
- }
1682
- }
1683
1651
  #normalizePath(p) {
1684
1652
  return path2.posix.resolve("/", p);
1685
1653
  }
@@ -1691,43 +1659,33 @@ var SqliteFs = class {
1691
1659
  const parent = this.#dirname(filePath);
1692
1660
  const rootPath = this.#root || "/";
1693
1661
  if (parent === rootPath || parent === "/") return;
1694
- const entry = typeRow.optional().parse(
1695
- this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(parent)
1696
- );
1662
+ const entry = typeRow.optional().parse(this.#sql.get`SELECT type FROM fs_entries WHERE path = ${parent}`);
1697
1663
  if (!entry) {
1698
1664
  this.#ensureParentExists(parent);
1699
- this.#stmt(
1700
- `INSERT INTO fs_entries (path, type, mode, size, mtime)
1701
- VALUES (?, 'directory', 493, 0, ?)`
1702
- ).run(parent, Date.now());
1665
+ this.#sql.run`INSERT INTO fs_entries (path, type, mode, size, mtime)
1666
+ VALUES (${parent}, 'directory', 493, 0, ${Date.now()})`;
1703
1667
  } else if (entry.type !== "directory") {
1704
1668
  throw new Error(`mkdir: parent is not a directory: ${parent}`);
1705
1669
  }
1706
1670
  }
1707
1671
  #writeChunks(filePath, content) {
1708
- this.#stmt("DELETE FROM fs_chunks WHERE path = ?").run(filePath);
1672
+ this.#sql.run`DELETE FROM fs_chunks WHERE path = ${filePath}`;
1709
1673
  for (let i = 0; i < content.length; i += this.#chunkSize) {
1710
1674
  const chunk = content.slice(
1711
1675
  i,
1712
1676
  Math.min(i + this.#chunkSize, content.length)
1713
1677
  );
1714
- this.#stmt(
1715
- "INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (?, ?, ?)"
1716
- ).run(filePath, Math.floor(i / this.#chunkSize), chunk);
1678
+ this.#sql.run`INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (${filePath}, ${Math.floor(i / this.#chunkSize)}, ${chunk})`;
1717
1679
  }
1718
1680
  }
1719
1681
  #chunks(filePath) {
1720
1682
  return z6.array(chunkRow).parse(
1721
- this.#stmt("SELECT chunkIndex, data FROM fs_chunks WHERE path = ?").all(
1722
- filePath
1723
- )
1683
+ this.#sql.all`SELECT chunkIndex, data FROM fs_chunks WHERE path = ${filePath}`
1724
1684
  );
1725
1685
  }
1726
1686
  #readChunks(filePath) {
1727
1687
  const rows = z6.array(dataRow).parse(
1728
- this.#stmt(
1729
- "SELECT data FROM fs_chunks WHERE path = ? ORDER BY chunkIndex"
1730
- ).all(filePath)
1688
+ this.#sql.all`SELECT data FROM fs_chunks WHERE path = ${filePath} ORDER BY chunkIndex`
1731
1689
  );
1732
1690
  if (rows.length === 0) {
1733
1691
  return new Uint8Array(0);
@@ -1746,9 +1704,7 @@ var SqliteFs = class {
1746
1704
  throw new Error(`readFile: circular symlink: ${p}`);
1747
1705
  }
1748
1706
  const entry = linkRow.optional().parse(
1749
- this.#stmt(
1750
- "SELECT type, symlinkTarget FROM fs_entries WHERE path = ?"
1751
- ).get(p)
1707
+ this.#sql.get`SELECT type, symlinkTarget FROM fs_entries WHERE path = ${p}`
1752
1708
  );
1753
1709
  if (!entry) {
1754
1710
  throw new Error(`ENOENT: no such file or directory: ${p}`);
@@ -1776,7 +1732,7 @@ var SqliteFs = class {
1776
1732
  const prefixed = this.#prefixPath(normalized);
1777
1733
  const resolved = this.#resolveSymlink(prefixed);
1778
1734
  const entry = typeRow.optional().parse(
1779
- this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(resolved)
1735
+ this.#sql.get`SELECT type FROM fs_entries WHERE path = ${resolved}`
1780
1736
  );
1781
1737
  if (!entry) {
1782
1738
  throw new Error(`ENOENT: no such file or directory: ${filePath}`);
@@ -1793,7 +1749,7 @@ var SqliteFs = class {
1793
1749
  const prefixed = this.#prefixPath(normalized);
1794
1750
  const resolved = this.#resolveSymlink(prefixed);
1795
1751
  const entry = typeRow.optional().parse(
1796
- this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(resolved)
1752
+ this.#sql.get`SELECT type FROM fs_entries WHERE path = ${resolved}`
1797
1753
  );
1798
1754
  if (!entry) {
1799
1755
  throw new Error(`ENOENT: no such file or directory: ${filePath}`);
@@ -1808,63 +1764,61 @@ var SqliteFs = class {
1808
1764
  const prefixed = this.#prefixPath(normalized);
1809
1765
  const encoding = typeof options === "string" ? options : options?.encoding;
1810
1766
  const data = this.#toUint8Array(content, encoding);
1811
- this.#useTransaction(() => {
1812
- this.#ensureParentExists(prefixed);
1813
- this.#stmt(
1814
- `INSERT INTO fs_entries (path, type, mode, size, mtime)
1815
- VALUES (?, 'file', 420, ?, ?)
1767
+ transaction(
1768
+ this.#db,
1769
+ () => {
1770
+ this.#ensureParentExists(prefixed);
1771
+ this.#sql.run`INSERT INTO fs_entries (path, type, mode, size, mtime)
1772
+ VALUES (${prefixed}, 'file', 420, ${data.length}, ${Date.now()})
1816
1773
  ON CONFLICT(path) DO UPDATE SET
1817
1774
  type = 'file',
1818
1775
  size = excluded.size,
1819
- mtime = excluded.mtime`
1820
- ).run(prefixed, data.length, Date.now());
1821
- this.#writeChunks(prefixed, data);
1822
- });
1776
+ mtime = excluded.mtime`;
1777
+ this.#writeChunks(prefixed, data);
1778
+ },
1779
+ { mode: "deferred" }
1780
+ );
1823
1781
  }
1824
1782
  async appendFile(filePath, content, options) {
1825
1783
  const normalized = this.#normalizePath(filePath);
1826
1784
  const prefixed = this.#prefixPath(normalized);
1827
1785
  const encoding = typeof options === "string" ? options : options?.encoding;
1828
1786
  const newData = this.#toUint8Array(content, encoding);
1829
- this.#useTransaction(() => {
1830
- this.#ensureParentExists(prefixed);
1831
- const entry = typeRow.optional().parse(
1832
- this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(
1833
- prefixed
1834
- )
1835
- );
1836
- if (entry && entry.type !== "file") {
1837
- throw new Error(`appendFile: not a file: ${filePath}`);
1838
- }
1839
- const existing = entry ? this.#readChunks(prefixed) : new Uint8Array(0);
1840
- const combined = new Uint8Array(existing.length + newData.length);
1841
- combined.set(existing, 0);
1842
- combined.set(newData, existing.length);
1843
- this.#stmt(
1844
- `INSERT INTO fs_entries (path, type, mode, size, mtime)
1845
- VALUES (?, 'file', 420, ?, ?)
1787
+ transaction(
1788
+ this.#db,
1789
+ () => {
1790
+ this.#ensureParentExists(prefixed);
1791
+ const entry = typeRow.optional().parse(
1792
+ this.#sql.get`SELECT type FROM fs_entries WHERE path = ${prefixed}`
1793
+ );
1794
+ if (entry && entry.type !== "file") {
1795
+ throw new Error(`appendFile: not a file: ${filePath}`);
1796
+ }
1797
+ const existing = entry ? this.#readChunks(prefixed) : new Uint8Array(0);
1798
+ const combined = new Uint8Array(existing.length + newData.length);
1799
+ combined.set(existing, 0);
1800
+ combined.set(newData, existing.length);
1801
+ this.#sql.run`INSERT INTO fs_entries (path, type, mode, size, mtime)
1802
+ VALUES (${prefixed}, 'file', 420, ${combined.length}, ${Date.now()})
1846
1803
  ON CONFLICT(path) DO UPDATE SET
1847
1804
  size = excluded.size,
1848
- mtime = excluded.mtime`
1849
- ).run(prefixed, combined.length, Date.now());
1850
- this.#writeChunks(prefixed, combined);
1851
- });
1805
+ mtime = excluded.mtime`;
1806
+ this.#writeChunks(prefixed, combined);
1807
+ },
1808
+ { mode: "deferred" }
1809
+ );
1852
1810
  }
1853
1811
  async exists(filePath) {
1854
1812
  const normalized = this.#normalizePath(filePath);
1855
1813
  const prefixed = this.#prefixPath(normalized);
1856
- const row = this.#stmt("SELECT 1 FROM fs_entries WHERE path = ?").get(
1857
- prefixed
1858
- );
1814
+ const row = this.#sql.get`SELECT 1 FROM fs_entries WHERE path = ${prefixed}`;
1859
1815
  return row !== void 0;
1860
1816
  }
1861
1817
  async stat(filePath) {
1862
1818
  const normalized = this.#normalizePath(filePath);
1863
1819
  const prefixed = this.#prefixPath(normalized);
1864
1820
  const resolved = this.#resolveSymlink(prefixed);
1865
- const entry = entryRow.optional().parse(
1866
- this.#stmt("SELECT * FROM fs_entries WHERE path = ?").get(resolved)
1867
- );
1821
+ const entry = entryRow.optional().parse(this.#sql.get`SELECT * FROM fs_entries WHERE path = ${resolved}`);
1868
1822
  if (!entry) {
1869
1823
  throw new Error(`ENOENT: no such file or directory: ${filePath}`);
1870
1824
  }
@@ -1881,9 +1835,7 @@ var SqliteFs = class {
1881
1835
  async lstat(filePath) {
1882
1836
  const normalized = this.#normalizePath(filePath);
1883
1837
  const prefixed = this.#prefixPath(normalized);
1884
- const entry = entryRow.optional().parse(
1885
- this.#stmt("SELECT * FROM fs_entries WHERE path = ?").get(prefixed)
1886
- );
1838
+ const entry = entryRow.optional().parse(this.#sql.get`SELECT * FROM fs_entries WHERE path = ${prefixed}`);
1887
1839
  if (!entry) {
1888
1840
  throw new Error(`ENOENT: no such file or directory: ${filePath}`);
1889
1841
  }
@@ -1900,7 +1852,7 @@ var SqliteFs = class {
1900
1852
  const normalized = this.#normalizePath(dirPath);
1901
1853
  const prefixed = this.#prefixPath(normalized);
1902
1854
  const existing = typeRow.optional().parse(
1903
- this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(prefixed)
1855
+ this.#sql.get`SELECT type FROM fs_entries WHERE path = ${prefixed}`
1904
1856
  );
1905
1857
  if (existing) {
1906
1858
  if (options?.recursive) {
@@ -1908,54 +1860,50 @@ var SqliteFs = class {
1908
1860
  }
1909
1861
  throw new Error(`EEXIST: file already exists: ${dirPath}`);
1910
1862
  }
1911
- this.#useTransaction(() => {
1912
- if (options?.recursive) {
1913
- const rootPath = this.#root || "/";
1914
- const relativePath = path2.posix.relative(rootPath, prefixed);
1915
- const segments = relativePath.split("/").filter(Boolean);
1916
- let currentPath = rootPath;
1917
- for (const segment of segments) {
1918
- currentPath = path2.posix.join(currentPath, segment);
1919
- const exists = typeRow.optional().parse(
1920
- this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(
1921
- currentPath
1922
- )
1863
+ transaction(
1864
+ this.#db,
1865
+ () => {
1866
+ if (options?.recursive) {
1867
+ const rootPath = this.#root || "/";
1868
+ const relativePath = path2.posix.relative(rootPath, prefixed);
1869
+ const segments = relativePath.split("/").filter(Boolean);
1870
+ let currentPath = rootPath;
1871
+ for (const segment of segments) {
1872
+ currentPath = path2.posix.join(currentPath, segment);
1873
+ const exists = typeRow.optional().parse(
1874
+ this.#sql.get`SELECT type FROM fs_entries WHERE path = ${currentPath}`
1875
+ );
1876
+ if (!exists) {
1877
+ this.#sql.run`INSERT INTO fs_entries (path, type, mode, size, mtime)
1878
+ VALUES (${currentPath}, 'directory', 493, 0, ${Date.now()})`;
1879
+ } else if (exists.type !== "directory") {
1880
+ throw new Error(`mkdir: not a directory: ${currentPath}`);
1881
+ }
1882
+ }
1883
+ } else {
1884
+ const parent = this.#dirname(prefixed);
1885
+ const parentEntry = typeRow.optional().parse(
1886
+ this.#sql.get`SELECT type FROM fs_entries WHERE path = ${parent}`
1923
1887
  );
1924
- if (!exists) {
1925
- this.#stmt(
1926
- `INSERT INTO fs_entries (path, type, mode, size, mtime)
1927
- VALUES (?, 'directory', 493, 0, ?)`
1928
- ).run(currentPath, Date.now());
1929
- } else if (exists.type !== "directory") {
1930
- throw new Error(`mkdir: not a directory: ${currentPath}`);
1888
+ if (!parentEntry) {
1889
+ throw new Error(`mkdir: parent does not exist: ${parent}`);
1931
1890
  }
1891
+ if (parentEntry.type !== "directory") {
1892
+ throw new Error(`mkdir: parent is not a directory: ${parent}`);
1893
+ }
1894
+ this.#sql.run`INSERT INTO fs_entries (path, type, mode, size, mtime)
1895
+ VALUES (${prefixed}, 'directory', 493, 0, ${Date.now()})`;
1932
1896
  }
1933
- } else {
1934
- const parent = this.#dirname(prefixed);
1935
- const parentEntry = typeRow.optional().parse(
1936
- this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(
1937
- parent
1938
- )
1939
- );
1940
- if (!parentEntry) {
1941
- throw new Error(`mkdir: parent does not exist: ${parent}`);
1942
- }
1943
- if (parentEntry.type !== "directory") {
1944
- throw new Error(`mkdir: parent is not a directory: ${parent}`);
1945
- }
1946
- this.#stmt(
1947
- `INSERT INTO fs_entries (path, type, mode, size, mtime)
1948
- VALUES (?, 'directory', 493, 0, ?)`
1949
- ).run(prefixed, Date.now());
1950
- }
1951
- });
1897
+ },
1898
+ { mode: "deferred" }
1899
+ );
1952
1900
  }
1953
1901
  async readdir(dirPath) {
1954
1902
  const normalized = this.#normalizePath(dirPath);
1955
1903
  const prefixed = this.#prefixPath(normalized);
1956
1904
  const resolved = this.#resolveSymlink(prefixed);
1957
1905
  const entry = typeRow.optional().parse(
1958
- this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(resolved)
1906
+ this.#sql.get`SELECT type FROM fs_entries WHERE path = ${resolved}`
1959
1907
  );
1960
1908
  if (!entry) {
1961
1909
  throw new Error(`ENOENT: no such file or directory: ${dirPath}`);
@@ -1965,12 +1913,10 @@ var SqliteFs = class {
1965
1913
  }
1966
1914
  const prefix = resolved === "/" ? "/" : resolved + "/";
1967
1915
  const rows = z6.array(pathRow).parse(
1968
- this.#stmt(
1969
- `SELECT path FROM fs_entries
1970
- WHERE path LIKE ? || '%'
1971
- AND path != ?
1972
- AND path NOT LIKE ? || '%/%'`
1973
- ).all(prefix, resolved, prefix)
1916
+ this.#sql.all`SELECT path FROM fs_entries
1917
+ WHERE path LIKE ${prefix} || '%'
1918
+ AND path != ${resolved}
1919
+ AND path NOT LIKE ${prefix} || '%/%'`
1974
1920
  );
1975
1921
  return rows.map((row) => path2.posix.basename(row.path));
1976
1922
  }
@@ -1979,7 +1925,7 @@ var SqliteFs = class {
1979
1925
  const prefixed = this.#prefixPath(normalized);
1980
1926
  const resolved = this.#resolveSymlink(prefixed);
1981
1927
  const entry = typeRow.optional().parse(
1982
- this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(resolved)
1928
+ this.#sql.get`SELECT type FROM fs_entries WHERE path = ${resolved}`
1983
1929
  );
1984
1930
  if (!entry) {
1985
1931
  throw new Error(`ENOENT: no such file or directory: ${dirPath}`);
@@ -1989,12 +1935,10 @@ var SqliteFs = class {
1989
1935
  }
1990
1936
  const prefix = resolved === "/" ? "/" : resolved + "/";
1991
1937
  const rows = z6.array(pathTypeRow).parse(
1992
- this.#stmt(
1993
- `SELECT path, type FROM fs_entries
1994
- WHERE path LIKE ? || '%'
1995
- AND path != ?
1996
- AND path NOT LIKE ? || '%/%'`
1997
- ).all(prefix, resolved, prefix)
1938
+ this.#sql.all`SELECT path, type FROM fs_entries
1939
+ WHERE path LIKE ${prefix} || '%'
1940
+ AND path != ${resolved}
1941
+ AND path NOT LIKE ${prefix} || '%/%'`
1998
1942
  );
1999
1943
  return rows.map((row) => ({
2000
1944
  name: path2.posix.basename(row.path),
@@ -2007,7 +1951,7 @@ var SqliteFs = class {
2007
1951
  const normalized = this.#normalizePath(filePath);
2008
1952
  const prefixed = this.#prefixPath(normalized);
2009
1953
  const entry = typeRow.optional().parse(
2010
- this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(prefixed)
1954
+ this.#sql.get`SELECT type FROM fs_entries WHERE path = ${prefixed}`
2011
1955
  );
2012
1956
  if (!entry) {
2013
1957
  if (options?.force) {
@@ -2015,21 +1959,21 @@ var SqliteFs = class {
2015
1959
  }
2016
1960
  throw new Error(`ENOENT: no such file or directory: ${filePath}`);
2017
1961
  }
2018
- this.#useTransaction(() => {
2019
- if (entry.type === "directory") {
2020
- const children = this.#stmt(
2021
- `SELECT 1 FROM fs_entries WHERE path LIKE ? || '/%' LIMIT 1`
2022
- ).get(prefixed);
2023
- if (children && !options?.recursive) {
2024
- throw new Error(`ENOTEMPTY: directory not empty: ${filePath}`);
1962
+ transaction(
1963
+ this.#db,
1964
+ () => {
1965
+ if (entry.type === "directory") {
1966
+ const children = this.#sql.get`SELECT 1 FROM fs_entries WHERE path LIKE ${prefixed} || '/%' LIMIT 1`;
1967
+ if (children && !options?.recursive) {
1968
+ throw new Error(`ENOTEMPTY: directory not empty: ${filePath}`);
1969
+ }
1970
+ this.#sql.run`DELETE FROM fs_entries WHERE path = ${prefixed} OR path LIKE ${prefixed} || '/%'`;
1971
+ } else {
1972
+ this.#sql.run`DELETE FROM fs_entries WHERE path = ${prefixed}`;
2025
1973
  }
2026
- this.#stmt(
2027
- `DELETE FROM fs_entries WHERE path = ? OR path LIKE ? || '/%'`
2028
- ).run(prefixed, prefixed);
2029
- } else {
2030
- this.#stmt("DELETE FROM fs_entries WHERE path = ?").run(prefixed);
2031
- }
2032
- });
1974
+ },
1975
+ { mode: "deferred" }
1976
+ );
2033
1977
  }
2034
1978
  async cp(src, dest, options) {
2035
1979
  const srcNormalized = this.#normalizePath(src);
@@ -2037,7 +1981,7 @@ var SqliteFs = class {
2037
1981
  const srcPrefixed = this.#prefixPath(srcNormalized);
2038
1982
  const destPrefixed = this.#prefixPath(destNormalized);
2039
1983
  const srcEntry = entryRow.optional().parse(
2040
- this.#stmt("SELECT * FROM fs_entries WHERE path = ?").get(srcPrefixed)
1984
+ this.#sql.get`SELECT * FROM fs_entries WHERE path = ${srcPrefixed}`
2041
1985
  );
2042
1986
  if (!srcEntry) {
2043
1987
  throw new Error(`ENOENT: no such file or directory: ${src}`);
@@ -2045,60 +1989,40 @@ var SqliteFs = class {
2045
1989
  if (srcEntry.type === "directory" && !options?.recursive) {
2046
1990
  throw new Error(`cp: -r not specified; omitting directory: ${src}`);
2047
1991
  }
2048
- this.#useTransaction(() => {
2049
- this.#ensureParentExists(destPrefixed);
2050
- if (srcEntry.type === "directory") {
2051
- const allEntries = z6.array(entryRow).parse(
2052
- this.#stmt(
2053
- `SELECT * FROM fs_entries WHERE path = ? OR path LIKE ? || '/%'`
2054
- ).all(srcPrefixed, srcPrefixed)
2055
- );
2056
- for (const entry of allEntries) {
2057
- const relativePath = path2.posix.relative(srcPrefixed, entry.path);
2058
- const newPath = path2.posix.join(destPrefixed, relativePath);
2059
- this.#stmt(
2060
- `INSERT OR REPLACE INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
2061
- VALUES (?, ?, ?, ?, ?, ?)`
2062
- ).run(
2063
- newPath,
2064
- entry.type,
2065
- entry.mode,
2066
- entry.size,
2067
- Date.now(),
2068
- entry.symlinkTarget
1992
+ transaction(
1993
+ this.#db,
1994
+ () => {
1995
+ this.#ensureParentExists(destPrefixed);
1996
+ if (srcEntry.type === "directory") {
1997
+ const allEntries = z6.array(entryRow).parse(
1998
+ this.#sql.all`SELECT * FROM fs_entries WHERE path = ${srcPrefixed} OR path LIKE ${srcPrefixed} || '/%'`
2069
1999
  );
2070
- if (entry.type === "file") {
2071
- const chunks = this.#chunks(entry.path);
2072
- for (const chunk of chunks) {
2073
- this.#stmt(
2074
- "INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (?, ?, ?)"
2075
- ).run(newPath, chunk.chunkIndex, chunk.data);
2000
+ for (const entry of allEntries) {
2001
+ const relativePath = path2.posix.relative(srcPrefixed, entry.path);
2002
+ const newPath = path2.posix.join(destPrefixed, relativePath);
2003
+ this.#sql.run`INSERT OR REPLACE INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
2004
+ VALUES (${newPath}, ${entry.type}, ${entry.mode}, ${entry.size}, ${Date.now()}, ${entry.symlinkTarget})`;
2005
+ if (entry.type === "file") {
2006
+ const chunks = this.#chunks(entry.path);
2007
+ for (const chunk of chunks) {
2008
+ this.#sql.run`INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (${newPath}, ${chunk.chunkIndex}, ${chunk.data})`;
2009
+ }
2076
2010
  }
2077
2011
  }
2078
- }
2079
- } else {
2080
- this.#stmt(
2081
- `INSERT OR REPLACE INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
2082
- VALUES (?, ?, ?, ?, ?, ?)`
2083
- ).run(
2084
- destPrefixed,
2085
- srcEntry.type,
2086
- srcEntry.mode,
2087
- srcEntry.size,
2088
- Date.now(),
2089
- srcEntry.symlinkTarget
2090
- );
2091
- if (srcEntry.type === "file") {
2092
- const chunks = this.#chunks(srcPrefixed);
2093
- this.#stmt("DELETE FROM fs_chunks WHERE path = ?").run(destPrefixed);
2094
- for (const chunk of chunks) {
2095
- this.#stmt(
2096
- "INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (?, ?, ?)"
2097
- ).run(destPrefixed, chunk.chunkIndex, chunk.data);
2012
+ } else {
2013
+ this.#sql.run`INSERT OR REPLACE INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
2014
+ VALUES (${destPrefixed}, ${srcEntry.type}, ${srcEntry.mode}, ${srcEntry.size}, ${Date.now()}, ${srcEntry.symlinkTarget})`;
2015
+ if (srcEntry.type === "file") {
2016
+ const chunks = this.#chunks(srcPrefixed);
2017
+ this.#sql.run`DELETE FROM fs_chunks WHERE path = ${destPrefixed}`;
2018
+ for (const chunk of chunks) {
2019
+ this.#sql.run`INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (${destPrefixed}, ${chunk.chunkIndex}, ${chunk.data})`;
2020
+ }
2098
2021
  }
2099
2022
  }
2100
- }
2101
- });
2023
+ },
2024
+ { mode: "deferred" }
2025
+ );
2102
2026
  }
2103
2027
  async mv(src, dest) {
2104
2028
  const srcNormalized = this.#normalizePath(src);
@@ -2106,83 +2030,59 @@ var SqliteFs = class {
2106
2030
  const srcPrefixed = this.#prefixPath(srcNormalized);
2107
2031
  const destPrefixed = this.#prefixPath(destNormalized);
2108
2032
  const srcEntry = entryRow.optional().parse(
2109
- this.#stmt("SELECT * FROM fs_entries WHERE path = ?").get(srcPrefixed)
2033
+ this.#sql.get`SELECT * FROM fs_entries WHERE path = ${srcPrefixed}`
2110
2034
  );
2111
2035
  if (!srcEntry) {
2112
2036
  throw new Error(`ENOENT: no such file or directory: ${src}`);
2113
2037
  }
2114
- this.#useTransaction(() => {
2115
- this.#ensureParentExists(destPrefixed);
2116
- if (srcEntry.type === "directory") {
2117
- const allEntries = z6.array(entryRow).parse(
2118
- this.#stmt(
2119
- `SELECT * FROM fs_entries WHERE path = ? OR path LIKE ? || '/%' ORDER BY path DESC`
2120
- ).all(srcPrefixed, srcPrefixed)
2121
- );
2122
- for (const entry of [...allEntries].reverse()) {
2123
- const relativePath = path2.posix.relative(srcPrefixed, entry.path);
2124
- const newPath = path2.posix.join(destPrefixed, relativePath);
2125
- this.#stmt(
2126
- `INSERT INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
2127
- VALUES (?, ?, ?, ?, ?, ?)`
2128
- ).run(
2129
- newPath,
2130
- entry.type,
2131
- entry.mode,
2132
- entry.size,
2133
- Date.now(),
2134
- entry.symlinkTarget
2038
+ transaction(
2039
+ this.#db,
2040
+ () => {
2041
+ this.#ensureParentExists(destPrefixed);
2042
+ if (srcEntry.type === "directory") {
2043
+ const allEntries = z6.array(entryRow).parse(
2044
+ this.#sql.all`SELECT * FROM fs_entries WHERE path = ${srcPrefixed} OR path LIKE ${srcPrefixed} || '/%' ORDER BY path DESC`
2135
2045
  );
2136
- if (entry.type === "file") {
2137
- const chunks = this.#chunks(entry.path);
2138
- for (const chunk of chunks) {
2139
- this.#stmt(
2140
- "INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (?, ?, ?)"
2141
- ).run(newPath, chunk.chunkIndex, chunk.data);
2046
+ for (const entry of [...allEntries].reverse()) {
2047
+ const relativePath = path2.posix.relative(srcPrefixed, entry.path);
2048
+ const newPath = path2.posix.join(destPrefixed, relativePath);
2049
+ this.#sql.run`INSERT INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
2050
+ VALUES (${newPath}, ${entry.type}, ${entry.mode}, ${entry.size}, ${Date.now()}, ${entry.symlinkTarget})`;
2051
+ if (entry.type === "file") {
2052
+ const chunks = this.#chunks(entry.path);
2053
+ for (const chunk of chunks) {
2054
+ this.#sql.run`INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (${newPath}, ${chunk.chunkIndex}, ${chunk.data})`;
2055
+ }
2142
2056
  }
2143
2057
  }
2144
- }
2145
- this.#stmt(
2146
- `DELETE FROM fs_entries WHERE path = ? OR path LIKE ? || '/%'`
2147
- ).run(srcPrefixed, srcPrefixed);
2148
- } else {
2149
- this.#stmt(
2150
- `INSERT INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
2151
- VALUES (?, ?, ?, ?, ?, ?)`
2152
- ).run(
2153
- destPrefixed,
2154
- srcEntry.type,
2155
- srcEntry.mode,
2156
- srcEntry.size,
2157
- Date.now(),
2158
- srcEntry.symlinkTarget
2159
- );
2160
- if (srcEntry.type === "file") {
2161
- const chunks = this.#chunks(srcPrefixed);
2162
- for (const chunk of chunks) {
2163
- this.#stmt(
2164
- "INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (?, ?, ?)"
2165
- ).run(destPrefixed, chunk.chunkIndex, chunk.data);
2058
+ this.#sql.run`DELETE FROM fs_entries WHERE path = ${srcPrefixed} OR path LIKE ${srcPrefixed} || '/%'`;
2059
+ } else {
2060
+ this.#sql.run`INSERT INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
2061
+ VALUES (${destPrefixed}, ${srcEntry.type}, ${srcEntry.mode}, ${srcEntry.size}, ${Date.now()}, ${srcEntry.symlinkTarget})`;
2062
+ if (srcEntry.type === "file") {
2063
+ const chunks = this.#chunks(srcPrefixed);
2064
+ for (const chunk of chunks) {
2065
+ this.#sql.run`INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (${destPrefixed}, ${chunk.chunkIndex}, ${chunk.data})`;
2066
+ }
2166
2067
  }
2068
+ this.#sql.run`DELETE FROM fs_entries WHERE path = ${srcPrefixed}`;
2167
2069
  }
2168
- this.#stmt("DELETE FROM fs_entries WHERE path = ?").run(srcPrefixed);
2169
- }
2170
- });
2070
+ },
2071
+ { mode: "deferred" }
2072
+ );
2171
2073
  }
2172
2074
  resolvePath(base, relativePath) {
2173
2075
  return path2.posix.resolve(base, relativePath);
2174
2076
  }
2175
2077
  getAllPaths() {
2176
- const rows = z6.array(pathRow).parse(this.#stmt("SELECT path FROM fs_entries ORDER BY path").all());
2078
+ const rows = z6.array(pathRow).parse(this.#sql.all`SELECT path FROM fs_entries ORDER BY path`);
2177
2079
  return rows.map((row) => row.path);
2178
2080
  }
2179
2081
  async realpath(filePath) {
2180
2082
  const normalized = this.#normalizePath(filePath);
2181
2083
  const prefixed = this.#prefixPath(normalized);
2182
2084
  const resolved = this.#resolveSymlink(prefixed);
2183
- const exists = this.#stmt("SELECT 1 FROM fs_entries WHERE path = ?").get(
2184
- resolved
2185
- );
2085
+ const exists = this.#sql.get`SELECT 1 FROM fs_entries WHERE path = ${resolved}`;
2186
2086
  if (!exists) {
2187
2087
  throw new Error(`ENOENT: no such file or directory: ${filePath}`);
2188
2088
  }
@@ -2192,9 +2092,7 @@ var SqliteFs = class {
2192
2092
  const normalized = this.#normalizePath(filePath);
2193
2093
  const prefixed = this.#prefixPath(normalized);
2194
2094
  const resolved = this.#resolveSymlink(prefixed);
2195
- const result = this.#stmt(
2196
- "UPDATE fs_entries SET mtime = ? WHERE path = ?"
2197
- ).run(mtime.getTime(), resolved);
2095
+ const result = this.#sql.run`UPDATE fs_entries SET mtime = ${mtime.getTime()} WHERE path = ${resolved}`;
2198
2096
  if (result.changes === 0) {
2199
2097
  throw new Error(`ENOENT: no such file or directory: ${filePath}`);
2200
2098
  }
@@ -2202,9 +2100,7 @@ var SqliteFs = class {
2202
2100
  async chmod(filePath, mode) {
2203
2101
  const normalized = this.#normalizePath(filePath);
2204
2102
  const prefixed = this.#prefixPath(normalized);
2205
- const result = this.#stmt(
2206
- "UPDATE fs_entries SET mode = ? WHERE path = ?"
2207
- ).run(mode, prefixed);
2103
+ const result = this.#sql.run`UPDATE fs_entries SET mode = ${mode} WHERE path = ${prefixed}`;
2208
2104
  if (result.changes === 0) {
2209
2105
  throw new Error(`ENOENT: no such file or directory: ${filePath}`);
2210
2106
  }
@@ -2212,19 +2108,19 @@ var SqliteFs = class {
2212
2108
  async symlink(target, linkPath) {
2213
2109
  const normalized = this.#normalizePath(linkPath);
2214
2110
  const prefixed = this.#prefixPath(normalized);
2215
- const existing = this.#stmt("SELECT 1 FROM fs_entries WHERE path = ?").get(
2216
- prefixed
2217
- );
2111
+ const existing = this.#sql.get`SELECT 1 FROM fs_entries WHERE path = ${prefixed}`;
2218
2112
  if (existing) {
2219
2113
  throw new Error(`EEXIST: file already exists: ${linkPath}`);
2220
2114
  }
2221
- this.#useTransaction(() => {
2222
- this.#ensureParentExists(prefixed);
2223
- this.#stmt(
2224
- `INSERT INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
2225
- VALUES (?, 'symlink', 511, 0, ?, ?)`
2226
- ).run(prefixed, Date.now(), target);
2227
- });
2115
+ transaction(
2116
+ this.#db,
2117
+ () => {
2118
+ this.#ensureParentExists(prefixed);
2119
+ this.#sql.run`INSERT INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
2120
+ VALUES (${prefixed}, 'symlink', 511, 0, ${Date.now()}, ${target})`;
2121
+ },
2122
+ { mode: "deferred" }
2123
+ );
2228
2124
  }
2229
2125
  async link(existingPath, newPath) {
2230
2126
  const srcNormalized = this.#normalizePath(existingPath);
@@ -2232,7 +2128,7 @@ var SqliteFs = class {
2232
2128
  const srcPrefixed = this.#prefixPath(srcNormalized);
2233
2129
  const destPrefixed = this.#prefixPath(destNormalized);
2234
2130
  const srcEntry = entryRow.optional().parse(
2235
- this.#stmt("SELECT * FROM fs_entries WHERE path = ?").get(srcPrefixed)
2131
+ this.#sql.get`SELECT * FROM fs_entries WHERE path = ${srcPrefixed}`
2236
2132
  );
2237
2133
  if (!srcEntry) {
2238
2134
  throw new Error(`ENOENT: no such file or directory: ${existingPath}`);
@@ -2240,33 +2136,29 @@ var SqliteFs = class {
2240
2136
  if (srcEntry.type !== "file") {
2241
2137
  throw new Error(`link: not supported for directories: ${existingPath}`);
2242
2138
  }
2243
- const existing = this.#stmt("SELECT 1 FROM fs_entries WHERE path = ?").get(
2244
- destPrefixed
2245
- );
2139
+ const existing = this.#sql.get`SELECT 1 FROM fs_entries WHERE path = ${destPrefixed}`;
2246
2140
  if (existing) {
2247
2141
  throw new Error(`EEXIST: file already exists: ${newPath}`);
2248
2142
  }
2249
- this.#useTransaction(() => {
2250
- this.#ensureParentExists(destPrefixed);
2251
- this.#stmt(
2252
- `INSERT INTO fs_entries (path, type, mode, size, mtime)
2253
- VALUES (?, 'file', ?, ?, ?)`
2254
- ).run(destPrefixed, srcEntry.mode, srcEntry.size, Date.now());
2255
- const chunks = this.#chunks(srcPrefixed);
2256
- for (const chunk of chunks) {
2257
- this.#stmt(
2258
- "INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (?, ?, ?)"
2259
- ).run(destPrefixed, chunk.chunkIndex, chunk.data);
2260
- }
2261
- });
2143
+ transaction(
2144
+ this.#db,
2145
+ () => {
2146
+ this.#ensureParentExists(destPrefixed);
2147
+ this.#sql.run`INSERT INTO fs_entries (path, type, mode, size, mtime)
2148
+ VALUES (${destPrefixed}, 'file', ${srcEntry.mode}, ${srcEntry.size}, ${Date.now()})`;
2149
+ const chunks = this.#chunks(srcPrefixed);
2150
+ for (const chunk of chunks) {
2151
+ this.#sql.run`INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (${destPrefixed}, ${chunk.chunkIndex}, ${chunk.data})`;
2152
+ }
2153
+ },
2154
+ { mode: "deferred" }
2155
+ );
2262
2156
  }
2263
2157
  async readlink(linkPath) {
2264
2158
  const normalized = this.#normalizePath(linkPath);
2265
2159
  const prefixed = this.#prefixPath(normalized);
2266
2160
  const entry = linkRow.optional().parse(
2267
- this.#stmt(
2268
- "SELECT type, symlinkTarget FROM fs_entries WHERE path = ?"
2269
- ).get(prefixed)
2161
+ this.#sql.get`SELECT type, symlinkTarget FROM fs_entries WHERE path = ${prefixed}`
2270
2162
  );
2271
2163
  if (!entry) {
2272
2164
  throw new Error(`ENOENT: no such file or directory: ${linkPath}`);
@@ -2313,6 +2205,38 @@ GO
2313
2205
  `;
2314
2206
  }
2315
2207
 
2208
+ // packages/text2sql/src/lib/fs/mssql/sqlserver-schema-lock.ts
2209
+ async function withSqlServerSchemaLock(mssql, pool, schema, run) {
2210
+ const holder = new mssql.Transaction(pool);
2211
+ await holder.begin();
2212
+ let result;
2213
+ try {
2214
+ const lock = new mssql.Request(holder);
2215
+ lock.input(
2216
+ "resource",
2217
+ mssql.NVarChar(255),
2218
+ `deepagents:schema-ddl:${schema}`
2219
+ );
2220
+ const { recordset } = await lock.query(`
2221
+ DECLARE @status int;
2222
+ EXEC @status = sp_getapplock @Resource = @resource, @LockMode = 'Exclusive', @LockOwner = 'Transaction', @LockTimeout = -1;
2223
+ SELECT @status AS status;
2224
+ `);
2225
+ if (recordset[0].status < 0) {
2226
+ throw new Error(
2227
+ `Could not lock the DDL of schema "${schema}": sp_getapplock returned ${recordset[0].status}.`
2228
+ );
2229
+ }
2230
+ result = await run();
2231
+ } catch (error) {
2232
+ await holder.rollback().catch(() => {
2233
+ });
2234
+ throw error;
2235
+ }
2236
+ await holder.rollback();
2237
+ return result;
2238
+ }
2239
+
2316
2240
  // packages/text2sql/src/lib/fs/mssql/mssql-fs.ts
2317
2241
  var entryRow2 = z7.object({
2318
2242
  path: z7.string(),
@@ -2347,7 +2271,6 @@ var MssqlFs = class _MssqlFs {
2347
2271
  #root;
2348
2272
  #schema;
2349
2273
  #ownsPool;
2350
- #isInitialized = false;
2351
2274
  constructor(options) {
2352
2275
  this.#chunkSize = options.chunkSize ?? 1024 * 1024;
2353
2276
  const schema = options.schema ?? "dbo";
@@ -2379,91 +2302,72 @@ var MssqlFs = class _MssqlFs {
2379
2302
  #t(name) {
2380
2303
  return `[${this.#schema}].[${name}]`;
2381
2304
  }
2382
- async initialize() {
2305
+ /** Opens the file system: creates its tables and its root directories. */
2306
+ static async create(options) {
2307
+ const fs = new _MssqlFs(options);
2308
+ try {
2309
+ await fs.#initialize();
2310
+ } catch (error) {
2311
+ await fs.close();
2312
+ throw error;
2313
+ }
2314
+ return fs;
2315
+ }
2316
+ async #initialize() {
2383
2317
  if (this.#ownsPool) {
2384
2318
  await this.#pool.connect();
2385
2319
  }
2386
- const schemaReq = this.#pool.request();
2387
- schemaReq.input("schema", this.#schema);
2388
- await schemaReq.query(`
2389
- IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = @schema)
2390
- BEGIN
2391
- DECLARE @sql NVARCHAR(MAX) = N'CREATE SCHEMA ' + QUOTENAME(@schema);
2392
- EXEC sp_executesql @sql;
2393
- END
2394
- `);
2395
- const ddl = mssqlFsDDL(this.#schema);
2396
- const batches = ddl.split(/\bGO\b/i).filter((b) => b.trim());
2397
- for (const batch of batches) {
2398
- if (batch.trim()) {
2399
- await this.#pool.request().batch(batch);
2320
+ await withSqlServerSchemaLock(
2321
+ _MssqlFs.#requireMssql(),
2322
+ this.#pool,
2323
+ this.#schema,
2324
+ async () => {
2325
+ const schemaReq = this.#pool.request();
2326
+ schemaReq.input("schema", this.#schema);
2327
+ await schemaReq.query(`
2328
+ IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = @schema)
2329
+ BEGIN
2330
+ DECLARE @sql NVARCHAR(MAX) = N'CREATE SCHEMA ' + QUOTENAME(@schema);
2331
+ EXEC sp_executesql @sql;
2332
+ END
2333
+ `);
2334
+ const ddl = mssqlFsDDL(this.#schema);
2335
+ const batches = ddl.split(/\bGO\b/i).filter((b) => b.trim());
2336
+ for (const batch of batches) {
2337
+ await this.#pool.request().batch(batch);
2338
+ }
2339
+ await this.#ensureDirectory("/");
2340
+ if (this.#root) {
2341
+ await this.#createParentDirs(this.#root);
2342
+ await this.#ensureDirectory(this.#root);
2343
+ }
2400
2344
  }
2401
- }
2402
- const rootSlashExists = rowsOf(
2403
- existsRow,
2404
- await this.#rawQuery(
2405
- `SELECT CASE WHEN EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = '/') THEN 1 ELSE 0 END as [exists]`
2406
- )
2407
2345
  );
2408
- if (rootSlashExists[0].exists === 0) {
2409
- await this.#rawExec(
2410
- `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ('/', 'directory', 493, 0, @p0)`,
2411
- [Date.now()]
2412
- );
2413
- }
2414
- if (this.#root) {
2415
- await this.#createParentDirs(this.#root);
2416
- const rootExists = rowsOf(
2417
- existsRow,
2418
- await this.#rawQuery(
2419
- `SELECT CASE WHEN EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = @p0) THEN 1 ELSE 0 END as [exists]`,
2420
- [this.#root]
2421
- )
2422
- );
2423
- if (rootExists[0].exists === 0) {
2424
- await this.#rawExec(
2425
- `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES (@p0, 'directory', 493, 0, @p1)`,
2426
- [this.#root, Date.now()]
2427
- );
2428
- }
2429
- }
2430
- this.#isInitialized = true;
2431
- }
2432
- #ensureInitialized() {
2433
- if (!this.#isInitialized) {
2434
- throw new Error(
2435
- "MssqlFs not initialized. Call await fs.initialize() after construction."
2436
- );
2437
- }
2438
2346
  }
2439
2347
  async #createParentDirs(p) {
2440
2348
  const segments = p.split("/").filter(Boolean);
2441
2349
  let currentPath = "/";
2442
2350
  for (let i = 0; i < segments.length - 1; i++) {
2443
2351
  currentPath = path3.posix.join(currentPath, segments[i]);
2444
- const exists = rowsOf(
2445
- existsRow,
2446
- await this.#rawQuery(
2447
- `SELECT CASE WHEN EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = @p0) THEN 1 ELSE 0 END as [exists]`,
2448
- [currentPath]
2449
- )
2450
- );
2451
- if (exists[0].exists === 0) {
2452
- await this.#rawExec(
2453
- `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES (@p0, 'directory', 493, 0, @p1)`,
2454
- [currentPath, Date.now()]
2455
- );
2456
- }
2352
+ await this.#ensureDirectory(currentPath);
2457
2353
  }
2458
2354
  }
2459
- #rawQuery(sql, params) {
2355
+ async #ensureDirectory(p) {
2356
+ await this.#exec(
2357
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime)
2358
+ SELECT @p0, 'directory', 493, 0, @p1
2359
+ WHERE NOT EXISTS (SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = @p0)`,
2360
+ [p, Date.now()]
2361
+ );
2362
+ }
2363
+ #query(sql, params) {
2460
2364
  const request = this.#pool.request();
2461
2365
  params?.forEach((value, index2) => {
2462
2366
  request.input(`p${index2}`, value);
2463
2367
  });
2464
2368
  return request.query(sql);
2465
2369
  }
2466
- async #rawExec(sql, params) {
2370
+ async #exec(sql, params) {
2467
2371
  const request = this.#pool.request();
2468
2372
  params?.forEach((value, index2) => {
2469
2373
  request.input(`p${index2}`, value);
@@ -2471,25 +2375,16 @@ var MssqlFs = class _MssqlFs {
2471
2375
  const result = await request.query(sql);
2472
2376
  return result.rowsAffected[0] ?? 0;
2473
2377
  }
2474
- async #query(sql, params) {
2475
- this.#ensureInitialized();
2476
- return this.#rawQuery(sql, params);
2477
- }
2478
- async #exec(sql, params) {
2479
- this.#ensureInitialized();
2480
- return this.#rawExec(sql, params);
2481
- }
2482
2378
  async #useTransaction(fn) {
2483
- this.#ensureInitialized();
2484
2379
  const mssql = _MssqlFs.#requireMssql();
2485
- const transaction = new mssql.Transaction(this.#pool);
2380
+ const transaction2 = new mssql.Transaction(this.#pool);
2486
2381
  try {
2487
- await transaction.begin();
2488
- const result = await fn(transaction);
2489
- await transaction.commit();
2382
+ await transaction2.begin();
2383
+ const result = await fn(transaction2);
2384
+ await transaction2.commit();
2490
2385
  return result;
2491
2386
  } catch (error) {
2492
- await transaction.rollback();
2387
+ await transaction2.rollback();
2493
2388
  throw error;
2494
2389
  }
2495
2390
  }
@@ -2524,11 +2419,11 @@ var MssqlFs = class _MssqlFs {
2524
2419
  const dir = path3.posix.dirname(p);
2525
2420
  return dir === "" ? "/" : dir;
2526
2421
  }
2527
- async #ensureParentExists(filePath, transaction) {
2422
+ async #ensureParentExists(filePath, transaction2) {
2528
2423
  const parent = this.#dirname(filePath);
2529
2424
  const rootPath = this.#root || "/";
2530
2425
  if (parent === rootPath || parent === "/") return;
2531
- const request = transaction.request();
2426
+ const request = transaction2.request();
2532
2427
  request.input("p0", parent);
2533
2428
  const [entry] = rowsOf(
2534
2429
  typeRow2,
@@ -2537,8 +2432,8 @@ var MssqlFs = class _MssqlFs {
2537
2432
  )
2538
2433
  );
2539
2434
  if (!entry) {
2540
- await this.#ensureParentExists(parent, transaction);
2541
- const insertReq = transaction.request();
2435
+ await this.#ensureParentExists(parent, transaction2);
2436
+ const insertReq = transaction2.request();
2542
2437
  insertReq.input("p0", parent);
2543
2438
  insertReq.input("p1", Date.now());
2544
2439
  await insertReq.query(
@@ -2548,8 +2443,8 @@ var MssqlFs = class _MssqlFs {
2548
2443
  throw new Error(`mkdir: parent is not a directory: ${parent}`);
2549
2444
  }
2550
2445
  }
2551
- async #writeChunks(filePath, content, transaction) {
2552
- const deleteReq = transaction.request();
2446
+ async #writeChunks(filePath, content, transaction2) {
2447
+ const deleteReq = transaction2.request();
2553
2448
  deleteReq.input("p0", filePath);
2554
2449
  await deleteReq.query(
2555
2450
  `DELETE FROM ${this.#t("fs_chunks")} WHERE path = @p0`
@@ -2559,7 +2454,7 @@ var MssqlFs = class _MssqlFs {
2559
2454
  i,
2560
2455
  Math.min(i + this.#chunkSize, content.length)
2561
2456
  );
2562
- const insertReq = transaction.request();
2457
+ const insertReq = transaction2.request();
2563
2458
  insertReq.input("p0", filePath);
2564
2459
  insertReq.input("p1", Math.floor(i / this.#chunkSize));
2565
2460
  insertReq.input("p2", Buffer.from(chunk));
@@ -2568,11 +2463,11 @@ var MssqlFs = class _MssqlFs {
2568
2463
  );
2569
2464
  }
2570
2465
  }
2571
- async #readChunks(filePath, transaction) {
2466
+ async #readChunks(filePath, transaction2) {
2572
2467
  const sql = `SELECT data FROM ${this.#t("fs_chunks")} WHERE path = @p0 ORDER BY chunkIndex`;
2573
2468
  const rows = rowsOf(
2574
2469
  dataRow2,
2575
- transaction ? await transaction.request().input("p0", filePath).query(sql) : await this.#query(sql, [filePath])
2470
+ transaction2 ? await transaction2.request().input("p0", filePath).query(sql) : await this.#query(sql, [filePath])
2576
2471
  );
2577
2472
  if (rows.length === 0) {
2578
2473
  return new Uint8Array(0);
@@ -2668,9 +2563,9 @@ var MssqlFs = class _MssqlFs {
2668
2563
  const prefixed = this.#prefixPath(normalized);
2669
2564
  const encoding = typeof options === "string" ? options : options?.encoding;
2670
2565
  const data = this.#toUint8Array(content, encoding);
2671
- await this.#useTransaction(async (transaction) => {
2672
- await this.#ensureParentExists(prefixed, transaction);
2673
- const request = transaction.request();
2566
+ await this.#useTransaction(async (transaction2) => {
2567
+ await this.#ensureParentExists(prefixed, transaction2);
2568
+ const request = transaction2.request();
2674
2569
  request.input("p0", prefixed);
2675
2570
  request.input("p1", data.length);
2676
2571
  request.input("p2", Date.now());
@@ -2684,7 +2579,7 @@ var MssqlFs = class _MssqlFs {
2684
2579
  INSERT (path, type, mode, size, mtime)
2685
2580
  VALUES (@p0, 'file', 420, @p1, @p2);
2686
2581
  `);
2687
- await this.#writeChunks(prefixed, data, transaction);
2582
+ await this.#writeChunks(prefixed, data, transaction2);
2688
2583
  });
2689
2584
  }
2690
2585
  async appendFile(filePath, content, options) {
@@ -2692,9 +2587,9 @@ var MssqlFs = class _MssqlFs {
2692
2587
  const prefixed = this.#prefixPath(normalized);
2693
2588
  const encoding = typeof options === "string" ? options : options?.encoding;
2694
2589
  const newData = this.#toUint8Array(content, encoding);
2695
- await this.#useTransaction(async (transaction) => {
2696
- await this.#ensureParentExists(prefixed, transaction);
2697
- const checkReq = transaction.request();
2590
+ await this.#useTransaction(async (transaction2) => {
2591
+ await this.#ensureParentExists(prefixed, transaction2);
2592
+ const checkReq = transaction2.request();
2698
2593
  checkReq.input("p0", prefixed);
2699
2594
  const [entry] = rowsOf(
2700
2595
  typeRow2,
@@ -2705,11 +2600,11 @@ var MssqlFs = class _MssqlFs {
2705
2600
  if (entry && entry.type !== "file") {
2706
2601
  throw new Error(`appendFile: not a file: ${filePath}`);
2707
2602
  }
2708
- const existing = entry ? await this.#readChunks(prefixed, transaction) : new Uint8Array(0);
2603
+ const existing = entry ? await this.#readChunks(prefixed, transaction2) : new Uint8Array(0);
2709
2604
  const combined = new Uint8Array(existing.length + newData.length);
2710
2605
  combined.set(existing, 0);
2711
2606
  combined.set(newData, existing.length);
2712
- const upsertReq = transaction.request();
2607
+ const upsertReq = transaction2.request();
2713
2608
  upsertReq.input("p0", prefixed);
2714
2609
  upsertReq.input("p1", combined.length);
2715
2610
  upsertReq.input("p2", Date.now());
@@ -2723,7 +2618,7 @@ var MssqlFs = class _MssqlFs {
2723
2618
  INSERT (path, type, mode, size, mtime)
2724
2619
  VALUES (@p0, 'file', 420, @p1, @p2);
2725
2620
  `);
2726
- await this.#writeChunks(prefixed, combined, transaction);
2621
+ await this.#writeChunks(prefixed, combined, transaction2);
2727
2622
  });
2728
2623
  }
2729
2624
  async exists(filePath) {
@@ -2799,7 +2694,7 @@ var MssqlFs = class _MssqlFs {
2799
2694
  }
2800
2695
  throw new Error(`EEXIST: file already exists: ${dirPath}`);
2801
2696
  }
2802
- await this.#useTransaction(async (transaction) => {
2697
+ await this.#useTransaction(async (transaction2) => {
2803
2698
  if (options?.recursive) {
2804
2699
  const rootPath = this.#root || "/";
2805
2700
  const relativePath = path3.posix.relative(rootPath, prefixed);
@@ -2807,7 +2702,7 @@ var MssqlFs = class _MssqlFs {
2807
2702
  let currentPath = rootPath;
2808
2703
  for (const segment of segments) {
2809
2704
  currentPath = path3.posix.join(currentPath, segment);
2810
- const checkReq = transaction.request();
2705
+ const checkReq = transaction2.request();
2811
2706
  checkReq.input("p0", currentPath);
2812
2707
  const [exists] = rowsOf(
2813
2708
  typeRow2,
@@ -2816,7 +2711,7 @@ var MssqlFs = class _MssqlFs {
2816
2711
  )
2817
2712
  );
2818
2713
  if (!exists) {
2819
- const insertReq = transaction.request();
2714
+ const insertReq = transaction2.request();
2820
2715
  insertReq.input("p0", currentPath);
2821
2716
  insertReq.input("p1", Date.now());
2822
2717
  await insertReq.query(
@@ -2828,7 +2723,7 @@ var MssqlFs = class _MssqlFs {
2828
2723
  }
2829
2724
  } else {
2830
2725
  const parent = this.#dirname(prefixed);
2831
- const parentReq = transaction.request();
2726
+ const parentReq = transaction2.request();
2832
2727
  parentReq.input("p0", parent);
2833
2728
  const [parentEntry] = rowsOf(
2834
2729
  typeRow2,
@@ -2842,7 +2737,7 @@ var MssqlFs = class _MssqlFs {
2842
2737
  if (parentEntry.type !== "directory") {
2843
2738
  throw new Error(`mkdir: parent is not a directory: ${parent}`);
2844
2739
  }
2845
- const insertReq = transaction.request();
2740
+ const insertReq = transaction2.request();
2846
2741
  insertReq.input("p0", prefixed);
2847
2742
  insertReq.input("p1", Date.now());
2848
2743
  await insertReq.query(
@@ -2932,9 +2827,9 @@ var MssqlFs = class _MssqlFs {
2932
2827
  }
2933
2828
  throw new Error(`ENOENT: no such file or directory: ${filePath}`);
2934
2829
  }
2935
- await this.#useTransaction(async (transaction) => {
2830
+ await this.#useTransaction(async (transaction2) => {
2936
2831
  if (entry.type === "directory") {
2937
- const childrenReq = transaction.request();
2832
+ const childrenReq = transaction2.request();
2938
2833
  childrenReq.input("p0", prefixed);
2939
2834
  const children = rowsOf(
2940
2835
  existsRow,
@@ -2945,13 +2840,13 @@ var MssqlFs = class _MssqlFs {
2945
2840
  if (children[0].exists === 1 && !options?.recursive) {
2946
2841
  throw new Error(`ENOTEMPTY: directory not empty: ${filePath}`);
2947
2842
  }
2948
- const deleteReq = transaction.request();
2843
+ const deleteReq = transaction2.request();
2949
2844
  deleteReq.input("p0", prefixed);
2950
2845
  await deleteReq.query(
2951
2846
  `DELETE FROM ${this.#t("fs_entries")} WHERE path = @p0 OR path LIKE @p0 + '/%'`
2952
2847
  );
2953
2848
  } else {
2954
- const deleteReq = transaction.request();
2849
+ const deleteReq = transaction2.request();
2955
2850
  deleteReq.input("p0", prefixed);
2956
2851
  await deleteReq.query(
2957
2852
  `DELETE FROM ${this.#t("fs_entries")} WHERE path = @p0`
@@ -2977,10 +2872,10 @@ var MssqlFs = class _MssqlFs {
2977
2872
  if (srcEntry.type === "directory" && !options?.recursive) {
2978
2873
  throw new Error(`cp: -r not specified; omitting directory: ${src}`);
2979
2874
  }
2980
- await this.#useTransaction(async (transaction) => {
2981
- await this.#ensureParentExists(destPrefixed, transaction);
2875
+ await this.#useTransaction(async (transaction2) => {
2876
+ await this.#ensureParentExists(destPrefixed, transaction2);
2982
2877
  if (srcEntry.type === "directory") {
2983
- const allEntriesReq = transaction.request();
2878
+ const allEntriesReq = transaction2.request();
2984
2879
  allEntriesReq.input("p0", srcPrefixed);
2985
2880
  const allEntries = rowsOf(
2986
2881
  entryRow2,
@@ -2991,7 +2886,7 @@ var MssqlFs = class _MssqlFs {
2991
2886
  for (const entry of allEntries) {
2992
2887
  const relativePath = path3.posix.relative(srcPrefixed, entry.path);
2993
2888
  const newPath = path3.posix.join(destPrefixed, relativePath);
2994
- const insertReq = transaction.request();
2889
+ const insertReq = transaction2.request();
2995
2890
  insertReq.input("p0", newPath);
2996
2891
  insertReq.input("p1", entry.type);
2997
2892
  insertReq.input("p2", entry.mode);
@@ -3009,12 +2904,12 @@ var MssqlFs = class _MssqlFs {
3009
2904
  VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
3010
2905
  `);
3011
2906
  if (entry.type === "file") {
3012
- const deleteChunksReq = transaction.request();
2907
+ const deleteChunksReq = transaction2.request();
3013
2908
  deleteChunksReq.input("p0", newPath);
3014
2909
  await deleteChunksReq.query(
3015
2910
  `DELETE FROM ${this.#t("fs_chunks")} WHERE path = @p0`
3016
2911
  );
3017
- const chunksReq = transaction.request();
2912
+ const chunksReq = transaction2.request();
3018
2913
  chunksReq.input("p0", entry.path);
3019
2914
  const chunks = rowsOf(
3020
2915
  chunkRow2,
@@ -3023,7 +2918,7 @@ var MssqlFs = class _MssqlFs {
3023
2918
  )
3024
2919
  );
3025
2920
  for (const chunk of chunks) {
3026
- const chunkInsertReq = transaction.request();
2921
+ const chunkInsertReq = transaction2.request();
3027
2922
  chunkInsertReq.input("p0", newPath);
3028
2923
  chunkInsertReq.input("p1", chunk.chunkIndex);
3029
2924
  chunkInsertReq.input("p2", chunk.data);
@@ -3034,7 +2929,7 @@ var MssqlFs = class _MssqlFs {
3034
2929
  }
3035
2930
  }
3036
2931
  } else {
3037
- const insertReq = transaction.request();
2932
+ const insertReq = transaction2.request();
3038
2933
  insertReq.input("p0", destPrefixed);
3039
2934
  insertReq.input("p1", srcEntry.type);
3040
2935
  insertReq.input("p2", srcEntry.mode);
@@ -3052,7 +2947,7 @@ var MssqlFs = class _MssqlFs {
3052
2947
  VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
3053
2948
  `);
3054
2949
  if (srcEntry.type === "file") {
3055
- const chunksReq = transaction.request();
2950
+ const chunksReq = transaction2.request();
3056
2951
  chunksReq.input("p0", srcPrefixed);
3057
2952
  const chunks = rowsOf(
3058
2953
  chunkRow2,
@@ -3060,13 +2955,13 @@ var MssqlFs = class _MssqlFs {
3060
2955
  `SELECT chunkIndex, data FROM ${this.#t("fs_chunks")} WHERE path = @p0`
3061
2956
  )
3062
2957
  );
3063
- const deleteChunksReq = transaction.request();
2958
+ const deleteChunksReq = transaction2.request();
3064
2959
  deleteChunksReq.input("p0", destPrefixed);
3065
2960
  await deleteChunksReq.query(
3066
2961
  `DELETE FROM ${this.#t("fs_chunks")} WHERE path = @p0`
3067
2962
  );
3068
2963
  for (const chunk of chunks) {
3069
- const chunkInsertReq = transaction.request();
2964
+ const chunkInsertReq = transaction2.request();
3070
2965
  chunkInsertReq.input("p0", destPrefixed);
3071
2966
  chunkInsertReq.input("p1", chunk.chunkIndex);
3072
2967
  chunkInsertReq.input("p2", chunk.data);
@@ -3093,10 +2988,10 @@ var MssqlFs = class _MssqlFs {
3093
2988
  if (!srcEntry) {
3094
2989
  throw new Error(`ENOENT: no such file or directory: ${src}`);
3095
2990
  }
3096
- await this.#useTransaction(async (transaction) => {
3097
- await this.#ensureParentExists(destPrefixed, transaction);
2991
+ await this.#useTransaction(async (transaction2) => {
2992
+ await this.#ensureParentExists(destPrefixed, transaction2);
3098
2993
  if (srcEntry.type === "directory") {
3099
- const allEntriesReq = transaction.request();
2994
+ const allEntriesReq = transaction2.request();
3100
2995
  allEntriesReq.input("p0", srcPrefixed);
3101
2996
  const allEntries = rowsOf(
3102
2997
  entryRow2,
@@ -3104,7 +2999,7 @@ var MssqlFs = class _MssqlFs {
3104
2999
  `SELECT * FROM ${this.#t("fs_entries")} WHERE path = @p0 OR path LIKE @p0 + '/%' ORDER BY path DESC`
3105
3000
  )
3106
3001
  );
3107
- const destDeleteReq = transaction.request();
3002
+ const destDeleteReq = transaction2.request();
3108
3003
  destDeleteReq.input("dp0", destPrefixed);
3109
3004
  await destDeleteReq.query(
3110
3005
  `DELETE FROM ${this.#t("fs_entries")} WHERE path = @dp0 OR path LIKE @dp0 + '/%'`
@@ -3112,7 +3007,7 @@ var MssqlFs = class _MssqlFs {
3112
3007
  for (const entry of [...allEntries].reverse()) {
3113
3008
  const relativePath = path3.posix.relative(srcPrefixed, entry.path);
3114
3009
  const newPath = path3.posix.join(destPrefixed, relativePath);
3115
- const insertReq = transaction.request();
3010
+ const insertReq = transaction2.request();
3116
3011
  insertReq.input("p0", newPath);
3117
3012
  insertReq.input("p1", entry.type);
3118
3013
  insertReq.input("p2", entry.mode);
@@ -3130,12 +3025,12 @@ var MssqlFs = class _MssqlFs {
3130
3025
  VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
3131
3026
  `);
3132
3027
  if (entry.type === "file") {
3133
- const deleteChunksReq = transaction.request();
3028
+ const deleteChunksReq = transaction2.request();
3134
3029
  deleteChunksReq.input("p0", newPath);
3135
3030
  await deleteChunksReq.query(
3136
3031
  `DELETE FROM ${this.#t("fs_chunks")} WHERE path = @p0`
3137
3032
  );
3138
- const chunksReq = transaction.request();
3033
+ const chunksReq = transaction2.request();
3139
3034
  chunksReq.input("p0", entry.path);
3140
3035
  const chunks = rowsOf(
3141
3036
  chunkRow2,
@@ -3144,7 +3039,7 @@ var MssqlFs = class _MssqlFs {
3144
3039
  )
3145
3040
  );
3146
3041
  for (const chunk of chunks) {
3147
- const chunkInsertReq = transaction.request();
3042
+ const chunkInsertReq = transaction2.request();
3148
3043
  chunkInsertReq.input("p0", newPath);
3149
3044
  chunkInsertReq.input("p1", chunk.chunkIndex);
3150
3045
  chunkInsertReq.input("p2", chunk.data);
@@ -3154,13 +3049,13 @@ var MssqlFs = class _MssqlFs {
3154
3049
  }
3155
3050
  }
3156
3051
  }
3157
- const deleteReq = transaction.request();
3052
+ const deleteReq = transaction2.request();
3158
3053
  deleteReq.input("p0", srcPrefixed);
3159
3054
  await deleteReq.query(
3160
3055
  `DELETE FROM ${this.#t("fs_entries")} WHERE path = @p0 OR path LIKE @p0 + '/%'`
3161
3056
  );
3162
3057
  } else {
3163
- const insertReq = transaction.request();
3058
+ const insertReq = transaction2.request();
3164
3059
  insertReq.input("p0", destPrefixed);
3165
3060
  insertReq.input("p1", srcEntry.type);
3166
3061
  insertReq.input("p2", srcEntry.mode);
@@ -3178,12 +3073,12 @@ var MssqlFs = class _MssqlFs {
3178
3073
  VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
3179
3074
  `);
3180
3075
  if (srcEntry.type === "file") {
3181
- const deleteChunksReq = transaction.request();
3076
+ const deleteChunksReq = transaction2.request();
3182
3077
  deleteChunksReq.input("p0", destPrefixed);
3183
3078
  await deleteChunksReq.query(
3184
3079
  `DELETE FROM ${this.#t("fs_chunks")} WHERE path = @p0`
3185
3080
  );
3186
- const chunksReq = transaction.request();
3081
+ const chunksReq = transaction2.request();
3187
3082
  chunksReq.input("p0", srcPrefixed);
3188
3083
  const chunks = rowsOf(
3189
3084
  chunkRow2,
@@ -3192,7 +3087,7 @@ var MssqlFs = class _MssqlFs {
3192
3087
  )
3193
3088
  );
3194
3089
  for (const chunk of chunks) {
3195
- const chunkInsertReq = transaction.request();
3090
+ const chunkInsertReq = transaction2.request();
3196
3091
  chunkInsertReq.input("p0", destPrefixed);
3197
3092
  chunkInsertReq.input("p1", chunk.chunkIndex);
3198
3093
  chunkInsertReq.input("p2", chunk.data);
@@ -3201,7 +3096,7 @@ var MssqlFs = class _MssqlFs {
3201
3096
  );
3202
3097
  }
3203
3098
  }
3204
- const deleteReq = transaction.request();
3099
+ const deleteReq = transaction2.request();
3205
3100
  deleteReq.input("p0", srcPrefixed);
3206
3101
  await deleteReq.query(
3207
3102
  `DELETE FROM ${this.#t("fs_entries")} WHERE path = @p0`
@@ -3278,9 +3173,9 @@ var MssqlFs = class _MssqlFs {
3278
3173
  if (existingRows[0].exists === 1) {
3279
3174
  throw new Error(`EEXIST: file already exists: ${linkPath}`);
3280
3175
  }
3281
- await this.#useTransaction(async (transaction) => {
3282
- await this.#ensureParentExists(prefixed, transaction);
3283
- const insertReq = transaction.request();
3176
+ await this.#useTransaction(async (transaction2) => {
3177
+ await this.#ensureParentExists(prefixed, transaction2);
3178
+ const insertReq = transaction2.request();
3284
3179
  insertReq.input("p0", prefixed);
3285
3180
  insertReq.input("p1", Date.now());
3286
3181
  insertReq.input("p2", target);
@@ -3318,9 +3213,9 @@ var MssqlFs = class _MssqlFs {
3318
3213
  if (existingRows[0].exists === 1) {
3319
3214
  throw new Error(`EEXIST: file already exists: ${newPath}`);
3320
3215
  }
3321
- await this.#useTransaction(async (transaction) => {
3322
- await this.#ensureParentExists(destPrefixed, transaction);
3323
- const insertReq = transaction.request();
3216
+ await this.#useTransaction(async (transaction2) => {
3217
+ await this.#ensureParentExists(destPrefixed, transaction2);
3218
+ const insertReq = transaction2.request();
3324
3219
  insertReq.input("p0", destPrefixed);
3325
3220
  insertReq.input("p1", srcEntry.mode);
3326
3221
  insertReq.input("p2", srcEntry.size);
@@ -3329,7 +3224,7 @@ var MssqlFs = class _MssqlFs {
3329
3224
  `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime)
3330
3225
  VALUES (@p0, 'file', @p1, @p2, @p3)`
3331
3226
  );
3332
- const chunksReq = transaction.request();
3227
+ const chunksReq = transaction2.request();
3333
3228
  chunksReq.input("p0", srcPrefixed);
3334
3229
  const chunks = rowsOf(
3335
3230
  chunkRow2,
@@ -3338,7 +3233,7 @@ var MssqlFs = class _MssqlFs {
3338
3233
  )
3339
3234
  );
3340
3235
  for (const chunk of chunks) {
3341
- const chunkInsertReq = transaction.request();
3236
+ const chunkInsertReq = transaction2.request();
3342
3237
  chunkInsertReq.input("p0", destPrefixed);
3343
3238
  chunkInsertReq.input("p1", chunk.chunkIndex);
3344
3239
  chunkInsertReq.input("p2", chunk.data);
@@ -3372,6 +3267,7 @@ var MssqlFs = class _MssqlFs {
3372
3267
  import { createRequire as createRequire2 } from "node:module";
3373
3268
  import * as path4 from "node:path";
3374
3269
  import z8 from "zod";
3270
+ import { lockSchemaDDL } from "@deepagents/context";
3375
3271
 
3376
3272
  // packages/text2sql/src/lib/fs/postgres/ddl.postgres-fs.ts
3377
3273
  function postgresFsDDL(schema) {
@@ -3433,7 +3329,6 @@ var PostgresFs = class _PostgresFs {
3433
3329
  #root;
3434
3330
  #schema;
3435
3331
  #ownsPool;
3436
- #isInitialized = false;
3437
3332
  constructor(options) {
3438
3333
  this.#chunkSize = options.chunkSize ?? 1024 * 1024;
3439
3334
  const schema = options.schema ?? "public";
@@ -3465,83 +3360,53 @@ var PostgresFs = class _PostgresFs {
3465
3360
  #t(name) {
3466
3361
  return `"${this.#schema}"."${name}"`;
3467
3362
  }
3468
- async initialize() {
3469
- const ddl = postgresFsDDL(this.#schema);
3470
- await this.#pool.query(ddl);
3471
- const rootSlashExists = rowsOf2(
3472
- existsRow2,
3473
- await this.#rawQuery(
3474
- `SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = '/') AS exists`
3475
- )
3476
- );
3477
- if (!rootSlashExists[0].exists) {
3478
- await this.#rawExec(
3479
- `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ('/', 'directory', 493, 0, $1)`,
3480
- [Date.now()]
3481
- );
3482
- }
3483
- if (this.#root) {
3484
- await this.#createParentDirs(this.#root);
3485
- const rootExists = rowsOf2(
3486
- existsRow2,
3487
- await this.#rawQuery(
3488
- `SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
3489
- [this.#root]
3490
- )
3491
- );
3492
- if (!rootExists[0].exists) {
3493
- await this.#rawExec(
3494
- `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ($1, 'directory', 493, 0, $2)`,
3495
- [this.#root, Date.now()]
3496
- );
3497
- }
3363
+ /** Opens the file system: creates its tables and its root directories. */
3364
+ static async create(options) {
3365
+ const fs = new _PostgresFs(options);
3366
+ try {
3367
+ await fs.#initialize();
3368
+ } catch (error) {
3369
+ await fs.close();
3370
+ throw error;
3498
3371
  }
3499
- this.#isInitialized = true;
3372
+ return fs;
3500
3373
  }
3501
- #ensureInitialized() {
3502
- if (!this.#isInitialized) {
3503
- throw new Error(
3504
- "PostgresFs not initialized. Call await fs.initialize() after construction."
3374
+ // The schema lock is held until this transaction commits, so concurrent
3375
+ // openers also create the root directories one at a time.
3376
+ async #initialize() {
3377
+ await this.#useTransaction(async (client) => {
3378
+ await client.query(
3379
+ lockSchemaDDL(postgresFsDDL(this.#schema), this.#schema)
3505
3380
  );
3506
- }
3381
+ await this.#ensureDirectory(client, "/");
3382
+ if (this.#root) {
3383
+ await this.#createParentDirs(client, this.#root);
3384
+ await this.#ensureDirectory(client, this.#root);
3385
+ }
3386
+ });
3507
3387
  }
3508
- async #createParentDirs(p) {
3388
+ async #createParentDirs(client, p) {
3509
3389
  const segments = p.split("/").filter(Boolean);
3510
3390
  let currentPath = "/";
3511
3391
  for (let i = 0; i < segments.length - 1; i++) {
3512
3392
  currentPath = path4.posix.join(currentPath, segments[i]);
3513
- const exists = rowsOf2(
3514
- existsRow2,
3515
- await this.#rawQuery(
3516
- `SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
3517
- [currentPath]
3518
- )
3519
- );
3520
- if (!exists[0].exists) {
3521
- await this.#rawExec(
3522
- `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ($1, 'directory', 493, 0, $2)`,
3523
- [currentPath, Date.now()]
3524
- );
3525
- }
3393
+ await this.#ensureDirectory(client, currentPath);
3526
3394
  }
3527
3395
  }
3528
- #rawQuery(sql, params) {
3396
+ async #ensureDirectory(client, p) {
3397
+ await client.query(
3398
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ($1, 'directory', 493, 0, $2) ON CONFLICT (path) DO NOTHING`,
3399
+ [p, Date.now()]
3400
+ );
3401
+ }
3402
+ #query(sql, params) {
3529
3403
  return this.#pool.query(sql, params);
3530
3404
  }
3531
- async #rawExec(sql, params) {
3405
+ async #exec(sql, params) {
3532
3406
  const result = await this.#pool.query(sql, params);
3533
3407
  return result.rowCount ?? 0;
3534
3408
  }
3535
- async #query(sql, params) {
3536
- this.#ensureInitialized();
3537
- return this.#rawQuery(sql, params);
3538
- }
3539
- async #exec(sql, params) {
3540
- this.#ensureInitialized();
3541
- return this.#rawExec(sql, params);
3542
- }
3543
3409
  async #useTransaction(fn) {
3544
- this.#ensureInitialized();
3545
3410
  const client = await this.#pool.connect();
3546
3411
  try {
3547
3412
  await client.query("BEGIN");