@humbdb/core 0.0.1

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 Humb contributors
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 ADDED
@@ -0,0 +1,5 @@
1
+ # @humbdb/core
2
+
3
+ Shared domain types and contracts for Humb (connection targets, schema/table metadata, row pages).
4
+
5
+ This package is UI-, server-, and engine-agnostic. See [`ARCHITECTURE.md`](../../ARCHITECTURE.md).
@@ -0,0 +1,155 @@
1
+ import { z } from 'zod';
2
+
3
+ /** Database engines Humb can target. Postgres is supported first, SQLite second. */
4
+ type DatabaseEngine = "postgres" | "sqlite";
5
+ /** A parsed, validated database connection target. */
6
+ interface ConnectionTarget {
7
+ readonly engine: DatabaseEngine;
8
+ /** The original connection string (may contain credentials - never log unredacted). */
9
+ readonly raw: string;
10
+ }
11
+
12
+ /** Metadata for a single column in a table. */
13
+ interface ColumnMetadata {
14
+ readonly name: string;
15
+ readonly dataType: string;
16
+ readonly nullable: boolean;
17
+ readonly isPrimaryKey: boolean;
18
+ readonly isForeignKey: boolean;
19
+ }
20
+ /** Metadata for a single index on a table. */
21
+ interface IndexMetadata {
22
+ readonly name: string;
23
+ readonly columns: string[];
24
+ readonly unique: boolean;
25
+ readonly primary: boolean;
26
+ }
27
+ /** Metadata for a single table. */
28
+ interface TableMetadata {
29
+ readonly schema: string;
30
+ readonly name: string;
31
+ readonly columns: ColumnMetadata[];
32
+ readonly indexes?: IndexMetadata[];
33
+ readonly rowCount?: number;
34
+ }
35
+
36
+ /** A schema and the tables it contains. */
37
+ interface SchemaMetadata {
38
+ readonly name: string;
39
+ readonly tables: string[];
40
+ }
41
+ /** Top-level overview of a database's structure. */
42
+ interface DatabaseOverview {
43
+ readonly engine: DatabaseEngine;
44
+ readonly schemas: SchemaMetadata[];
45
+ }
46
+
47
+ /** A page of rows returned for a table or read-only query. */
48
+ interface RowPage {
49
+ readonly columns: string[];
50
+ readonly rows: Array<Record<string, unknown>>;
51
+ readonly page: number;
52
+ readonly pageSize: number;
53
+ readonly total?: number;
54
+ }
55
+
56
+ /** Database connection status as reported by the server's health endpoint. */
57
+ type ConnectionStatus = "connected" | "disconnected" | "unconfigured";
58
+ /**
59
+ * Response shape for `GET /api/health`. Shared by the server (which produces it) and the web UI
60
+ * (which consumes it), so the two can never silently drift apart.
61
+ */
62
+ interface HealthResponse {
63
+ readonly status: "ok";
64
+ readonly database: ConnectionStatus;
65
+ readonly target: string | null;
66
+ /** e.g. "PostgreSQL 16.1", "SQLite 3.45.0". Null when not connected or unavailable. */
67
+ readonly engineVersion: string | null;
68
+ }
69
+
70
+ /** A node in the Files tab's read-only file tree - either a directory or a `.sql` file. */
71
+ interface FileNode {
72
+ readonly name: string;
73
+ readonly path: string;
74
+ readonly type: "file" | "directory";
75
+ readonly children?: FileNode[];
76
+ }
77
+ /** Response for `GET /api/files`. `enabled` is false when no `--files-dir` was configured. */
78
+ interface FilesOverview {
79
+ readonly enabled: boolean;
80
+ readonly tree: FileNode[];
81
+ }
82
+ /** Response for `GET /api/files/content`. */
83
+ interface FileContent {
84
+ readonly path: string;
85
+ readonly content: string;
86
+ }
87
+
88
+ type ConsoleEventLevel = "info" | "warn" | "error";
89
+ /** A single entry in the Console tab's recent connection/query activity stream. */
90
+ interface ConsoleEvent {
91
+ readonly id: number;
92
+ readonly timestamp: string;
93
+ readonly level: ConsoleEventLevel;
94
+ readonly message: string;
95
+ }
96
+ /** Response for `GET /api/console` (and the cleared state `DELETE /api/console` returns). */
97
+ interface ConsoleEvents {
98
+ readonly events: ConsoleEvent[];
99
+ }
100
+
101
+ /** Thrown when a connection target cannot be parsed or is unsupported. */
102
+ declare class InvalidConnectionTargetError extends Error {
103
+ constructor(message: string);
104
+ }
105
+
106
+ /** Default local port Humb's server listens on. */
107
+ declare const DEFAULT_PORT = 7717;
108
+ /**
109
+ * Redact credentials from a connection string so it is safe to log.
110
+ * Returns a best-effort redacted form; on parse failure returns a generic mask.
111
+ */
112
+ declare function redactConnectionString(raw: string): string;
113
+ /**
114
+ * Parse and validate a user-provided database target at the boundary.
115
+ * Throws {@link InvalidConnectionTargetError} with actionable guidance on failure.
116
+ */
117
+ declare function parseConnectionTarget(input: string | undefined): ConnectionTarget;
118
+
119
+ /**
120
+ * Request body schema for `POST /api/query`. Shared so the server enforces the same shape the web
121
+ * UI's query form (and any future client) can validate against before sending a request.
122
+ */
123
+ declare const runQuerySchema: z.ZodObject<{
124
+ sql: z.ZodString;
125
+ }, "strip", z.ZodTypeAny, {
126
+ sql: string;
127
+ }, {
128
+ sql: string;
129
+ }>;
130
+ type RunQueryRequest = z.infer<typeof runQuerySchema>;
131
+
132
+ /** Query-string schema for `GET /api/tables/:schema/:table/rows` pagination. */
133
+ declare const rowsQuerySchema: z.ZodObject<{
134
+ page: z.ZodDefault<z.ZodNumber>;
135
+ pageSize: z.ZodDefault<z.ZodNumber>;
136
+ }, "strip", z.ZodTypeAny, {
137
+ page: number;
138
+ pageSize: number;
139
+ }, {
140
+ page?: number | undefined;
141
+ pageSize?: number | undefined;
142
+ }>;
143
+ type RowsQuery = z.infer<typeof rowsQuerySchema>;
144
+
145
+ /** Query-string schema for `GET /api/files/content`. */
146
+ declare const fileContentQuerySchema: z.ZodObject<{
147
+ path: z.ZodString;
148
+ }, "strip", z.ZodTypeAny, {
149
+ path: string;
150
+ }, {
151
+ path: string;
152
+ }>;
153
+ type FileContentQuery = z.infer<typeof fileContentQuerySchema>;
154
+
155
+ export { type ColumnMetadata, type ConnectionStatus, type ConnectionTarget, type ConsoleEvent, type ConsoleEventLevel, type ConsoleEvents, DEFAULT_PORT, type DatabaseEngine, type DatabaseOverview, type FileContent, type FileContentQuery, type FileNode, type FilesOverview, type HealthResponse, type IndexMetadata, InvalidConnectionTargetError, type RowPage, type RowsQuery, type RunQueryRequest, type SchemaMetadata, type TableMetadata, fileContentQuerySchema, parseConnectionTarget, redactConnectionString, rowsQuerySchema, runQuerySchema };
package/dist/index.js ADDED
@@ -0,0 +1,87 @@
1
+ // src/errors.ts
2
+ var InvalidConnectionTargetError = class extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = "InvalidConnectionTargetError";
6
+ }
7
+ };
8
+
9
+ // src/connection-target.ts
10
+ import { existsSync } from "fs";
11
+ import { resolve } from "path";
12
+ import { fileURLToPath } from "url";
13
+ var POSTGRES_PROTOCOLS = /* @__PURE__ */ new Set(["postgres:", "postgresql:"]);
14
+ var DEFAULT_PORT = 7717;
15
+ function redactConnectionString(raw) {
16
+ try {
17
+ const url = new URL(raw);
18
+ if (url.password) {
19
+ url.password = "***";
20
+ }
21
+ return url.toString();
22
+ } catch {
23
+ return "<unparseable connection string>";
24
+ }
25
+ }
26
+ function resolveSqliteTarget(raw, path) {
27
+ const resolvedPath = resolve(path);
28
+ if (!existsSync(resolvedPath)) {
29
+ throw new InvalidConnectionTargetError(
30
+ `No file found at "${resolvedPath}". Expected a path to an existing SQLite file (e.g. ./app.db), or a Postgres connection string (postgres://user:pass@localhost:5432/mydb).`
31
+ );
32
+ }
33
+ return { engine: "sqlite", raw };
34
+ }
35
+ function parseConnectionTarget(input) {
36
+ const trimmed = input?.trim();
37
+ if (!trimmed) {
38
+ throw new InvalidConnectionTargetError(
39
+ "No database target provided. Expected a Postgres connection string (e.g. postgres://user:pass@localhost:5432/mydb) or a path to a SQLite file (e.g. ./app.db)."
40
+ );
41
+ }
42
+ let url;
43
+ try {
44
+ url = new URL(trimmed);
45
+ } catch {
46
+ url = void 0;
47
+ }
48
+ if (url) {
49
+ if (POSTGRES_PROTOCOLS.has(url.protocol)) {
50
+ return { engine: "postgres", raw: trimmed };
51
+ }
52
+ if (url.protocol === "file:") {
53
+ return resolveSqliteTarget(trimmed, fileURLToPath(url));
54
+ }
55
+ throw new InvalidConnectionTargetError(
56
+ `Unsupported database target protocol "${url.protocol}". Humb currently supports Postgres (postgres:// or postgresql://) and SQLite (a file path).`
57
+ );
58
+ }
59
+ return resolveSqliteTarget(trimmed, trimmed);
60
+ }
61
+
62
+ // src/validation/query.ts
63
+ import { z } from "zod";
64
+ var runQuerySchema = z.object({ sql: z.string().min(1) });
65
+
66
+ // src/validation/rows.ts
67
+ import { z as z2 } from "zod";
68
+ var rowsQuerySchema = z2.object({
69
+ page: z2.coerce.number().int().min(0).default(0),
70
+ pageSize: z2.coerce.number().int().min(1).max(200).default(50)
71
+ });
72
+
73
+ // src/validation/files.ts
74
+ import { z as z3 } from "zod";
75
+ var fileContentQuerySchema = z3.object({
76
+ path: z3.string().min(1)
77
+ });
78
+ export {
79
+ DEFAULT_PORT,
80
+ InvalidConnectionTargetError,
81
+ fileContentQuerySchema,
82
+ parseConnectionTarget,
83
+ redactConnectionString,
84
+ rowsQuerySchema,
85
+ runQuerySchema
86
+ };
87
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/connection-target.ts","../src/validation/query.ts","../src/validation/rows.ts","../src/validation/files.ts"],"sourcesContent":["/** Thrown when a connection target cannot be parsed or is unsupported. */\nexport class InvalidConnectionTargetError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidConnectionTargetError\";\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { InvalidConnectionTargetError } from \"./errors.js\";\nimport type { ConnectionTarget } from \"./types/connection.js\";\n\nconst POSTGRES_PROTOCOLS = new Set([\"postgres:\", \"postgresql:\"]);\n\n/** Default local port Humb's server listens on. */\nexport const DEFAULT_PORT = 7717;\n\n/**\n * Redact credentials from a connection string so it is safe to log.\n * Returns a best-effort redacted form; on parse failure returns a generic mask.\n */\nexport function redactConnectionString(raw: string): string {\n try {\n const url = new URL(raw);\n if (url.password) {\n url.password = \"***\";\n }\n return url.toString();\n } catch {\n return \"<unparseable connection string>\";\n }\n}\n\n/**\n * Resolve a candidate SQLite file path (bare path or `file:` URL) into a {@link ConnectionTarget}.\n * Existence is checked here, at the parse boundary, so the CLI fails fast with the exact resolved\n * path it looked for, matching Postgres's fail-fast-on-invalid-input behavior - see\n * `docs/product-specs/connect-and-inspect-sqlite.md`.\n */\nfunction resolveSqliteTarget(raw: string, path: string): ConnectionTarget {\n const resolvedPath = resolve(path);\n if (!existsSync(resolvedPath)) {\n throw new InvalidConnectionTargetError(\n `No file found at \"${resolvedPath}\". Expected a path to an existing SQLite file (e.g. ` +\n \"./app.db), or a Postgres connection string (postgres://user:pass@localhost:5432/mydb).\"\n );\n }\n return { engine: \"sqlite\", raw };\n}\n\n/**\n * Parse and validate a user-provided database target at the boundary.\n * Throws {@link InvalidConnectionTargetError} with actionable guidance on failure.\n */\nexport function parseConnectionTarget(input: string | undefined): ConnectionTarget {\n const trimmed = input?.trim();\n if (!trimmed) {\n throw new InvalidConnectionTargetError(\n \"No database target provided. Expected a Postgres connection string (e.g. \" +\n \"postgres://user:pass@localhost:5432/mydb) or a path to a SQLite file (e.g. ./app.db).\"\n );\n }\n\n let url: URL | undefined;\n try {\n url = new URL(trimmed);\n } catch {\n url = undefined;\n }\n\n if (url) {\n if (POSTGRES_PROTOCOLS.has(url.protocol)) {\n return { engine: \"postgres\", raw: trimmed };\n }\n if (url.protocol === \"file:\") {\n return resolveSqliteTarget(trimmed, fileURLToPath(url));\n }\n throw new InvalidConnectionTargetError(\n `Unsupported database target protocol \"${url.protocol}\". ` +\n \"Humb currently supports Postgres (postgres:// or postgresql://) and SQLite (a file path).\"\n );\n }\n\n // Not URL-shaped at all (e.g. `./app.db`, `data.sqlite`) - treat as a candidate SQLite file path.\n return resolveSqliteTarget(trimmed, trimmed);\n}\n","import { z } from \"zod\";\n\n/**\n * Request body schema for `POST /api/query`. Shared so the server enforces the same shape the web\n * UI's query form (and any future client) can validate against before sending a request.\n */\nexport const runQuerySchema = z.object({ sql: z.string().min(1) });\nexport type RunQueryRequest = z.infer<typeof runQuerySchema>;\n","import { z } from \"zod\";\n\n/** Query-string schema for `GET /api/tables/:schema/:table/rows` pagination. */\nexport const rowsQuerySchema = z.object({\n page: z.coerce.number().int().min(0).default(0),\n pageSize: z.coerce.number().int().min(1).max(200).default(50)\n});\nexport type RowsQuery = z.infer<typeof rowsQuerySchema>;\n","import { z } from \"zod\";\n\n/** Query-string schema for `GET /api/files/content`. */\nexport const fileContentQuerySchema = z.object({\n path: z.string().min(1)\n});\nexport type FileContentQuery = z.infer<typeof fileContentQuerySchema>;\n"],"mappings":";AACO,IAAM,+BAAN,cAA2C,MAAM;AAAA,EACtD,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ACNA,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,qBAAqB;AAI9B,IAAM,qBAAqB,oBAAI,IAAI,CAAC,aAAa,aAAa,CAAC;AAGxD,IAAM,eAAe;AAMrB,SAAS,uBAAuB,KAAqB;AAC1D,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,GAAG;AACvB,QAAI,IAAI,UAAU;AAChB,UAAI,WAAW;AAAA,IACjB;AACA,WAAO,IAAI,SAAS;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQA,SAAS,oBAAoB,KAAa,MAAgC;AACxE,QAAM,eAAe,QAAQ,IAAI;AACjC,MAAI,CAAC,WAAW,YAAY,GAAG;AAC7B,UAAM,IAAI;AAAA,MACR,qBAAqB,YAAY;AAAA,IAEnC;AAAA,EACF;AACA,SAAO,EAAE,QAAQ,UAAU,IAAI;AACjC;AAMO,SAAS,sBAAsB,OAA6C;AACjF,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,OAAO;AAAA,EACvB,QAAQ;AACN,UAAM;AAAA,EACR;AAEA,MAAI,KAAK;AACP,QAAI,mBAAmB,IAAI,IAAI,QAAQ,GAAG;AACxC,aAAO,EAAE,QAAQ,YAAY,KAAK,QAAQ;AAAA,IAC5C;AACA,QAAI,IAAI,aAAa,SAAS;AAC5B,aAAO,oBAAoB,SAAS,cAAc,GAAG,CAAC;AAAA,IACxD;AACA,UAAM,IAAI;AAAA,MACR,yCAAyC,IAAI,QAAQ;AAAA,IAEvD;AAAA,EACF;AAGA,SAAO,oBAAoB,SAAS,OAAO;AAC7C;;;AC/EA,SAAS,SAAS;AAMX,IAAM,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;;;ACNjE,SAAS,KAAAA,UAAS;AAGX,IAAM,kBAAkBA,GAAE,OAAO;AAAA,EACtC,MAAMA,GAAE,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EAC9C,UAAUA,GAAE,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAC9D,CAAC;;;ACND,SAAS,KAAAC,UAAS;AAGX,IAAM,yBAAyBA,GAAE,OAAO;AAAA,EAC7C,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC;AACxB,CAAC;","names":["z","z"]}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@humbdb/core",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "Shared domain types and contracts for Humb.",
6
+ "license": "MIT",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "dependencies": {
19
+ "zod": "^3.24.1"
20
+ },
21
+ "devDependencies": {
22
+ "@humbdb/config": "0.0.0"
23
+ },
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "dev": "tsup --watch",
27
+ "typecheck": "tsc --noEmit",
28
+ "test": "vitest run",
29
+ "clean": "rimraf dist .turbo"
30
+ }
31
+ }