@noy-db/to-smb 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 +21 -0
- package/README.md +33 -0
- package/dist/index.cjs +127 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +66 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +102 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
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-smb
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@noy-db/to-smb)
|
|
4
|
+
|
|
5
|
+
> SMB/CIFS network file store for noy-db. Works against Windows file servers, NAS devices (Synology/QNAP/Netgear), and corporate shared drives via NTLM or Kerberos auth
|
|
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-smb
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## What it is
|
|
16
|
+
|
|
17
|
+
SMB/CIFS network file store for noy-db. Works against Windows file servers, NAS devices (Synology/QNAP/Netgear), and corporate shared drives via NTLM or Kerberos auth — no OS mount required.
|
|
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-smb`](https://github.com/vLannaAi/noy-db/tree/main/packages/to-smb)
|
|
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,127 @@
|
|
|
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
|
+
smb: () => smb
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
function join(...parts) {
|
|
27
|
+
return parts.filter((p) => p !== "").map((p) => p.replace(/^\/+|\/+$/g, "")).filter((p) => p !== "").join("/");
|
|
28
|
+
}
|
|
29
|
+
function decode(bytes) {
|
|
30
|
+
if (typeof Buffer !== "undefined" && bytes instanceof Buffer) return bytes.toString("utf-8");
|
|
31
|
+
return new TextDecoder().decode(bytes);
|
|
32
|
+
}
|
|
33
|
+
function smb(options) {
|
|
34
|
+
const { smb: client, remotePath = "noydb", name = "smb" } = options;
|
|
35
|
+
const root = remotePath.replace(/^\/+|\/+$/g, "");
|
|
36
|
+
const recordPath = (v, c, id) => join(root, v, c, `${id}.json`);
|
|
37
|
+
const collPath = (v, c) => join(root, v, c);
|
|
38
|
+
const vaultPath = (v) => join(root, v);
|
|
39
|
+
async function ensureDir(path) {
|
|
40
|
+
const parts = path.split("/").filter(Boolean);
|
|
41
|
+
let cur = "";
|
|
42
|
+
for (const seg of parts) {
|
|
43
|
+
cur = cur ? `${cur}/${seg}` : seg;
|
|
44
|
+
try {
|
|
45
|
+
await client.mkdir(cur);
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function safeReaddir(path) {
|
|
51
|
+
try {
|
|
52
|
+
return await client.readdir(path);
|
|
53
|
+
} catch {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const store = {
|
|
58
|
+
name,
|
|
59
|
+
async get(vault, collection, id) {
|
|
60
|
+
const bytes = await client.readFile(recordPath(vault, collection, id));
|
|
61
|
+
if (bytes === null) return null;
|
|
62
|
+
return JSON.parse(decode(bytes));
|
|
63
|
+
},
|
|
64
|
+
async put(vault, collection, id, envelope) {
|
|
65
|
+
const target = recordPath(vault, collection, id);
|
|
66
|
+
const tmp = `${target}.tmp`;
|
|
67
|
+
await ensureDir(collPath(vault, collection));
|
|
68
|
+
await client.writeFile(tmp, JSON.stringify(envelope));
|
|
69
|
+
await client.rename(tmp, target);
|
|
70
|
+
},
|
|
71
|
+
async delete(vault, collection, id) {
|
|
72
|
+
try {
|
|
73
|
+
await client.unlink(recordPath(vault, collection, id));
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
async list(vault, collection) {
|
|
78
|
+
const entries = await safeReaddir(collPath(vault, collection));
|
|
79
|
+
return entries.filter((e) => e.endsWith(".json") && !e.endsWith(".tmp")).map((e) => e.slice(0, -".json".length)).sort();
|
|
80
|
+
},
|
|
81
|
+
async loadAll(vault) {
|
|
82
|
+
const snap = {};
|
|
83
|
+
const collections = await safeReaddir(vaultPath(vault));
|
|
84
|
+
for (const collection of collections) {
|
|
85
|
+
if (collection.startsWith("_")) continue;
|
|
86
|
+
const ids = await store.list(vault, collection);
|
|
87
|
+
const bucket = {};
|
|
88
|
+
for (const id of ids) {
|
|
89
|
+
const env = await store.get(vault, collection, id);
|
|
90
|
+
if (env) bucket[id] = env;
|
|
91
|
+
}
|
|
92
|
+
if (Object.keys(bucket).length > 0) snap[collection] = bucket;
|
|
93
|
+
}
|
|
94
|
+
return snap;
|
|
95
|
+
},
|
|
96
|
+
async saveAll(vault, data) {
|
|
97
|
+
const existing = await safeReaddir(vaultPath(vault));
|
|
98
|
+
for (const collection of existing) {
|
|
99
|
+
const ids = await safeReaddir(collPath(vault, collection));
|
|
100
|
+
for (const fname of ids) {
|
|
101
|
+
await client.unlink(join(root, vault, collection, fname)).catch(() => void 0);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
for (const [collection, recs] of Object.entries(data)) {
|
|
105
|
+
await ensureDir(collPath(vault, collection));
|
|
106
|
+
for (const [id, envelope] of Object.entries(recs)) {
|
|
107
|
+
await store.put(vault, collection, id, envelope);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
async ping() {
|
|
112
|
+
if (client.ping) return client.ping();
|
|
113
|
+
try {
|
|
114
|
+
await client.readdir(root);
|
|
115
|
+
return true;
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
return store;
|
|
122
|
+
}
|
|
123
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
124
|
+
0 && (module.exports = {
|
|
125
|
+
smb
|
|
126
|
+
});
|
|
127
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * **@noy-db/to-smb** — SMB / CIFS network file store for noy-db.\n *\n * Works against Windows file servers, NAS devices (Synology, QNAP,\n * Netgear), and corporate shared drives. Unlike pointing\n * `@noy-db/to-file` at an OS-mounted path, this package speaks SMB\n * directly — no mount step, explicit credential handling, richer\n * reconnection semantics.\n *\n * ## Driver — bring your own\n *\n * Any SMB2/3 client with a promise-style API works. Ready-made: the\n * `smb2` or `@marsaud/smb2` packages on npm. A duck-typed handle is\n * the injection seam — pass a connected client in, noy-db talks to it.\n *\n * ## Auth\n *\n * - **NTLM** — `username` + `password` (+ optional `domain`). Typical\n * for workgroup NAS.\n * - **Kerberos** — domain-joined environments, ticket from the OS\n * cache. Setting `authKind: 'kerberos'` tells the driver to leave\n * password empty and defer to `kinit`.\n *\n * Credentials never hit disk through this adapter — the caller is\n * responsible for sourcing them from a secrets manager. For\n * long-lived integrations, stash them in the vault's encrypted\n * `_sync_credentials` collection and pass them at open time.\n *\n * ## Atomicity\n *\n * SMB has rename but no atomic CAS — same caveat as WebDAV. Per-record\n * writes go through `{id}.json.tmp` + rename, avoiding the partial-write\n * failure mode. For multi-writer CAS, pair with a primary store that\n * implements it.\n *\n * @packageDocumentation\n */\n\nimport type { NoydbStore, EncryptedEnvelope, VaultSnapshot } from '@noy-db/hub'\n\n/**\n * Duck-typed SMB client. Compatible with `smb2` / `@marsaud/smb2`\n * promise wrappers. File paths use Windows-style forward slashes\n * (the SMB client translates to backslashes on the wire).\n */\nexport interface SmbHandle {\n readFile(path: string): Promise<Uint8Array | Buffer | null>\n writeFile(path: string, data: Uint8Array | Buffer | string): Promise<void>\n unlink(path: string): Promise<void>\n mkdir(path: string): Promise<void>\n rename(from: string, to: string): Promise<void>\n readdir(path: string): Promise<string[]>\n /** Optional liveness check — returns true on a reachable share. */\n ping?(): Promise<boolean>\n}\n\nexport interface SmbStoreOptions {\n /** Connected SMB client — consumer supplies this. */\n readonly smb: SmbHandle\n /** Relative subdirectory within the share. Default `'noydb'`. */\n readonly remotePath?: string\n /** Diagnostic name. Default `'smb'`. */\n readonly name?: string\n}\n\nfunction join(...parts: string[]): string {\n return parts\n .filter(p => p !== '')\n .map(p => p.replace(/^\\/+|\\/+$/g, ''))\n .filter(p => p !== '')\n .join('/')\n}\n\nfunction decode(bytes: Uint8Array | Buffer): string {\n if (typeof Buffer !== 'undefined' && bytes instanceof Buffer) return bytes.toString('utf-8')\n return new TextDecoder().decode(bytes)\n}\n\nexport function smb(options: SmbStoreOptions): NoydbStore {\n const { smb: client, remotePath = 'noydb', name = 'smb' } = options\n const root = remotePath.replace(/^\\/+|\\/+$/g, '')\n\n const recordPath = (v: string, c: string, id: string): string => join(root, v, c, `${id}.json`)\n const collPath = (v: string, c: string): string => join(root, v, c)\n const vaultPath = (v: string): string => join(root, v)\n\n async function ensureDir(path: string): Promise<void> {\n // SMB mkdir is not always recursive — walk parents.\n const parts = path.split('/').filter(Boolean)\n let cur = ''\n for (const seg of parts) {\n cur = cur ? `${cur}/${seg}` : seg\n try {\n await client.mkdir(cur)\n } catch {\n // Exists or race — continue.\n }\n }\n }\n\n async function safeReaddir(path: string): Promise<string[]> {\n try {\n return await client.readdir(path)\n } catch {\n return []\n }\n }\n\n const store: NoydbStore = {\n name,\n\n async get(vault, collection, id) {\n const bytes = await client.readFile(recordPath(vault, collection, id))\n if (bytes === null) return null\n return JSON.parse(decode(bytes)) as EncryptedEnvelope\n },\n\n async put(vault, collection, id, envelope) {\n // casAtomic: false — expectedVersion ignored.\n const target = recordPath(vault, collection, id)\n const tmp = `${target}.tmp`\n await ensureDir(collPath(vault, collection))\n await client.writeFile(tmp, JSON.stringify(envelope))\n await client.rename(tmp, target)\n },\n\n async delete(vault, collection, id) {\n try {\n await client.unlink(recordPath(vault, collection, id))\n } catch {\n /* idempotent */\n }\n },\n\n async list(vault, collection) {\n const entries = await safeReaddir(collPath(vault, collection))\n return entries\n .filter(e => e.endsWith('.json') && !e.endsWith('.tmp'))\n .map(e => e.slice(0, -'.json'.length))\n .sort()\n },\n\n async loadAll(vault) {\n const snap: VaultSnapshot = {}\n const collections = await safeReaddir(vaultPath(vault))\n for (const collection of collections) {\n if (collection.startsWith('_')) continue\n const ids = await store.list(vault, collection)\n const bucket: Record<string, EncryptedEnvelope> = {}\n for (const id of ids) {\n const env = await store.get(vault, collection, id)\n if (env) bucket[id] = env\n }\n if (Object.keys(bucket).length > 0) snap[collection] = bucket\n }\n return snap\n },\n\n async saveAll(vault, data) {\n const existing = await safeReaddir(vaultPath(vault))\n for (const collection of existing) {\n const ids = await safeReaddir(collPath(vault, collection))\n for (const fname of ids) {\n await client.unlink(join(root, vault, collection, fname)).catch(() => undefined)\n }\n }\n for (const [collection, recs] of Object.entries(data)) {\n await ensureDir(collPath(vault, collection))\n for (const [id, envelope] of Object.entries(recs)) {\n await store.put(vault, collection, id, envelope)\n }\n }\n },\n\n async ping() {\n if (client.ping) return client.ping()\n try {\n await client.readdir(root)\n return true\n } catch {\n return false\n }\n },\n }\n\n return store\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { NoydbStore } from '@noy-db/hub';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* **@noy-db/to-smb** — SMB / CIFS network file store for noy-db.
|
|
5
|
+
*
|
|
6
|
+
* Works against Windows file servers, NAS devices (Synology, QNAP,
|
|
7
|
+
* Netgear), and corporate shared drives. Unlike pointing
|
|
8
|
+
* `@noy-db/to-file` at an OS-mounted path, this package speaks SMB
|
|
9
|
+
* directly — no mount step, explicit credential handling, richer
|
|
10
|
+
* reconnection semantics.
|
|
11
|
+
*
|
|
12
|
+
* ## Driver — bring your own
|
|
13
|
+
*
|
|
14
|
+
* Any SMB2/3 client with a promise-style API works. Ready-made: the
|
|
15
|
+
* `smb2` or `@marsaud/smb2` packages on npm. A duck-typed handle is
|
|
16
|
+
* the injection seam — pass a connected client in, noy-db talks to it.
|
|
17
|
+
*
|
|
18
|
+
* ## Auth
|
|
19
|
+
*
|
|
20
|
+
* - **NTLM** — `username` + `password` (+ optional `domain`). Typical
|
|
21
|
+
* for workgroup NAS.
|
|
22
|
+
* - **Kerberos** — domain-joined environments, ticket from the OS
|
|
23
|
+
* cache. Setting `authKind: 'kerberos'` tells the driver to leave
|
|
24
|
+
* password empty and defer to `kinit`.
|
|
25
|
+
*
|
|
26
|
+
* Credentials never hit disk through this adapter — the caller is
|
|
27
|
+
* responsible for sourcing them from a secrets manager. For
|
|
28
|
+
* long-lived integrations, stash them in the vault's encrypted
|
|
29
|
+
* `_sync_credentials` collection and pass them at open time.
|
|
30
|
+
*
|
|
31
|
+
* ## Atomicity
|
|
32
|
+
*
|
|
33
|
+
* SMB has rename but no atomic CAS — same caveat as WebDAV. Per-record
|
|
34
|
+
* writes go through `{id}.json.tmp` + rename, avoiding the partial-write
|
|
35
|
+
* failure mode. For multi-writer CAS, pair with a primary store that
|
|
36
|
+
* implements it.
|
|
37
|
+
*
|
|
38
|
+
* @packageDocumentation
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Duck-typed SMB client. Compatible with `smb2` / `@marsaud/smb2`
|
|
43
|
+
* promise wrappers. File paths use Windows-style forward slashes
|
|
44
|
+
* (the SMB client translates to backslashes on the wire).
|
|
45
|
+
*/
|
|
46
|
+
interface SmbHandle {
|
|
47
|
+
readFile(path: string): Promise<Uint8Array | Buffer | null>;
|
|
48
|
+
writeFile(path: string, data: Uint8Array | Buffer | string): Promise<void>;
|
|
49
|
+
unlink(path: string): Promise<void>;
|
|
50
|
+
mkdir(path: string): Promise<void>;
|
|
51
|
+
rename(from: string, to: string): Promise<void>;
|
|
52
|
+
readdir(path: string): Promise<string[]>;
|
|
53
|
+
/** Optional liveness check — returns true on a reachable share. */
|
|
54
|
+
ping?(): Promise<boolean>;
|
|
55
|
+
}
|
|
56
|
+
interface SmbStoreOptions {
|
|
57
|
+
/** Connected SMB client — consumer supplies this. */
|
|
58
|
+
readonly smb: SmbHandle;
|
|
59
|
+
/** Relative subdirectory within the share. Default `'noydb'`. */
|
|
60
|
+
readonly remotePath?: string;
|
|
61
|
+
/** Diagnostic name. Default `'smb'`. */
|
|
62
|
+
readonly name?: string;
|
|
63
|
+
}
|
|
64
|
+
declare function smb(options: SmbStoreOptions): NoydbStore;
|
|
65
|
+
|
|
66
|
+
export { type SmbHandle, type SmbStoreOptions, smb };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { NoydbStore } from '@noy-db/hub';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* **@noy-db/to-smb** — SMB / CIFS network file store for noy-db.
|
|
5
|
+
*
|
|
6
|
+
* Works against Windows file servers, NAS devices (Synology, QNAP,
|
|
7
|
+
* Netgear), and corporate shared drives. Unlike pointing
|
|
8
|
+
* `@noy-db/to-file` at an OS-mounted path, this package speaks SMB
|
|
9
|
+
* directly — no mount step, explicit credential handling, richer
|
|
10
|
+
* reconnection semantics.
|
|
11
|
+
*
|
|
12
|
+
* ## Driver — bring your own
|
|
13
|
+
*
|
|
14
|
+
* Any SMB2/3 client with a promise-style API works. Ready-made: the
|
|
15
|
+
* `smb2` or `@marsaud/smb2` packages on npm. A duck-typed handle is
|
|
16
|
+
* the injection seam — pass a connected client in, noy-db talks to it.
|
|
17
|
+
*
|
|
18
|
+
* ## Auth
|
|
19
|
+
*
|
|
20
|
+
* - **NTLM** — `username` + `password` (+ optional `domain`). Typical
|
|
21
|
+
* for workgroup NAS.
|
|
22
|
+
* - **Kerberos** — domain-joined environments, ticket from the OS
|
|
23
|
+
* cache. Setting `authKind: 'kerberos'` tells the driver to leave
|
|
24
|
+
* password empty and defer to `kinit`.
|
|
25
|
+
*
|
|
26
|
+
* Credentials never hit disk through this adapter — the caller is
|
|
27
|
+
* responsible for sourcing them from a secrets manager. For
|
|
28
|
+
* long-lived integrations, stash them in the vault's encrypted
|
|
29
|
+
* `_sync_credentials` collection and pass them at open time.
|
|
30
|
+
*
|
|
31
|
+
* ## Atomicity
|
|
32
|
+
*
|
|
33
|
+
* SMB has rename but no atomic CAS — same caveat as WebDAV. Per-record
|
|
34
|
+
* writes go through `{id}.json.tmp` + rename, avoiding the partial-write
|
|
35
|
+
* failure mode. For multi-writer CAS, pair with a primary store that
|
|
36
|
+
* implements it.
|
|
37
|
+
*
|
|
38
|
+
* @packageDocumentation
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Duck-typed SMB client. Compatible with `smb2` / `@marsaud/smb2`
|
|
43
|
+
* promise wrappers. File paths use Windows-style forward slashes
|
|
44
|
+
* (the SMB client translates to backslashes on the wire).
|
|
45
|
+
*/
|
|
46
|
+
interface SmbHandle {
|
|
47
|
+
readFile(path: string): Promise<Uint8Array | Buffer | null>;
|
|
48
|
+
writeFile(path: string, data: Uint8Array | Buffer | string): Promise<void>;
|
|
49
|
+
unlink(path: string): Promise<void>;
|
|
50
|
+
mkdir(path: string): Promise<void>;
|
|
51
|
+
rename(from: string, to: string): Promise<void>;
|
|
52
|
+
readdir(path: string): Promise<string[]>;
|
|
53
|
+
/** Optional liveness check — returns true on a reachable share. */
|
|
54
|
+
ping?(): Promise<boolean>;
|
|
55
|
+
}
|
|
56
|
+
interface SmbStoreOptions {
|
|
57
|
+
/** Connected SMB client — consumer supplies this. */
|
|
58
|
+
readonly smb: SmbHandle;
|
|
59
|
+
/** Relative subdirectory within the share. Default `'noydb'`. */
|
|
60
|
+
readonly remotePath?: string;
|
|
61
|
+
/** Diagnostic name. Default `'smb'`. */
|
|
62
|
+
readonly name?: string;
|
|
63
|
+
}
|
|
64
|
+
declare function smb(options: SmbStoreOptions): NoydbStore;
|
|
65
|
+
|
|
66
|
+
export { type SmbHandle, type SmbStoreOptions, smb };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
function join(...parts) {
|
|
3
|
+
return parts.filter((p) => p !== "").map((p) => p.replace(/^\/+|\/+$/g, "")).filter((p) => p !== "").join("/");
|
|
4
|
+
}
|
|
5
|
+
function decode(bytes) {
|
|
6
|
+
if (typeof Buffer !== "undefined" && bytes instanceof Buffer) return bytes.toString("utf-8");
|
|
7
|
+
return new TextDecoder().decode(bytes);
|
|
8
|
+
}
|
|
9
|
+
function smb(options) {
|
|
10
|
+
const { smb: client, remotePath = "noydb", name = "smb" } = options;
|
|
11
|
+
const root = remotePath.replace(/^\/+|\/+$/g, "");
|
|
12
|
+
const recordPath = (v, c, id) => join(root, v, c, `${id}.json`);
|
|
13
|
+
const collPath = (v, c) => join(root, v, c);
|
|
14
|
+
const vaultPath = (v) => join(root, v);
|
|
15
|
+
async function ensureDir(path) {
|
|
16
|
+
const parts = path.split("/").filter(Boolean);
|
|
17
|
+
let cur = "";
|
|
18
|
+
for (const seg of parts) {
|
|
19
|
+
cur = cur ? `${cur}/${seg}` : seg;
|
|
20
|
+
try {
|
|
21
|
+
await client.mkdir(cur);
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function safeReaddir(path) {
|
|
27
|
+
try {
|
|
28
|
+
return await client.readdir(path);
|
|
29
|
+
} catch {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const store = {
|
|
34
|
+
name,
|
|
35
|
+
async get(vault, collection, id) {
|
|
36
|
+
const bytes = await client.readFile(recordPath(vault, collection, id));
|
|
37
|
+
if (bytes === null) return null;
|
|
38
|
+
return JSON.parse(decode(bytes));
|
|
39
|
+
},
|
|
40
|
+
async put(vault, collection, id, envelope) {
|
|
41
|
+
const target = recordPath(vault, collection, id);
|
|
42
|
+
const tmp = `${target}.tmp`;
|
|
43
|
+
await ensureDir(collPath(vault, collection));
|
|
44
|
+
await client.writeFile(tmp, JSON.stringify(envelope));
|
|
45
|
+
await client.rename(tmp, target);
|
|
46
|
+
},
|
|
47
|
+
async delete(vault, collection, id) {
|
|
48
|
+
try {
|
|
49
|
+
await client.unlink(recordPath(vault, collection, id));
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
async list(vault, collection) {
|
|
54
|
+
const entries = await safeReaddir(collPath(vault, collection));
|
|
55
|
+
return entries.filter((e) => e.endsWith(".json") && !e.endsWith(".tmp")).map((e) => e.slice(0, -".json".length)).sort();
|
|
56
|
+
},
|
|
57
|
+
async loadAll(vault) {
|
|
58
|
+
const snap = {};
|
|
59
|
+
const collections = await safeReaddir(vaultPath(vault));
|
|
60
|
+
for (const collection of collections) {
|
|
61
|
+
if (collection.startsWith("_")) continue;
|
|
62
|
+
const ids = await store.list(vault, collection);
|
|
63
|
+
const bucket = {};
|
|
64
|
+
for (const id of ids) {
|
|
65
|
+
const env = await store.get(vault, collection, id);
|
|
66
|
+
if (env) bucket[id] = env;
|
|
67
|
+
}
|
|
68
|
+
if (Object.keys(bucket).length > 0) snap[collection] = bucket;
|
|
69
|
+
}
|
|
70
|
+
return snap;
|
|
71
|
+
},
|
|
72
|
+
async saveAll(vault, data) {
|
|
73
|
+
const existing = await safeReaddir(vaultPath(vault));
|
|
74
|
+
for (const collection of existing) {
|
|
75
|
+
const ids = await safeReaddir(collPath(vault, collection));
|
|
76
|
+
for (const fname of ids) {
|
|
77
|
+
await client.unlink(join(root, vault, collection, fname)).catch(() => void 0);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (const [collection, recs] of Object.entries(data)) {
|
|
81
|
+
await ensureDir(collPath(vault, collection));
|
|
82
|
+
for (const [id, envelope] of Object.entries(recs)) {
|
|
83
|
+
await store.put(vault, collection, id, envelope);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
async ping() {
|
|
88
|
+
if (client.ping) return client.ping();
|
|
89
|
+
try {
|
|
90
|
+
await client.readdir(root);
|
|
91
|
+
return true;
|
|
92
|
+
} catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
return store;
|
|
98
|
+
}
|
|
99
|
+
export {
|
|
100
|
+
smb
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * **@noy-db/to-smb** — SMB / CIFS network file store for noy-db.\n *\n * Works against Windows file servers, NAS devices (Synology, QNAP,\n * Netgear), and corporate shared drives. Unlike pointing\n * `@noy-db/to-file` at an OS-mounted path, this package speaks SMB\n * directly — no mount step, explicit credential handling, richer\n * reconnection semantics.\n *\n * ## Driver — bring your own\n *\n * Any SMB2/3 client with a promise-style API works. Ready-made: the\n * `smb2` or `@marsaud/smb2` packages on npm. A duck-typed handle is\n * the injection seam — pass a connected client in, noy-db talks to it.\n *\n * ## Auth\n *\n * - **NTLM** — `username` + `password` (+ optional `domain`). Typical\n * for workgroup NAS.\n * - **Kerberos** — domain-joined environments, ticket from the OS\n * cache. Setting `authKind: 'kerberos'` tells the driver to leave\n * password empty and defer to `kinit`.\n *\n * Credentials never hit disk through this adapter — the caller is\n * responsible for sourcing them from a secrets manager. For\n * long-lived integrations, stash them in the vault's encrypted\n * `_sync_credentials` collection and pass them at open time.\n *\n * ## Atomicity\n *\n * SMB has rename but no atomic CAS — same caveat as WebDAV. Per-record\n * writes go through `{id}.json.tmp` + rename, avoiding the partial-write\n * failure mode. For multi-writer CAS, pair with a primary store that\n * implements it.\n *\n * @packageDocumentation\n */\n\nimport type { NoydbStore, EncryptedEnvelope, VaultSnapshot } from '@noy-db/hub'\n\n/**\n * Duck-typed SMB client. Compatible with `smb2` / `@marsaud/smb2`\n * promise wrappers. File paths use Windows-style forward slashes\n * (the SMB client translates to backslashes on the wire).\n */\nexport interface SmbHandle {\n readFile(path: string): Promise<Uint8Array | Buffer | null>\n writeFile(path: string, data: Uint8Array | Buffer | string): Promise<void>\n unlink(path: string): Promise<void>\n mkdir(path: string): Promise<void>\n rename(from: string, to: string): Promise<void>\n readdir(path: string): Promise<string[]>\n /** Optional liveness check — returns true on a reachable share. */\n ping?(): Promise<boolean>\n}\n\nexport interface SmbStoreOptions {\n /** Connected SMB client — consumer supplies this. */\n readonly smb: SmbHandle\n /** Relative subdirectory within the share. Default `'noydb'`. */\n readonly remotePath?: string\n /** Diagnostic name. Default `'smb'`. */\n readonly name?: string\n}\n\nfunction join(...parts: string[]): string {\n return parts\n .filter(p => p !== '')\n .map(p => p.replace(/^\\/+|\\/+$/g, ''))\n .filter(p => p !== '')\n .join('/')\n}\n\nfunction decode(bytes: Uint8Array | Buffer): string {\n if (typeof Buffer !== 'undefined' && bytes instanceof Buffer) return bytes.toString('utf-8')\n return new TextDecoder().decode(bytes)\n}\n\nexport function smb(options: SmbStoreOptions): NoydbStore {\n const { smb: client, remotePath = 'noydb', name = 'smb' } = options\n const root = remotePath.replace(/^\\/+|\\/+$/g, '')\n\n const recordPath = (v: string, c: string, id: string): string => join(root, v, c, `${id}.json`)\n const collPath = (v: string, c: string): string => join(root, v, c)\n const vaultPath = (v: string): string => join(root, v)\n\n async function ensureDir(path: string): Promise<void> {\n // SMB mkdir is not always recursive — walk parents.\n const parts = path.split('/').filter(Boolean)\n let cur = ''\n for (const seg of parts) {\n cur = cur ? `${cur}/${seg}` : seg\n try {\n await client.mkdir(cur)\n } catch {\n // Exists or race — continue.\n }\n }\n }\n\n async function safeReaddir(path: string): Promise<string[]> {\n try {\n return await client.readdir(path)\n } catch {\n return []\n }\n }\n\n const store: NoydbStore = {\n name,\n\n async get(vault, collection, id) {\n const bytes = await client.readFile(recordPath(vault, collection, id))\n if (bytes === null) return null\n return JSON.parse(decode(bytes)) as EncryptedEnvelope\n },\n\n async put(vault, collection, id, envelope) {\n // casAtomic: false — expectedVersion ignored.\n const target = recordPath(vault, collection, id)\n const tmp = `${target}.tmp`\n await ensureDir(collPath(vault, collection))\n await client.writeFile(tmp, JSON.stringify(envelope))\n await client.rename(tmp, target)\n },\n\n async delete(vault, collection, id) {\n try {\n await client.unlink(recordPath(vault, collection, id))\n } catch {\n /* idempotent */\n }\n },\n\n async list(vault, collection) {\n const entries = await safeReaddir(collPath(vault, collection))\n return entries\n .filter(e => e.endsWith('.json') && !e.endsWith('.tmp'))\n .map(e => e.slice(0, -'.json'.length))\n .sort()\n },\n\n async loadAll(vault) {\n const snap: VaultSnapshot = {}\n const collections = await safeReaddir(vaultPath(vault))\n for (const collection of collections) {\n if (collection.startsWith('_')) continue\n const ids = await store.list(vault, collection)\n const bucket: Record<string, EncryptedEnvelope> = {}\n for (const id of ids) {\n const env = await store.get(vault, collection, id)\n if (env) bucket[id] = env\n }\n if (Object.keys(bucket).length > 0) snap[collection] = bucket\n }\n return snap\n },\n\n async saveAll(vault, data) {\n const existing = await safeReaddir(vaultPath(vault))\n for (const collection of existing) {\n const ids = await safeReaddir(collPath(vault, collection))\n for (const fname of ids) {\n await client.unlink(join(root, vault, collection, fname)).catch(() => undefined)\n }\n }\n for (const [collection, recs] of Object.entries(data)) {\n await ensureDir(collPath(vault, collection))\n for (const [id, envelope] of Object.entries(recs)) {\n await store.put(vault, collection, id, envelope)\n }\n }\n },\n\n async ping() {\n if (client.ping) return client.ping()\n try {\n await client.readdir(root)\n return true\n } catch {\n return false\n }\n },\n }\n\n return store\n}\n"],"mappings":";AAiEA,SAAS,QAAQ,OAAyB;AACxC,SAAO,MACJ,OAAO,OAAK,MAAM,EAAE,EACpB,IAAI,OAAK,EAAE,QAAQ,cAAc,EAAE,CAAC,EACpC,OAAO,OAAK,MAAM,EAAE,EACpB,KAAK,GAAG;AACb;AAEA,SAAS,OAAO,OAAoC;AAClD,MAAI,OAAO,WAAW,eAAe,iBAAiB,OAAQ,QAAO,MAAM,SAAS,OAAO;AAC3F,SAAO,IAAI,YAAY,EAAE,OAAO,KAAK;AACvC;AAEO,SAAS,IAAI,SAAsC;AACxD,QAAM,EAAE,KAAK,QAAQ,aAAa,SAAS,OAAO,MAAM,IAAI;AAC5D,QAAM,OAAO,WAAW,QAAQ,cAAc,EAAE;AAEhD,QAAM,aAAa,CAAC,GAAW,GAAW,OAAuB,KAAK,MAAM,GAAG,GAAG,GAAG,EAAE,OAAO;AAC9F,QAAM,WAAW,CAAC,GAAW,MAAsB,KAAK,MAAM,GAAG,CAAC;AAClE,QAAM,YAAY,CAAC,MAAsB,KAAK,MAAM,CAAC;AAErD,iBAAe,UAAU,MAA6B;AAEpD,UAAM,QAAQ,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO;AAC5C,QAAI,MAAM;AACV,eAAW,OAAO,OAAO;AACvB,YAAM,MAAM,GAAG,GAAG,IAAI,GAAG,KAAK;AAC9B,UAAI;AACF,cAAM,OAAO,MAAM,GAAG;AAAA,MACxB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,iBAAe,YAAY,MAAiC;AAC1D,QAAI;AACF,aAAO,MAAM,OAAO,QAAQ,IAAI;AAAA,IAClC,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAEA,QAAM,QAAoB;AAAA,IACxB;AAAA,IAEA,MAAM,IAAI,OAAO,YAAY,IAAI;AAC/B,YAAM,QAAQ,MAAM,OAAO,SAAS,WAAW,OAAO,YAAY,EAAE,CAAC;AACrE,UAAI,UAAU,KAAM,QAAO;AAC3B,aAAO,KAAK,MAAM,OAAO,KAAK,CAAC;AAAA,IACjC;AAAA,IAEA,MAAM,IAAI,OAAO,YAAY,IAAI,UAAU;AAEzC,YAAM,SAAS,WAAW,OAAO,YAAY,EAAE;AAC/C,YAAM,MAAM,GAAG,MAAM;AACrB,YAAM,UAAU,SAAS,OAAO,UAAU,CAAC;AAC3C,YAAM,OAAO,UAAU,KAAK,KAAK,UAAU,QAAQ,CAAC;AACpD,YAAM,OAAO,OAAO,KAAK,MAAM;AAAA,IACjC;AAAA,IAEA,MAAM,OAAO,OAAO,YAAY,IAAI;AAClC,UAAI;AACF,cAAM,OAAO,OAAO,WAAW,OAAO,YAAY,EAAE,CAAC;AAAA,MACvD,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IAEA,MAAM,KAAK,OAAO,YAAY;AAC5B,YAAM,UAAU,MAAM,YAAY,SAAS,OAAO,UAAU,CAAC;AAC7D,aAAO,QACJ,OAAO,OAAK,EAAE,SAAS,OAAO,KAAK,CAAC,EAAE,SAAS,MAAM,CAAC,EACtD,IAAI,OAAK,EAAE,MAAM,GAAG,CAAC,QAAQ,MAAM,CAAC,EACpC,KAAK;AAAA,IACV;AAAA,IAEA,MAAM,QAAQ,OAAO;AACnB,YAAM,OAAsB,CAAC;AAC7B,YAAM,cAAc,MAAM,YAAY,UAAU,KAAK,CAAC;AACtD,iBAAW,cAAc,aAAa;AACpC,YAAI,WAAW,WAAW,GAAG,EAAG;AAChC,cAAM,MAAM,MAAM,MAAM,KAAK,OAAO,UAAU;AAC9C,cAAM,SAA4C,CAAC;AACnD,mBAAW,MAAM,KAAK;AACpB,gBAAM,MAAM,MAAM,MAAM,IAAI,OAAO,YAAY,EAAE;AACjD,cAAI,IAAK,QAAO,EAAE,IAAI;AAAA,QACxB;AACA,YAAI,OAAO,KAAK,MAAM,EAAE,SAAS,EAAG,MAAK,UAAU,IAAI;AAAA,MACzD;AACA,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,QAAQ,OAAO,MAAM;AACzB,YAAM,WAAW,MAAM,YAAY,UAAU,KAAK,CAAC;AACnD,iBAAW,cAAc,UAAU;AACjC,cAAM,MAAM,MAAM,YAAY,SAAS,OAAO,UAAU,CAAC;AACzD,mBAAW,SAAS,KAAK;AACvB,gBAAM,OAAO,OAAO,KAAK,MAAM,OAAO,YAAY,KAAK,CAAC,EAAE,MAAM,MAAM,MAAS;AAAA,QACjF;AAAA,MACF;AACA,iBAAW,CAAC,YAAY,IAAI,KAAK,OAAO,QAAQ,IAAI,GAAG;AACrD,cAAM,UAAU,SAAS,OAAO,UAAU,CAAC;AAC3C,mBAAW,CAAC,IAAI,QAAQ,KAAK,OAAO,QAAQ,IAAI,GAAG;AACjD,gBAAM,MAAM,IAAI,OAAO,YAAY,IAAI,QAAQ;AAAA,QACjD;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,OAAO;AACX,UAAI,OAAO,KAAM,QAAO,OAAO,KAAK;AACpC,UAAI;AACF,cAAM,OAAO,QAAQ,IAAI;AACzB,eAAO;AAAA,MACT,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@noy-db/to-smb",
|
|
3
|
+
"version": "0.1.0-pre.3",
|
|
4
|
+
"description": "SMB/CIFS network file store for noy-db. Works against Windows file servers, NAS devices (Synology/QNAP/Netgear), and corporate shared drives via NTLM or Kerberos auth — no OS mount required.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "vLannaAi <vicio@lanna.ai>",
|
|
7
|
+
"homepage": "https://github.com/vLannaAi/noy-db/tree/main/packages/to-smb#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/vLannaAi/noy-db.git",
|
|
11
|
+
"directory": "packages/to-smb"
|
|
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-smb",
|
|
51
|
+
"smb",
|
|
52
|
+
"cifs",
|
|
53
|
+
"nas",
|
|
54
|
+
"storage"
|
|
55
|
+
],
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public",
|
|
58
|
+
"tag": "latest"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsup",
|
|
62
|
+
"test": "vitest run",
|
|
63
|
+
"lint": "eslint src/",
|
|
64
|
+
"typecheck": "tsc --noEmit"
|
|
65
|
+
}
|
|
66
|
+
}
|