@super-line/store-sync-libsql 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.cjs +98 -0
- package/dist/index.d.cts +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +73 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mert
|
|
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/dist/index.cjs
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
libsqlSyncStore: () => libsqlSyncStore
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
var import_client = require("@libsql/client");
|
|
27
|
+
var import_store_sync = require("@super-line/store-sync");
|
|
28
|
+
async function libsqlSyncStore(opts) {
|
|
29
|
+
const table = opts.table ?? "resources";
|
|
30
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(table)) throw new Error(`Invalid table name: ${table}`);
|
|
31
|
+
const debounceMs = opts.debounceMs ?? 250;
|
|
32
|
+
const client = (0, import_client.createClient)({ url: opts.url, authToken: opts.authToken });
|
|
33
|
+
await client.execute(`CREATE TABLE IF NOT EXISTS "${table}" (id TEXT PRIMARY KEY, state TEXT NOT NULL, access TEXT NOT NULL)`);
|
|
34
|
+
const inner = (0, import_store_sync.syncStoreServer)({ resolveOptions: opts.resolveOptions });
|
|
35
|
+
const timers = /* @__PURE__ */ new Map();
|
|
36
|
+
const snapshot = async (id) => {
|
|
37
|
+
const res = await inner.read(id);
|
|
38
|
+
if (!res) return;
|
|
39
|
+
await client.execute({
|
|
40
|
+
sql: `INSERT INTO "${table}" (id, state, access) VALUES (?, ?, ?) ON CONFLICT(id) DO UPDATE SET state = excluded.state`,
|
|
41
|
+
args: [id, res.data, JSON.stringify(res.accessRules)]
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
const cancel = (id) => {
|
|
45
|
+
const t = timers.get(id);
|
|
46
|
+
if (!t) return;
|
|
47
|
+
clearTimeout(t);
|
|
48
|
+
timers.delete(id);
|
|
49
|
+
};
|
|
50
|
+
const { rows } = await client.execute(`SELECT id, state, access FROM "${table}"`);
|
|
51
|
+
for (const row of rows) {
|
|
52
|
+
const id = row.id;
|
|
53
|
+
const access = JSON.parse(row.access);
|
|
54
|
+
await inner.create(id, {}, access);
|
|
55
|
+
await inner.apply({ id, update: row.state, origin: "restore" });
|
|
56
|
+
}
|
|
57
|
+
inner.onChange((change) => {
|
|
58
|
+
cancel(change.id);
|
|
59
|
+
timers.set(
|
|
60
|
+
change.id,
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
timers.delete(change.id);
|
|
63
|
+
void snapshot(change.id);
|
|
64
|
+
}, debounceMs)
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
...inner,
|
|
69
|
+
async create(id, data, accessRules) {
|
|
70
|
+
await inner.create(id, data, accessRules);
|
|
71
|
+
const res = await inner.read(id);
|
|
72
|
+
await client.execute({
|
|
73
|
+
sql: `INSERT INTO "${table}" (id, state, access) VALUES (?, ?, ?) ON CONFLICT(id) DO NOTHING`,
|
|
74
|
+
args: [id, res?.data ?? "", JSON.stringify(accessRules)]
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
async setAccess(id, accessRules) {
|
|
78
|
+
await inner.setAccess(id, accessRules);
|
|
79
|
+
await client.execute({ sql: `UPDATE "${table}" SET access = ? WHERE id = ?`, args: [JSON.stringify(accessRules), id] });
|
|
80
|
+
},
|
|
81
|
+
async delete(id) {
|
|
82
|
+
cancel(id);
|
|
83
|
+
await inner.delete(id);
|
|
84
|
+
await client.execute({ sql: `DELETE FROM "${table}" WHERE id = ?`, args: [id] });
|
|
85
|
+
},
|
|
86
|
+
async close() {
|
|
87
|
+
const ids = [...timers.keys()];
|
|
88
|
+
for (const t of timers.values()) clearTimeout(t);
|
|
89
|
+
timers.clear();
|
|
90
|
+
for (const id of ids) await snapshot(id);
|
|
91
|
+
client.close();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
96
|
+
0 && (module.exports = {
|
|
97
|
+
libsqlSyncStore
|
|
98
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { DocOptions } from '@super-line/store-sync';
|
|
2
|
+
import { ServerStore } from '@super-line/core';
|
|
3
|
+
|
|
4
|
+
interface LibsqlSyncStoreOptions {
|
|
5
|
+
/** libsql URL: `file:x.db`, `:memory:`, `libsql://` (Turso) or `http://`/`https://` (sqld). */
|
|
6
|
+
url: string;
|
|
7
|
+
/** Auth token for Turso Cloud. */
|
|
8
|
+
authToken?: string;
|
|
9
|
+
/** Table this store owns; defaults to `resources`. Validated `/^[A-Za-z_][A-Za-z0-9_]*$/`. */
|
|
10
|
+
table?: string;
|
|
11
|
+
/** Coalesce rapid edits into one snapshot write; defaults to 250ms. */
|
|
12
|
+
debounceMs?: number;
|
|
13
|
+
/** Per-resource super-store config; MUST match the client's (the store-sync rule). */
|
|
14
|
+
resolveOptions?: (id: string) => DocOptions | undefined;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Durable CRDT **server half**: `syncStoreServer`'s Yjs merge engine, snapshotted per Resource to a shared
|
|
18
|
+
* libsql so state survives a restart. A thin wrapper — persistence is an extra `onChange` subscriber that
|
|
19
|
+
* debounces a full-state upsert; the hot path (`apply`) stays synchronous and relay-safe. Async factory:
|
|
20
|
+
* it rehydrates every Resource (history-preserving `applyUpdate`) before returning a ready store.
|
|
21
|
+
*/
|
|
22
|
+
declare function libsqlSyncStore(opts: LibsqlSyncStoreOptions): Promise<ServerStore>;
|
|
23
|
+
|
|
24
|
+
export { type LibsqlSyncStoreOptions, libsqlSyncStore };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { DocOptions } from '@super-line/store-sync';
|
|
2
|
+
import { ServerStore } from '@super-line/core';
|
|
3
|
+
|
|
4
|
+
interface LibsqlSyncStoreOptions {
|
|
5
|
+
/** libsql URL: `file:x.db`, `:memory:`, `libsql://` (Turso) or `http://`/`https://` (sqld). */
|
|
6
|
+
url: string;
|
|
7
|
+
/** Auth token for Turso Cloud. */
|
|
8
|
+
authToken?: string;
|
|
9
|
+
/** Table this store owns; defaults to `resources`. Validated `/^[A-Za-z_][A-Za-z0-9_]*$/`. */
|
|
10
|
+
table?: string;
|
|
11
|
+
/** Coalesce rapid edits into one snapshot write; defaults to 250ms. */
|
|
12
|
+
debounceMs?: number;
|
|
13
|
+
/** Per-resource super-store config; MUST match the client's (the store-sync rule). */
|
|
14
|
+
resolveOptions?: (id: string) => DocOptions | undefined;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Durable CRDT **server half**: `syncStoreServer`'s Yjs merge engine, snapshotted per Resource to a shared
|
|
18
|
+
* libsql so state survives a restart. A thin wrapper — persistence is an extra `onChange` subscriber that
|
|
19
|
+
* debounces a full-state upsert; the hot path (`apply`) stays synchronous and relay-safe. Async factory:
|
|
20
|
+
* it rehydrates every Resource (history-preserving `applyUpdate`) before returning a ready store.
|
|
21
|
+
*/
|
|
22
|
+
declare function libsqlSyncStore(opts: LibsqlSyncStoreOptions): Promise<ServerStore>;
|
|
23
|
+
|
|
24
|
+
export { type LibsqlSyncStoreOptions, libsqlSyncStore };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { createClient } from "@libsql/client";
|
|
3
|
+
import { syncStoreServer } from "@super-line/store-sync";
|
|
4
|
+
async function libsqlSyncStore(opts) {
|
|
5
|
+
const table = opts.table ?? "resources";
|
|
6
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(table)) throw new Error(`Invalid table name: ${table}`);
|
|
7
|
+
const debounceMs = opts.debounceMs ?? 250;
|
|
8
|
+
const client = createClient({ url: opts.url, authToken: opts.authToken });
|
|
9
|
+
await client.execute(`CREATE TABLE IF NOT EXISTS "${table}" (id TEXT PRIMARY KEY, state TEXT NOT NULL, access TEXT NOT NULL)`);
|
|
10
|
+
const inner = syncStoreServer({ resolveOptions: opts.resolveOptions });
|
|
11
|
+
const timers = /* @__PURE__ */ new Map();
|
|
12
|
+
const snapshot = async (id) => {
|
|
13
|
+
const res = await inner.read(id);
|
|
14
|
+
if (!res) return;
|
|
15
|
+
await client.execute({
|
|
16
|
+
sql: `INSERT INTO "${table}" (id, state, access) VALUES (?, ?, ?) ON CONFLICT(id) DO UPDATE SET state = excluded.state`,
|
|
17
|
+
args: [id, res.data, JSON.stringify(res.accessRules)]
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
const cancel = (id) => {
|
|
21
|
+
const t = timers.get(id);
|
|
22
|
+
if (!t) return;
|
|
23
|
+
clearTimeout(t);
|
|
24
|
+
timers.delete(id);
|
|
25
|
+
};
|
|
26
|
+
const { rows } = await client.execute(`SELECT id, state, access FROM "${table}"`);
|
|
27
|
+
for (const row of rows) {
|
|
28
|
+
const id = row.id;
|
|
29
|
+
const access = JSON.parse(row.access);
|
|
30
|
+
await inner.create(id, {}, access);
|
|
31
|
+
await inner.apply({ id, update: row.state, origin: "restore" });
|
|
32
|
+
}
|
|
33
|
+
inner.onChange((change) => {
|
|
34
|
+
cancel(change.id);
|
|
35
|
+
timers.set(
|
|
36
|
+
change.id,
|
|
37
|
+
setTimeout(() => {
|
|
38
|
+
timers.delete(change.id);
|
|
39
|
+
void snapshot(change.id);
|
|
40
|
+
}, debounceMs)
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
...inner,
|
|
45
|
+
async create(id, data, accessRules) {
|
|
46
|
+
await inner.create(id, data, accessRules);
|
|
47
|
+
const res = await inner.read(id);
|
|
48
|
+
await client.execute({
|
|
49
|
+
sql: `INSERT INTO "${table}" (id, state, access) VALUES (?, ?, ?) ON CONFLICT(id) DO NOTHING`,
|
|
50
|
+
args: [id, res?.data ?? "", JSON.stringify(accessRules)]
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
async setAccess(id, accessRules) {
|
|
54
|
+
await inner.setAccess(id, accessRules);
|
|
55
|
+
await client.execute({ sql: `UPDATE "${table}" SET access = ? WHERE id = ?`, args: [JSON.stringify(accessRules), id] });
|
|
56
|
+
},
|
|
57
|
+
async delete(id) {
|
|
58
|
+
cancel(id);
|
|
59
|
+
await inner.delete(id);
|
|
60
|
+
await client.execute({ sql: `DELETE FROM "${table}" WHERE id = ?`, args: [id] });
|
|
61
|
+
},
|
|
62
|
+
async close() {
|
|
63
|
+
const ids = [...timers.keys()];
|
|
64
|
+
for (const t of timers.values()) clearTimeout(t);
|
|
65
|
+
timers.clear();
|
|
66
|
+
for (const id of ids) await snapshot(id);
|
|
67
|
+
client.close();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
libsqlSyncStore
|
|
73
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@super-line/store-sync-libsql",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Durable CRDT Store for super-line — store-sync's Yjs merge engine backed by libsql (Turso / sqld).",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Mert",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"super-line",
|
|
10
|
+
"store",
|
|
11
|
+
"crdt",
|
|
12
|
+
"yjs",
|
|
13
|
+
"libsql",
|
|
14
|
+
"turso",
|
|
15
|
+
"persistence",
|
|
16
|
+
"durable",
|
|
17
|
+
"sync"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/mertdogar/super-line.git",
|
|
22
|
+
"directory": "packages/store-sync-libsql"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://mertdogar.github.io/super-line/",
|
|
25
|
+
"bugs": "https://github.com/mertdogar/super-line/issues",
|
|
26
|
+
"main": "./dist/index.cjs",
|
|
27
|
+
"module": "./dist/index.js",
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"import": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"require": {
|
|
36
|
+
"types": "./dist/index.d.cts",
|
|
37
|
+
"default": "./dist/index.cjs"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist"
|
|
43
|
+
],
|
|
44
|
+
"sideEffects": false,
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@libsql/client": "^0.15.0",
|
|
53
|
+
"@super-line/store-sync": "^0.8.0",
|
|
54
|
+
"@super-line/core": "^0.8.0"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsup"
|
|
58
|
+
}
|
|
59
|
+
}
|