dirsql 0.3.24 → 0.3.26
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/cli/interpret/buildTables.test.js +12 -14
- package/dist/cli/interpret/buildTables.test.js.map +1 -1
- package/dist/cli/interpret/interpret.test.js +55 -56
- package/dist/cli/interpret/interpret.test.js.map +1 -1
- package/dist/cli/interpret/loadApp.d.ts +3 -1
- package/dist/cli/interpret/loadApp.d.ts.map +1 -1
- package/dist/cli/interpret/loadApp.js +6 -2
- package/dist/cli/interpret/loadApp.js.map +1 -1
- package/dist/cli/interpret/loadApp.test.js +22 -30
- package/dist/cli/interpret/loadApp.test.js.map +1 -1
- package/dist/resolveConfig.test.js +19 -20
- package/dist/resolveConfig.test.js.map +1 -1
- package/docs/api/index.md +6 -4
- package/docs/getting-started.md +1 -1
- package/docs/guide/async.md +55 -12
- package/docs/guide/crdt.md +1 -1
- package/docs/guide/querying.md +3 -2
- package/docs/guide/tables.md +1 -1
- package/docs/guide/watching.md +6 -2
- package/package.json +11 -11
|
@@ -1,23 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { __setCoreForTesting, } from "../../index.js";
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
2
|
import { buildTables } from "./buildTables.js";
|
|
3
|
+
// `buildTables`'s only collaborator is `parseTableName` from the core barrel.
|
|
4
|
+
// Mock it so the SUT picks up the fake directly -- no real core, no
|
|
5
|
+
// `__setCoreForTesting` seam.
|
|
6
|
+
// Anchor the double to the real barrel (so it can't drift), then override the
|
|
7
|
+
// only collaborator `buildTables` reaches for. The barrel's `parseTableName`
|
|
8
|
+
// lazy-loads the native core *when called*; the fake below is what runs, so
|
|
9
|
+
// the real native implementation is never invoked.
|
|
10
|
+
vi.mock("../../index.js", async () => ({
|
|
11
|
+
...(await vi.importActual("../../index.js")),
|
|
12
|
+
parseTableName: vi.fn((ddl) => /CREATE\s+TABLE\s+(\w+)/i.exec(ddl)?.[1] ?? null),
|
|
13
|
+
}));
|
|
4
14
|
const noopExtract = () => [];
|
|
5
15
|
function fakeApp(tables) {
|
|
6
16
|
return { _options: { tables } };
|
|
7
17
|
}
|
|
8
18
|
describe("buildTables", () => {
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
__setCoreForTesting({
|
|
11
|
-
// biome-ignore lint/suspicious/noExplicitAny: minimal core stub
|
|
12
|
-
DirSQL: {},
|
|
13
|
-
parseTableName: vi
|
|
14
|
-
.fn()
|
|
15
|
-
.mockImplementation((ddl) => /CREATE\s+TABLE\s+(\w+)/i.exec(ddl)?.[1] ?? null),
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
__setCoreForTesting(null);
|
|
20
|
-
});
|
|
21
19
|
it("returns an empty map when the app has no tables", () => {
|
|
22
20
|
expect(buildTables(fakeApp([]))).toEqual(new Map());
|
|
23
21
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildTables.test.js","sourceRoot":"","sources":["../../../src/cli/interpret/buildTables.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"buildTables.test.js","sourceRoot":"","sources":["../../../src/cli/interpret/buildTables.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,23 @@
|
|
|
1
|
-
import { Readable } from "node:stream";
|
|
2
1
|
import { afterEach, beforeEach, describe, expect, it, vi, } from "vitest";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
2
|
+
import { buildTables } from "./buildTables.js";
|
|
3
|
+
import { dispatchExtract } from "./dispatchExtract.js";
|
|
5
4
|
import { interpret } from "./interpret.js";
|
|
6
|
-
import
|
|
7
|
-
import
|
|
5
|
+
import { loadApp } from "./loadApp.js";
|
|
6
|
+
import { writeMessage } from "./writeMessage.js";
|
|
7
|
+
// Every collaborator is mocked so the test isolates `interpret`'s glue logic.
|
|
8
|
+
// `node:readline`'s `createInterface` is faked to return an async iterable of
|
|
9
|
+
// lines directly -- this stands in for the line-buffered read over stdin, so
|
|
10
|
+
// the test never needs a real `Readable` stream.
|
|
11
|
+
vi.mock("./buildTables.js");
|
|
12
|
+
vi.mock("./dispatchExtract.js");
|
|
13
|
+
vi.mock("./loadApp.js");
|
|
14
|
+
vi.mock("./writeMessage.js");
|
|
15
|
+
vi.mock("node:readline", async () => ({
|
|
16
|
+
...(await vi.importActual("node:readline")),
|
|
17
|
+
createInterface: vi.fn(),
|
|
18
|
+
}));
|
|
19
|
+
// Imported after the mock so we get the mocked module.
|
|
20
|
+
const { createInterface } = await import("node:readline");
|
|
8
21
|
function fakeApp(overrides = {}) {
|
|
9
22
|
const app = {
|
|
10
23
|
ready: Promise.resolve(),
|
|
@@ -14,8 +27,14 @@ function fakeApp(overrides = {}) {
|
|
|
14
27
|
};
|
|
15
28
|
return app;
|
|
16
29
|
}
|
|
17
|
-
|
|
18
|
-
|
|
30
|
+
// Fake the readline interface as an async iterable yielding the given lines.
|
|
31
|
+
function stubLines(lines) {
|
|
32
|
+
vi.mocked(createInterface).mockReturnValue((async function* () {
|
|
33
|
+
for (const l of lines) {
|
|
34
|
+
yield l;
|
|
35
|
+
}
|
|
36
|
+
// biome-ignore lint/suspicious/noExplicitAny: minimal readline stub
|
|
37
|
+
})());
|
|
19
38
|
}
|
|
20
39
|
describe("interpret", () => {
|
|
21
40
|
let stderrWrite;
|
|
@@ -23,12 +42,12 @@ describe("interpret", () => {
|
|
|
23
42
|
stderrWrite = vi.fn();
|
|
24
43
|
vi.stubGlobal("process", {
|
|
25
44
|
...process,
|
|
26
|
-
stdin: asLines([]),
|
|
27
45
|
stderr: { write: stderrWrite },
|
|
28
46
|
});
|
|
29
|
-
|
|
30
|
-
vi.
|
|
31
|
-
vi.
|
|
47
|
+
stubLines([]);
|
|
48
|
+
vi.mocked(writeMessage).mockImplementation(() => { });
|
|
49
|
+
vi.mocked(buildTables).mockReturnValue(new Map());
|
|
50
|
+
vi.mocked(dispatchExtract).mockResolvedValue({
|
|
32
51
|
type: "result",
|
|
33
52
|
id: 1,
|
|
34
53
|
ok: true,
|
|
@@ -45,12 +64,12 @@ describe("interpret", () => {
|
|
|
45
64
|
expect(stderrWrite).toHaveBeenCalledExactlyOnceWith("dirsql interpret: expected one config path, got 0\n");
|
|
46
65
|
});
|
|
47
66
|
it("returns 1 and writes a dirsql-prefixed line when loadApp throws", async () => {
|
|
48
|
-
vi.
|
|
67
|
+
vi.mocked(loadApp).mockRejectedValue(new Error("no app"));
|
|
49
68
|
expect(await interpret("bad.mjs")).toBe(1);
|
|
50
69
|
expect(stderrWrite).toHaveBeenCalledExactlyOnceWith("dirsql interpret: no app\n");
|
|
51
70
|
});
|
|
52
71
|
it("coerces non-Error throws via String() in the stderr message", async () => {
|
|
53
|
-
vi.
|
|
72
|
+
vi.mocked(loadApp).mockRejectedValue("plain string");
|
|
54
73
|
expect(await interpret("bad.mjs")).toBe(1);
|
|
55
74
|
expect(stderrWrite).toHaveBeenCalledExactlyOnceWith("dirsql interpret: plain string\n");
|
|
56
75
|
});
|
|
@@ -60,9 +79,9 @@ describe("interpret", () => {
|
|
|
60
79
|
const app = fakeApp({
|
|
61
80
|
toJSON: () => ({ root: "/here", tables: [], ignore: [], persist: false }),
|
|
62
81
|
});
|
|
63
|
-
vi.
|
|
82
|
+
vi.mocked(loadApp).mockResolvedValue(app);
|
|
64
83
|
await interpret("good.mjs");
|
|
65
|
-
expect(
|
|
84
|
+
expect(writeMessage).toHaveBeenCalledWith({
|
|
66
85
|
type: "config",
|
|
67
86
|
state: { root: "/here", tables: [], ignore: [], persist: false },
|
|
68
87
|
});
|
|
@@ -75,77 +94,57 @@ describe("interpret", () => {
|
|
|
75
94
|
const app = fakeApp({
|
|
76
95
|
ready: { catch: catchSpy },
|
|
77
96
|
});
|
|
78
|
-
vi.
|
|
97
|
+
vi.mocked(loadApp).mockResolvedValue(app);
|
|
79
98
|
await interpret("good.mjs");
|
|
80
99
|
expect(catchSpy).toHaveBeenCalledOnce();
|
|
81
100
|
});
|
|
82
101
|
});
|
|
83
102
|
describe("extract loop", () => {
|
|
84
103
|
it("dispatches one extract request and writes the response", async () => {
|
|
85
|
-
vi.
|
|
104
|
+
vi.mocked(loadApp).mockResolvedValue(fakeApp());
|
|
86
105
|
const req = { type: "extract", id: 1, table: "t", path: "/a" };
|
|
87
|
-
|
|
88
|
-
...process,
|
|
89
|
-
stdin: asLines([JSON.stringify(req)]),
|
|
90
|
-
stderr: { write: stderrWrite },
|
|
91
|
-
});
|
|
106
|
+
stubLines([JSON.stringify(req)]);
|
|
92
107
|
const expected = {
|
|
93
108
|
type: "result",
|
|
94
109
|
id: 1,
|
|
95
110
|
ok: true,
|
|
96
111
|
rows: [{ row: "/a" }],
|
|
97
112
|
};
|
|
98
|
-
vi.mocked(
|
|
113
|
+
vi.mocked(dispatchExtract).mockResolvedValue(expected);
|
|
99
114
|
expect(await interpret("good.mjs")).toBe(0);
|
|
100
|
-
expect(
|
|
115
|
+
expect(dispatchExtract).toHaveBeenCalledWith(req, new Map());
|
|
101
116
|
// handshake first, then response
|
|
102
|
-
expect(
|
|
103
|
-
expect(
|
|
117
|
+
expect(writeMessage).toHaveBeenCalledTimes(2);
|
|
118
|
+
expect(writeMessage).toHaveBeenLastCalledWith(expected);
|
|
104
119
|
});
|
|
105
120
|
it("skips blank lines silently", async () => {
|
|
106
|
-
vi.
|
|
107
|
-
|
|
108
|
-
...process,
|
|
109
|
-
stdin: asLines(["", " "]),
|
|
110
|
-
stderr: { write: stderrWrite },
|
|
111
|
-
});
|
|
121
|
+
vi.mocked(loadApp).mockResolvedValue(fakeApp());
|
|
122
|
+
stubLines(["", " "]);
|
|
112
123
|
expect(await interpret("good.mjs")).toBe(0);
|
|
113
|
-
expect(
|
|
124
|
+
expect(dispatchExtract).not.toHaveBeenCalled();
|
|
114
125
|
// handshake only
|
|
115
|
-
expect(
|
|
126
|
+
expect(writeMessage).toHaveBeenCalledOnce();
|
|
116
127
|
});
|
|
117
128
|
it("skips malformed JSON silently", async () => {
|
|
118
|
-
vi.
|
|
119
|
-
|
|
120
|
-
...process,
|
|
121
|
-
stdin: asLines(["not json", "{also bad"]),
|
|
122
|
-
stderr: { write: stderrWrite },
|
|
123
|
-
});
|
|
129
|
+
vi.mocked(loadApp).mockResolvedValue(fakeApp());
|
|
130
|
+
stubLines(["not json", "{also bad"]);
|
|
124
131
|
expect(await interpret("good.mjs")).toBe(0);
|
|
125
|
-
expect(
|
|
132
|
+
expect(dispatchExtract).not.toHaveBeenCalled();
|
|
126
133
|
});
|
|
127
134
|
it("skips non-extract messages silently", async () => {
|
|
128
|
-
vi.
|
|
129
|
-
|
|
130
|
-
...process,
|
|
131
|
-
stdin: asLines([JSON.stringify({ type: "ping" })]),
|
|
132
|
-
stderr: { write: stderrWrite },
|
|
133
|
-
});
|
|
135
|
+
vi.mocked(loadApp).mockResolvedValue(fakeApp());
|
|
136
|
+
stubLines([JSON.stringify({ type: "ping" })]);
|
|
134
137
|
expect(await interpret("good.mjs")).toBe(0);
|
|
135
|
-
expect(
|
|
138
|
+
expect(dispatchExtract).not.toHaveBeenCalled();
|
|
136
139
|
});
|
|
137
140
|
it("skips a null / non-object JSON payload silently", async () => {
|
|
138
|
-
vi.
|
|
139
|
-
|
|
140
|
-
...process,
|
|
141
|
-
stdin: asLines(["null", "42", "[]"]),
|
|
142
|
-
stderr: { write: stderrWrite },
|
|
143
|
-
});
|
|
141
|
+
vi.mocked(loadApp).mockResolvedValue(fakeApp());
|
|
142
|
+
stubLines(["null", "42", "[]"]);
|
|
144
143
|
expect(await interpret("good.mjs")).toBe(0);
|
|
145
|
-
expect(
|
|
144
|
+
expect(dispatchExtract).not.toHaveBeenCalled();
|
|
146
145
|
});
|
|
147
146
|
it("returns 0 when stdin closes", async () => {
|
|
148
|
-
vi.
|
|
147
|
+
vi.mocked(loadApp).mockResolvedValue(fakeApp());
|
|
149
148
|
expect(await interpret("good.mjs")).toBe(0);
|
|
150
149
|
});
|
|
151
150
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interpret.test.js","sourceRoot":"","sources":["../../../src/cli/interpret/interpret.test.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"interpret.test.js","sourceRoot":"","sources":["../../../src/cli/interpret/interpret.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { DirSQL } from "../../index.js";
|
|
2
|
-
export declare function loadApp(configPath: string
|
|
2
|
+
export declare function loadApp(configPath: string, importer?: (url: string) => Promise<{
|
|
3
|
+
default?: unknown;
|
|
4
|
+
}>): Promise<DirSQL>;
|
|
3
5
|
//# sourceMappingURL=loadApp.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadApp.d.ts","sourceRoot":"","sources":["../../../src/cli/interpret/loadApp.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"loadApp.d.ts","sourceRoot":"","sources":["../../../src/cli/interpret/loadApp.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAM7C,wBAAsB,OAAO,CAC3B,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAC3C,GACZ,OAAO,CAAC,MAAM,CAAC,CAQjB"}
|
|
@@ -8,8 +8,12 @@
|
|
|
8
8
|
// no `default` export. The launcher in `cli/main.ts` catches both as
|
|
9
9
|
// startup failures.
|
|
10
10
|
import { pathToFileURL } from "node:url";
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
// The dynamic `import()` is the effectful collaborator. It can't be
|
|
12
|
+
// `vi.mock()`-ed (the specifier is a computed file URL), so it's exposed as
|
|
13
|
+
// an injected `importer` callback. The default preserves production behavior
|
|
14
|
+
// exactly; tests pass a fake `importer` to isolate the unit.
|
|
15
|
+
export async function loadApp(configPath, importer = (url) => import(url)) {
|
|
16
|
+
const mod = await importer(pathToFileURL(configPath).href);
|
|
13
17
|
if (!mod.default) {
|
|
14
18
|
throw new Error(`${configPath}: module must default-export a DirSQL instance`);
|
|
15
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadApp.js","sourceRoot":"","sources":["../../../src/cli/interpret/loadApp.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,sEAAsE;AACtE,oEAAoE;AACpE,oEAAoE;AACpE,qBAAqB;AACrB,EAAE;AACF,qEAAqE;AACrE,qEAAqE;AACrE,oBAAoB;AAEpB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"loadApp.js","sourceRoot":"","sources":["../../../src/cli/interpret/loadApp.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,sEAAsE;AACtE,oEAAoE;AACpE,oEAAoE;AACpE,qBAAqB;AACrB,EAAE;AACF,qEAAqE;AACrE,qEAAqE;AACrE,oBAAoB;AAEpB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,oEAAoE;AACpE,4EAA4E;AAC5E,6EAA6E;AAC7E,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,UAAkB,EAClB,WAA4D,CAAC,GAAG,EAAE,EAAE,CAClE,MAAM,CAAC,GAAG,CAAC;IAEb,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3D,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,GAAG,UAAU,gDAAgD,CAC9D,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC,OAAiB,CAAC;AAC/B,CAAC"}
|
|
@@ -1,41 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
5
2
|
import { loadApp } from "./loadApp.js";
|
|
6
3
|
describe("loadApp", () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
rmSync(dir, { recursive: true, force: true });
|
|
13
|
-
});
|
|
14
|
-
it("returns the default export of an .mjs config", async () => {
|
|
15
|
-
const path = join(dir, "config.mjs");
|
|
16
|
-
writeFileSync(path, "export default { sentinel: 'value' };\n");
|
|
17
|
-
const app = (await loadApp(path));
|
|
4
|
+
it("returns the default export the importer yields", async () => {
|
|
5
|
+
const importer = vi
|
|
6
|
+
.fn()
|
|
7
|
+
.mockResolvedValue({ default: { sentinel: "value" } });
|
|
8
|
+
const app = (await loadApp("/cfg/config.mjs", importer));
|
|
18
9
|
expect(app.sentinel).toBe("value");
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const path = join(dir, "config.cjs");
|
|
22
|
-
writeFileSync(path, "module.exports = { sentinel: 'cjs' };\n");
|
|
23
|
-
const app = (await loadApp(path));
|
|
24
|
-
expect(app.sentinel).toBe("cjs");
|
|
10
|
+
// The importer receives the file:// URL form of the config path.
|
|
11
|
+
expect(importer).toHaveBeenCalledWith(expect.stringMatching(/^file:.*config\.mjs$/));
|
|
25
12
|
});
|
|
26
13
|
it("throws a path-aware error when the module has no default export", async () => {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
await expect(loadApp(
|
|
30
|
-
await expect(loadApp(path)).rejects.toThrow(path);
|
|
14
|
+
const importer = vi.fn().mockResolvedValue({ x: 1 });
|
|
15
|
+
await expect(loadApp("/cfg/no_default.mjs", importer)).rejects.toThrow(/must default-export a DirSQL instance/);
|
|
16
|
+
await expect(loadApp("/cfg/no_default.mjs", importer)).rejects.toThrow("/cfg/no_default.mjs");
|
|
31
17
|
});
|
|
32
18
|
it("propagates errors thrown during module evaluation", async () => {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
19
|
+
const importer = vi.fn().mockRejectedValue(new Error("synthetic boom"));
|
|
20
|
+
await expect(loadApp("/cfg/boom.mjs", importer)).rejects.toThrow(/synthetic boom/);
|
|
21
|
+
});
|
|
22
|
+
it("rejects when the importer rejects (missing file)", async () => {
|
|
23
|
+
const importer = vi.fn().mockRejectedValue(new Error("Cannot find module"));
|
|
24
|
+
await expect(loadApp("/cfg/nope.mjs", importer)).rejects.toThrow();
|
|
36
25
|
});
|
|
37
|
-
it("
|
|
38
|
-
|
|
26
|
+
it("defaults to the real dynamic import when no importer is injected", async () => {
|
|
27
|
+
// Exercises the default `importer` arg (a real `import()`). The path
|
|
28
|
+
// doesn't exist, so the underlying import rejects -- which is all we
|
|
29
|
+
// need to drive the default callback's single line.
|
|
30
|
+
await expect(loadApp("/no/such/dirsql.config.mjs")).rejects.toThrow();
|
|
39
31
|
});
|
|
40
32
|
});
|
|
41
33
|
//# sourceMappingURL=loadApp.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadApp.test.js","sourceRoot":"","sources":["../../../src/cli/interpret/loadApp.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"loadApp.test.js","sourceRoot":"","sources":["../../../src/cli/interpret/loadApp.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE;IACvB,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,QAAQ,GAAG,EAAE;aAChB,EAAE,EAAE;aACJ,iBAAiB,CAAC,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;QACzD,MAAM,GAAG,GAAG,CAAC,MAAM,OAAO,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAEtD,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnC,iEAAiE;QACjE,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CACnC,MAAM,CAAC,cAAc,CAAC,sBAAsB,CAAC,CAC9C,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACrD,MAAM,MAAM,CAAC,OAAO,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACpE,uCAAuC,CACxC,CAAC;QACF,MAAM,MAAM,CAAC,OAAO,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACpE,qBAAqB,CACtB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACjE,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;QACxE,MAAM,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAC9D,gBAAgB,CACjB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAC5E,MAAM,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,qEAAqE;QACrE,qEAAqE;QACrE,oDAAoD;QACpD,MAAM,MAAM,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACxE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
// Unit tests for `resolveConfig`.
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { isAbsolute, join } from "node:path";
|
|
5
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
6
4
|
import { resolveConfig } from "./resolveConfig.js";
|
|
5
|
+
// `resolveConfig`'s only effectful collaborator is `readFileSync`; `node:path`
|
|
6
|
+
// and `smol-toml` are pure. Mock fs so the test never touches a real file --
|
|
7
|
+
// each "with a config file" case stubs the TOML body it wants to parse.
|
|
8
|
+
vi.mock("node:fs", async () => ({
|
|
9
|
+
...(await vi.importActual("node:fs")),
|
|
10
|
+
readFileSync: vi.fn(),
|
|
11
|
+
}));
|
|
7
12
|
const noopExtract = () => [];
|
|
13
|
+
// Fixed posix config path. CI is linux, so `dirname(resolve(cfgPath))` is
|
|
14
|
+
// deterministically `/cfg` -- relative config paths resolve against it.
|
|
15
|
+
const cfgPath = "/cfg/.dirsql.toml";
|
|
16
|
+
const cfgDir = "/cfg";
|
|
8
17
|
describe("resolveConfig", () => {
|
|
9
|
-
let dir;
|
|
10
|
-
let cfgPath;
|
|
11
|
-
beforeEach(() => {
|
|
12
|
-
dir = mkdtempSync(join(tmpdir(), "dirsql-resolveConfig-"));
|
|
13
|
-
cfgPath = join(dir, ".dirsql.toml");
|
|
14
|
-
});
|
|
15
|
-
afterEach(() => {
|
|
16
|
-
rmSync(dir, { recursive: true, force: true });
|
|
17
|
-
});
|
|
18
18
|
const writeCfg = (body) => {
|
|
19
|
-
|
|
19
|
+
vi.mocked(readFileSync).mockReturnValue(body);
|
|
20
20
|
};
|
|
21
21
|
describe("without a config file", () => {
|
|
22
22
|
it("forwards the root kwarg, defaults everything else", () => {
|
|
@@ -67,8 +67,7 @@ describe("resolveConfig", () => {
|
|
|
67
67
|
it("resolves a relative [dirsql].root against the config file's parent", () => {
|
|
68
68
|
writeCfg('[dirsql]\nroot = "data"\n');
|
|
69
69
|
const out = resolveConfig({ config: cfgPath });
|
|
70
|
-
expect(out.root).toBe(
|
|
71
|
-
expect(isAbsolute(out.root)).toBe(true);
|
|
70
|
+
expect(out.root).toBe("/cfg/data");
|
|
72
71
|
});
|
|
73
72
|
it("preserves an absolute [dirsql].root verbatim", () => {
|
|
74
73
|
writeCfg('[dirsql]\nroot = "/other/abs/path"\n');
|
|
@@ -78,17 +77,17 @@ describe("resolveConfig", () => {
|
|
|
78
77
|
it("defaults the root to the config file's parent when [dirsql].root is absent", () => {
|
|
79
78
|
writeCfg('[dirsql]\nignore = ["x"]\n');
|
|
80
79
|
const out = resolveConfig({ config: cfgPath });
|
|
81
|
-
expect(out.root).toBe(
|
|
80
|
+
expect(out.root).toBe(cfgDir);
|
|
82
81
|
});
|
|
83
82
|
it("defaults the root to the config file's parent when [dirsql] is absent entirely", () => {
|
|
84
83
|
writeCfg('[[table]]\nddl = "CREATE TABLE t (x TEXT)"\nglob = "*.json"\n');
|
|
85
84
|
const out = resolveConfig({ config: cfgPath });
|
|
86
|
-
expect(out.root).toBe(
|
|
85
|
+
expect(out.root).toBe(cfgDir);
|
|
87
86
|
});
|
|
88
87
|
it("handles a non-string [dirsql].root by falling back to the config dir", () => {
|
|
89
88
|
writeCfg("[dirsql]\nroot = 42\n");
|
|
90
89
|
const out = resolveConfig({ config: cfgPath });
|
|
91
|
-
expect(out.root).toBe(
|
|
90
|
+
expect(out.root).toBe(cfgDir);
|
|
92
91
|
});
|
|
93
92
|
it("reads [[table]] entries with strict defaulting to false", () => {
|
|
94
93
|
writeCfg('[[table]]\nddl = "CREATE TABLE t (x TEXT)"\nglob = "*.json"\n');
|
|
@@ -134,7 +133,7 @@ describe("resolveConfig", () => {
|
|
|
134
133
|
it("resolves a relative [dirsql].persist_path against the config dir", () => {
|
|
135
134
|
writeCfg('[dirsql]\npersist = true\npersist_path = "cache/dirsql.db"\n');
|
|
136
135
|
const out = resolveConfig({ config: cfgPath });
|
|
137
|
-
expect(out.persistPath).toBe(
|
|
136
|
+
expect(out.persistPath).toBe("/cfg/cache/dirsql.db");
|
|
138
137
|
});
|
|
139
138
|
it("preserves an absolute [dirsql].persist_path verbatim", () => {
|
|
140
139
|
writeCfg('[dirsql]\npersist_path = "/var/cache/dirsql.db"\n');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveConfig.test.js","sourceRoot":"","sources":["../src/resolveConfig.test.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"resolveConfig.test.js","sourceRoot":"","sources":["../src/resolveConfig.test.ts"],"names":[],"mappings":"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"}
|
package/docs/api/index.md
CHANGED
|
@@ -188,11 +188,12 @@ import { Table } from 'dirsql';
|
|
|
188
188
|
::: code-group
|
|
189
189
|
|
|
190
190
|
```python [Python]
|
|
191
|
-
Table(*, ddl: str, glob: str, extract: Callable[[str], list[dict]])
|
|
191
|
+
Table(*, ddl: str, glob: str, extract: Callable[[str], list[dict]], strict: bool = False)
|
|
192
192
|
```
|
|
193
193
|
|
|
194
194
|
```rust [Rust]
|
|
195
|
-
|
|
195
|
+
// Row = HashMap<String, Value>
|
|
196
|
+
Table::new(ddl: &str, glob: &str, extract: fn(&str) -> Vec<HashMap<String, Value>>)
|
|
196
197
|
```
|
|
197
198
|
|
|
198
199
|
```typescript [TypeScript]
|
|
@@ -207,7 +208,8 @@ Defines a mapping from files to SQLite table rows.
|
|
|
207
208
|
|
|
208
209
|
- `ddl` -- A `CREATE TABLE` statement. The table name is parsed from this DDL.
|
|
209
210
|
- `glob` -- A glob pattern matched against file paths relative to the root directory.
|
|
210
|
-
- `extract` -- A callable `(path) -> list[dict]`. Receives the
|
|
211
|
+
- `extract` -- A callable `(path) -> list[dict]`. Receives the path of the matched file -- relative to the scan root, or absolute when `root` is absolute. `dirsql` does not read file contents; a callback that needs the file body reads `path` itself. Returns a list of dicts/maps mapping column names to values. Return an empty list to skip a file.
|
|
212
|
+
- `strict` -- Optional (default `False`). Controls row/schema validation. In the default relaxed mode, extra row keys are dropped and missing columns become `NULL`. When `True`, every row key must be a valid column identifier and any extra or missing key raises an error. Surfaced in [serialization](#serialization) above as part of each table's `{ ddl, glob, strict }`.
|
|
211
213
|
|
|
212
214
|
**Attributes:**
|
|
213
215
|
|
|
@@ -231,7 +233,7 @@ use dirsql::RowEvent;
|
|
|
231
233
|
```
|
|
232
234
|
|
|
233
235
|
```typescript [TypeScript]
|
|
234
|
-
import { RowEvent } from 'dirsql';
|
|
236
|
+
import type { RowEvent } from 'dirsql';
|
|
235
237
|
```
|
|
236
238
|
|
|
237
239
|
:::
|
package/docs/getting-started.md
CHANGED
|
@@ -171,7 +171,7 @@ const results = await db.query(`
|
|
|
171
171
|
|
|
172
172
|
1. `dirsql` walks the directory tree
|
|
173
173
|
2. Files matching each table's glob pattern are identified
|
|
174
|
-
3. The `extract` function receives each matched file's absolute
|
|
174
|
+
3. The `extract` function receives each matched file's path (relative to the scan root, or absolute when `root` is absolute) and returns rows
|
|
175
175
|
4. Rows are inserted into an in-memory SQLite database
|
|
176
176
|
5. SQL queries run against that database
|
|
177
177
|
|
package/docs/guide/async.md
CHANGED
|
@@ -28,6 +28,7 @@ async def main():
|
|
|
28
28
|
),
|
|
29
29
|
],
|
|
30
30
|
)
|
|
31
|
+
await db.ready()
|
|
31
32
|
|
|
32
33
|
# Query (runs in a thread, does not block the event loop)
|
|
33
34
|
results = await db.query("SELECT * FROM items WHERE value > 10")
|
|
@@ -37,7 +38,31 @@ asyncio.run(main())
|
|
|
37
38
|
```
|
|
38
39
|
|
|
39
40
|
```rust [Rust]
|
|
40
|
-
use dirsql::{DirSQL, Table};
|
|
41
|
+
use dirsql::{DirSQL, Table, Value};
|
|
42
|
+
use std::collections::HashMap;
|
|
43
|
+
|
|
44
|
+
// See `row_from_json` in getting-started.md for a reusable helper that
|
|
45
|
+
// turns a JSON object into a dirsql row (dirsql::Value is not Deserialize,
|
|
46
|
+
// so a row can't be produced by serde_json::from_str directly).
|
|
47
|
+
fn row_from_json(raw: &str) -> HashMap<String, Value> {
|
|
48
|
+
let v: serde_json::Value = serde_json::from_str(raw).unwrap();
|
|
49
|
+
let serde_json::Value::Object(obj) = v else { return HashMap::new() };
|
|
50
|
+
obj.into_iter()
|
|
51
|
+
.map(|(k, val)| {
|
|
52
|
+
let v = match val {
|
|
53
|
+
serde_json::Value::String(s) => Value::Text(s),
|
|
54
|
+
serde_json::Value::Number(n) => n
|
|
55
|
+
.as_i64()
|
|
56
|
+
.map(Value::Integer)
|
|
57
|
+
.unwrap_or_else(|| Value::Real(n.as_f64().unwrap_or(0.0))),
|
|
58
|
+
serde_json::Value::Bool(b) => Value::Integer(b as i64),
|
|
59
|
+
serde_json::Value::Null => Value::Null,
|
|
60
|
+
other => Value::Text(other.to_string()),
|
|
61
|
+
};
|
|
62
|
+
(k, v)
|
|
63
|
+
})
|
|
64
|
+
.collect()
|
|
65
|
+
}
|
|
41
66
|
|
|
42
67
|
#[tokio::main]
|
|
43
68
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
@@ -47,7 +72,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
47
72
|
Table::new(
|
|
48
73
|
"CREATE TABLE items (name TEXT, value INTEGER)",
|
|
49
74
|
"data/*.json",
|
|
50
|
-
|path| vec![
|
|
75
|
+
|path| vec![row_from_json(&std::fs::read_to_string(path).unwrap())],
|
|
51
76
|
),
|
|
52
77
|
],
|
|
53
78
|
)?;
|
|
@@ -83,7 +108,7 @@ console.log(results);
|
|
|
83
108
|
## Constructor
|
|
84
109
|
|
|
85
110
|
```python
|
|
86
|
-
DirSQL(root=None, *, tables=None, ignore=None, config=None)
|
|
111
|
+
DirSQL(root=None, *, tables=None, ignore=None, config=None, persist=False, persist_path=None)
|
|
87
112
|
```
|
|
88
113
|
|
|
89
114
|
The constructor immediately starts scanning in a background thread via `asyncio.ensure_future`. The constructor itself returns immediately without blocking.
|
|
@@ -132,15 +157,29 @@ async for event in db.watch():
|
|
|
132
157
|
```
|
|
133
158
|
|
|
134
159
|
```rust [Rust]
|
|
160
|
+
// `RowEvent` is an enum; match on the variant to destructure its fields.
|
|
161
|
+
// `StreamExt` (for `.next()`) comes from the `futures` crate, which is only a
|
|
162
|
+
// dirsql dependency under its `cli` feature -- add it to your own project:
|
|
163
|
+
//
|
|
164
|
+
// cargo add futures
|
|
165
|
+
use dirsql::RowEvent;
|
|
135
166
|
use futures::StreamExt;
|
|
136
167
|
|
|
137
|
-
let mut stream = db.watch()
|
|
168
|
+
let mut stream = db.watch()?; // watch() returns Result<WatchStream>
|
|
138
169
|
while let Some(event) = stream.next().await {
|
|
139
|
-
match event
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
170
|
+
match event {
|
|
171
|
+
RowEvent::Insert { table, row, file_path } => {
|
|
172
|
+
println!("New row in {table} ({file_path}): {row:?}")
|
|
173
|
+
}
|
|
174
|
+
RowEvent::Update { table, new_row, file_path, .. } => {
|
|
175
|
+
println!("Updated row in {table} ({file_path}): {new_row:?}")
|
|
176
|
+
}
|
|
177
|
+
RowEvent::Delete { table, row, file_path } => {
|
|
178
|
+
println!("Deleted row from {table} ({file_path}): {row:?}")
|
|
179
|
+
}
|
|
180
|
+
RowEvent::Error { file_path, error, .. } => {
|
|
181
|
+
eprintln!("Error on {file_path:?}: {error}")
|
|
182
|
+
}
|
|
144
183
|
}
|
|
145
184
|
}
|
|
146
185
|
```
|
|
@@ -188,16 +227,20 @@ async def main():
|
|
|
188
227
|
```
|
|
189
228
|
|
|
190
229
|
```rust [Rust]
|
|
191
|
-
|
|
192
|
-
|
|
230
|
+
// `.next()` needs `StreamExt` from the `futures` crate (`cargo add futures`).
|
|
231
|
+
use futures::StreamExt;
|
|
232
|
+
|
|
233
|
+
async fn watch_and_serve(db: &DirSQL) -> Result<(), Box<dyn std::error::Error>> {
|
|
234
|
+
let mut stream = db.watch()?; // watch() returns Result<WatchStream>
|
|
193
235
|
while let Some(event) = stream.next().await {
|
|
194
236
|
notify_clients(&event).await;
|
|
195
237
|
}
|
|
238
|
+
Ok(())
|
|
196
239
|
}
|
|
197
240
|
|
|
198
241
|
#[tokio::main]
|
|
199
242
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
200
|
-
let db = DirSQL::new("./data", vec![
|
|
243
|
+
let db = DirSQL::new("./data", vec![/* tables */])?;
|
|
201
244
|
|
|
202
245
|
tokio::join!(
|
|
203
246
|
watch_and_serve(&db),
|
package/docs/guide/crdt.md
CHANGED
package/docs/guide/querying.md
CHANGED
|
@@ -160,8 +160,9 @@ assert!(matches!(err, dirsql::DirSqlError::WriteForbidden));
|
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
```typescript [TypeScript]
|
|
163
|
-
//
|
|
164
|
-
|
|
163
|
+
// Rejects with an Error whose message explains writes are not accepted.
|
|
164
|
+
// `db.query` is async, so assert on the rejected promise.
|
|
165
|
+
await expect(db.query('DELETE FROM posts')).rejects.toThrow(/read-only/i);
|
|
165
166
|
```
|
|
166
167
|
|
|
167
168
|
:::
|
package/docs/guide/tables.md
CHANGED
|
@@ -91,7 +91,7 @@ Glob syntax follows standard Unix globbing rules. `**` matches any number of dir
|
|
|
91
91
|
|
|
92
92
|
A callable `(path: str) -> list[dict]` that converts a file into rows.
|
|
93
93
|
|
|
94
|
-
- `path` is the **
|
|
94
|
+
- `path` is the path of the matched file, **relative to the scan root** (or absolute when the `root` passed to `DirSQL` is absolute)
|
|
95
95
|
- Return a list of dicts, where each dict maps column names to values
|
|
96
96
|
- Return an empty list to skip a file
|
|
97
97
|
|
package/docs/guide/watching.md
CHANGED
|
@@ -36,6 +36,10 @@ async for event in db.watch():
|
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
```rust [Rust]
|
|
39
|
+
// `StreamExt` (for `.next()`) comes from the `futures` crate. dirsql only
|
|
40
|
+
// depends on `futures` under its `cli` feature, so add it to your project:
|
|
41
|
+
//
|
|
42
|
+
// cargo add futures
|
|
39
43
|
use dirsql::{DirSQL, RowEvent, Table, Value};
|
|
40
44
|
use futures::StreamExt;
|
|
41
45
|
use std::collections::HashMap;
|
|
@@ -84,8 +88,8 @@ while let Some(event) = stream.next().await {
|
|
|
84
88
|
RowEvent::Delete { table, row, file_path } => {
|
|
85
89
|
println!("delete on {table} ({file_path}): {row:?}")
|
|
86
90
|
}
|
|
87
|
-
RowEvent::Error { file_path, error } => {
|
|
88
|
-
println!("error on {file_path:?}: {error}")
|
|
91
|
+
RowEvent::Error { table, file_path, error } => {
|
|
92
|
+
println!("error on {table:?} {file_path:?}: {error}")
|
|
89
93
|
}
|
|
90
94
|
}
|
|
91
95
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dirsql",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.26",
|
|
4
4
|
"description": "Ephemeral SQL index over a local directory",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "https://github.com/thekevinscott/dirsql",
|
|
@@ -195,15 +195,15 @@
|
|
|
195
195
|
"smol-toml": "^1.6.1"
|
|
196
196
|
},
|
|
197
197
|
"optionalDependencies": {
|
|
198
|
-
"@dirsql/lib-linux-x64-gnu": "0.3.
|
|
199
|
-
"@dirsql/lib-linux-arm64-gnu": "0.3.
|
|
200
|
-
"@dirsql/lib-darwin-x64": "0.3.
|
|
201
|
-
"@dirsql/lib-darwin-arm64": "0.3.
|
|
202
|
-
"@dirsql/lib-win32-x64-msvc": "0.3.
|
|
203
|
-
"@dirsql/cli-linux-x64-gnu": "0.3.
|
|
204
|
-
"@dirsql/cli-linux-arm64-gnu": "0.3.
|
|
205
|
-
"@dirsql/cli-darwin-x64": "0.3.
|
|
206
|
-
"@dirsql/cli-darwin-arm64": "0.3.
|
|
207
|
-
"@dirsql/cli-win32-x64-msvc": "0.3.
|
|
198
|
+
"@dirsql/lib-linux-x64-gnu": "0.3.26",
|
|
199
|
+
"@dirsql/lib-linux-arm64-gnu": "0.3.26",
|
|
200
|
+
"@dirsql/lib-darwin-x64": "0.3.26",
|
|
201
|
+
"@dirsql/lib-darwin-arm64": "0.3.26",
|
|
202
|
+
"@dirsql/lib-win32-x64-msvc": "0.3.26",
|
|
203
|
+
"@dirsql/cli-linux-x64-gnu": "0.3.26",
|
|
204
|
+
"@dirsql/cli-linux-arm64-gnu": "0.3.26",
|
|
205
|
+
"@dirsql/cli-darwin-x64": "0.3.26",
|
|
206
|
+
"@dirsql/cli-darwin-arm64": "0.3.26",
|
|
207
|
+
"@dirsql/cli-win32-x64-msvc": "0.3.26"
|
|
208
208
|
}
|
|
209
209
|
}
|