@resourcexjs/node-provider 2.5.6
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/README.md +164 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +228 -0
- package/dist/index.js.map +12 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# @resourcexjs/node-provider
|
|
2
|
+
|
|
3
|
+
Node.js/Bun platform provider for ResourceX.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @resourcexjs/node-provider
|
|
9
|
+
# or
|
|
10
|
+
npm install @resourcexjs/node-provider
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### With ResourceX Client
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { createResourceX, setProvider } from "resourcexjs";
|
|
19
|
+
import { NodeProvider } from "@resourcexjs/node-provider";
|
|
20
|
+
|
|
21
|
+
// Register the provider before creating client
|
|
22
|
+
setProvider(new NodeProvider());
|
|
23
|
+
|
|
24
|
+
const rx = createResourceX({
|
|
25
|
+
path: "~/.resourcex", // optional, defaults to ~/.resourcex
|
|
26
|
+
registry: "https://registry.example.com",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Now use ResourceX
|
|
30
|
+
await rx.add("./my-prompt");
|
|
31
|
+
const result = await rx.use("my-prompt:1.0.0");
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### With ResourceX Server
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { createRegistryServer } from "@resourcexjs/server";
|
|
38
|
+
import { FileSystemRXAStore, FileSystemRXMStore } from "@resourcexjs/node-provider";
|
|
39
|
+
|
|
40
|
+
const server = createRegistryServer({
|
|
41
|
+
rxaStore: new FileSystemRXAStore("./data/blobs"),
|
|
42
|
+
rxmStore: new FileSystemRXMStore("./data/manifests"),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Deploy with any runtime
|
|
46
|
+
Bun.serve({ fetch: server.fetch, port: 3000 });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Exports
|
|
50
|
+
|
|
51
|
+
### NodeProvider
|
|
52
|
+
|
|
53
|
+
Full provider implementation for Node.js/Bun platform.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { NodeProvider } from "@resourcexjs/node-provider";
|
|
57
|
+
|
|
58
|
+
const provider = new NodeProvider();
|
|
59
|
+
provider.platform; // "node"
|
|
60
|
+
provider.createStores({ path: "~/.resourcex" }); // { rxaStore, rxmStore }
|
|
61
|
+
provider.createLoader({}); // FolderLoader
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### FileSystemRXAStore
|
|
65
|
+
|
|
66
|
+
Content-addressable blob storage using filesystem.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { FileSystemRXAStore } from "@resourcexjs/node-provider";
|
|
70
|
+
|
|
71
|
+
const store = new FileSystemRXAStore("./data/blobs");
|
|
72
|
+
|
|
73
|
+
// Store blob, returns digest
|
|
74
|
+
const digest = await store.put(buffer);
|
|
75
|
+
|
|
76
|
+
// Get blob by digest
|
|
77
|
+
const data = await store.get(digest);
|
|
78
|
+
|
|
79
|
+
// Check existence
|
|
80
|
+
const exists = await store.has(digest);
|
|
81
|
+
|
|
82
|
+
// Delete blob
|
|
83
|
+
await store.delete(digest);
|
|
84
|
+
|
|
85
|
+
// List all digests
|
|
86
|
+
const digests = await store.list();
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Storage structure:**
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
./data/blobs/
|
|
93
|
+
├── ab/
|
|
94
|
+
│ └── sha256:abcd1234...
|
|
95
|
+
├── cd/
|
|
96
|
+
│ └── sha256:cdef5678...
|
|
97
|
+
└── ...
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### FileSystemRXMStore
|
|
101
|
+
|
|
102
|
+
Manifest storage using JSON files.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { FileSystemRXMStore } from "@resourcexjs/node-provider";
|
|
106
|
+
|
|
107
|
+
const store = new FileSystemRXMStore("./data/manifests");
|
|
108
|
+
|
|
109
|
+
// Store manifest
|
|
110
|
+
await store.put(storedRxm);
|
|
111
|
+
|
|
112
|
+
// Get manifest
|
|
113
|
+
const manifest = await store.get("my-prompt", "1.0.0");
|
|
114
|
+
|
|
115
|
+
// Get cached manifest (with registry)
|
|
116
|
+
const cached = await store.get("my-prompt", "1.0.0", "registry.example.com");
|
|
117
|
+
|
|
118
|
+
// Check existence
|
|
119
|
+
const exists = await store.has("my-prompt", "1.0.0");
|
|
120
|
+
|
|
121
|
+
// List all tags for a resource
|
|
122
|
+
const tags = await store.listTags("my-prompt");
|
|
123
|
+
|
|
124
|
+
// Search manifests
|
|
125
|
+
const results = await store.search({ query: "prompt", limit: 10 });
|
|
126
|
+
|
|
127
|
+
// Delete by registry (clear cache)
|
|
128
|
+
await store.deleteByRegistry("registry.example.com");
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Storage structure:**
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
./data/manifests/
|
|
135
|
+
├── _local/ # Local resources (no registry)
|
|
136
|
+
│ └── my-prompt/
|
|
137
|
+
│ └── 1.0.0.json
|
|
138
|
+
└── registry.example.com/ # Cached resources (with registry)
|
|
139
|
+
└── shared-prompt/
|
|
140
|
+
└── 1.0.0.json
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Architecture
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
┌─────────────────────────────────────────────────────────┐
|
|
147
|
+
│ Applications │
|
|
148
|
+
├─────────────────────┬───────────────────────────────────┤
|
|
149
|
+
│ resourcexjs │ @resourcexjs/server │
|
|
150
|
+
│ (Client SDK) │ (Registry Server) │
|
|
151
|
+
├─────────────────────┴───────────────────────────────────┤
|
|
152
|
+
│ @resourcexjs/node-provider │
|
|
153
|
+
│ ┌─────────────────┬─────────────────┬──────────────┐ │
|
|
154
|
+
│ │ NodeProvider │FileSystemRXAStore│FileSystemRXMStore│
|
|
155
|
+
│ └─────────────────┴─────────────────┴──────────────┘ │
|
|
156
|
+
├─────────────────────────────────────────────────────────┤
|
|
157
|
+
│ @resourcexjs/core │
|
|
158
|
+
│ (CASRegistry, RXAStore/RXMStore interfaces) │
|
|
159
|
+
└─────────────────────────────────────────────────────────┘
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
Apache-2.0
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ResourceXProvider, ProviderConfig, ProviderStores } from "@resourcexjs/core";
|
|
2
|
+
import { FolderLoader } from "@resourcexjs/core";
|
|
3
|
+
/**
|
|
4
|
+
* Node.js/Bun provider for ResourceX.
|
|
5
|
+
*
|
|
6
|
+
* Storage structure:
|
|
7
|
+
* - ~/.resourcex/blobs/ - Content-addressable blob storage
|
|
8
|
+
* - ~/.resourcex/manifests/ - Manifest JSON files
|
|
9
|
+
*/
|
|
10
|
+
declare class NodeProvider implements ResourceXProvider {
|
|
11
|
+
readonly platform = "node";
|
|
12
|
+
createStores(config: ProviderConfig): ProviderStores;
|
|
13
|
+
createLoader(_config: ProviderConfig): FolderLoader;
|
|
14
|
+
}
|
|
15
|
+
import { RXAStore } from "@resourcexjs/core";
|
|
16
|
+
declare class FileSystemRXAStore implements RXAStore {
|
|
17
|
+
private readonly basePath;
|
|
18
|
+
constructor(basePath: string);
|
|
19
|
+
/**
|
|
20
|
+
* Get path for a digest.
|
|
21
|
+
* Uses first 2 chars as subdirectory for better filesystem performance.
|
|
22
|
+
*/
|
|
23
|
+
private getPath;
|
|
24
|
+
get(digest: string): Promise<Buffer>;
|
|
25
|
+
put(data: Buffer): Promise<string>;
|
|
26
|
+
has(digest: string): Promise<boolean>;
|
|
27
|
+
delete(digest: string): Promise<void>;
|
|
28
|
+
list(): Promise<string[]>;
|
|
29
|
+
}
|
|
30
|
+
import { RXMStore, StoredRXM, RXMSearchOptions } from "@resourcexjs/core";
|
|
31
|
+
declare class FileSystemRXMStore implements RXMStore {
|
|
32
|
+
private readonly basePath;
|
|
33
|
+
constructor(basePath: string);
|
|
34
|
+
/**
|
|
35
|
+
* Get directory path for a manifest.
|
|
36
|
+
*/
|
|
37
|
+
private getDir;
|
|
38
|
+
/**
|
|
39
|
+
* Get file path for a manifest.
|
|
40
|
+
*/
|
|
41
|
+
private getPath;
|
|
42
|
+
get(name: string, tag: string, registry?: string): Promise<StoredRXM | null>;
|
|
43
|
+
put(manifest: StoredRXM): Promise<void>;
|
|
44
|
+
has(name: string, tag: string, registry?: string): Promise<boolean>;
|
|
45
|
+
delete(name: string, tag: string, registry?: string): Promise<void>;
|
|
46
|
+
listTags(name: string, registry?: string): Promise<string[]>;
|
|
47
|
+
listNames(registry?: string, query?: string): Promise<string[]>;
|
|
48
|
+
search(options?: RXMSearchOptions): Promise<StoredRXM[]>;
|
|
49
|
+
deleteByRegistry(registry: string): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
export { NodeProvider, FileSystemRXMStore, FileSystemRXAStore };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// src/NodeProvider.ts
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join as join3 } from "node:path";
|
|
4
|
+
import { FolderLoader } from "@resourcexjs/core";
|
|
5
|
+
|
|
6
|
+
// src/FileSystemRXAStore.ts
|
|
7
|
+
import { mkdir, readFile, writeFile, unlink, readdir, stat } from "node:fs/promises";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { computeDigest, RegistryError } from "@resourcexjs/core";
|
|
10
|
+
|
|
11
|
+
class FileSystemRXAStore {
|
|
12
|
+
basePath;
|
|
13
|
+
constructor(basePath) {
|
|
14
|
+
this.basePath = basePath;
|
|
15
|
+
}
|
|
16
|
+
getPath(digest) {
|
|
17
|
+
const prefix = digest.substring(7, 9);
|
|
18
|
+
return join(this.basePath, prefix, digest);
|
|
19
|
+
}
|
|
20
|
+
async get(digest) {
|
|
21
|
+
const path = this.getPath(digest);
|
|
22
|
+
try {
|
|
23
|
+
return await readFile(path);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
throw new RegistryError(`Blob not found: ${digest}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async put(data) {
|
|
29
|
+
const digest = computeDigest(data);
|
|
30
|
+
const path = this.getPath(digest);
|
|
31
|
+
if (await this.has(digest)) {
|
|
32
|
+
return digest;
|
|
33
|
+
}
|
|
34
|
+
const dir = join(path, "..");
|
|
35
|
+
await mkdir(dir, { recursive: true });
|
|
36
|
+
await writeFile(path, data);
|
|
37
|
+
return digest;
|
|
38
|
+
}
|
|
39
|
+
async has(digest) {
|
|
40
|
+
const path = this.getPath(digest);
|
|
41
|
+
try {
|
|
42
|
+
await stat(path);
|
|
43
|
+
return true;
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async delete(digest) {
|
|
49
|
+
const path = this.getPath(digest);
|
|
50
|
+
try {
|
|
51
|
+
await unlink(path);
|
|
52
|
+
} catch {}
|
|
53
|
+
}
|
|
54
|
+
async list() {
|
|
55
|
+
const digests = [];
|
|
56
|
+
try {
|
|
57
|
+
const prefixes = await readdir(this.basePath);
|
|
58
|
+
for (const prefix of prefixes) {
|
|
59
|
+
const prefixPath = join(this.basePath, prefix);
|
|
60
|
+
try {
|
|
61
|
+
const files = await readdir(prefixPath);
|
|
62
|
+
for (const file of files) {
|
|
63
|
+
if (file.startsWith("sha256:")) {
|
|
64
|
+
digests.push(file);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} catch {}
|
|
68
|
+
}
|
|
69
|
+
} catch {}
|
|
70
|
+
return digests;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/FileSystemRXMStore.ts
|
|
75
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, unlink as unlink2, readdir as readdir2, stat as stat2, rm } from "node:fs/promises";
|
|
76
|
+
import { join as join2 } from "node:path";
|
|
77
|
+
var LOCAL_DIR = "_local";
|
|
78
|
+
|
|
79
|
+
class FileSystemRXMStore {
|
|
80
|
+
basePath;
|
|
81
|
+
constructor(basePath) {
|
|
82
|
+
this.basePath = basePath;
|
|
83
|
+
}
|
|
84
|
+
getDir(name, registry) {
|
|
85
|
+
const registryDir = registry ?? LOCAL_DIR;
|
|
86
|
+
return join2(this.basePath, registryDir, name);
|
|
87
|
+
}
|
|
88
|
+
getPath(name, tag, registry) {
|
|
89
|
+
return join2(this.getDir(name, registry), `${tag}.json`);
|
|
90
|
+
}
|
|
91
|
+
async get(name, tag, registry) {
|
|
92
|
+
const path = this.getPath(name, tag, registry);
|
|
93
|
+
try {
|
|
94
|
+
const data = await readFile2(path, "utf-8");
|
|
95
|
+
return JSON.parse(data);
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async put(manifest) {
|
|
101
|
+
const path = this.getPath(manifest.name, manifest.tag, manifest.registry);
|
|
102
|
+
const dir = join2(path, "..");
|
|
103
|
+
await mkdir2(dir, { recursive: true });
|
|
104
|
+
await writeFile2(path, JSON.stringify(manifest, null, 2), "utf-8");
|
|
105
|
+
}
|
|
106
|
+
async has(name, tag, registry) {
|
|
107
|
+
const path = this.getPath(name, tag, registry);
|
|
108
|
+
try {
|
|
109
|
+
await stat2(path);
|
|
110
|
+
return true;
|
|
111
|
+
} catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async delete(name, tag, registry) {
|
|
116
|
+
const path = this.getPath(name, tag, registry);
|
|
117
|
+
try {
|
|
118
|
+
await unlink2(path);
|
|
119
|
+
} catch {}
|
|
120
|
+
}
|
|
121
|
+
async listTags(name, registry) {
|
|
122
|
+
const dir = this.getDir(name, registry);
|
|
123
|
+
const tags = [];
|
|
124
|
+
try {
|
|
125
|
+
const files = await readdir2(dir);
|
|
126
|
+
for (const file of files) {
|
|
127
|
+
if (file.endsWith(".json")) {
|
|
128
|
+
tags.push(file.replace(".json", ""));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} catch {}
|
|
132
|
+
return tags;
|
|
133
|
+
}
|
|
134
|
+
async listNames(registry, query) {
|
|
135
|
+
const registryDir = registry ?? LOCAL_DIR;
|
|
136
|
+
const basePath = join2(this.basePath, registryDir);
|
|
137
|
+
const names = [];
|
|
138
|
+
try {
|
|
139
|
+
const entries = await readdir2(basePath);
|
|
140
|
+
for (const entry of entries) {
|
|
141
|
+
const entryPath = join2(basePath, entry);
|
|
142
|
+
try {
|
|
143
|
+
const entryStat = await stat2(entryPath);
|
|
144
|
+
if (entryStat.isDirectory()) {
|
|
145
|
+
if (!query || entry.toLowerCase().includes(query.toLowerCase())) {
|
|
146
|
+
names.push(entry);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
} catch {}
|
|
150
|
+
}
|
|
151
|
+
} catch {}
|
|
152
|
+
return names;
|
|
153
|
+
}
|
|
154
|
+
async search(options) {
|
|
155
|
+
const { registry, query, limit, offset = 0 } = options ?? {};
|
|
156
|
+
const results = [];
|
|
157
|
+
let registryDirs = [];
|
|
158
|
+
if (registry === null) {
|
|
159
|
+
registryDirs = [LOCAL_DIR];
|
|
160
|
+
} else if (registry !== undefined) {
|
|
161
|
+
registryDirs = [registry];
|
|
162
|
+
} else {
|
|
163
|
+
try {
|
|
164
|
+
registryDirs = await readdir2(this.basePath);
|
|
165
|
+
} catch {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
for (const regDir of registryDirs) {
|
|
170
|
+
const regPath = join2(this.basePath, regDir);
|
|
171
|
+
try {
|
|
172
|
+
const names = await readdir2(regPath);
|
|
173
|
+
for (const name of names) {
|
|
174
|
+
if (query && !name.toLowerCase().includes(query.toLowerCase())) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const namePath = join2(regPath, name);
|
|
178
|
+
try {
|
|
179
|
+
const files = await readdir2(namePath);
|
|
180
|
+
for (const file of files) {
|
|
181
|
+
if (file.endsWith(".json")) {
|
|
182
|
+
const filePath = join2(namePath, file);
|
|
183
|
+
const data = await readFile2(filePath, "utf-8");
|
|
184
|
+
const manifest = JSON.parse(data);
|
|
185
|
+
results.push(manifest);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
} catch {}
|
|
189
|
+
}
|
|
190
|
+
} catch {}
|
|
191
|
+
}
|
|
192
|
+
let paginated = results.slice(offset);
|
|
193
|
+
if (limit !== undefined) {
|
|
194
|
+
paginated = paginated.slice(0, limit);
|
|
195
|
+
}
|
|
196
|
+
return paginated;
|
|
197
|
+
}
|
|
198
|
+
async deleteByRegistry(registry) {
|
|
199
|
+
const regPath = join2(this.basePath, registry);
|
|
200
|
+
try {
|
|
201
|
+
await rm(regPath, { recursive: true });
|
|
202
|
+
} catch {}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/NodeProvider.ts
|
|
207
|
+
var DEFAULT_BASE_PATH = `${homedir()}/.resourcex`;
|
|
208
|
+
|
|
209
|
+
class NodeProvider {
|
|
210
|
+
platform = "node";
|
|
211
|
+
createStores(config) {
|
|
212
|
+
const basePath = config.path ?? DEFAULT_BASE_PATH;
|
|
213
|
+
return {
|
|
214
|
+
rxaStore: new FileSystemRXAStore(join3(basePath, "blobs")),
|
|
215
|
+
rxmStore: new FileSystemRXMStore(join3(basePath, "manifests"))
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
createLoader(_config) {
|
|
219
|
+
return new FolderLoader;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
export {
|
|
223
|
+
NodeProvider,
|
|
224
|
+
FileSystemRXMStore,
|
|
225
|
+
FileSystemRXAStore
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
//# debugId=4D70AA91122D166964756E2164756E21
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/NodeProvider.ts", "../src/FileSystemRXAStore.ts", "../src/FileSystemRXMStore.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * NodeProvider - Node.js/Bun implementation of ResourceXProvider.\n *\n * Uses filesystem for blob storage and JSON files for manifest storage.\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ResourceXProvider, ProviderConfig, ProviderStores } from \"@resourcexjs/core\";\nimport { FolderLoader } from \"@resourcexjs/core\";\nimport { FileSystemRXAStore } from \"./FileSystemRXAStore.js\";\nimport { FileSystemRXMStore } from \"./FileSystemRXMStore.js\";\n\nconst DEFAULT_BASE_PATH = `${homedir()}/.resourcex`;\n\n/**\n * Node.js/Bun provider for ResourceX.\n *\n * Storage structure:\n * - ~/.resourcex/blobs/ - Content-addressable blob storage\n * - ~/.resourcex/manifests/ - Manifest JSON files\n */\nexport class NodeProvider implements ResourceXProvider {\n readonly platform = \"node\";\n\n createStores(config: ProviderConfig): ProviderStores {\n const basePath = (config.path as string) ?? DEFAULT_BASE_PATH;\n\n return {\n rxaStore: new FileSystemRXAStore(join(basePath, \"blobs\")),\n rxmStore: new FileSystemRXMStore(join(basePath, \"manifests\")),\n };\n }\n\n createLoader(_config: ProviderConfig): FolderLoader {\n return new FolderLoader();\n }\n}\n",
|
|
6
|
+
"/**\n * FileSystemRXAStore - File system implementation of RXAStore.\n *\n * Stores blobs as files named by their digest.\n * Directory structure: {basePath}/{digest-prefix}/{digest}\n */\n\nimport { mkdir, readFile, writeFile, unlink, readdir, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { RXAStore } from \"@resourcexjs/core\";\nimport { computeDigest, RegistryError } from \"@resourcexjs/core\";\n\nexport class FileSystemRXAStore implements RXAStore {\n constructor(private readonly basePath: string) {}\n\n /**\n * Get path for a digest.\n * Uses first 2 chars as subdirectory for better filesystem performance.\n */\n private getPath(digest: string): string {\n const prefix = digest.substring(7, 9); // Skip \"sha256:\" prefix\n return join(this.basePath, prefix, digest);\n }\n\n async get(digest: string): Promise<Buffer> {\n const path = this.getPath(digest);\n try {\n return await readFile(path);\n } catch (error) {\n throw new RegistryError(`Blob not found: ${digest}`);\n }\n }\n\n async put(data: Buffer): Promise<string> {\n const digest = computeDigest(data);\n const path = this.getPath(digest);\n\n // Skip if already exists (deduplication)\n if (await this.has(digest)) {\n return digest;\n }\n\n // Ensure directory exists\n const dir = join(path, \"..\");\n await mkdir(dir, { recursive: true });\n\n // Write file\n await writeFile(path, data);\n\n return digest;\n }\n\n async has(digest: string): Promise<boolean> {\n const path = this.getPath(digest);\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n }\n\n async delete(digest: string): Promise<void> {\n const path = this.getPath(digest);\n try {\n await unlink(path);\n } catch {\n // Ignore if not exists\n }\n }\n\n async list(): Promise<string[]> {\n const digests: string[] = [];\n\n try {\n const prefixes = await readdir(this.basePath);\n\n for (const prefix of prefixes) {\n const prefixPath = join(this.basePath, prefix);\n try {\n const files = await readdir(prefixPath);\n for (const file of files) {\n if (file.startsWith(\"sha256:\")) {\n digests.push(file);\n }\n }\n } catch {\n // Skip if not a directory\n }\n }\n } catch {\n // Base path doesn't exist\n }\n\n return digests;\n }\n}\n",
|
|
7
|
+
"/**\n * FileSystemRXMStore - File system implementation of RXMStore.\n *\n * Stores manifests as JSON files.\n * Directory structure: {basePath}/{registry|_local}/{name}/{tag}.json\n */\n\nimport { mkdir, readFile, writeFile, unlink, readdir, stat, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { RXMStore, StoredRXM, RXMSearchOptions } from \"@resourcexjs/core\";\n\nconst LOCAL_DIR = \"_local\";\n\nexport class FileSystemRXMStore implements RXMStore {\n constructor(private readonly basePath: string) {}\n\n /**\n * Get directory path for a manifest.\n */\n private getDir(name: string, registry?: string): string {\n const registryDir = registry ?? LOCAL_DIR;\n return join(this.basePath, registryDir, name);\n }\n\n /**\n * Get file path for a manifest.\n */\n private getPath(name: string, tag: string, registry?: string): string {\n return join(this.getDir(name, registry), `${tag}.json`);\n }\n\n async get(name: string, tag: string, registry?: string): Promise<StoredRXM | null> {\n const path = this.getPath(name, tag, registry);\n try {\n const data = await readFile(path, \"utf-8\");\n return JSON.parse(data) as StoredRXM;\n } catch {\n return null;\n }\n }\n\n async put(manifest: StoredRXM): Promise<void> {\n const path = this.getPath(manifest.name, manifest.tag, manifest.registry);\n const dir = join(path, \"..\");\n\n await mkdir(dir, { recursive: true });\n await writeFile(path, JSON.stringify(manifest, null, 2), \"utf-8\");\n }\n\n async has(name: string, tag: string, registry?: string): Promise<boolean> {\n const path = this.getPath(name, tag, registry);\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n }\n\n async delete(name: string, tag: string, registry?: string): Promise<void> {\n const path = this.getPath(name, tag, registry);\n try {\n await unlink(path);\n } catch {\n // Ignore if not exists\n }\n }\n\n async listTags(name: string, registry?: string): Promise<string[]> {\n const dir = this.getDir(name, registry);\n const tags: string[] = [];\n\n try {\n const files = await readdir(dir);\n for (const file of files) {\n if (file.endsWith(\".json\")) {\n tags.push(file.replace(\".json\", \"\"));\n }\n }\n } catch {\n // Directory doesn't exist\n }\n\n return tags;\n }\n\n async listNames(registry?: string, query?: string): Promise<string[]> {\n const registryDir = registry ?? LOCAL_DIR;\n const basePath = join(this.basePath, registryDir);\n const names: string[] = [];\n\n try {\n const entries = await readdir(basePath);\n for (const entry of entries) {\n const entryPath = join(basePath, entry);\n try {\n const entryStat = await stat(entryPath);\n if (entryStat.isDirectory()) {\n if (!query || entry.toLowerCase().includes(query.toLowerCase())) {\n names.push(entry);\n }\n }\n } catch {\n // Skip\n }\n }\n } catch {\n // Base path doesn't exist\n }\n\n return names;\n }\n\n async search(options?: RXMSearchOptions): Promise<StoredRXM[]> {\n const { registry, query, limit, offset = 0 } = options ?? {};\n const results: StoredRXM[] = [];\n\n // Determine which registry directories to scan\n let registryDirs: string[] = [];\n\n if (registry === null) {\n // Local only\n registryDirs = [LOCAL_DIR];\n } else if (registry !== undefined) {\n // Specific registry\n registryDirs = [registry];\n } else {\n // All registries\n try {\n registryDirs = await readdir(this.basePath);\n } catch {\n return [];\n }\n }\n\n // Scan each registry directory\n for (const regDir of registryDirs) {\n const regPath = join(this.basePath, regDir);\n\n try {\n const names = await readdir(regPath);\n\n for (const name of names) {\n // Filter by query\n if (query && !name.toLowerCase().includes(query.toLowerCase())) {\n continue;\n }\n\n const namePath = join(regPath, name);\n try {\n const files = await readdir(namePath);\n\n for (const file of files) {\n if (file.endsWith(\".json\")) {\n const filePath = join(namePath, file);\n const data = await readFile(filePath, \"utf-8\");\n const manifest = JSON.parse(data) as StoredRXM;\n results.push(manifest);\n }\n }\n } catch {\n // Skip if not a directory\n }\n }\n } catch {\n // Skip if registry dir doesn't exist\n }\n }\n\n // Apply pagination\n let paginated = results.slice(offset);\n if (limit !== undefined) {\n paginated = paginated.slice(0, limit);\n }\n\n return paginated;\n }\n\n async deleteByRegistry(registry: string): Promise<void> {\n const regPath = join(this.basePath, registry);\n try {\n await rm(regPath, { recursive: true });\n } catch {\n // Ignore if not exists\n }\n }\n}\n"
|
|
8
|
+
],
|
|
9
|
+
"mappings": ";AAMA;AACA,iBAAS;AAET;;;ACFA;AACA;AAEA;AAAA;AAEO,MAAM,mBAAuC;AAAA,EACrB;AAAA,EAA7B,WAAW,CAAkB,UAAkB;AAAA,IAAlB;AAAA;AAAA,EAMrB,OAAO,CAAC,QAAwB;AAAA,IACtC,MAAM,SAAS,OAAO,UAAU,GAAG,CAAC;AAAA,IACpC,OAAO,KAAK,KAAK,UAAU,QAAQ,MAAM;AAAA;AAAA,OAGrC,IAAG,CAAC,QAAiC;AAAA,IACzC,MAAM,OAAO,KAAK,QAAQ,MAAM;AAAA,IAChC,IAAI;AAAA,MACF,OAAO,MAAM,SAAS,IAAI;AAAA,MAC1B,OAAO,OAAO;AAAA,MACd,MAAM,IAAI,cAAc,mBAAmB,QAAQ;AAAA;AAAA;AAAA,OAIjD,IAAG,CAAC,MAA+B;AAAA,IACvC,MAAM,SAAS,cAAc,IAAI;AAAA,IACjC,MAAM,OAAO,KAAK,QAAQ,MAAM;AAAA,IAGhC,IAAI,MAAM,KAAK,IAAI,MAAM,GAAG;AAAA,MAC1B,OAAO;AAAA,IACT;AAAA,IAGA,MAAM,MAAM,KAAK,MAAM,IAAI;AAAA,IAC3B,MAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,IAGpC,MAAM,UAAU,MAAM,IAAI;AAAA,IAE1B,OAAO;AAAA;AAAA,OAGH,IAAG,CAAC,QAAkC;AAAA,IAC1C,MAAM,OAAO,KAAK,QAAQ,MAAM;AAAA,IAChC,IAAI;AAAA,MACF,MAAM,KAAK,IAAI;AAAA,MACf,OAAO;AAAA,MACP,MAAM;AAAA,MACN,OAAO;AAAA;AAAA;AAAA,OAIL,OAAM,CAAC,QAA+B;AAAA,IAC1C,MAAM,OAAO,KAAK,QAAQ,MAAM;AAAA,IAChC,IAAI;AAAA,MACF,MAAM,OAAO,IAAI;AAAA,MACjB,MAAM;AAAA;AAAA,OAKJ,KAAI,GAAsB;AAAA,IAC9B,MAAM,UAAoB,CAAC;AAAA,IAE3B,IAAI;AAAA,MACF,MAAM,WAAW,MAAM,QAAQ,KAAK,QAAQ;AAAA,MAE5C,WAAW,UAAU,UAAU;AAAA,QAC7B,MAAM,aAAa,KAAK,KAAK,UAAU,MAAM;AAAA,QAC7C,IAAI;AAAA,UACF,MAAM,QAAQ,MAAM,QAAQ,UAAU;AAAA,UACtC,WAAW,QAAQ,OAAO;AAAA,YACxB,IAAI,KAAK,WAAW,SAAS,GAAG;AAAA,cAC9B,QAAQ,KAAK,IAAI;AAAA,YACnB;AAAA,UACF;AAAA,UACA,MAAM;AAAA,MAGV;AAAA,MACA,MAAM;AAAA,IAIR,OAAO;AAAA;AAEX;;;ACzFA,kBAAS,oBAAO,wBAAU,sBAAW,oBAAQ,kBAAS;AACtD,iBAAS;AAGT,IAAM,YAAY;AAAA;AAEX,MAAM,mBAAuC;AAAA,EACrB;AAAA,EAA7B,WAAW,CAAkB,UAAkB;AAAA,IAAlB;AAAA;AAAA,EAKrB,MAAM,CAAC,MAAc,UAA2B;AAAA,IACtD,MAAM,cAAc,YAAY;AAAA,IAChC,OAAO,MAAK,KAAK,UAAU,aAAa,IAAI;AAAA;AAAA,EAMtC,OAAO,CAAC,MAAc,KAAa,UAA2B;AAAA,IACpE,OAAO,MAAK,KAAK,OAAO,MAAM,QAAQ,GAAG,GAAG,UAAU;AAAA;AAAA,OAGlD,IAAG,CAAC,MAAc,KAAa,UAA8C;AAAA,IACjF,MAAM,OAAO,KAAK,QAAQ,MAAM,KAAK,QAAQ;AAAA,IAC7C,IAAI;AAAA,MACF,MAAM,OAAO,MAAM,UAAS,MAAM,OAAO;AAAA,MACzC,OAAO,KAAK,MAAM,IAAI;AAAA,MACtB,MAAM;AAAA,MACN,OAAO;AAAA;AAAA;AAAA,OAIL,IAAG,CAAC,UAAoC;AAAA,IAC5C,MAAM,OAAO,KAAK,QAAQ,SAAS,MAAM,SAAS,KAAK,SAAS,QAAQ;AAAA,IACxE,MAAM,MAAM,MAAK,MAAM,IAAI;AAAA,IAE3B,MAAM,OAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,IACpC,MAAM,WAAU,MAAM,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AAAA;AAAA,OAG5D,IAAG,CAAC,MAAc,KAAa,UAAqC;AAAA,IACxE,MAAM,OAAO,KAAK,QAAQ,MAAM,KAAK,QAAQ;AAAA,IAC7C,IAAI;AAAA,MACF,MAAM,MAAK,IAAI;AAAA,MACf,OAAO;AAAA,MACP,MAAM;AAAA,MACN,OAAO;AAAA;AAAA;AAAA,OAIL,OAAM,CAAC,MAAc,KAAa,UAAkC;AAAA,IACxE,MAAM,OAAO,KAAK,QAAQ,MAAM,KAAK,QAAQ;AAAA,IAC7C,IAAI;AAAA,MACF,MAAM,QAAO,IAAI;AAAA,MACjB,MAAM;AAAA;AAAA,OAKJ,SAAQ,CAAC,MAAc,UAAsC;AAAA,IACjE,MAAM,MAAM,KAAK,OAAO,MAAM,QAAQ;AAAA,IACtC,MAAM,OAAiB,CAAC;AAAA,IAExB,IAAI;AAAA,MACF,MAAM,QAAQ,MAAM,SAAQ,GAAG;AAAA,MAC/B,WAAW,QAAQ,OAAO;AAAA,QACxB,IAAI,KAAK,SAAS,OAAO,GAAG;AAAA,UAC1B,KAAK,KAAK,KAAK,QAAQ,SAAS,EAAE,CAAC;AAAA,QACrC;AAAA,MACF;AAAA,MACA,MAAM;AAAA,IAIR,OAAO;AAAA;AAAA,OAGH,UAAS,CAAC,UAAmB,OAAmC;AAAA,IACpE,MAAM,cAAc,YAAY;AAAA,IAChC,MAAM,WAAW,MAAK,KAAK,UAAU,WAAW;AAAA,IAChD,MAAM,QAAkB,CAAC;AAAA,IAEzB,IAAI;AAAA,MACF,MAAM,UAAU,MAAM,SAAQ,QAAQ;AAAA,MACtC,WAAW,SAAS,SAAS;AAAA,QAC3B,MAAM,YAAY,MAAK,UAAU,KAAK;AAAA,QACtC,IAAI;AAAA,UACF,MAAM,YAAY,MAAM,MAAK,SAAS;AAAA,UACtC,IAAI,UAAU,YAAY,GAAG;AAAA,YAC3B,IAAI,CAAC,SAAS,MAAM,YAAY,EAAE,SAAS,MAAM,YAAY,CAAC,GAAG;AAAA,cAC/D,MAAM,KAAK,KAAK;AAAA,YAClB;AAAA,UACF;AAAA,UACA,MAAM;AAAA,MAGV;AAAA,MACA,MAAM;AAAA,IAIR,OAAO;AAAA;AAAA,OAGH,OAAM,CAAC,SAAkD;AAAA,IAC7D,QAAQ,UAAU,OAAO,OAAO,SAAS,MAAM,WAAW,CAAC;AAAA,IAC3D,MAAM,UAAuB,CAAC;AAAA,IAG9B,IAAI,eAAyB,CAAC;AAAA,IAE9B,IAAI,aAAa,MAAM;AAAA,MAErB,eAAe,CAAC,SAAS;AAAA,IAC3B,EAAO,SAAI,aAAa,WAAW;AAAA,MAEjC,eAAe,CAAC,QAAQ;AAAA,IAC1B,EAAO;AAAA,MAEL,IAAI;AAAA,QACF,eAAe,MAAM,SAAQ,KAAK,QAAQ;AAAA,QAC1C,MAAM;AAAA,QACN,OAAO,CAAC;AAAA;AAAA;AAAA,IAKZ,WAAW,UAAU,cAAc;AAAA,MACjC,MAAM,UAAU,MAAK,KAAK,UAAU,MAAM;AAAA,MAE1C,IAAI;AAAA,QACF,MAAM,QAAQ,MAAM,SAAQ,OAAO;AAAA,QAEnC,WAAW,QAAQ,OAAO;AAAA,UAExB,IAAI,SAAS,CAAC,KAAK,YAAY,EAAE,SAAS,MAAM,YAAY,CAAC,GAAG;AAAA,YAC9D;AAAA,UACF;AAAA,UAEA,MAAM,WAAW,MAAK,SAAS,IAAI;AAAA,UACnC,IAAI;AAAA,YACF,MAAM,QAAQ,MAAM,SAAQ,QAAQ;AAAA,YAEpC,WAAW,QAAQ,OAAO;AAAA,cACxB,IAAI,KAAK,SAAS,OAAO,GAAG;AAAA,gBAC1B,MAAM,WAAW,MAAK,UAAU,IAAI;AAAA,gBACpC,MAAM,OAAO,MAAM,UAAS,UAAU,OAAO;AAAA,gBAC7C,MAAM,WAAW,KAAK,MAAM,IAAI;AAAA,gBAChC,QAAQ,KAAK,QAAQ;AAAA,cACvB;AAAA,YACF;AAAA,YACA,MAAM;AAAA,QAGV;AAAA,QACA,MAAM;AAAA,IAGV;AAAA,IAGA,IAAI,YAAY,QAAQ,MAAM,MAAM;AAAA,IACpC,IAAI,UAAU,WAAW;AAAA,MACvB,YAAY,UAAU,MAAM,GAAG,KAAK;AAAA,IACtC;AAAA,IAEA,OAAO;AAAA;AAAA,OAGH,iBAAgB,CAAC,UAAiC;AAAA,IACtD,MAAM,UAAU,MAAK,KAAK,UAAU,QAAQ;AAAA,IAC5C,IAAI;AAAA,MACF,MAAM,GAAG,SAAS,EAAE,WAAW,KAAK,CAAC;AAAA,MACrC,MAAM;AAAA;AAIZ;;;AF7KA,IAAM,oBAAoB,GAAG,QAAQ;AAAA;AAS9B,MAAM,aAA0C;AAAA,EAC5C,WAAW;AAAA,EAEpB,YAAY,CAAC,QAAwC;AAAA,IACnD,MAAM,WAAY,OAAO,QAAmB;AAAA,IAE5C,OAAO;AAAA,MACL,UAAU,IAAI,mBAAmB,MAAK,UAAU,OAAO,CAAC;AAAA,MACxD,UAAU,IAAI,mBAAmB,MAAK,UAAU,WAAW,CAAC;AAAA,IAC9D;AAAA;AAAA,EAGF,YAAY,CAAC,SAAuC;AAAA,IAClD,OAAO,IAAI;AAAA;AAEf;",
|
|
10
|
+
"debugId": "4D70AA91122D166964756E2164756E21",
|
|
11
|
+
"names": []
|
|
12
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@resourcexjs/node-provider",
|
|
3
|
+
"version": "2.5.6",
|
|
4
|
+
"description": "ResourceX Node.js/Bun Provider - FileSystem stores implementation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "bun run build.ts",
|
|
19
|
+
"lint": "eslint .",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"clean": "rm -rf dist"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@resourcexjs/core": "^2.5.6"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"license": "Apache-2.0"
|
|
31
|
+
}
|