@deepagents/text2sql 0.16.0 → 0.17.0
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.
- package/dist/index.js +1028 -114
- package/dist/index.js.map +4 -4
- package/dist/lib/adapters/adapter.d.ts +2 -1
- package/dist/lib/adapters/adapter.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/index.js.map +2 -2
- package/dist/lib/adapters/mysql/index.js.map +2 -2
- package/dist/lib/adapters/postgres/index.js.map +2 -2
- package/dist/lib/adapters/spreadsheet/index.js.map +2 -2
- package/dist/lib/adapters/sqlite/index.js.map +2 -2
- package/dist/lib/adapters/sqlserver/index.js.map +2 -2
- package/dist/lib/agents/result-tools.d.ts +9 -1
- package/dist/lib/agents/result-tools.d.ts.map +1 -1
- package/dist/lib/agents/sql.agent.d.ts.map +1 -1
- package/dist/lib/fs/index.d.ts +1 -0
- package/dist/lib/fs/index.d.ts.map +1 -1
- package/dist/lib/fs/mssql/ddl.mssql-fs.d.ts.map +1 -1
- package/dist/lib/fs/mssql/mssql-fs.d.ts.map +1 -1
- package/dist/lib/fs/postgres/ddl.postgres-fs.d.ts +2 -0
- package/dist/lib/fs/postgres/ddl.postgres-fs.d.ts.map +1 -0
- package/dist/lib/fs/postgres/postgres-fs.d.ts +50 -0
- package/dist/lib/fs/postgres/postgres-fs.d.ts.map +1 -0
- package/dist/lib/synthesis/index.js +0 -8
- package/dist/lib/synthesis/index.js.map +2 -2
- package/package.json +15 -5
package/dist/index.js
CHANGED
|
@@ -522,6 +522,7 @@ import {
|
|
|
522
522
|
OverlayFs,
|
|
523
523
|
defineCommand
|
|
524
524
|
} from "just-bash";
|
|
525
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
525
526
|
import * as path from "node:path";
|
|
526
527
|
import { v7 } from "uuid";
|
|
527
528
|
import z3 from "zod";
|
|
@@ -550,7 +551,7 @@ function validateReadOnly(query) {
|
|
|
550
551
|
}
|
|
551
552
|
return { valid: true };
|
|
552
553
|
}
|
|
553
|
-
function createSqlCommand(adapter) {
|
|
554
|
+
function createSqlCommand(adapter, metaStore) {
|
|
554
555
|
return createCommand("sql", {
|
|
555
556
|
run: {
|
|
556
557
|
usage: 'run "SELECT ..."',
|
|
@@ -573,6 +574,8 @@ function createSqlCommand(adapter) {
|
|
|
573
574
|
};
|
|
574
575
|
}
|
|
575
576
|
const query = adapter.format(rawQuery);
|
|
577
|
+
const store = metaStore.getStore();
|
|
578
|
+
if (store) store.value = { formattedSql: query };
|
|
576
579
|
const syntaxError = await adapter.validate(query);
|
|
577
580
|
if (syntaxError) {
|
|
578
581
|
return {
|
|
@@ -629,6 +632,8 @@ function createSqlCommand(adapter) {
|
|
|
629
632
|
};
|
|
630
633
|
}
|
|
631
634
|
const query = adapter.format(rawQuery);
|
|
635
|
+
const store = metaStore.getStore();
|
|
636
|
+
if (store) store.value = { formattedSql: query };
|
|
632
637
|
const syntaxError = await adapter.validate(query);
|
|
633
638
|
if (syntaxError) {
|
|
634
639
|
return {
|
|
@@ -648,7 +653,8 @@ function createSqlCommand(adapter) {
|
|
|
648
653
|
}
|
|
649
654
|
async function createResultTools(options) {
|
|
650
655
|
const { adapter, skillMounts, filesystem: baseFs } = options;
|
|
651
|
-
const
|
|
656
|
+
const metaStore = new AsyncLocalStorage();
|
|
657
|
+
const sqlCommand = createSqlCommand(adapter, metaStore);
|
|
652
658
|
const fsMounts = skillMounts.map(({ host, sandbox: sandbox2 }) => ({
|
|
653
659
|
mountPoint: path.dirname(sandbox2),
|
|
654
660
|
filesystem: new OverlayFs({
|
|
@@ -687,10 +693,19 @@ async function createResultTools(options) {
|
|
|
687
693
|
reasoning: z3.string().trim().describe("Brief reason for executing this command")
|
|
688
694
|
}),
|
|
689
695
|
execute: async ({ command }, execOptions) => {
|
|
690
|
-
|
|
696
|
+
const execute = tools2.bash.execute;
|
|
697
|
+
if (!execute) {
|
|
691
698
|
throw new Error("bash tool execution is not available");
|
|
692
699
|
}
|
|
693
|
-
return
|
|
700
|
+
return metaStore.run({}, async () => {
|
|
701
|
+
const result = await execute({ command }, execOptions);
|
|
702
|
+
const meta = metaStore.getStore()?.value;
|
|
703
|
+
return meta ? { ...result, meta } : result;
|
|
704
|
+
});
|
|
705
|
+
},
|
|
706
|
+
toModelOutput: ({ output }) => {
|
|
707
|
+
const { meta, ...rest } = output;
|
|
708
|
+
return { type: "json", value: rest };
|
|
694
709
|
}
|
|
695
710
|
});
|
|
696
711
|
return {
|
|
@@ -714,8 +729,6 @@ import {
|
|
|
714
729
|
defaultSettingsMiddleware,
|
|
715
730
|
wrapLanguageModel
|
|
716
731
|
} from "ai";
|
|
717
|
-
import { Console } from "node:console";
|
|
718
|
-
import { createWriteStream } from "node:fs";
|
|
719
732
|
import pRetry from "p-retry";
|
|
720
733
|
import z4 from "zod";
|
|
721
734
|
import "@deepagents/agent";
|
|
@@ -726,11 +739,6 @@ import {
|
|
|
726
739
|
structuredOutput as structuredOutput2,
|
|
727
740
|
user as user2
|
|
728
741
|
} from "@deepagents/context";
|
|
729
|
-
var logger = new Console({
|
|
730
|
-
stdout: createWriteStream("./sql-agent.log", { flags: "a" }),
|
|
731
|
-
stderr: createWriteStream("./sql-agent-error.log", { flags: "a" }),
|
|
732
|
-
inspectOptions: { depth: null }
|
|
733
|
-
});
|
|
734
742
|
var RETRY_TEMPERATURES = [0, 0.2, 0.3];
|
|
735
743
|
function extractSql(output) {
|
|
736
744
|
const match = output.match(/```sql\n?([\s\S]*?)```/);
|
|
@@ -869,7 +877,6 @@ async function withRetry(computation, options = { retries: 3 }) {
|
|
|
869
877
|
return APICallError.isInstance(context.error) || JSONParseError.isInstance(context.error) || TypeValidationError.isInstance(context.error) || NoObjectGeneratedError.isInstance(context.error) || NoOutputGeneratedError.isInstance(context.error) || NoContentGeneratedError.isInstance(context.error);
|
|
870
878
|
},
|
|
871
879
|
onFailedAttempt(context) {
|
|
872
|
-
logger.error(`toSQL`, context.error);
|
|
873
880
|
console.log(
|
|
874
881
|
`Attempt ${context.attemptNumber} failed. There are ${context.retriesLeft} retries left.`
|
|
875
882
|
);
|
|
@@ -939,9 +946,9 @@ var Checkpoint = class _Checkpoint {
|
|
|
939
946
|
points;
|
|
940
947
|
path;
|
|
941
948
|
configHash;
|
|
942
|
-
constructor(
|
|
949
|
+
constructor(path6, configHash, points) {
|
|
943
950
|
this.points = points;
|
|
944
|
-
this.path =
|
|
951
|
+
this.path = path6;
|
|
945
952
|
this.configHash = configHash;
|
|
946
953
|
}
|
|
947
954
|
/**
|
|
@@ -949,14 +956,14 @@ var Checkpoint = class _Checkpoint {
|
|
|
949
956
|
* Handles corrupted files and config changes gracefully.
|
|
950
957
|
*/
|
|
951
958
|
static async load(options) {
|
|
952
|
-
const { path:
|
|
953
|
-
if (existsSync(
|
|
959
|
+
const { path: path6, configHash } = options;
|
|
960
|
+
if (existsSync(path6)) {
|
|
954
961
|
try {
|
|
955
|
-
const content = readFileSync(
|
|
962
|
+
const content = readFileSync(path6, "utf-8");
|
|
956
963
|
const file = JSON.parse(content);
|
|
957
964
|
if (configHash && file.configHash && file.configHash !== configHash) {
|
|
958
965
|
console.log("\u26A0 Config changed, starting fresh");
|
|
959
|
-
return new _Checkpoint(
|
|
966
|
+
return new _Checkpoint(path6, configHash, {});
|
|
960
967
|
}
|
|
961
968
|
const points = file.points ?? {};
|
|
962
969
|
const totalEntries = Object.values(points).reduce(
|
|
@@ -964,14 +971,14 @@ var Checkpoint = class _Checkpoint {
|
|
|
964
971
|
0
|
|
965
972
|
);
|
|
966
973
|
console.log(`\u2713 Resuming from checkpoint (${totalEntries} entries)`);
|
|
967
|
-
return new _Checkpoint(
|
|
974
|
+
return new _Checkpoint(path6, configHash, points);
|
|
968
975
|
} catch {
|
|
969
976
|
console.log("\u26A0 Checkpoint corrupted, starting fresh");
|
|
970
|
-
return new _Checkpoint(
|
|
977
|
+
return new _Checkpoint(path6, configHash, {});
|
|
971
978
|
}
|
|
972
979
|
}
|
|
973
980
|
console.log("Starting new checkpoint");
|
|
974
|
-
return new _Checkpoint(
|
|
981
|
+
return new _Checkpoint(path6, configHash, {});
|
|
975
982
|
}
|
|
976
983
|
/**
|
|
977
984
|
* Run a single computation with checkpointing.
|
|
@@ -1832,10 +1839,9 @@ import * as path4 from "node:path";
|
|
|
1832
1839
|
|
|
1833
1840
|
// packages/text2sql/src/lib/fs/mssql/ddl.mssql-fs.ts
|
|
1834
1841
|
function mssqlFsDDL(schema) {
|
|
1835
|
-
const s = schema;
|
|
1836
1842
|
return `
|
|
1837
|
-
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[${
|
|
1838
|
-
CREATE TABLE [${
|
|
1843
|
+
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[${schema}].[fs_entries]') AND type = 'U')
|
|
1844
|
+
CREATE TABLE [${schema}].[fs_entries] (
|
|
1839
1845
|
path NVARCHAR(900) PRIMARY KEY,
|
|
1840
1846
|
type NVARCHAR(20) NOT NULL,
|
|
1841
1847
|
mode INT NOT NULL,
|
|
@@ -1845,17 +1851,17 @@ CREATE TABLE [${s}].[fs_entries] (
|
|
|
1845
1851
|
);
|
|
1846
1852
|
GO
|
|
1847
1853
|
|
|
1848
|
-
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_${
|
|
1849
|
-
CREATE INDEX [idx_${
|
|
1854
|
+
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_${schema}_fs_entries_type')
|
|
1855
|
+
CREATE INDEX [idx_${schema}_fs_entries_type] ON [${schema}].[fs_entries](type);
|
|
1850
1856
|
GO
|
|
1851
1857
|
|
|
1852
|
-
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[${
|
|
1853
|
-
CREATE TABLE [${
|
|
1858
|
+
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[${schema}].[fs_chunks]') AND type = 'U')
|
|
1859
|
+
CREATE TABLE [${schema}].[fs_chunks] (
|
|
1854
1860
|
path NVARCHAR(900) NOT NULL,
|
|
1855
1861
|
chunkIndex INT NOT NULL,
|
|
1856
1862
|
data VARBINARY(MAX) NOT NULL,
|
|
1857
1863
|
PRIMARY KEY (path, chunkIndex),
|
|
1858
|
-
FOREIGN KEY (path) REFERENCES [${
|
|
1864
|
+
FOREIGN KEY (path) REFERENCES [${schema}].[fs_entries](path) ON DELETE CASCADE ON UPDATE CASCADE
|
|
1859
1865
|
);
|
|
1860
1866
|
GO
|
|
1861
1867
|
`;
|
|
@@ -2079,11 +2085,21 @@ var MssqlFs = class _MssqlFs {
|
|
|
2079
2085
|
);
|
|
2080
2086
|
}
|
|
2081
2087
|
}
|
|
2082
|
-
async #readChunks(filePath) {
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2088
|
+
async #readChunks(filePath, transaction) {
|
|
2089
|
+
let rows;
|
|
2090
|
+
if (transaction) {
|
|
2091
|
+
const req = transaction.request();
|
|
2092
|
+
req.input("p0", filePath);
|
|
2093
|
+
const result2 = await req.query(
|
|
2094
|
+
`SELECT data FROM ${this.#t("fs_chunks")} WHERE path = @p0 ORDER BY chunkIndex`
|
|
2095
|
+
);
|
|
2096
|
+
rows = result2.recordset;
|
|
2097
|
+
} else {
|
|
2098
|
+
rows = await this.#query(
|
|
2099
|
+
`SELECT data FROM ${this.#t("fs_chunks")} WHERE path = @p0 ORDER BY chunkIndex`,
|
|
2100
|
+
[filePath]
|
|
2101
|
+
);
|
|
2102
|
+
}
|
|
2087
2103
|
if (rows.length === 0) {
|
|
2088
2104
|
return new Uint8Array(0);
|
|
2089
2105
|
}
|
|
@@ -2208,7 +2224,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
2208
2224
|
if (entry && entry.type !== "file") {
|
|
2209
2225
|
throw new Error(`appendFile: not a file: ${filePath}`);
|
|
2210
2226
|
}
|
|
2211
|
-
const existing = entry ? await this.#readChunks(prefixed) : new Uint8Array(0);
|
|
2227
|
+
const existing = entry ? await this.#readChunks(prefixed, transaction) : new Uint8Array(0);
|
|
2212
2228
|
const combined = new Uint8Array(existing.length + newData.length);
|
|
2213
2229
|
combined.set(existing, 0);
|
|
2214
2230
|
combined.set(newData, existing.length);
|
|
@@ -2563,6 +2579,11 @@ var MssqlFs = class _MssqlFs {
|
|
|
2563
2579
|
const allEntriesResult = await allEntriesReq.query(
|
|
2564
2580
|
`SELECT * FROM ${this.#t("fs_entries")} WHERE path = @p0 OR path LIKE @p0 + '/%' ORDER BY path DESC`
|
|
2565
2581
|
);
|
|
2582
|
+
const destDeleteReq = transaction.request();
|
|
2583
|
+
destDeleteReq.input("dp0", destPrefixed);
|
|
2584
|
+
await destDeleteReq.query(
|
|
2585
|
+
`DELETE FROM ${this.#t("fs_entries")} WHERE path = @dp0 OR path LIKE @dp0 + '/%'`
|
|
2586
|
+
);
|
|
2566
2587
|
for (const entry of [...allEntriesResult.recordset].reverse()) {
|
|
2567
2588
|
const relativePath = path4.posix.relative(srcPrefixed, entry.path);
|
|
2568
2589
|
const newPath = path4.posix.join(destPrefixed, relativePath);
|
|
@@ -2573,11 +2594,22 @@ var MssqlFs = class _MssqlFs {
|
|
|
2573
2594
|
insertReq.input("p3", entry.size);
|
|
2574
2595
|
insertReq.input("p4", Date.now());
|
|
2575
2596
|
insertReq.input("p5", entry.symlinkTarget);
|
|
2576
|
-
await insertReq.query(
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2597
|
+
await insertReq.query(`
|
|
2598
|
+
MERGE ${this.#t("fs_entries")} AS target
|
|
2599
|
+
USING (SELECT @p0 AS path) AS source
|
|
2600
|
+
ON target.path = source.path
|
|
2601
|
+
WHEN MATCHED THEN
|
|
2602
|
+
UPDATE SET type = @p1, mode = @p2, size = @p3, mtime = @p4, symlinkTarget = @p5
|
|
2603
|
+
WHEN NOT MATCHED THEN
|
|
2604
|
+
INSERT (path, type, mode, size, mtime, symlinkTarget)
|
|
2605
|
+
VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
|
|
2606
|
+
`);
|
|
2580
2607
|
if (entry.type === "file") {
|
|
2608
|
+
const deleteChunksReq = transaction.request();
|
|
2609
|
+
deleteChunksReq.input("p0", newPath);
|
|
2610
|
+
await deleteChunksReq.query(
|
|
2611
|
+
`DELETE FROM ${this.#t("fs_chunks")} WHERE path = @p0`
|
|
2612
|
+
);
|
|
2581
2613
|
const chunksReq = transaction.request();
|
|
2582
2614
|
chunksReq.input("p0", entry.path);
|
|
2583
2615
|
const chunksResult = await chunksReq.query(
|
|
@@ -2607,11 +2639,22 @@ var MssqlFs = class _MssqlFs {
|
|
|
2607
2639
|
insertReq.input("p3", srcEntry.size);
|
|
2608
2640
|
insertReq.input("p4", Date.now());
|
|
2609
2641
|
insertReq.input("p5", srcEntry.symlinkTarget);
|
|
2610
|
-
await insertReq.query(
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2642
|
+
await insertReq.query(`
|
|
2643
|
+
MERGE ${this.#t("fs_entries")} AS target
|
|
2644
|
+
USING (SELECT @p0 AS path) AS source
|
|
2645
|
+
ON target.path = source.path
|
|
2646
|
+
WHEN MATCHED THEN
|
|
2647
|
+
UPDATE SET type = @p1, mode = @p2, size = @p3, mtime = @p4, symlinkTarget = @p5
|
|
2648
|
+
WHEN NOT MATCHED THEN
|
|
2649
|
+
INSERT (path, type, mode, size, mtime, symlinkTarget)
|
|
2650
|
+
VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
|
|
2651
|
+
`);
|
|
2614
2652
|
if (srcEntry.type === "file") {
|
|
2653
|
+
const deleteChunksReq = transaction.request();
|
|
2654
|
+
deleteChunksReq.input("p0", destPrefixed);
|
|
2655
|
+
await deleteChunksReq.query(
|
|
2656
|
+
`DELETE FROM ${this.#t("fs_chunks")} WHERE path = @p0`
|
|
2657
|
+
);
|
|
2615
2658
|
const chunksReq = transaction.request();
|
|
2616
2659
|
chunksReq.input("p0", srcPrefixed);
|
|
2617
2660
|
const chunksResult = await chunksReq.query(
|
|
@@ -2775,6 +2818,876 @@ var MssqlFs = class _MssqlFs {
|
|
|
2775
2818
|
}
|
|
2776
2819
|
};
|
|
2777
2820
|
|
|
2821
|
+
// packages/text2sql/src/lib/fs/postgres/postgres-fs.ts
|
|
2822
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
2823
|
+
import * as path5 from "node:path";
|
|
2824
|
+
|
|
2825
|
+
// packages/text2sql/src/lib/fs/postgres/ddl.postgres-fs.ts
|
|
2826
|
+
function postgresFsDDL(schema) {
|
|
2827
|
+
return `
|
|
2828
|
+
CREATE SCHEMA IF NOT EXISTS "${schema}";
|
|
2829
|
+
|
|
2830
|
+
CREATE TABLE IF NOT EXISTS "${schema}"."fs_entries" (
|
|
2831
|
+
path TEXT PRIMARY KEY,
|
|
2832
|
+
type TEXT NOT NULL,
|
|
2833
|
+
mode INTEGER NOT NULL,
|
|
2834
|
+
size BIGINT NOT NULL,
|
|
2835
|
+
mtime BIGINT NOT NULL,
|
|
2836
|
+
symlink_target TEXT
|
|
2837
|
+
);
|
|
2838
|
+
|
|
2839
|
+
CREATE INDEX IF NOT EXISTS "idx_${schema}_fs_entries_type" ON "${schema}"."fs_entries"(type);
|
|
2840
|
+
|
|
2841
|
+
CREATE TABLE IF NOT EXISTS "${schema}"."fs_chunks" (
|
|
2842
|
+
path TEXT NOT NULL,
|
|
2843
|
+
chunk_index INTEGER NOT NULL,
|
|
2844
|
+
data BYTEA NOT NULL,
|
|
2845
|
+
PRIMARY KEY (path, chunk_index),
|
|
2846
|
+
FOREIGN KEY (path) REFERENCES "${schema}"."fs_entries"(path) ON DELETE CASCADE ON UPDATE CASCADE
|
|
2847
|
+
);
|
|
2848
|
+
`;
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2851
|
+
// packages/text2sql/src/lib/fs/postgres/postgres-fs.ts
|
|
2852
|
+
var PostgresFs = class _PostgresFs {
|
|
2853
|
+
#pool;
|
|
2854
|
+
#chunkSize;
|
|
2855
|
+
#root;
|
|
2856
|
+
#schema;
|
|
2857
|
+
#ownsPool;
|
|
2858
|
+
#isInitialized = false;
|
|
2859
|
+
constructor(options) {
|
|
2860
|
+
this.#chunkSize = options.chunkSize ?? 1024 * 1024;
|
|
2861
|
+
const schema = options.schema ?? "public";
|
|
2862
|
+
if (!/^[a-zA-Z_]\w*$/.test(schema)) {
|
|
2863
|
+
throw new Error(`Invalid schema name: "${schema}"`);
|
|
2864
|
+
}
|
|
2865
|
+
this.#schema = schema;
|
|
2866
|
+
const normalizedRoot = this.#normalizeRoot(options.root);
|
|
2867
|
+
this.#root = normalizedRoot === "/" ? "" : normalizedRoot;
|
|
2868
|
+
const pg = _PostgresFs.#requirePg();
|
|
2869
|
+
if (options.pool instanceof pg.Pool) {
|
|
2870
|
+
this.#pool = options.pool;
|
|
2871
|
+
this.#ownsPool = false;
|
|
2872
|
+
} else {
|
|
2873
|
+
this.#pool = typeof options.pool === "string" ? new pg.Pool({ connectionString: options.pool }) : new pg.Pool(options.pool);
|
|
2874
|
+
this.#ownsPool = true;
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
static #requirePg() {
|
|
2878
|
+
try {
|
|
2879
|
+
const require2 = createRequire2(import.meta.url);
|
|
2880
|
+
return require2("pg");
|
|
2881
|
+
} catch {
|
|
2882
|
+
throw new Error(
|
|
2883
|
+
'PostgresFs requires the "pg" package. Install it with: npm install pg'
|
|
2884
|
+
);
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
#t(name) {
|
|
2888
|
+
return `"${this.#schema}"."${name}"`;
|
|
2889
|
+
}
|
|
2890
|
+
async initialize() {
|
|
2891
|
+
const ddl = postgresFsDDL(this.#schema);
|
|
2892
|
+
await this.#pool.query(ddl);
|
|
2893
|
+
const rootSlashExists = await this.#rawQuery(
|
|
2894
|
+
`SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = '/') AS exists`
|
|
2895
|
+
);
|
|
2896
|
+
if (!rootSlashExists[0].exists) {
|
|
2897
|
+
await this.#rawExec(
|
|
2898
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ('/', 'directory', 493, 0, $1)`,
|
|
2899
|
+
[Date.now()]
|
|
2900
|
+
);
|
|
2901
|
+
}
|
|
2902
|
+
if (this.#root) {
|
|
2903
|
+
await this.#createParentDirs(this.#root);
|
|
2904
|
+
const rootExists = await this.#rawQuery(
|
|
2905
|
+
`SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
|
|
2906
|
+
[this.#root]
|
|
2907
|
+
);
|
|
2908
|
+
if (!rootExists[0].exists) {
|
|
2909
|
+
await this.#rawExec(
|
|
2910
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ($1, 'directory', 493, 0, $2)`,
|
|
2911
|
+
[this.#root, Date.now()]
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
this.#isInitialized = true;
|
|
2916
|
+
}
|
|
2917
|
+
#ensureInitialized() {
|
|
2918
|
+
if (!this.#isInitialized) {
|
|
2919
|
+
throw new Error(
|
|
2920
|
+
"PostgresFs not initialized. Call await fs.initialize() after construction."
|
|
2921
|
+
);
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
async #createParentDirs(p) {
|
|
2925
|
+
const segments = p.split("/").filter(Boolean);
|
|
2926
|
+
let currentPath = "/";
|
|
2927
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
2928
|
+
currentPath = path5.posix.join(currentPath, segments[i]);
|
|
2929
|
+
const exists = await this.#rawQuery(
|
|
2930
|
+
`SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
|
|
2931
|
+
[currentPath]
|
|
2932
|
+
);
|
|
2933
|
+
if (!exists[0].exists) {
|
|
2934
|
+
await this.#rawExec(
|
|
2935
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ($1, 'directory', 493, 0, $2)`,
|
|
2936
|
+
[currentPath, Date.now()]
|
|
2937
|
+
);
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
async #rawQuery(sql, params) {
|
|
2942
|
+
const result = await this.#pool.query(sql, params);
|
|
2943
|
+
return result.rows;
|
|
2944
|
+
}
|
|
2945
|
+
async #rawExec(sql, params) {
|
|
2946
|
+
const result = await this.#pool.query(sql, params);
|
|
2947
|
+
return result.rowCount ?? 0;
|
|
2948
|
+
}
|
|
2949
|
+
async #query(sql, params) {
|
|
2950
|
+
this.#ensureInitialized();
|
|
2951
|
+
return this.#rawQuery(sql, params);
|
|
2952
|
+
}
|
|
2953
|
+
async #exec(sql, params) {
|
|
2954
|
+
this.#ensureInitialized();
|
|
2955
|
+
return this.#rawExec(sql, params);
|
|
2956
|
+
}
|
|
2957
|
+
async #useTransaction(fn) {
|
|
2958
|
+
this.#ensureInitialized();
|
|
2959
|
+
const client = await this.#pool.connect();
|
|
2960
|
+
try {
|
|
2961
|
+
await client.query("BEGIN");
|
|
2962
|
+
const result = await fn(client);
|
|
2963
|
+
await client.query("COMMIT");
|
|
2964
|
+
return result;
|
|
2965
|
+
} catch (error) {
|
|
2966
|
+
await client.query("ROLLBACK");
|
|
2967
|
+
throw error;
|
|
2968
|
+
} finally {
|
|
2969
|
+
client.release();
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
#normalizeRoot(root) {
|
|
2973
|
+
return path5.posix.resolve("/", root.trim());
|
|
2974
|
+
}
|
|
2975
|
+
#prefixPath(p) {
|
|
2976
|
+
if (!this.#root) {
|
|
2977
|
+
return p;
|
|
2978
|
+
}
|
|
2979
|
+
if (p === "/") {
|
|
2980
|
+
return this.#root;
|
|
2981
|
+
}
|
|
2982
|
+
return path5.posix.join(this.#root, p);
|
|
2983
|
+
}
|
|
2984
|
+
#unprefixPath(p) {
|
|
2985
|
+
if (!this.#root) {
|
|
2986
|
+
return p;
|
|
2987
|
+
}
|
|
2988
|
+
if (p === this.#root) {
|
|
2989
|
+
return "/";
|
|
2990
|
+
}
|
|
2991
|
+
if (p.startsWith(this.#root + "/")) {
|
|
2992
|
+
return p.slice(this.#root.length) || "/";
|
|
2993
|
+
}
|
|
2994
|
+
return p;
|
|
2995
|
+
}
|
|
2996
|
+
#normalizePath(p) {
|
|
2997
|
+
return path5.posix.resolve("/", p);
|
|
2998
|
+
}
|
|
2999
|
+
#dirname(p) {
|
|
3000
|
+
const dir = path5.posix.dirname(p);
|
|
3001
|
+
return dir === "" ? "/" : dir;
|
|
3002
|
+
}
|
|
3003
|
+
async #ensureParentExists(filePath, client) {
|
|
3004
|
+
const parent = this.#dirname(filePath);
|
|
3005
|
+
const rootPath = this.#root || "/";
|
|
3006
|
+
if (parent === rootPath || parent === "/") return;
|
|
3007
|
+
const result = await client.query(
|
|
3008
|
+
`SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3009
|
+
[parent]
|
|
3010
|
+
);
|
|
3011
|
+
const entry = result.rows[0];
|
|
3012
|
+
if (!entry) {
|
|
3013
|
+
await this.#ensureParentExists(parent, client);
|
|
3014
|
+
await client.query(
|
|
3015
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ($1, 'directory', 493, 0, $2)`,
|
|
3016
|
+
[parent, Date.now()]
|
|
3017
|
+
);
|
|
3018
|
+
} else if (entry.type !== "directory") {
|
|
3019
|
+
throw new Error(`mkdir: parent is not a directory: ${parent}`);
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
async #writeChunks(filePath, content, client) {
|
|
3023
|
+
await client.query(`DELETE FROM ${this.#t("fs_chunks")} WHERE path = $1`, [
|
|
3024
|
+
filePath
|
|
3025
|
+
]);
|
|
3026
|
+
for (let i = 0; i < content.length; i += this.#chunkSize) {
|
|
3027
|
+
const chunk = content.slice(
|
|
3028
|
+
i,
|
|
3029
|
+
Math.min(i + this.#chunkSize, content.length)
|
|
3030
|
+
);
|
|
3031
|
+
await client.query(
|
|
3032
|
+
`INSERT INTO ${this.#t("fs_chunks")} (path, chunk_index, data) VALUES ($1, $2, $3)`,
|
|
3033
|
+
[filePath, Math.floor(i / this.#chunkSize), Buffer.from(chunk)]
|
|
3034
|
+
);
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
async #readChunks(filePath, client) {
|
|
3038
|
+
let rows;
|
|
3039
|
+
if (client) {
|
|
3040
|
+
const result2 = await client.query(
|
|
3041
|
+
`SELECT data FROM ${this.#t("fs_chunks")} WHERE path = $1 ORDER BY chunk_index`,
|
|
3042
|
+
[filePath]
|
|
3043
|
+
);
|
|
3044
|
+
rows = result2.rows;
|
|
3045
|
+
} else {
|
|
3046
|
+
rows = await this.#query(
|
|
3047
|
+
`SELECT data FROM ${this.#t("fs_chunks")} WHERE path = $1 ORDER BY chunk_index`,
|
|
3048
|
+
[filePath]
|
|
3049
|
+
);
|
|
3050
|
+
}
|
|
3051
|
+
if (rows.length === 0) {
|
|
3052
|
+
return new Uint8Array(0);
|
|
3053
|
+
}
|
|
3054
|
+
const totalSize = rows.reduce((sum, row) => sum + row.data.length, 0);
|
|
3055
|
+
const result = new Uint8Array(totalSize);
|
|
3056
|
+
let offset = 0;
|
|
3057
|
+
for (const row of rows) {
|
|
3058
|
+
result.set(new Uint8Array(row.data), offset);
|
|
3059
|
+
offset += row.data.length;
|
|
3060
|
+
}
|
|
3061
|
+
return result;
|
|
3062
|
+
}
|
|
3063
|
+
async #resolveSymlink(p, seen = /* @__PURE__ */ new Set()) {
|
|
3064
|
+
if (seen.has(p)) {
|
|
3065
|
+
throw new Error(`readFile: circular symlink: ${p}`);
|
|
3066
|
+
}
|
|
3067
|
+
const rows = await this.#query(
|
|
3068
|
+
`SELECT type, symlink_target FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3069
|
+
[p]
|
|
3070
|
+
);
|
|
3071
|
+
const entry = rows[0];
|
|
3072
|
+
if (!entry) {
|
|
3073
|
+
throw new Error(`ENOENT: no such file or directory: ${p}`);
|
|
3074
|
+
}
|
|
3075
|
+
if (entry.type !== "symlink") {
|
|
3076
|
+
return p;
|
|
3077
|
+
}
|
|
3078
|
+
seen.add(p);
|
|
3079
|
+
const target = this.#normalizePath(
|
|
3080
|
+
path5.posix.resolve(this.#dirname(p), entry.symlink_target)
|
|
3081
|
+
);
|
|
3082
|
+
return this.#resolveSymlink(target, seen);
|
|
3083
|
+
}
|
|
3084
|
+
#toUint8Array(content, encoding) {
|
|
3085
|
+
if (content instanceof Uint8Array) {
|
|
3086
|
+
return content;
|
|
3087
|
+
}
|
|
3088
|
+
const enc = encoding ?? "utf8";
|
|
3089
|
+
return new Uint8Array(Buffer.from(content, enc));
|
|
3090
|
+
}
|
|
3091
|
+
async close() {
|
|
3092
|
+
if (this.#ownsPool) {
|
|
3093
|
+
await this.#pool.end();
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
// ============================================================================
|
|
3097
|
+
// IFileSystem Implementation
|
|
3098
|
+
// ============================================================================
|
|
3099
|
+
async readFile(filePath, options) {
|
|
3100
|
+
const normalized = this.#normalizePath(filePath);
|
|
3101
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3102
|
+
const resolved = await this.#resolveSymlink(prefixed);
|
|
3103
|
+
const rows = await this.#query(
|
|
3104
|
+
`SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3105
|
+
[resolved]
|
|
3106
|
+
);
|
|
3107
|
+
const entry = rows[0];
|
|
3108
|
+
if (!entry) {
|
|
3109
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
3110
|
+
}
|
|
3111
|
+
if (entry.type === "directory") {
|
|
3112
|
+
throw new Error(`EISDIR: illegal operation on a directory: ${filePath}`);
|
|
3113
|
+
}
|
|
3114
|
+
const content = await this.#readChunks(resolved);
|
|
3115
|
+
const encoding = typeof options === "string" ? options : options?.encoding ?? "utf8";
|
|
3116
|
+
return Buffer.from(content).toString(encoding);
|
|
3117
|
+
}
|
|
3118
|
+
async readFileBuffer(filePath) {
|
|
3119
|
+
const normalized = this.#normalizePath(filePath);
|
|
3120
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3121
|
+
const resolved = await this.#resolveSymlink(prefixed);
|
|
3122
|
+
const rows = await this.#query(
|
|
3123
|
+
`SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3124
|
+
[resolved]
|
|
3125
|
+
);
|
|
3126
|
+
const entry = rows[0];
|
|
3127
|
+
if (!entry) {
|
|
3128
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
3129
|
+
}
|
|
3130
|
+
if (entry.type === "directory") {
|
|
3131
|
+
throw new Error(`EISDIR: illegal operation on a directory: ${filePath}`);
|
|
3132
|
+
}
|
|
3133
|
+
return this.#readChunks(resolved);
|
|
3134
|
+
}
|
|
3135
|
+
async writeFile(filePath, content, options) {
|
|
3136
|
+
const normalized = this.#normalizePath(filePath);
|
|
3137
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3138
|
+
const encoding = typeof options === "string" ? options : options?.encoding;
|
|
3139
|
+
const data = this.#toUint8Array(content, encoding);
|
|
3140
|
+
await this.#useTransaction(async (client) => {
|
|
3141
|
+
await this.#ensureParentExists(prefixed, client);
|
|
3142
|
+
await client.query(
|
|
3143
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime)
|
|
3144
|
+
VALUES ($1, 'file', 420, $2, $3)
|
|
3145
|
+
ON CONFLICT (path) DO UPDATE SET type = 'file', size = EXCLUDED.size, mtime = EXCLUDED.mtime`,
|
|
3146
|
+
[prefixed, data.length, Date.now()]
|
|
3147
|
+
);
|
|
3148
|
+
await this.#writeChunks(prefixed, data, client);
|
|
3149
|
+
});
|
|
3150
|
+
}
|
|
3151
|
+
async appendFile(filePath, content, options) {
|
|
3152
|
+
const normalized = this.#normalizePath(filePath);
|
|
3153
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3154
|
+
const encoding = typeof options === "string" ? options : options?.encoding;
|
|
3155
|
+
const newData = this.#toUint8Array(content, encoding);
|
|
3156
|
+
await this.#useTransaction(async (client) => {
|
|
3157
|
+
await this.#ensureParentExists(prefixed, client);
|
|
3158
|
+
const result = await client.query(
|
|
3159
|
+
`SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3160
|
+
[prefixed]
|
|
3161
|
+
);
|
|
3162
|
+
const entry = result.rows[0];
|
|
3163
|
+
if (entry && entry.type !== "file") {
|
|
3164
|
+
throw new Error(`appendFile: not a file: ${filePath}`);
|
|
3165
|
+
}
|
|
3166
|
+
const existing = entry ? await this.#readChunks(prefixed, client) : new Uint8Array(0);
|
|
3167
|
+
const combined = new Uint8Array(existing.length + newData.length);
|
|
3168
|
+
combined.set(existing, 0);
|
|
3169
|
+
combined.set(newData, existing.length);
|
|
3170
|
+
await client.query(
|
|
3171
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime)
|
|
3172
|
+
VALUES ($1, 'file', 420, $2, $3)
|
|
3173
|
+
ON CONFLICT (path) DO UPDATE SET size = EXCLUDED.size, mtime = EXCLUDED.mtime`,
|
|
3174
|
+
[prefixed, combined.length, Date.now()]
|
|
3175
|
+
);
|
|
3176
|
+
await this.#writeChunks(prefixed, combined, client);
|
|
3177
|
+
});
|
|
3178
|
+
}
|
|
3179
|
+
async exists(filePath) {
|
|
3180
|
+
const normalized = this.#normalizePath(filePath);
|
|
3181
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3182
|
+
const rows = await this.#query(
|
|
3183
|
+
`SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
|
|
3184
|
+
[prefixed]
|
|
3185
|
+
);
|
|
3186
|
+
return rows[0].exists;
|
|
3187
|
+
}
|
|
3188
|
+
async stat(filePath) {
|
|
3189
|
+
const normalized = this.#normalizePath(filePath);
|
|
3190
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3191
|
+
const resolved = await this.#resolveSymlink(prefixed);
|
|
3192
|
+
const rows = await this.#query(
|
|
3193
|
+
`SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3194
|
+
[resolved]
|
|
3195
|
+
);
|
|
3196
|
+
const entry = rows[0];
|
|
3197
|
+
if (!entry) {
|
|
3198
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
3199
|
+
}
|
|
3200
|
+
return {
|
|
3201
|
+
isFile: entry.type === "file",
|
|
3202
|
+
isDirectory: entry.type === "directory",
|
|
3203
|
+
isSymbolicLink: false,
|
|
3204
|
+
mode: Number(entry.mode),
|
|
3205
|
+
size: Number(entry.size),
|
|
3206
|
+
mtime: new Date(Number(entry.mtime))
|
|
3207
|
+
};
|
|
3208
|
+
}
|
|
3209
|
+
async lstat(filePath) {
|
|
3210
|
+
const normalized = this.#normalizePath(filePath);
|
|
3211
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3212
|
+
const rows = await this.#query(
|
|
3213
|
+
`SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3214
|
+
[prefixed]
|
|
3215
|
+
);
|
|
3216
|
+
const entry = rows[0];
|
|
3217
|
+
if (!entry) {
|
|
3218
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
3219
|
+
}
|
|
3220
|
+
return {
|
|
3221
|
+
isFile: entry.type === "file",
|
|
3222
|
+
isDirectory: entry.type === "directory",
|
|
3223
|
+
isSymbolicLink: entry.type === "symlink",
|
|
3224
|
+
mode: Number(entry.mode),
|
|
3225
|
+
size: Number(entry.size),
|
|
3226
|
+
mtime: new Date(Number(entry.mtime))
|
|
3227
|
+
};
|
|
3228
|
+
}
|
|
3229
|
+
async mkdir(dirPath, options) {
|
|
3230
|
+
const normalized = this.#normalizePath(dirPath);
|
|
3231
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3232
|
+
const existingRows = await this.#query(
|
|
3233
|
+
`SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3234
|
+
[prefixed]
|
|
3235
|
+
);
|
|
3236
|
+
const existing = existingRows[0];
|
|
3237
|
+
if (existing) {
|
|
3238
|
+
if (options?.recursive) {
|
|
3239
|
+
return;
|
|
3240
|
+
}
|
|
3241
|
+
throw new Error(`EEXIST: file already exists: ${dirPath}`);
|
|
3242
|
+
}
|
|
3243
|
+
await this.#useTransaction(async (client) => {
|
|
3244
|
+
if (options?.recursive) {
|
|
3245
|
+
const rootPath = this.#root || "/";
|
|
3246
|
+
const relativePath = path5.posix.relative(rootPath, prefixed);
|
|
3247
|
+
const segments = relativePath.split("/").filter(Boolean);
|
|
3248
|
+
let currentPath = rootPath;
|
|
3249
|
+
for (const segment of segments) {
|
|
3250
|
+
currentPath = path5.posix.join(currentPath, segment);
|
|
3251
|
+
const result = await client.query(
|
|
3252
|
+
`SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3253
|
+
[currentPath]
|
|
3254
|
+
);
|
|
3255
|
+
const exists = result.rows[0];
|
|
3256
|
+
if (!exists) {
|
|
3257
|
+
await client.query(
|
|
3258
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ($1, 'directory', 493, 0, $2)`,
|
|
3259
|
+
[currentPath, Date.now()]
|
|
3260
|
+
);
|
|
3261
|
+
} else if (exists.type !== "directory") {
|
|
3262
|
+
throw new Error(`mkdir: not a directory: ${currentPath}`);
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
} else {
|
|
3266
|
+
const parent = this.#dirname(prefixed);
|
|
3267
|
+
const parentResult = await client.query(
|
|
3268
|
+
`SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3269
|
+
[parent]
|
|
3270
|
+
);
|
|
3271
|
+
const parentEntry = parentResult.rows[0];
|
|
3272
|
+
if (!parentEntry) {
|
|
3273
|
+
throw new Error(`mkdir: parent does not exist: ${parent}`);
|
|
3274
|
+
}
|
|
3275
|
+
if (parentEntry.type !== "directory") {
|
|
3276
|
+
throw new Error(`mkdir: parent is not a directory: ${parent}`);
|
|
3277
|
+
}
|
|
3278
|
+
await client.query(
|
|
3279
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ($1, 'directory', 493, 0, $2)`,
|
|
3280
|
+
[prefixed, Date.now()]
|
|
3281
|
+
);
|
|
3282
|
+
}
|
|
3283
|
+
});
|
|
3284
|
+
}
|
|
3285
|
+
async readdir(dirPath) {
|
|
3286
|
+
const normalized = this.#normalizePath(dirPath);
|
|
3287
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3288
|
+
const resolved = await this.#resolveSymlink(prefixed);
|
|
3289
|
+
const entryRows = await this.#query(
|
|
3290
|
+
`SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3291
|
+
[resolved]
|
|
3292
|
+
);
|
|
3293
|
+
const entry = entryRows[0];
|
|
3294
|
+
if (!entry) {
|
|
3295
|
+
throw new Error(`ENOENT: no such file or directory: ${dirPath}`);
|
|
3296
|
+
}
|
|
3297
|
+
if (entry.type !== "directory") {
|
|
3298
|
+
throw new Error(`ENOTDIR: not a directory: ${dirPath}`);
|
|
3299
|
+
}
|
|
3300
|
+
const prefix = resolved === "/" ? "/" : resolved + "/";
|
|
3301
|
+
const rows = await this.#query(
|
|
3302
|
+
`SELECT path FROM ${this.#t("fs_entries")}
|
|
3303
|
+
WHERE path LIKE $1 || '%'
|
|
3304
|
+
AND path != $2
|
|
3305
|
+
AND path NOT LIKE $1 || '%/%'`,
|
|
3306
|
+
[prefix, resolved]
|
|
3307
|
+
);
|
|
3308
|
+
return rows.map((row) => path5.posix.basename(row.path));
|
|
3309
|
+
}
|
|
3310
|
+
async readdirWithFileTypes(dirPath) {
|
|
3311
|
+
const normalized = this.#normalizePath(dirPath);
|
|
3312
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3313
|
+
const resolved = await this.#resolveSymlink(prefixed);
|
|
3314
|
+
const entryRows = await this.#query(
|
|
3315
|
+
`SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3316
|
+
[resolved]
|
|
3317
|
+
);
|
|
3318
|
+
const entry = entryRows[0];
|
|
3319
|
+
if (!entry) {
|
|
3320
|
+
throw new Error(`ENOENT: no such file or directory: ${dirPath}`);
|
|
3321
|
+
}
|
|
3322
|
+
if (entry.type !== "directory") {
|
|
3323
|
+
throw new Error(`ENOTDIR: not a directory: ${dirPath}`);
|
|
3324
|
+
}
|
|
3325
|
+
const prefix = resolved === "/" ? "/" : resolved + "/";
|
|
3326
|
+
const rows = await this.#query(
|
|
3327
|
+
`SELECT path, type FROM ${this.#t("fs_entries")}
|
|
3328
|
+
WHERE path LIKE $1 || '%'
|
|
3329
|
+
AND path != $2
|
|
3330
|
+
AND path NOT LIKE $1 || '%/%'`,
|
|
3331
|
+
[prefix, resolved]
|
|
3332
|
+
);
|
|
3333
|
+
return rows.map((row) => ({
|
|
3334
|
+
name: path5.posix.basename(row.path),
|
|
3335
|
+
isFile: row.type === "file",
|
|
3336
|
+
isDirectory: row.type === "directory",
|
|
3337
|
+
isSymbolicLink: row.type === "symlink"
|
|
3338
|
+
}));
|
|
3339
|
+
}
|
|
3340
|
+
async rm(filePath, options) {
|
|
3341
|
+
const normalized = this.#normalizePath(filePath);
|
|
3342
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3343
|
+
const rows = await this.#query(
|
|
3344
|
+
`SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3345
|
+
[prefixed]
|
|
3346
|
+
);
|
|
3347
|
+
const entry = rows[0];
|
|
3348
|
+
if (!entry) {
|
|
3349
|
+
if (options?.force) {
|
|
3350
|
+
return;
|
|
3351
|
+
}
|
|
3352
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
3353
|
+
}
|
|
3354
|
+
await this.#useTransaction(async (client) => {
|
|
3355
|
+
if (entry.type === "directory") {
|
|
3356
|
+
const childrenResult = await client.query(
|
|
3357
|
+
`SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path LIKE $1 || '/%') AS exists`,
|
|
3358
|
+
[prefixed]
|
|
3359
|
+
);
|
|
3360
|
+
if (childrenResult.rows[0].exists && !options?.recursive) {
|
|
3361
|
+
throw new Error(`ENOTEMPTY: directory not empty: ${filePath}`);
|
|
3362
|
+
}
|
|
3363
|
+
await client.query(
|
|
3364
|
+
`DELETE FROM ${this.#t("fs_entries")} WHERE path = $1 OR path LIKE $1 || '/%'`,
|
|
3365
|
+
[prefixed]
|
|
3366
|
+
);
|
|
3367
|
+
} else {
|
|
3368
|
+
await client.query(
|
|
3369
|
+
`DELETE FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3370
|
+
[prefixed]
|
|
3371
|
+
);
|
|
3372
|
+
}
|
|
3373
|
+
});
|
|
3374
|
+
}
|
|
3375
|
+
async cp(src, dest, options) {
|
|
3376
|
+
const srcNormalized = this.#normalizePath(src);
|
|
3377
|
+
const destNormalized = this.#normalizePath(dest);
|
|
3378
|
+
const srcPrefixed = this.#prefixPath(srcNormalized);
|
|
3379
|
+
const destPrefixed = this.#prefixPath(destNormalized);
|
|
3380
|
+
const srcRows = await this.#query(
|
|
3381
|
+
`SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3382
|
+
[srcPrefixed]
|
|
3383
|
+
);
|
|
3384
|
+
const srcEntry = srcRows[0];
|
|
3385
|
+
if (!srcEntry) {
|
|
3386
|
+
throw new Error(`ENOENT: no such file or directory: ${src}`);
|
|
3387
|
+
}
|
|
3388
|
+
if (srcEntry.type === "directory" && !options?.recursive) {
|
|
3389
|
+
throw new Error(`cp: -r not specified; omitting directory: ${src}`);
|
|
3390
|
+
}
|
|
3391
|
+
await this.#useTransaction(async (client) => {
|
|
3392
|
+
await this.#ensureParentExists(destPrefixed, client);
|
|
3393
|
+
if (srcEntry.type === "directory") {
|
|
3394
|
+
const allEntriesResult = await client.query(
|
|
3395
|
+
`SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1 OR path LIKE $1 || '/%'`,
|
|
3396
|
+
[srcPrefixed]
|
|
3397
|
+
);
|
|
3398
|
+
for (const entry of allEntriesResult.rows) {
|
|
3399
|
+
const relativePath = path5.posix.relative(srcPrefixed, entry.path);
|
|
3400
|
+
const newPath = path5.posix.join(destPrefixed, relativePath);
|
|
3401
|
+
await client.query(
|
|
3402
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
|
|
3403
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
3404
|
+
ON CONFLICT (path) DO UPDATE SET type = EXCLUDED.type, mode = EXCLUDED.mode, size = EXCLUDED.size, mtime = EXCLUDED.mtime, symlink_target = EXCLUDED.symlink_target`,
|
|
3405
|
+
[
|
|
3406
|
+
newPath,
|
|
3407
|
+
entry.type,
|
|
3408
|
+
entry.mode,
|
|
3409
|
+
entry.size,
|
|
3410
|
+
Date.now(),
|
|
3411
|
+
entry.symlink_target
|
|
3412
|
+
]
|
|
3413
|
+
);
|
|
3414
|
+
if (entry.type === "file") {
|
|
3415
|
+
await client.query(
|
|
3416
|
+
`DELETE FROM ${this.#t("fs_chunks")} WHERE path = $1`,
|
|
3417
|
+
[newPath]
|
|
3418
|
+
);
|
|
3419
|
+
const chunksResult = await client.query(
|
|
3420
|
+
`SELECT chunk_index, data FROM ${this.#t("fs_chunks")} WHERE path = $1`,
|
|
3421
|
+
[entry.path]
|
|
3422
|
+
);
|
|
3423
|
+
for (const chunk of chunksResult.rows) {
|
|
3424
|
+
await client.query(
|
|
3425
|
+
`INSERT INTO ${this.#t("fs_chunks")} (path, chunk_index, data) VALUES ($1, $2, $3)`,
|
|
3426
|
+
[newPath, chunk.chunk_index, chunk.data]
|
|
3427
|
+
);
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
} else {
|
|
3432
|
+
await client.query(
|
|
3433
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
|
|
3434
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
3435
|
+
ON CONFLICT (path) DO UPDATE SET type = EXCLUDED.type, mode = EXCLUDED.mode, size = EXCLUDED.size, mtime = EXCLUDED.mtime, symlink_target = EXCLUDED.symlink_target`,
|
|
3436
|
+
[
|
|
3437
|
+
destPrefixed,
|
|
3438
|
+
srcEntry.type,
|
|
3439
|
+
srcEntry.mode,
|
|
3440
|
+
srcEntry.size,
|
|
3441
|
+
Date.now(),
|
|
3442
|
+
srcEntry.symlink_target
|
|
3443
|
+
]
|
|
3444
|
+
);
|
|
3445
|
+
if (srcEntry.type === "file") {
|
|
3446
|
+
const chunksResult = await client.query(
|
|
3447
|
+
`SELECT chunk_index, data FROM ${this.#t("fs_chunks")} WHERE path = $1`,
|
|
3448
|
+
[srcPrefixed]
|
|
3449
|
+
);
|
|
3450
|
+
await client.query(
|
|
3451
|
+
`DELETE FROM ${this.#t("fs_chunks")} WHERE path = $1`,
|
|
3452
|
+
[destPrefixed]
|
|
3453
|
+
);
|
|
3454
|
+
for (const chunk of chunksResult.rows) {
|
|
3455
|
+
await client.query(
|
|
3456
|
+
`INSERT INTO ${this.#t("fs_chunks")} (path, chunk_index, data) VALUES ($1, $2, $3)`,
|
|
3457
|
+
[destPrefixed, chunk.chunk_index, chunk.data]
|
|
3458
|
+
);
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
});
|
|
3463
|
+
}
|
|
3464
|
+
async mv(src, dest) {
|
|
3465
|
+
const srcNormalized = this.#normalizePath(src);
|
|
3466
|
+
const destNormalized = this.#normalizePath(dest);
|
|
3467
|
+
const srcPrefixed = this.#prefixPath(srcNormalized);
|
|
3468
|
+
const destPrefixed = this.#prefixPath(destNormalized);
|
|
3469
|
+
const srcRows = await this.#query(
|
|
3470
|
+
`SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3471
|
+
[srcPrefixed]
|
|
3472
|
+
);
|
|
3473
|
+
const srcEntry = srcRows[0];
|
|
3474
|
+
if (!srcEntry) {
|
|
3475
|
+
throw new Error(`ENOENT: no such file or directory: ${src}`);
|
|
3476
|
+
}
|
|
3477
|
+
await this.#useTransaction(async (client) => {
|
|
3478
|
+
await this.#ensureParentExists(destPrefixed, client);
|
|
3479
|
+
if (srcEntry.type === "directory") {
|
|
3480
|
+
const allEntriesResult = await client.query(
|
|
3481
|
+
`SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1 OR path LIKE $1 || '/%' ORDER BY path DESC`,
|
|
3482
|
+
[srcPrefixed]
|
|
3483
|
+
);
|
|
3484
|
+
await client.query(
|
|
3485
|
+
`DELETE FROM ${this.#t("fs_entries")} WHERE path = $1 OR path LIKE $1 || '/%'`,
|
|
3486
|
+
[destPrefixed]
|
|
3487
|
+
);
|
|
3488
|
+
for (const entry of [...allEntriesResult.rows].reverse()) {
|
|
3489
|
+
const relativePath = path5.posix.relative(srcPrefixed, entry.path);
|
|
3490
|
+
const newPath = path5.posix.join(destPrefixed, relativePath);
|
|
3491
|
+
await client.query(
|
|
3492
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
|
|
3493
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
3494
|
+
ON CONFLICT (path) DO UPDATE SET type = EXCLUDED.type, mode = EXCLUDED.mode, size = EXCLUDED.size, mtime = EXCLUDED.mtime, symlink_target = EXCLUDED.symlink_target`,
|
|
3495
|
+
[
|
|
3496
|
+
newPath,
|
|
3497
|
+
entry.type,
|
|
3498
|
+
entry.mode,
|
|
3499
|
+
entry.size,
|
|
3500
|
+
Date.now(),
|
|
3501
|
+
entry.symlink_target
|
|
3502
|
+
]
|
|
3503
|
+
);
|
|
3504
|
+
if (entry.type === "file") {
|
|
3505
|
+
await client.query(
|
|
3506
|
+
`DELETE FROM ${this.#t("fs_chunks")} WHERE path = $1`,
|
|
3507
|
+
[newPath]
|
|
3508
|
+
);
|
|
3509
|
+
const chunksResult = await client.query(
|
|
3510
|
+
`SELECT chunk_index, data FROM ${this.#t("fs_chunks")} WHERE path = $1`,
|
|
3511
|
+
[entry.path]
|
|
3512
|
+
);
|
|
3513
|
+
for (const chunk of chunksResult.rows) {
|
|
3514
|
+
await client.query(
|
|
3515
|
+
`INSERT INTO ${this.#t("fs_chunks")} (path, chunk_index, data) VALUES ($1, $2, $3)`,
|
|
3516
|
+
[newPath, chunk.chunk_index, chunk.data]
|
|
3517
|
+
);
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
await client.query(
|
|
3522
|
+
`DELETE FROM ${this.#t("fs_entries")} WHERE path = $1 OR path LIKE $1 || '/%'`,
|
|
3523
|
+
[srcPrefixed]
|
|
3524
|
+
);
|
|
3525
|
+
} else {
|
|
3526
|
+
await client.query(
|
|
3527
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
|
|
3528
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
3529
|
+
ON CONFLICT (path) DO UPDATE SET type = EXCLUDED.type, mode = EXCLUDED.mode, size = EXCLUDED.size, mtime = EXCLUDED.mtime, symlink_target = EXCLUDED.symlink_target`,
|
|
3530
|
+
[
|
|
3531
|
+
destPrefixed,
|
|
3532
|
+
srcEntry.type,
|
|
3533
|
+
srcEntry.mode,
|
|
3534
|
+
srcEntry.size,
|
|
3535
|
+
Date.now(),
|
|
3536
|
+
srcEntry.symlink_target
|
|
3537
|
+
]
|
|
3538
|
+
);
|
|
3539
|
+
if (srcEntry.type === "file") {
|
|
3540
|
+
await client.query(
|
|
3541
|
+
`DELETE FROM ${this.#t("fs_chunks")} WHERE path = $1`,
|
|
3542
|
+
[destPrefixed]
|
|
3543
|
+
);
|
|
3544
|
+
const chunksResult = await client.query(
|
|
3545
|
+
`SELECT chunk_index, data FROM ${this.#t("fs_chunks")} WHERE path = $1`,
|
|
3546
|
+
[srcPrefixed]
|
|
3547
|
+
);
|
|
3548
|
+
for (const chunk of chunksResult.rows) {
|
|
3549
|
+
await client.query(
|
|
3550
|
+
`INSERT INTO ${this.#t("fs_chunks")} (path, chunk_index, data) VALUES ($1, $2, $3)`,
|
|
3551
|
+
[destPrefixed, chunk.chunk_index, chunk.data]
|
|
3552
|
+
);
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
await client.query(
|
|
3556
|
+
`DELETE FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3557
|
+
[srcPrefixed]
|
|
3558
|
+
);
|
|
3559
|
+
}
|
|
3560
|
+
});
|
|
3561
|
+
}
|
|
3562
|
+
resolvePath(base, relativePath) {
|
|
3563
|
+
return path5.posix.resolve(base, relativePath);
|
|
3564
|
+
}
|
|
3565
|
+
getAllPaths() {
|
|
3566
|
+
throw new Error(
|
|
3567
|
+
"getAllPaths() is not supported in PostgresFs - use getAllPathsAsync() instead"
|
|
3568
|
+
);
|
|
3569
|
+
}
|
|
3570
|
+
async getAllPathsAsync() {
|
|
3571
|
+
const rows = await this.#query(
|
|
3572
|
+
`SELECT path FROM ${this.#t("fs_entries")} ORDER BY path`
|
|
3573
|
+
);
|
|
3574
|
+
return rows.map((row) => row.path);
|
|
3575
|
+
}
|
|
3576
|
+
async realpath(filePath) {
|
|
3577
|
+
const normalized = this.#normalizePath(filePath);
|
|
3578
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3579
|
+
const resolved = await this.#resolveSymlink(prefixed);
|
|
3580
|
+
const rows = await this.#query(
|
|
3581
|
+
`SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
|
|
3582
|
+
[resolved]
|
|
3583
|
+
);
|
|
3584
|
+
if (!rows[0].exists) {
|
|
3585
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
3586
|
+
}
|
|
3587
|
+
return this.#unprefixPath(resolved);
|
|
3588
|
+
}
|
|
3589
|
+
async utimes(filePath, _atime, mtime) {
|
|
3590
|
+
const normalized = this.#normalizePath(filePath);
|
|
3591
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3592
|
+
const resolved = await this.#resolveSymlink(prefixed);
|
|
3593
|
+
const result = await this.#exec(
|
|
3594
|
+
`UPDATE ${this.#t("fs_entries")} SET mtime = $1 WHERE path = $2`,
|
|
3595
|
+
[mtime.getTime(), resolved]
|
|
3596
|
+
);
|
|
3597
|
+
if (result === 0) {
|
|
3598
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
async chmod(filePath, mode) {
|
|
3602
|
+
const normalized = this.#normalizePath(filePath);
|
|
3603
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3604
|
+
const result = await this.#exec(
|
|
3605
|
+
`UPDATE ${this.#t("fs_entries")} SET mode = $1 WHERE path = $2`,
|
|
3606
|
+
[mode, prefixed]
|
|
3607
|
+
);
|
|
3608
|
+
if (result === 0) {
|
|
3609
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3612
|
+
async symlink(target, linkPath) {
|
|
3613
|
+
const normalized = this.#normalizePath(linkPath);
|
|
3614
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3615
|
+
const existingRows = await this.#query(
|
|
3616
|
+
`SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
|
|
3617
|
+
[prefixed]
|
|
3618
|
+
);
|
|
3619
|
+
if (existingRows[0].exists) {
|
|
3620
|
+
throw new Error(`EEXIST: file already exists: ${linkPath}`);
|
|
3621
|
+
}
|
|
3622
|
+
await this.#useTransaction(async (client) => {
|
|
3623
|
+
await this.#ensureParentExists(prefixed, client);
|
|
3624
|
+
await client.query(
|
|
3625
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
|
|
3626
|
+
VALUES ($1, 'symlink', 511, 0, $2, $3)`,
|
|
3627
|
+
[prefixed, Date.now(), target]
|
|
3628
|
+
);
|
|
3629
|
+
});
|
|
3630
|
+
}
|
|
3631
|
+
async link(existingPath, newPath) {
|
|
3632
|
+
const srcNormalized = this.#normalizePath(existingPath);
|
|
3633
|
+
const destNormalized = this.#normalizePath(newPath);
|
|
3634
|
+
const srcPrefixed = this.#prefixPath(srcNormalized);
|
|
3635
|
+
const destPrefixed = this.#prefixPath(destNormalized);
|
|
3636
|
+
const srcRows = await this.#query(
|
|
3637
|
+
`SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3638
|
+
[srcPrefixed]
|
|
3639
|
+
);
|
|
3640
|
+
const srcEntry = srcRows[0];
|
|
3641
|
+
if (!srcEntry) {
|
|
3642
|
+
throw new Error(`ENOENT: no such file or directory: ${existingPath}`);
|
|
3643
|
+
}
|
|
3644
|
+
if (srcEntry.type !== "file") {
|
|
3645
|
+
throw new Error(`link: not supported for directories: ${existingPath}`);
|
|
3646
|
+
}
|
|
3647
|
+
const existingRows = await this.#query(
|
|
3648
|
+
`SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
|
|
3649
|
+
[destPrefixed]
|
|
3650
|
+
);
|
|
3651
|
+
if (existingRows[0].exists) {
|
|
3652
|
+
throw new Error(`EEXIST: file already exists: ${newPath}`);
|
|
3653
|
+
}
|
|
3654
|
+
await this.#useTransaction(async (client) => {
|
|
3655
|
+
await this.#ensureParentExists(destPrefixed, client);
|
|
3656
|
+
await client.query(
|
|
3657
|
+
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime)
|
|
3658
|
+
VALUES ($1, 'file', $2, $3, $4)`,
|
|
3659
|
+
[destPrefixed, srcEntry.mode, srcEntry.size, Date.now()]
|
|
3660
|
+
);
|
|
3661
|
+
const chunksResult = await client.query(
|
|
3662
|
+
`SELECT chunk_index, data FROM ${this.#t("fs_chunks")} WHERE path = $1`,
|
|
3663
|
+
[srcPrefixed]
|
|
3664
|
+
);
|
|
3665
|
+
for (const chunk of chunksResult.rows) {
|
|
3666
|
+
await client.query(
|
|
3667
|
+
`INSERT INTO ${this.#t("fs_chunks")} (path, chunk_index, data) VALUES ($1, $2, $3)`,
|
|
3668
|
+
[destPrefixed, chunk.chunk_index, chunk.data]
|
|
3669
|
+
);
|
|
3670
|
+
}
|
|
3671
|
+
});
|
|
3672
|
+
}
|
|
3673
|
+
async readlink(linkPath) {
|
|
3674
|
+
const normalized = this.#normalizePath(linkPath);
|
|
3675
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3676
|
+
const rows = await this.#query(
|
|
3677
|
+
`SELECT type, symlink_target FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
3678
|
+
[prefixed]
|
|
3679
|
+
);
|
|
3680
|
+
const entry = rows[0];
|
|
3681
|
+
if (!entry) {
|
|
3682
|
+
throw new Error(`ENOENT: no such file or directory: ${linkPath}`);
|
|
3683
|
+
}
|
|
3684
|
+
if (entry.type !== "symlink") {
|
|
3685
|
+
throw new Error(`readlink: not a symbolic link: ${linkPath}`);
|
|
3686
|
+
}
|
|
3687
|
+
return entry.symlink_target;
|
|
3688
|
+
}
|
|
3689
|
+
};
|
|
3690
|
+
|
|
2778
3691
|
// packages/text2sql/src/lib/fs/scoped-fs.ts
|
|
2779
3692
|
var ScopedFs = class {
|
|
2780
3693
|
#base;
|
|
@@ -2783,29 +3696,29 @@ var ScopedFs = class {
|
|
|
2783
3696
|
this.#base = options.base;
|
|
2784
3697
|
this.#prefix = options.prefix.replace(/\/$/, "");
|
|
2785
3698
|
}
|
|
2786
|
-
#scope(
|
|
2787
|
-
return `${this.#prefix}${
|
|
3699
|
+
#scope(path6) {
|
|
3700
|
+
return `${this.#prefix}${path6}`;
|
|
2788
3701
|
}
|
|
2789
|
-
#unscope(
|
|
2790
|
-
if (
|
|
3702
|
+
#unscope(path6) {
|
|
3703
|
+
if (path6 === this.#prefix) {
|
|
2791
3704
|
return "/";
|
|
2792
3705
|
}
|
|
2793
|
-
if (
|
|
2794
|
-
return
|
|
3706
|
+
if (path6.startsWith(this.#prefix + "/")) {
|
|
3707
|
+
return path6.slice(this.#prefix.length) || "/";
|
|
2795
3708
|
}
|
|
2796
|
-
return
|
|
3709
|
+
return path6;
|
|
2797
3710
|
}
|
|
2798
|
-
async writeFile(
|
|
2799
|
-
await this.#base.writeFile(this.#scope(
|
|
3711
|
+
async writeFile(path6, content, options) {
|
|
3712
|
+
await this.#base.writeFile(this.#scope(path6), content, options);
|
|
2800
3713
|
}
|
|
2801
|
-
async appendFile(
|
|
2802
|
-
await this.#base.appendFile(this.#scope(
|
|
3714
|
+
async appendFile(path6, content, options) {
|
|
3715
|
+
await this.#base.appendFile(this.#scope(path6), content, options);
|
|
2803
3716
|
}
|
|
2804
|
-
async mkdir(
|
|
2805
|
-
return this.#base.mkdir(this.#scope(
|
|
3717
|
+
async mkdir(path6, options) {
|
|
3718
|
+
return this.#base.mkdir(this.#scope(path6), options);
|
|
2806
3719
|
}
|
|
2807
|
-
async rm(
|
|
2808
|
-
await this.#base.rm(this.#scope(
|
|
3720
|
+
async rm(path6, options) {
|
|
3721
|
+
await this.#base.rm(this.#scope(path6), options);
|
|
2809
3722
|
}
|
|
2810
3723
|
async cp(src, dest, options) {
|
|
2811
3724
|
await this.#base.cp(this.#scope(src), this.#scope(dest), options);
|
|
@@ -2813,8 +3726,8 @@ var ScopedFs = class {
|
|
|
2813
3726
|
async mv(src, dest) {
|
|
2814
3727
|
await this.#base.mv(this.#scope(src), this.#scope(dest));
|
|
2815
3728
|
}
|
|
2816
|
-
async chmod(
|
|
2817
|
-
return this.#base.chmod(this.#scope(
|
|
3729
|
+
async chmod(path6, mode) {
|
|
3730
|
+
return this.#base.chmod(this.#scope(path6), mode);
|
|
2818
3731
|
}
|
|
2819
3732
|
async symlink(target, linkPath) {
|
|
2820
3733
|
await this.#base.symlink(target, this.#scope(linkPath));
|
|
@@ -2822,35 +3735,35 @@ var ScopedFs = class {
|
|
|
2822
3735
|
async link(existingPath, newPath) {
|
|
2823
3736
|
await this.#base.link(this.#scope(existingPath), this.#scope(newPath));
|
|
2824
3737
|
}
|
|
2825
|
-
readFile(
|
|
2826
|
-
return this.#base.readFile(this.#scope(
|
|
3738
|
+
readFile(path6, options) {
|
|
3739
|
+
return this.#base.readFile(this.#scope(path6), options);
|
|
2827
3740
|
}
|
|
2828
|
-
readFileBuffer(
|
|
2829
|
-
return this.#base.readFileBuffer(this.#scope(
|
|
3741
|
+
readFileBuffer(path6) {
|
|
3742
|
+
return this.#base.readFileBuffer(this.#scope(path6));
|
|
2830
3743
|
}
|
|
2831
|
-
stat(
|
|
2832
|
-
return this.#base.stat(this.#scope(
|
|
3744
|
+
stat(path6) {
|
|
3745
|
+
return this.#base.stat(this.#scope(path6));
|
|
2833
3746
|
}
|
|
2834
|
-
lstat(
|
|
2835
|
-
return this.#base.lstat(this.#scope(
|
|
3747
|
+
lstat(path6) {
|
|
3748
|
+
return this.#base.lstat(this.#scope(path6));
|
|
2836
3749
|
}
|
|
2837
|
-
readdir(
|
|
2838
|
-
return this.#base.readdir(this.#scope(
|
|
3750
|
+
readdir(path6) {
|
|
3751
|
+
return this.#base.readdir(this.#scope(path6));
|
|
2839
3752
|
}
|
|
2840
|
-
readdirWithFileTypes(
|
|
2841
|
-
return this.#base.readdirWithFileTypes(this.#scope(
|
|
3753
|
+
readdirWithFileTypes(path6) {
|
|
3754
|
+
return this.#base.readdirWithFileTypes(this.#scope(path6));
|
|
2842
3755
|
}
|
|
2843
|
-
exists(
|
|
2844
|
-
return this.#base.exists(this.#scope(
|
|
3756
|
+
exists(path6) {
|
|
3757
|
+
return this.#base.exists(this.#scope(path6));
|
|
2845
3758
|
}
|
|
2846
|
-
readlink(
|
|
2847
|
-
return this.#base.readlink(this.#scope(
|
|
3759
|
+
readlink(path6) {
|
|
3760
|
+
return this.#base.readlink(this.#scope(path6));
|
|
2848
3761
|
}
|
|
2849
|
-
realpath(
|
|
2850
|
-
return this.#base.realpath(this.#scope(
|
|
3762
|
+
realpath(path6) {
|
|
3763
|
+
return this.#base.realpath(this.#scope(path6)).then((p) => this.#unscope(p));
|
|
2851
3764
|
}
|
|
2852
|
-
utimes(
|
|
2853
|
-
return this.#base.utimes(this.#scope(
|
|
3765
|
+
utimes(path6, atime, mtime) {
|
|
3766
|
+
return this.#base.utimes(this.#scope(path6), atime, mtime);
|
|
2854
3767
|
}
|
|
2855
3768
|
resolvePath(base, relativePath) {
|
|
2856
3769
|
return this.#base.resolvePath(base, relativePath);
|
|
@@ -2871,22 +3784,22 @@ var TrackedFs = class {
|
|
|
2871
3784
|
getCreatedFiles() {
|
|
2872
3785
|
return [...this.#createdFiles];
|
|
2873
3786
|
}
|
|
2874
|
-
async writeFile(
|
|
2875
|
-
await this.#base.writeFile(
|
|
2876
|
-
this.#createdFiles.add(
|
|
3787
|
+
async writeFile(path6, content, options) {
|
|
3788
|
+
await this.#base.writeFile(path6, content, options);
|
|
3789
|
+
this.#createdFiles.add(path6);
|
|
2877
3790
|
}
|
|
2878
|
-
async appendFile(
|
|
2879
|
-
await this.#base.appendFile(
|
|
2880
|
-
this.#createdFiles.add(
|
|
3791
|
+
async appendFile(path6, content, options) {
|
|
3792
|
+
await this.#base.appendFile(path6, content, options);
|
|
3793
|
+
this.#createdFiles.add(path6);
|
|
2881
3794
|
}
|
|
2882
|
-
async mkdir(
|
|
2883
|
-
return this.#base.mkdir(
|
|
3795
|
+
async mkdir(path6, options) {
|
|
3796
|
+
return this.#base.mkdir(path6, options);
|
|
2884
3797
|
}
|
|
2885
|
-
async rm(
|
|
2886
|
-
await this.#base.rm(
|
|
2887
|
-
this.#createdFiles.delete(
|
|
3798
|
+
async rm(path6, options) {
|
|
3799
|
+
await this.#base.rm(path6, options);
|
|
3800
|
+
this.#createdFiles.delete(path6);
|
|
2888
3801
|
if (options?.recursive) {
|
|
2889
|
-
const prefix =
|
|
3802
|
+
const prefix = path6.endsWith("/") ? path6 : path6 + "/";
|
|
2890
3803
|
for (const file of this.#createdFiles) {
|
|
2891
3804
|
if (file.startsWith(prefix)) {
|
|
2892
3805
|
this.#createdFiles.delete(file);
|
|
@@ -2903,8 +3816,8 @@ var TrackedFs = class {
|
|
|
2903
3816
|
this.#createdFiles.delete(src);
|
|
2904
3817
|
this.#createdFiles.add(dest);
|
|
2905
3818
|
}
|
|
2906
|
-
async chmod(
|
|
2907
|
-
return this.#base.chmod(
|
|
3819
|
+
async chmod(path6, mode) {
|
|
3820
|
+
return this.#base.chmod(path6, mode);
|
|
2908
3821
|
}
|
|
2909
3822
|
async symlink(target, linkPath) {
|
|
2910
3823
|
await this.#base.symlink(target, linkPath);
|
|
@@ -2914,29 +3827,29 @@ var TrackedFs = class {
|
|
|
2914
3827
|
await this.#base.link(existingPath, newPath);
|
|
2915
3828
|
this.#createdFiles.add(newPath);
|
|
2916
3829
|
}
|
|
2917
|
-
readFile(
|
|
2918
|
-
return this.#base.readFile(
|
|
3830
|
+
readFile(path6, options) {
|
|
3831
|
+
return this.#base.readFile(path6, options);
|
|
2919
3832
|
}
|
|
2920
|
-
readFileBuffer(
|
|
2921
|
-
return this.#base.readFileBuffer(
|
|
3833
|
+
readFileBuffer(path6) {
|
|
3834
|
+
return this.#base.readFileBuffer(path6);
|
|
2922
3835
|
}
|
|
2923
|
-
stat(
|
|
2924
|
-
return this.#base.stat(
|
|
3836
|
+
stat(path6) {
|
|
3837
|
+
return this.#base.stat(path6);
|
|
2925
3838
|
}
|
|
2926
|
-
lstat(
|
|
2927
|
-
return this.#base.lstat(
|
|
3839
|
+
lstat(path6) {
|
|
3840
|
+
return this.#base.lstat(path6);
|
|
2928
3841
|
}
|
|
2929
|
-
readdir(
|
|
2930
|
-
return this.#base.readdir(
|
|
3842
|
+
readdir(path6) {
|
|
3843
|
+
return this.#base.readdir(path6);
|
|
2931
3844
|
}
|
|
2932
|
-
readdirWithFileTypes(
|
|
2933
|
-
return this.#base.readdirWithFileTypes(
|
|
3845
|
+
readdirWithFileTypes(path6) {
|
|
3846
|
+
return this.#base.readdirWithFileTypes(path6);
|
|
2934
3847
|
}
|
|
2935
|
-
exists(
|
|
2936
|
-
return this.#base.exists(
|
|
3848
|
+
exists(path6) {
|
|
3849
|
+
return this.#base.exists(path6);
|
|
2937
3850
|
}
|
|
2938
|
-
readlink(
|
|
2939
|
-
return this.#base.readlink(
|
|
3851
|
+
readlink(path6) {
|
|
3852
|
+
return this.#base.readlink(path6);
|
|
2940
3853
|
}
|
|
2941
3854
|
resolvePath(base, relativePath) {
|
|
2942
3855
|
return this.#base.resolvePath(base, relativePath);
|
|
@@ -2944,11 +3857,11 @@ var TrackedFs = class {
|
|
|
2944
3857
|
getAllPaths() {
|
|
2945
3858
|
return this.#base.getAllPaths?.() ?? [];
|
|
2946
3859
|
}
|
|
2947
|
-
realpath(
|
|
2948
|
-
return this.#base.realpath(
|
|
3860
|
+
realpath(path6) {
|
|
3861
|
+
return this.#base.realpath(path6);
|
|
2949
3862
|
}
|
|
2950
|
-
utimes(
|
|
2951
|
-
return this.#base.utimes(
|
|
3863
|
+
utimes(path6, atime, mtime) {
|
|
3864
|
+
return this.#base.utimes(path6, atime, mtime);
|
|
2952
3865
|
}
|
|
2953
3866
|
};
|
|
2954
3867
|
|
|
@@ -3571,6 +4484,7 @@ export {
|
|
|
3571
4484
|
JsonCache,
|
|
3572
4485
|
MssqlFs,
|
|
3573
4486
|
Point,
|
|
4487
|
+
PostgresFs,
|
|
3574
4488
|
SQLValidationError,
|
|
3575
4489
|
ScopedFs,
|
|
3576
4490
|
SqliteFs,
|