@zukhruf/sqlite-reader 0.0.0-stage → 0.3.53

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ezzabuzaid
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,94 @@
1
- # Temporary Holding Version
1
+ # @zukhruf/sqlite-reader
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Some programs keep their data in a SQLite database, and write it while they run. Another program can read that database. It opens a read-only connection, a reader. The open can fail because the file is missing, or because the system denies access to it. The caller can fix these failures, so the reader must name them. This package opens the reader, and throws `UnavailableError` for these failures.
4
+
5
+ `@zukhruf/sqlite-snapshot` and `@zukhruf/sqlite-data-version` open their connections with this package. Each of them exports the same `UnavailableError`, so a catch with `instanceof` works with the package that the caller imports.
6
+
7
+ The words in these documents have one meaning each. See the glossary in [CONTEXT.md](./CONTEXT.md).
8
+
9
+ ## Use it
10
+
11
+ This project is an experiment. It uses only `node:sqlite`.
12
+
13
+ ```sh
14
+ npm install @zukhruf/sqlite-reader
15
+ ```
16
+
17
+ ```ts
18
+ import { mkdtempDisposable } from 'node:fs/promises';
19
+ import { tmpdir } from 'node:os';
20
+ import { join } from 'node:path';
21
+ import { DatabaseSync } from 'node:sqlite';
22
+
23
+ import { UnavailableError, openReader } from '@zukhruf/sqlite-reader';
24
+
25
+ await using scratch = await mkdtempDisposable(join(tmpdir(), 'reader-'));
26
+ const path = join(scratch.path, 'notes.sqlite');
27
+
28
+ // The owner: another connection that writes the database through a WAL.
29
+ using owner = new DatabaseSync(path);
30
+ owner.exec(`PRAGMA journal_mode = WAL;
31
+ CREATE TABLE notes (title TEXT);
32
+ INSERT INTO notes VALUES ('first')`);
33
+
34
+ using reader = openReader(path);
35
+ console.log(reader.prepare('SELECT title FROM notes').all());
36
+
37
+ try {
38
+ openReader(join(scratch.path, 'missing', 'notes.sqlite'));
39
+ } catch (error) {
40
+ console.log(error instanceof UnavailableError);
41
+ }
42
+ ```
43
+
44
+ Output:
45
+
46
+ ```
47
+ [ [Object: null prototype] { title: 'first' } ]
48
+ true
49
+ ```
50
+
51
+ ## The rules
52
+
53
+ - `openReader(path)` gives a `DatabaseSync` of `node:sqlite`, opened with `readOnly: true`. The caller owns the connection, and closes it.
54
+ - The reader never writes. A write fails with the error of SQLite.
55
+ - The package throws its own `UnavailableError`. A host that needs its own error translates the error of the package at its boundary, and keeps the error of the package as `cause`.
56
+
57
+ ## Errors
58
+
59
+ | Error | When |
60
+ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
61
+ | `UnavailableError` | SQLite cannot open the file: `SQLITE_CANTOPEN` (14), for a missing file or a file that the system denies, a privacy denial too. `path` is the path. `cause` is the error of SQLite. |
62
+ | The error of SQLite | All other failures. |
63
+
64
+ `UnavailableError` takes `(path, cause)`. Another package can throw it for the same failure from a different source. For example, `lastWrite` of `@zukhruf/sqlite-data-version` throws it when the file system cannot give the times of the file.
65
+
66
+ ## The same concepts in other systems
67
+
68
+ | System | What it does |
69
+ | ------------------- | -------------------------------------------------------------------------------------------------------------- |
70
+ | `sqlite3 -readonly` | It opens a database read-only. A missing file fails with "unable to open database file", the text of CANTOPEN. |
71
+ | Python `sqlite3` | `sqlite3.connect('file:path?mode=ro', uri=True)` opens a database read-only, and raises `OperationalError`. |
72
+
73
+ ## Documentation
74
+
75
+ - [0001 One package opens the reader and owns UnavailableError](./docs/adr/0001-one-package-opens-the-reader-and-owns-unavailable-error.md)
76
+
77
+ ## Development
78
+
79
+ This package is part of the [zukhruf](../../../README.md) workspace. Run the commands from the workspace root.
80
+
81
+ ```sh
82
+ npm install
83
+ npx nx run sqlite-reader:test # builds, then runs the tests in src/
84
+ npx nx run sqlite-reader:typecheck # formats, lints, then type checks
85
+ npx nx run sqlite-reader:build # compiles src/ to dist/
86
+ ```
87
+
88
+ ```
89
+ src/
90
+ index.ts the exports
91
+ open-reader.ts openReader
92
+ unavailable-error.ts UnavailableError
93
+ open-reader.test.ts the tests, on real SQLite files
94
+ ```
@@ -0,0 +1,3 @@
1
+ export { openReader } from './open-reader.ts';
2
+ export { UnavailableError } from './unavailable-error.ts';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { openReader } from "./open-reader.js";
2
+ export { UnavailableError } from "./unavailable-error.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ export declare function openReader(path: string): DatabaseSync;
3
+ //# sourceMappingURL=open-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-reader.d.ts","sourceRoot":"","sources":["../src/open-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAgBrD"}
@@ -0,0 +1,20 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import { UnavailableError } from "./unavailable-error.js";
3
+ // A read-only connection to a database that another process owns.
4
+ export function openReader(path) {
5
+ try {
6
+ return new DatabaseSync(path, { readOnly: true });
7
+ }
8
+ catch (cause) {
9
+ if (cause instanceof Error &&
10
+ 'errcode' in cause &&
11
+ // SQLite's CANTOPEN (14). A missing or denied file fails the open()
12
+ // syscall, and SQLite reports that as CANTOPEN, a privacy denial too.
13
+ // SQLite gives AUTH only for an authorizer, and this private connection
14
+ // has none. Any other failure is not one that access can fix.
15
+ cause.errcode === 14)
16
+ throw new UnavailableError(path, cause);
17
+ throw cause;
18
+ }
19
+ }
20
+ //# sourceMappingURL=open-reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-reader.js","sourceRoot":"","sources":["../src/open-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,kEAAkE;AAClE,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,IAAI,CAAC;QACH,OAAO,IAAI,YAAY,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IACE,KAAK,YAAY,KAAK;YACtB,SAAS,IAAI,KAAK;YAClB,oEAAoE;YACpE,sEAAsE;YACtE,wEAAwE;YACxE,8DAA8D;YAC9D,KAAK,CAAC,OAAO,KAAK,EAAE;YAEpB,MAAM,IAAI,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC1C,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** The database file is missing, or this process has no access to it. */
2
+ export declare class UnavailableError extends Error {
3
+ readonly path: string;
4
+ constructor(path: string, cause: unknown);
5
+ }
6
+ //# sourceMappingURL=unavailable-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unavailable-error.d.ts","sourceRoot":"","sources":["../src/unavailable-error.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAQzC"}
@@ -0,0 +1,10 @@
1
+ /** The database file is missing, or this process has no access to it. */
2
+ export class UnavailableError extends Error {
3
+ path;
4
+ constructor(path, cause) {
5
+ super(`The database at ${JSON.stringify(path)} cannot be opened: the file is missing, or this process has no access to it. Give the process access, or check the path.`, { cause });
6
+ this.name = 'UnavailableError';
7
+ this.path = path;
8
+ }
9
+ }
10
+ //# sourceMappingURL=unavailable-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unavailable-error.js","sourceRoot":"","sources":["../src/unavailable-error.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,IAAI,CAAS;IAEtB,YAAY,IAAY,EAAE,KAAc;QACtC,KAAK,CACH,mBAAmB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,0HAA0H,EACjK,EAAE,KAAK,EAAE,CACV,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,44 @@
1
1
  {
2
2
  "name": "@zukhruf/sqlite-reader",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.53",
4
+ "description": "A read-only connection to a SQLite database that another process owns and writes. It opens the database with node:sqlite, and gives UnavailableError when the file is missing or the system denies access, a failure that the caller can fix.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/JanuaryLabs/zukhruf",
9
+ "directory": "packages/sqlite/reader"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/JanuaryLabs/zukhruf/issues"
13
+ },
14
+ "homepage": "https://github.com/JanuaryLabs/zukhruf/tree/main/packages/sqlite/reader#readme",
15
+ "keywords": [
16
+ "sqlite",
17
+ "node-sqlite",
18
+ "read-only",
19
+ "reader",
20
+ "wal"
21
+ ],
22
+ "type": "module",
23
+ "exports": {
24
+ "./package.json": "./package.json",
25
+ ".": {
26
+ "@zukhruf/source": "./src/index.ts",
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js",
29
+ "default": "./dist/index.js"
30
+ }
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "!dist/spec/**",
35
+ "!**/*.tsbuildinfo",
36
+ "!**/*.test.*"
37
+ ],
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "devDependencies": {
42
+ "@zukhruf/testing": "^0.3.53"
43
+ }
44
+ }