@noy-db/to-mysql 0.1.0-pre.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vLannaAi
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,33 @@
1
+ # @noy-db/to-mysql
2
+
3
+ [![npm](https://img.shields.io/npm/v/%40noy-db/to-mysql.svg)](https://www.npmjs.com/package/@noy-db/to-mysql)
4
+
5
+ > MySQL / MariaDB adapter for noy-db
6
+
7
+ Part of [**`@noy-db/hub`**](https://www.npmjs.com/package/@noy-db/hub) — the zero-knowledge, offline-first, encrypted document store.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add @noy-db/hub @noy-db/to-mysql
13
+ ```
14
+
15
+ ## What it is
16
+
17
+ MySQL / MariaDB adapter for noy-db — encrypted-envelope KV with JSON column. Works with any duck-typed mysql2-style pool/connection. casAtomic: true via UPDATE … WHERE v = ?.
18
+
19
+ ## Status
20
+
21
+ **Pre-release** (`0.1.0-pre.1`). API may change before `1.0`.
22
+
23
+ ## Documentation
24
+
25
+ See the [main repository](https://github.com/vLannaAi/noy-db#readme) for setup, examples, and the full subsystem catalog.
26
+
27
+ - Source — [`packages/to-mysql`](https://github.com/vLannaAi/noy-db/tree/main/packages/to-mysql)
28
+ - Issues — [github.com/vLannaAi/noy-db/issues](https://github.com/vLannaAi/noy-db/issues)
29
+ - Spec — [`SPEC.md`](https://github.com/vLannaAi/noy-db/blob/main/SPEC.md)
30
+
31
+ ## License
32
+
33
+ [MIT](./LICENSE) © vLannaAi
package/dist/index.cjs ADDED
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ mysql: () => mysql
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+ var import_hub = require("@noy-db/hub");
27
+ function mysql(options) {
28
+ const { client, tableName = "noydb_envelopes", autoMigrate = true } = options;
29
+ let schemaReady = null;
30
+ async function runDDL(sql) {
31
+ if (client.query) await client.query(sql);
32
+ else await client.execute(sql);
33
+ }
34
+ async function ensureSchema() {
35
+ if (!autoMigrate) return;
36
+ if (!schemaReady) {
37
+ schemaReady = (async () => {
38
+ await runDDL(
39
+ `CREATE TABLE IF NOT EXISTS ${tableName} (
40
+ vault VARCHAR(255) NOT NULL,
41
+ collection VARCHAR(255) NOT NULL,
42
+ id VARCHAR(255) NOT NULL,
43
+ v BIGINT NOT NULL,
44
+ envelope JSON NOT NULL,
45
+ PRIMARY KEY (vault, collection, id),
46
+ INDEX idx_${tableName}_vc (vault, collection)
47
+ )`
48
+ );
49
+ })();
50
+ }
51
+ await schemaReady;
52
+ }
53
+ async function upsert(vault, collection, id, envelope, expectedVersion) {
54
+ await ensureSchema();
55
+ if (expectedVersion !== void 0) {
56
+ const [rows] = await client.execute(
57
+ `SELECT v FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,
58
+ [vault, collection, id]
59
+ );
60
+ const current = rows[0];
61
+ if (current && Number(current.v) !== expectedVersion) {
62
+ throw new import_hub.ConflictError(Number(current.v), `Version conflict: expected ${expectedVersion}, found ${current.v}`);
63
+ }
64
+ }
65
+ await client.execute(
66
+ `INSERT INTO ${tableName} (vault, collection, id, v, envelope)
67
+ VALUES (?, ?, ?, ?, ?)
68
+ ON DUPLICATE KEY UPDATE v = VALUES(v), envelope = VALUES(envelope)`,
69
+ [vault, collection, id, envelope._v, JSON.stringify(envelope)]
70
+ );
71
+ }
72
+ function parseEnvelope(raw) {
73
+ return typeof raw === "string" ? JSON.parse(raw) : raw;
74
+ }
75
+ const store = {
76
+ name: "mysql",
77
+ async get(vault, collection, id) {
78
+ await ensureSchema();
79
+ const [rows] = await client.execute(
80
+ `SELECT envelope FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,
81
+ [vault, collection, id]
82
+ );
83
+ if (rows.length === 0) return null;
84
+ return parseEnvelope(rows[0].envelope);
85
+ },
86
+ async put(vault, collection, id, envelope, expectedVersion) {
87
+ await upsert(vault, collection, id, envelope, expectedVersion);
88
+ },
89
+ async delete(vault, collection, id) {
90
+ await ensureSchema();
91
+ await client.execute(
92
+ `DELETE FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,
93
+ [vault, collection, id]
94
+ );
95
+ },
96
+ async list(vault, collection) {
97
+ await ensureSchema();
98
+ const [rows] = await client.execute(
99
+ `SELECT id FROM ${tableName} WHERE vault = ? AND collection = ? ORDER BY id`,
100
+ [vault, collection]
101
+ );
102
+ return rows.map((r) => r.id);
103
+ },
104
+ async loadAll(vault) {
105
+ await ensureSchema();
106
+ const [rows] = await client.execute(
107
+ `SELECT id, collection, v, envelope FROM ${tableName} WHERE vault = ?`,
108
+ [vault]
109
+ );
110
+ const snap = {};
111
+ for (const row of rows) {
112
+ const bucket = snap[row.collection] ?? (snap[row.collection] = {});
113
+ bucket[row.id] = parseEnvelope(row.envelope);
114
+ }
115
+ return snap;
116
+ },
117
+ async saveAll(vault, data) {
118
+ await ensureSchema();
119
+ await runDDL("START TRANSACTION");
120
+ try {
121
+ await client.execute(`DELETE FROM ${tableName} WHERE vault = ?`, [vault]);
122
+ for (const [collection, recs] of Object.entries(data)) {
123
+ for (const [id, envelope] of Object.entries(recs)) {
124
+ await upsert(vault, collection, id, envelope);
125
+ }
126
+ }
127
+ await runDDL("COMMIT");
128
+ } catch (err) {
129
+ await runDDL("ROLLBACK");
130
+ throw err;
131
+ }
132
+ },
133
+ async ping() {
134
+ try {
135
+ await client.execute("SELECT 1");
136
+ return true;
137
+ } catch {
138
+ return false;
139
+ }
140
+ },
141
+ async listPage(vault, collection, cursor, limit = 100) {
142
+ await ensureSchema();
143
+ const afterId = cursor ?? "";
144
+ const [rows] = await client.execute(
145
+ `SELECT id, envelope FROM ${tableName}
146
+ WHERE vault = ? AND collection = ? AND id > ?
147
+ ORDER BY id LIMIT ?`,
148
+ [vault, collection, afterId, limit + 1]
149
+ );
150
+ const hasMore = rows.length > limit;
151
+ const trimmed = hasMore ? rows.slice(0, limit) : rows;
152
+ const items = trimmed.map((r) => ({ id: r.id, envelope: parseEnvelope(r.envelope) }));
153
+ const result = {
154
+ items,
155
+ nextCursor: hasMore ? trimmed[trimmed.length - 1].id : null
156
+ };
157
+ return result;
158
+ },
159
+ async tx(ops) {
160
+ await ensureSchema();
161
+ await runDDL("START TRANSACTION");
162
+ try {
163
+ for (const op of ops) {
164
+ if (op.type === "put") {
165
+ if (!op.envelope) throw new Error(`tx put op missing envelope for ${op.id}`);
166
+ await upsert(op.vault, op.collection, op.id, op.envelope, op.expectedVersion);
167
+ } else {
168
+ if (op.expectedVersion !== void 0) {
169
+ const [rows] = await client.execute(
170
+ `SELECT v FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,
171
+ [op.vault, op.collection, op.id]
172
+ );
173
+ if (rows[0] && Number(rows[0].v) !== op.expectedVersion) {
174
+ throw new import_hub.ConflictError(Number(rows[0].v));
175
+ }
176
+ }
177
+ await client.execute(
178
+ `DELETE FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,
179
+ [op.vault, op.collection, op.id]
180
+ );
181
+ }
182
+ }
183
+ await runDDL("COMMIT");
184
+ } catch (err) {
185
+ await runDDL("ROLLBACK");
186
+ throw err;
187
+ }
188
+ }
189
+ };
190
+ return store;
191
+ }
192
+ // Annotate the CommonJS export names for ESM import in node:
193
+ 0 && (module.exports = {
194
+ mysql
195
+ });
196
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * **@noy-db/to-mysql** — MySQL / MariaDB-backed noy-db store.\n *\n * Parallel to `@noy-db/to-postgres` — same KV-shape, same transaction\n * story, adapted to MySQL 8+ `JSON` columns and `?`-style placeholders.\n *\n * ## Driver — bring your own\n *\n * Any client whose `execute(sql, params?)` returns `[rows, fields]`\n * (the `mysql2` convention) works:\n *\n * - `mysql2/promise` — `createPool()` or `createConnection()`\n * - PlanetScale's serverless driver\n * - Any pool wrapper that preserves the mysql2 Promise interface\n *\n * ## Capabilities\n *\n * | Capability | Value |\n * |-------------|-------|\n * | `casAtomic` | `true` — `UPDATE … WHERE v = ?` + row-count check |\n * | `txAtomic` | `true` — `START TRANSACTION … COMMIT` |\n * | `listPage` | ✓ — keyset paging by `id` |\n * | `ping` | ✓ — `SELECT 1` round-trip |\n *\n * @packageDocumentation\n */\n\nimport type { NoydbStore, EncryptedEnvelope, VaultSnapshot, TxOp, ListPageResult } from '@noy-db/hub'\nimport { ConflictError } from '@noy-db/hub'\n\n/** Duck-typed subset of the mysql2 Pool/Connection promise API. */\nexport interface MysqlClient {\n execute<T = unknown>(sql: string, params?: readonly unknown[]): Promise<[T[], unknown]>\n query?<T = unknown>(sql: string): Promise<[T[], unknown]>\n}\n\nexport interface MysqlStoreOptions {\n readonly client: MysqlClient\n /** Custom table name. Default `'noydb_envelopes'`. */\n readonly tableName?: string\n /** Run the CREATE TABLE DDL on store construction (async, lazy). Default `true`. */\n readonly autoMigrate?: boolean\n}\n\nexport function mysql(options: MysqlStoreOptions): NoydbStore {\n const { client, tableName = 'noydb_envelopes', autoMigrate = true } = options\n let schemaReady: Promise<void> | null = null\n\n async function runDDL(sql: string): Promise<void> {\n if (client.query) await client.query(sql)\n else await client.execute(sql)\n }\n\n async function ensureSchema(): Promise<void> {\n if (!autoMigrate) return\n if (!schemaReady) {\n schemaReady = (async () => {\n await runDDL(\n `CREATE TABLE IF NOT EXISTS ${tableName} (\n vault VARCHAR(255) NOT NULL,\n collection VARCHAR(255) NOT NULL,\n id VARCHAR(255) NOT NULL,\n v BIGINT NOT NULL,\n envelope JSON NOT NULL,\n PRIMARY KEY (vault, collection, id),\n INDEX idx_${tableName}_vc (vault, collection)\n )`,\n )\n })()\n }\n await schemaReady\n }\n\n async function upsert(\n vault: string,\n collection: string,\n id: string,\n envelope: EncryptedEnvelope,\n expectedVersion?: number,\n ): Promise<void> {\n await ensureSchema()\n if (expectedVersion !== undefined) {\n const [rows] = await client.execute<{ v: number }>(\n `SELECT v FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,\n [vault, collection, id],\n )\n const current = rows[0]\n if (current && Number(current.v) !== expectedVersion) {\n throw new ConflictError(Number(current.v), `Version conflict: expected ${expectedVersion}, found ${current.v}`)\n }\n }\n await client.execute(\n `INSERT INTO ${tableName} (vault, collection, id, v, envelope)\n VALUES (?, ?, ?, ?, ?)\n ON DUPLICATE KEY UPDATE v = VALUES(v), envelope = VALUES(envelope)`,\n [vault, collection, id, envelope._v, JSON.stringify(envelope)],\n )\n }\n\n function parseEnvelope(raw: unknown): EncryptedEnvelope {\n return typeof raw === 'string' ? (JSON.parse(raw) as EncryptedEnvelope) : (raw as EncryptedEnvelope)\n }\n\n const store: NoydbStore = {\n name: 'mysql',\n\n async get(vault, collection, id) {\n await ensureSchema()\n const [rows] = await client.execute<{ envelope: unknown }>(\n `SELECT envelope FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,\n [vault, collection, id],\n )\n if (rows.length === 0) return null\n return parseEnvelope(rows[0]!.envelope)\n },\n\n async put(vault, collection, id, envelope, expectedVersion) {\n await upsert(vault, collection, id, envelope, expectedVersion)\n },\n\n async delete(vault, collection, id) {\n await ensureSchema()\n await client.execute(\n `DELETE FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,\n [vault, collection, id],\n )\n },\n\n async list(vault, collection) {\n await ensureSchema()\n const [rows] = await client.execute<{ id: string }>(\n `SELECT id FROM ${tableName} WHERE vault = ? AND collection = ? ORDER BY id`,\n [vault, collection],\n )\n return rows.map(r => r.id)\n },\n\n async loadAll(vault) {\n await ensureSchema()\n const [rows] = await client.execute<{ id: string; collection: string; v: number; envelope: unknown }>(\n `SELECT id, collection, v, envelope FROM ${tableName} WHERE vault = ?`,\n [vault],\n )\n const snap: VaultSnapshot = {}\n for (const row of rows) {\n const bucket = snap[row.collection] ?? (snap[row.collection] = {})\n bucket[row.id] = parseEnvelope(row.envelope)\n }\n return snap\n },\n\n async saveAll(vault, data) {\n await ensureSchema()\n await runDDL('START TRANSACTION')\n try {\n await client.execute(`DELETE FROM ${tableName} WHERE vault = ?`, [vault])\n for (const [collection, recs] of Object.entries(data)) {\n for (const [id, envelope] of Object.entries(recs)) {\n await upsert(vault, collection, id, envelope)\n }\n }\n await runDDL('COMMIT')\n } catch (err) {\n await runDDL('ROLLBACK')\n throw err\n }\n },\n\n async ping() {\n try {\n await client.execute('SELECT 1')\n return true\n } catch {\n return false\n }\n },\n\n async listPage(vault, collection, cursor, limit = 100) {\n await ensureSchema()\n const afterId = cursor ?? ''\n const [rows] = await client.execute<{ id: string; envelope: unknown }>(\n `SELECT id, envelope FROM ${tableName}\n WHERE vault = ? AND collection = ? AND id > ?\n ORDER BY id LIMIT ?`,\n [vault, collection, afterId, limit + 1],\n )\n const hasMore = rows.length > limit\n const trimmed = hasMore ? rows.slice(0, limit) : rows\n const items = trimmed.map(r => ({ id: r.id, envelope: parseEnvelope(r.envelope) }))\n const result: ListPageResult = {\n items,\n nextCursor: hasMore ? trimmed[trimmed.length - 1]!.id : null,\n }\n return result\n },\n\n async tx(ops: readonly TxOp[]) {\n await ensureSchema()\n await runDDL('START TRANSACTION')\n try {\n for (const op of ops) {\n if (op.type === 'put') {\n if (!op.envelope) throw new Error(`tx put op missing envelope for ${op.id}`)\n await upsert(op.vault, op.collection, op.id, op.envelope, op.expectedVersion)\n } else {\n if (op.expectedVersion !== undefined) {\n const [rows] = await client.execute<{ v: number }>(\n `SELECT v FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,\n [op.vault, op.collection, op.id],\n )\n if (rows[0] && Number(rows[0].v) !== op.expectedVersion) {\n throw new ConflictError(Number(rows[0].v))\n }\n }\n await client.execute(\n `DELETE FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,\n [op.vault, op.collection, op.id],\n )\n }\n }\n await runDDL('COMMIT')\n } catch (err) {\n await runDDL('ROLLBACK')\n throw err\n }\n },\n }\n\n return store\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,44 @@
1
+ import { NoydbStore } from '@noy-db/hub';
2
+
3
+ /**
4
+ * **@noy-db/to-mysql** — MySQL / MariaDB-backed noy-db store.
5
+ *
6
+ * Parallel to `@noy-db/to-postgres` — same KV-shape, same transaction
7
+ * story, adapted to MySQL 8+ `JSON` columns and `?`-style placeholders.
8
+ *
9
+ * ## Driver — bring your own
10
+ *
11
+ * Any client whose `execute(sql, params?)` returns `[rows, fields]`
12
+ * (the `mysql2` convention) works:
13
+ *
14
+ * - `mysql2/promise` — `createPool()` or `createConnection()`
15
+ * - PlanetScale's serverless driver
16
+ * - Any pool wrapper that preserves the mysql2 Promise interface
17
+ *
18
+ * ## Capabilities
19
+ *
20
+ * | Capability | Value |
21
+ * |-------------|-------|
22
+ * | `casAtomic` | `true` — `UPDATE … WHERE v = ?` + row-count check |
23
+ * | `txAtomic` | `true` — `START TRANSACTION … COMMIT` |
24
+ * | `listPage` | ✓ — keyset paging by `id` |
25
+ * | `ping` | ✓ — `SELECT 1` round-trip |
26
+ *
27
+ * @packageDocumentation
28
+ */
29
+
30
+ /** Duck-typed subset of the mysql2 Pool/Connection promise API. */
31
+ interface MysqlClient {
32
+ execute<T = unknown>(sql: string, params?: readonly unknown[]): Promise<[T[], unknown]>;
33
+ query?<T = unknown>(sql: string): Promise<[T[], unknown]>;
34
+ }
35
+ interface MysqlStoreOptions {
36
+ readonly client: MysqlClient;
37
+ /** Custom table name. Default `'noydb_envelopes'`. */
38
+ readonly tableName?: string;
39
+ /** Run the CREATE TABLE DDL on store construction (async, lazy). Default `true`. */
40
+ readonly autoMigrate?: boolean;
41
+ }
42
+ declare function mysql(options: MysqlStoreOptions): NoydbStore;
43
+
44
+ export { type MysqlClient, type MysqlStoreOptions, mysql };
@@ -0,0 +1,44 @@
1
+ import { NoydbStore } from '@noy-db/hub';
2
+
3
+ /**
4
+ * **@noy-db/to-mysql** — MySQL / MariaDB-backed noy-db store.
5
+ *
6
+ * Parallel to `@noy-db/to-postgres` — same KV-shape, same transaction
7
+ * story, adapted to MySQL 8+ `JSON` columns and `?`-style placeholders.
8
+ *
9
+ * ## Driver — bring your own
10
+ *
11
+ * Any client whose `execute(sql, params?)` returns `[rows, fields]`
12
+ * (the `mysql2` convention) works:
13
+ *
14
+ * - `mysql2/promise` — `createPool()` or `createConnection()`
15
+ * - PlanetScale's serverless driver
16
+ * - Any pool wrapper that preserves the mysql2 Promise interface
17
+ *
18
+ * ## Capabilities
19
+ *
20
+ * | Capability | Value |
21
+ * |-------------|-------|
22
+ * | `casAtomic` | `true` — `UPDATE … WHERE v = ?` + row-count check |
23
+ * | `txAtomic` | `true` — `START TRANSACTION … COMMIT` |
24
+ * | `listPage` | ✓ — keyset paging by `id` |
25
+ * | `ping` | ✓ — `SELECT 1` round-trip |
26
+ *
27
+ * @packageDocumentation
28
+ */
29
+
30
+ /** Duck-typed subset of the mysql2 Pool/Connection promise API. */
31
+ interface MysqlClient {
32
+ execute<T = unknown>(sql: string, params?: readonly unknown[]): Promise<[T[], unknown]>;
33
+ query?<T = unknown>(sql: string): Promise<[T[], unknown]>;
34
+ }
35
+ interface MysqlStoreOptions {
36
+ readonly client: MysqlClient;
37
+ /** Custom table name. Default `'noydb_envelopes'`. */
38
+ readonly tableName?: string;
39
+ /** Run the CREATE TABLE DDL on store construction (async, lazy). Default `true`. */
40
+ readonly autoMigrate?: boolean;
41
+ }
42
+ declare function mysql(options: MysqlStoreOptions): NoydbStore;
43
+
44
+ export { type MysqlClient, type MysqlStoreOptions, mysql };
package/dist/index.js ADDED
@@ -0,0 +1,171 @@
1
+ // src/index.ts
2
+ import { ConflictError } from "@noy-db/hub";
3
+ function mysql(options) {
4
+ const { client, tableName = "noydb_envelopes", autoMigrate = true } = options;
5
+ let schemaReady = null;
6
+ async function runDDL(sql) {
7
+ if (client.query) await client.query(sql);
8
+ else await client.execute(sql);
9
+ }
10
+ async function ensureSchema() {
11
+ if (!autoMigrate) return;
12
+ if (!schemaReady) {
13
+ schemaReady = (async () => {
14
+ await runDDL(
15
+ `CREATE TABLE IF NOT EXISTS ${tableName} (
16
+ vault VARCHAR(255) NOT NULL,
17
+ collection VARCHAR(255) NOT NULL,
18
+ id VARCHAR(255) NOT NULL,
19
+ v BIGINT NOT NULL,
20
+ envelope JSON NOT NULL,
21
+ PRIMARY KEY (vault, collection, id),
22
+ INDEX idx_${tableName}_vc (vault, collection)
23
+ )`
24
+ );
25
+ })();
26
+ }
27
+ await schemaReady;
28
+ }
29
+ async function upsert(vault, collection, id, envelope, expectedVersion) {
30
+ await ensureSchema();
31
+ if (expectedVersion !== void 0) {
32
+ const [rows] = await client.execute(
33
+ `SELECT v FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,
34
+ [vault, collection, id]
35
+ );
36
+ const current = rows[0];
37
+ if (current && Number(current.v) !== expectedVersion) {
38
+ throw new ConflictError(Number(current.v), `Version conflict: expected ${expectedVersion}, found ${current.v}`);
39
+ }
40
+ }
41
+ await client.execute(
42
+ `INSERT INTO ${tableName} (vault, collection, id, v, envelope)
43
+ VALUES (?, ?, ?, ?, ?)
44
+ ON DUPLICATE KEY UPDATE v = VALUES(v), envelope = VALUES(envelope)`,
45
+ [vault, collection, id, envelope._v, JSON.stringify(envelope)]
46
+ );
47
+ }
48
+ function parseEnvelope(raw) {
49
+ return typeof raw === "string" ? JSON.parse(raw) : raw;
50
+ }
51
+ const store = {
52
+ name: "mysql",
53
+ async get(vault, collection, id) {
54
+ await ensureSchema();
55
+ const [rows] = await client.execute(
56
+ `SELECT envelope FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,
57
+ [vault, collection, id]
58
+ );
59
+ if (rows.length === 0) return null;
60
+ return parseEnvelope(rows[0].envelope);
61
+ },
62
+ async put(vault, collection, id, envelope, expectedVersion) {
63
+ await upsert(vault, collection, id, envelope, expectedVersion);
64
+ },
65
+ async delete(vault, collection, id) {
66
+ await ensureSchema();
67
+ await client.execute(
68
+ `DELETE FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,
69
+ [vault, collection, id]
70
+ );
71
+ },
72
+ async list(vault, collection) {
73
+ await ensureSchema();
74
+ const [rows] = await client.execute(
75
+ `SELECT id FROM ${tableName} WHERE vault = ? AND collection = ? ORDER BY id`,
76
+ [vault, collection]
77
+ );
78
+ return rows.map((r) => r.id);
79
+ },
80
+ async loadAll(vault) {
81
+ await ensureSchema();
82
+ const [rows] = await client.execute(
83
+ `SELECT id, collection, v, envelope FROM ${tableName} WHERE vault = ?`,
84
+ [vault]
85
+ );
86
+ const snap = {};
87
+ for (const row of rows) {
88
+ const bucket = snap[row.collection] ?? (snap[row.collection] = {});
89
+ bucket[row.id] = parseEnvelope(row.envelope);
90
+ }
91
+ return snap;
92
+ },
93
+ async saveAll(vault, data) {
94
+ await ensureSchema();
95
+ await runDDL("START TRANSACTION");
96
+ try {
97
+ await client.execute(`DELETE FROM ${tableName} WHERE vault = ?`, [vault]);
98
+ for (const [collection, recs] of Object.entries(data)) {
99
+ for (const [id, envelope] of Object.entries(recs)) {
100
+ await upsert(vault, collection, id, envelope);
101
+ }
102
+ }
103
+ await runDDL("COMMIT");
104
+ } catch (err) {
105
+ await runDDL("ROLLBACK");
106
+ throw err;
107
+ }
108
+ },
109
+ async ping() {
110
+ try {
111
+ await client.execute("SELECT 1");
112
+ return true;
113
+ } catch {
114
+ return false;
115
+ }
116
+ },
117
+ async listPage(vault, collection, cursor, limit = 100) {
118
+ await ensureSchema();
119
+ const afterId = cursor ?? "";
120
+ const [rows] = await client.execute(
121
+ `SELECT id, envelope FROM ${tableName}
122
+ WHERE vault = ? AND collection = ? AND id > ?
123
+ ORDER BY id LIMIT ?`,
124
+ [vault, collection, afterId, limit + 1]
125
+ );
126
+ const hasMore = rows.length > limit;
127
+ const trimmed = hasMore ? rows.slice(0, limit) : rows;
128
+ const items = trimmed.map((r) => ({ id: r.id, envelope: parseEnvelope(r.envelope) }));
129
+ const result = {
130
+ items,
131
+ nextCursor: hasMore ? trimmed[trimmed.length - 1].id : null
132
+ };
133
+ return result;
134
+ },
135
+ async tx(ops) {
136
+ await ensureSchema();
137
+ await runDDL("START TRANSACTION");
138
+ try {
139
+ for (const op of ops) {
140
+ if (op.type === "put") {
141
+ if (!op.envelope) throw new Error(`tx put op missing envelope for ${op.id}`);
142
+ await upsert(op.vault, op.collection, op.id, op.envelope, op.expectedVersion);
143
+ } else {
144
+ if (op.expectedVersion !== void 0) {
145
+ const [rows] = await client.execute(
146
+ `SELECT v FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,
147
+ [op.vault, op.collection, op.id]
148
+ );
149
+ if (rows[0] && Number(rows[0].v) !== op.expectedVersion) {
150
+ throw new ConflictError(Number(rows[0].v));
151
+ }
152
+ }
153
+ await client.execute(
154
+ `DELETE FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,
155
+ [op.vault, op.collection, op.id]
156
+ );
157
+ }
158
+ }
159
+ await runDDL("COMMIT");
160
+ } catch (err) {
161
+ await runDDL("ROLLBACK");
162
+ throw err;
163
+ }
164
+ }
165
+ };
166
+ return store;
167
+ }
168
+ export {
169
+ mysql
170
+ };
171
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * **@noy-db/to-mysql** — MySQL / MariaDB-backed noy-db store.\n *\n * Parallel to `@noy-db/to-postgres` — same KV-shape, same transaction\n * story, adapted to MySQL 8+ `JSON` columns and `?`-style placeholders.\n *\n * ## Driver — bring your own\n *\n * Any client whose `execute(sql, params?)` returns `[rows, fields]`\n * (the `mysql2` convention) works:\n *\n * - `mysql2/promise` — `createPool()` or `createConnection()`\n * - PlanetScale's serverless driver\n * - Any pool wrapper that preserves the mysql2 Promise interface\n *\n * ## Capabilities\n *\n * | Capability | Value |\n * |-------------|-------|\n * | `casAtomic` | `true` — `UPDATE … WHERE v = ?` + row-count check |\n * | `txAtomic` | `true` — `START TRANSACTION … COMMIT` |\n * | `listPage` | ✓ — keyset paging by `id` |\n * | `ping` | ✓ — `SELECT 1` round-trip |\n *\n * @packageDocumentation\n */\n\nimport type { NoydbStore, EncryptedEnvelope, VaultSnapshot, TxOp, ListPageResult } from '@noy-db/hub'\nimport { ConflictError } from '@noy-db/hub'\n\n/** Duck-typed subset of the mysql2 Pool/Connection promise API. */\nexport interface MysqlClient {\n execute<T = unknown>(sql: string, params?: readonly unknown[]): Promise<[T[], unknown]>\n query?<T = unknown>(sql: string): Promise<[T[], unknown]>\n}\n\nexport interface MysqlStoreOptions {\n readonly client: MysqlClient\n /** Custom table name. Default `'noydb_envelopes'`. */\n readonly tableName?: string\n /** Run the CREATE TABLE DDL on store construction (async, lazy). Default `true`. */\n readonly autoMigrate?: boolean\n}\n\nexport function mysql(options: MysqlStoreOptions): NoydbStore {\n const { client, tableName = 'noydb_envelopes', autoMigrate = true } = options\n let schemaReady: Promise<void> | null = null\n\n async function runDDL(sql: string): Promise<void> {\n if (client.query) await client.query(sql)\n else await client.execute(sql)\n }\n\n async function ensureSchema(): Promise<void> {\n if (!autoMigrate) return\n if (!schemaReady) {\n schemaReady = (async () => {\n await runDDL(\n `CREATE TABLE IF NOT EXISTS ${tableName} (\n vault VARCHAR(255) NOT NULL,\n collection VARCHAR(255) NOT NULL,\n id VARCHAR(255) NOT NULL,\n v BIGINT NOT NULL,\n envelope JSON NOT NULL,\n PRIMARY KEY (vault, collection, id),\n INDEX idx_${tableName}_vc (vault, collection)\n )`,\n )\n })()\n }\n await schemaReady\n }\n\n async function upsert(\n vault: string,\n collection: string,\n id: string,\n envelope: EncryptedEnvelope,\n expectedVersion?: number,\n ): Promise<void> {\n await ensureSchema()\n if (expectedVersion !== undefined) {\n const [rows] = await client.execute<{ v: number }>(\n `SELECT v FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,\n [vault, collection, id],\n )\n const current = rows[0]\n if (current && Number(current.v) !== expectedVersion) {\n throw new ConflictError(Number(current.v), `Version conflict: expected ${expectedVersion}, found ${current.v}`)\n }\n }\n await client.execute(\n `INSERT INTO ${tableName} (vault, collection, id, v, envelope)\n VALUES (?, ?, ?, ?, ?)\n ON DUPLICATE KEY UPDATE v = VALUES(v), envelope = VALUES(envelope)`,\n [vault, collection, id, envelope._v, JSON.stringify(envelope)],\n )\n }\n\n function parseEnvelope(raw: unknown): EncryptedEnvelope {\n return typeof raw === 'string' ? (JSON.parse(raw) as EncryptedEnvelope) : (raw as EncryptedEnvelope)\n }\n\n const store: NoydbStore = {\n name: 'mysql',\n\n async get(vault, collection, id) {\n await ensureSchema()\n const [rows] = await client.execute<{ envelope: unknown }>(\n `SELECT envelope FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,\n [vault, collection, id],\n )\n if (rows.length === 0) return null\n return parseEnvelope(rows[0]!.envelope)\n },\n\n async put(vault, collection, id, envelope, expectedVersion) {\n await upsert(vault, collection, id, envelope, expectedVersion)\n },\n\n async delete(vault, collection, id) {\n await ensureSchema()\n await client.execute(\n `DELETE FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,\n [vault, collection, id],\n )\n },\n\n async list(vault, collection) {\n await ensureSchema()\n const [rows] = await client.execute<{ id: string }>(\n `SELECT id FROM ${tableName} WHERE vault = ? AND collection = ? ORDER BY id`,\n [vault, collection],\n )\n return rows.map(r => r.id)\n },\n\n async loadAll(vault) {\n await ensureSchema()\n const [rows] = await client.execute<{ id: string; collection: string; v: number; envelope: unknown }>(\n `SELECT id, collection, v, envelope FROM ${tableName} WHERE vault = ?`,\n [vault],\n )\n const snap: VaultSnapshot = {}\n for (const row of rows) {\n const bucket = snap[row.collection] ?? (snap[row.collection] = {})\n bucket[row.id] = parseEnvelope(row.envelope)\n }\n return snap\n },\n\n async saveAll(vault, data) {\n await ensureSchema()\n await runDDL('START TRANSACTION')\n try {\n await client.execute(`DELETE FROM ${tableName} WHERE vault = ?`, [vault])\n for (const [collection, recs] of Object.entries(data)) {\n for (const [id, envelope] of Object.entries(recs)) {\n await upsert(vault, collection, id, envelope)\n }\n }\n await runDDL('COMMIT')\n } catch (err) {\n await runDDL('ROLLBACK')\n throw err\n }\n },\n\n async ping() {\n try {\n await client.execute('SELECT 1')\n return true\n } catch {\n return false\n }\n },\n\n async listPage(vault, collection, cursor, limit = 100) {\n await ensureSchema()\n const afterId = cursor ?? ''\n const [rows] = await client.execute<{ id: string; envelope: unknown }>(\n `SELECT id, envelope FROM ${tableName}\n WHERE vault = ? AND collection = ? AND id > ?\n ORDER BY id LIMIT ?`,\n [vault, collection, afterId, limit + 1],\n )\n const hasMore = rows.length > limit\n const trimmed = hasMore ? rows.slice(0, limit) : rows\n const items = trimmed.map(r => ({ id: r.id, envelope: parseEnvelope(r.envelope) }))\n const result: ListPageResult = {\n items,\n nextCursor: hasMore ? trimmed[trimmed.length - 1]!.id : null,\n }\n return result\n },\n\n async tx(ops: readonly TxOp[]) {\n await ensureSchema()\n await runDDL('START TRANSACTION')\n try {\n for (const op of ops) {\n if (op.type === 'put') {\n if (!op.envelope) throw new Error(`tx put op missing envelope for ${op.id}`)\n await upsert(op.vault, op.collection, op.id, op.envelope, op.expectedVersion)\n } else {\n if (op.expectedVersion !== undefined) {\n const [rows] = await client.execute<{ v: number }>(\n `SELECT v FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,\n [op.vault, op.collection, op.id],\n )\n if (rows[0] && Number(rows[0].v) !== op.expectedVersion) {\n throw new ConflictError(Number(rows[0].v))\n }\n }\n await client.execute(\n `DELETE FROM ${tableName} WHERE vault = ? AND collection = ? AND id = ?`,\n [op.vault, op.collection, op.id],\n )\n }\n }\n await runDDL('COMMIT')\n } catch (err) {\n await runDDL('ROLLBACK')\n throw err\n }\n },\n }\n\n return store\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@noy-db/to-mysql",
3
+ "version": "0.1.0-pre.3",
4
+ "description": "MySQL / MariaDB adapter for noy-db — encrypted-envelope KV with JSON column. Works with any duck-typed mysql2-style pool/connection. casAtomic: true via UPDATE … WHERE v = ?.",
5
+ "license": "MIT",
6
+ "author": "vLannaAi <vicio@lanna.ai>",
7
+ "homepage": "https://github.com/vLannaAi/noy-db/tree/main/packages/to-mysql#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/vLannaAi/noy-db.git",
11
+ "directory": "packages/to-mysql"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/vLannaAi/noy-db/issues"
15
+ },
16
+ "type": "module",
17
+ "sideEffects": false,
18
+ "exports": {
19
+ ".": {
20
+ "import": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/index.js"
23
+ },
24
+ "require": {
25
+ "types": "./dist/index.d.cts",
26
+ "default": "./dist/index.cjs"
27
+ }
28
+ }
29
+ },
30
+ "main": "./dist/index.cjs",
31
+ "module": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "files": [
34
+ "dist",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "engines": {
39
+ "node": ">=18.0.0"
40
+ },
41
+ "peerDependencies": {
42
+ "@noy-db/hub": "0.1.0-pre.3"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^22.0.0",
46
+ "@noy-db/hub": "0.1.0-pre.3"
47
+ },
48
+ "keywords": [
49
+ "noy-db",
50
+ "to-mysql",
51
+ "mysql",
52
+ "mariadb",
53
+ "storage"
54
+ ],
55
+ "publishConfig": {
56
+ "access": "public",
57
+ "tag": "latest"
58
+ },
59
+ "scripts": {
60
+ "build": "tsup",
61
+ "test": "vitest run",
62
+ "lint": "eslint src/",
63
+ "typecheck": "tsc --noEmit"
64
+ }
65
+ }