@resourcexjs/registry 2.5.0 → 2.5.2
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 +276 -302
- package/dist/index.d.ts +161 -34
- package/dist/index.js +257 -150
- package/dist/index.js.map +14 -9
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @resourcexjs/registry
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Registry layer for ResourceX. Provides business logic for RXR (ResourceX Resource) operations on top of the storage layer.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,439 +10,413 @@ bun add @resourcexjs/registry
|
|
|
10
10
|
|
|
11
11
|
## Overview
|
|
12
12
|
|
|
13
|
-
The `@resourcexjs/registry` package provides
|
|
13
|
+
The `@resourcexjs/registry` package provides:
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
- **Three registry types** for different use cases
|
|
16
|
+
- **Access chain** for read-through cache pattern
|
|
17
|
+
- **Middleware** for cross-cutting concerns
|
|
18
|
+
- **Discovery** for well-known registry endpoints
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
- **DefaultRegistry**: Main implementation combining Storage with type handling
|
|
19
|
-
- **LocalStorage**: Filesystem-based storage for local resources
|
|
20
|
-
- **Storage**: Abstract interface for different storage backends
|
|
21
|
-
- **Well-known discovery**: Auto-discover registry endpoints via `/.well-known/resourcex`
|
|
22
|
-
- **Isolator**: Sandbox execution for resolver code
|
|
20
|
+
All registries implement a common `Registry` interface and use `@resourcexjs/storage` for persistence.
|
|
23
21
|
|
|
24
|
-
##
|
|
22
|
+
## Registry Types
|
|
25
23
|
|
|
26
|
-
###
|
|
24
|
+
### LocalRegistry
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
import { createRegistry } from "@resourcexjs/registry";
|
|
30
|
-
|
|
31
|
-
// Default registry (uses LocalStorage at ~/.resourcex)
|
|
32
|
-
const registry = createRegistry();
|
|
33
|
-
|
|
34
|
-
// Custom local path
|
|
35
|
-
const registry2 = createRegistry({
|
|
36
|
-
path: "./my-registry",
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// With custom resource types
|
|
40
|
-
import { bundleResourceType } from "@resourcexjs/type";
|
|
41
|
-
const promptType = await bundleResourceType("./prompt.type.ts");
|
|
26
|
+
For local/owned resources without a registry domain in the path.
|
|
42
27
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
28
|
+
```typescript
|
|
29
|
+
import { LocalRegistry } from "@resourcexjs/registry";
|
|
30
|
+
import { FileSystemStorage } from "@resourcexjs/storage";
|
|
46
31
|
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
isolator: "srt", // "none" | "srt" | "cloudflare" | "e2b"
|
|
50
|
-
});
|
|
32
|
+
const storage = new FileSystemStorage("~/.resourcex/local");
|
|
33
|
+
const registry = new LocalRegistry(storage);
|
|
51
34
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
35
|
+
// Storage structure: {name}/{tag}/manifest.json + archive.tar.gz
|
|
36
|
+
await registry.put(rxr);
|
|
37
|
+
const resource = await registry.get(rxl);
|
|
38
|
+
```
|
|
56
39
|
|
|
57
|
-
|
|
58
|
-
import { LocalStorage } from "@resourcexjs/registry";
|
|
40
|
+
**Use cases:**
|
|
59
41
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
});
|
|
63
|
-
```
|
|
42
|
+
- Client local storage (`~/.resourcex/local/`)
|
|
43
|
+
- Server authoritative storage (`./data/`)
|
|
64
44
|
|
|
65
|
-
###
|
|
45
|
+
### MirrorRegistry
|
|
66
46
|
|
|
67
|
-
|
|
47
|
+
For cached/mirrored remote resources. Includes registry domain in the path.
|
|
68
48
|
|
|
69
49
|
```typescript
|
|
70
|
-
import {
|
|
71
|
-
import {
|
|
72
|
-
|
|
73
|
-
// Load resource from folder
|
|
74
|
-
const rxr = await loadResource("./my-prompt");
|
|
50
|
+
import { MirrorRegistry } from "@resourcexjs/registry";
|
|
51
|
+
import { FileSystemStorage } from "@resourcexjs/storage";
|
|
75
52
|
|
|
76
|
-
|
|
77
|
-
const registry =
|
|
78
|
-
await registry.add(rxr);
|
|
53
|
+
const storage = new FileSystemStorage("~/.resourcex/cache");
|
|
54
|
+
const registry = new MirrorRegistry(storage);
|
|
79
55
|
|
|
80
|
-
//
|
|
81
|
-
await registry.
|
|
56
|
+
// Storage structure: {registry}/{name}/{tag}/manifest.json + archive.tar.gz
|
|
57
|
+
await registry.put(rxr);
|
|
58
|
+
const resource = await registry.get(rxl);
|
|
82
59
|
|
|
83
|
-
//
|
|
60
|
+
// Cache-specific: clear cached resources
|
|
61
|
+
await registry.clear(); // Clear all
|
|
62
|
+
await registry.clear("deepractice.ai"); // Clear specific registry
|
|
84
63
|
```
|
|
85
64
|
|
|
86
|
-
|
|
65
|
+
**Use cases:**
|
|
87
66
|
|
|
88
|
-
|
|
67
|
+
- Caching remote resources locally
|
|
68
|
+
- Offline access to previously fetched resources
|
|
89
69
|
|
|
90
|
-
|
|
91
|
-
const registry = createRegistry();
|
|
92
|
-
|
|
93
|
-
// Link directory - changes reflect immediately
|
|
94
|
-
await registry.link("./my-prompts/assistant");
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### Resolve Resource
|
|
70
|
+
### LinkedRegistry
|
|
98
71
|
|
|
99
|
-
|
|
72
|
+
For development symlinks. Changes in the source directory are reflected immediately.
|
|
100
73
|
|
|
101
74
|
```typescript
|
|
102
|
-
|
|
75
|
+
import { LinkedRegistry } from "@resourcexjs/registry";
|
|
103
76
|
|
|
104
|
-
|
|
105
|
-
const resolved = await registry.resolve("localhost/my-prompt.text@1.0.0");
|
|
77
|
+
const registry = new LinkedRegistry("~/.resourcex/linked");
|
|
106
78
|
|
|
107
|
-
//
|
|
108
|
-
const
|
|
109
|
-
console.log(text);
|
|
79
|
+
// Create symlink to development directory
|
|
80
|
+
const rxl = await registry.link("./my-prompt");
|
|
110
81
|
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
```
|
|
82
|
+
// Get resource (reads from symlink target)
|
|
83
|
+
const resource = await registry.get(rxl);
|
|
114
84
|
|
|
115
|
-
|
|
85
|
+
// Remove symlink
|
|
86
|
+
await registry.unlink(rxl);
|
|
87
|
+
```
|
|
116
88
|
|
|
117
|
-
|
|
89
|
+
**Use cases:**
|
|
118
90
|
|
|
119
|
-
|
|
120
|
-
|
|
91
|
+
- Live development without re-adding resources
|
|
92
|
+
- Testing local changes before publishing
|
|
121
93
|
|
|
122
|
-
|
|
123
|
-
console.log(rxr.manifest.type); // "text"
|
|
124
|
-
```
|
|
94
|
+
## Registry Interface
|
|
125
95
|
|
|
126
|
-
|
|
96
|
+
All registries implement:
|
|
127
97
|
|
|
128
98
|
```typescript
|
|
129
|
-
|
|
130
|
-
|
|
99
|
+
interface Registry {
|
|
100
|
+
get(rxl: RXL): Promise<RXR>;
|
|
101
|
+
put(rxr: RXR): Promise<void>;
|
|
102
|
+
has(rxl: RXL): Promise<boolean>;
|
|
103
|
+
remove(rxl: RXL): Promise<void>;
|
|
104
|
+
list(options?: SearchOptions): Promise<RXL[]>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface SearchOptions {
|
|
108
|
+
query?: string; // Filter by name/path substring
|
|
109
|
+
limit?: number; // Max results
|
|
110
|
+
offset?: number; // Skip first N (pagination)
|
|
131
111
|
}
|
|
132
112
|
```
|
|
133
113
|
|
|
134
|
-
|
|
114
|
+
## Access Chain
|
|
135
115
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
116
|
+
The access chain implements a read-through cache pattern for unified resource access.
|
|
117
|
+
|
|
118
|
+
### RegistryAccessChain
|
|
139
119
|
|
|
140
|
-
|
|
120
|
+
Iterates through accessors in order. First accessor that can handle returns the result.
|
|
141
121
|
|
|
142
122
|
```typescript
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
123
|
+
import {
|
|
124
|
+
RegistryAccessChain,
|
|
125
|
+
LinkedAccessor,
|
|
126
|
+
LocalAccessor,
|
|
127
|
+
CacheAccessor,
|
|
128
|
+
RemoteAccessor,
|
|
129
|
+
LocalRegistry,
|
|
130
|
+
MirrorRegistry,
|
|
131
|
+
LinkedRegistry,
|
|
132
|
+
} from "@resourcexjs/registry";
|
|
133
|
+
import { FileSystemStorage } from "@resourcexjs/storage";
|
|
134
|
+
|
|
135
|
+
// Create registries
|
|
136
|
+
const linkedRegistry = new LinkedRegistry("~/.resourcex/linked");
|
|
137
|
+
const localRegistry = new LocalRegistry(new FileSystemStorage("~/.resourcex/local"));
|
|
138
|
+
const cacheRegistry = new MirrorRegistry(new FileSystemStorage("~/.resourcex/cache"));
|
|
139
|
+
|
|
140
|
+
// Create accessor chain
|
|
141
|
+
const chain = new RegistryAccessChain(
|
|
142
|
+
[
|
|
143
|
+
new LinkedAccessor(linkedRegistry), // 1. Dev symlinks (highest priority)
|
|
144
|
+
new LocalAccessor(localRegistry), // 2. Local resources (no domain)
|
|
145
|
+
new CacheAccessor(cacheRegistry), // 3. Cached remote resources
|
|
146
|
+
new RemoteAccessor(fetcher, cacheRegistry), // 4. Fetch + auto-cache
|
|
147
|
+
],
|
|
148
|
+
{ memCache: true } // Optional in-memory cache
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// Get resource (tries each accessor in order)
|
|
152
|
+
const rxr = await chain.get(rxl);
|
|
153
|
+
|
|
154
|
+
// Check existence
|
|
155
|
+
const exists = await chain.has(rxl);
|
|
156
|
+
|
|
157
|
+
// Cache management
|
|
158
|
+
chain.invalidate(rxl); // Remove specific resource from memory cache
|
|
159
|
+
chain.clearCache(); // Clear entire memory cache
|
|
155
160
|
```
|
|
156
161
|
|
|
157
|
-
###
|
|
162
|
+
### Accessor Types
|
|
158
163
|
|
|
159
|
-
|
|
160
|
-
|
|
164
|
+
| Accessor | Handles | Description |
|
|
165
|
+
| ---------------- | -------------------------- | ------------------------------ |
|
|
166
|
+
| `LinkedAccessor` | All (if linked) | Dev symlinks, highest priority |
|
|
167
|
+
| `LocalAccessor` | Resources without registry | Local storage |
|
|
168
|
+
| `CacheAccessor` | Resources with registry | Cached remote resources |
|
|
169
|
+
| `RemoteAccessor` | Resources with registry | Fetch + auto-cache |
|
|
170
|
+
|
|
171
|
+
### RemoteFetcher Interface
|
|
161
172
|
|
|
162
|
-
|
|
173
|
+
`RemoteAccessor` requires a fetcher implementation:
|
|
163
174
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
175
|
+
```typescript
|
|
176
|
+
interface RemoteFetcher {
|
|
177
|
+
fetch(rxl: RXL): Promise<RXR>;
|
|
178
|
+
}
|
|
167
179
|
|
|
168
|
-
//
|
|
169
|
-
const
|
|
180
|
+
// Example implementation
|
|
181
|
+
const fetcher: RemoteFetcher = {
|
|
182
|
+
async fetch(rxl) {
|
|
183
|
+
const url = `https://${rxl.registry}/api/v1/resources/${rxl.name}/${rxl.tag}`;
|
|
184
|
+
const response = await fetch(url);
|
|
185
|
+
// ... parse response to RXR
|
|
186
|
+
return rxr;
|
|
187
|
+
},
|
|
188
|
+
};
|
|
170
189
|
```
|
|
171
190
|
|
|
172
|
-
##
|
|
191
|
+
## Middleware
|
|
173
192
|
|
|
174
|
-
###
|
|
193
|
+
### RegistryMiddleware
|
|
175
194
|
|
|
176
|
-
|
|
195
|
+
Base class for creating custom middleware. Delegates all operations to the inner registry.
|
|
177
196
|
|
|
178
|
-
|
|
197
|
+
```typescript
|
|
198
|
+
import { RegistryMiddleware } from "@resourcexjs/registry";
|
|
199
|
+
import type { RXL, RXR } from "@resourcexjs/core";
|
|
179
200
|
|
|
180
|
-
|
|
201
|
+
class LoggingMiddleware extends RegistryMiddleware {
|
|
202
|
+
async get(rxl: RXL): Promise<RXR> {
|
|
203
|
+
console.log("Getting:", rxl);
|
|
204
|
+
const rxr = await this.inner.get(rxl);
|
|
205
|
+
console.log("Got:", rxr.manifest.name);
|
|
206
|
+
return rxr;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
181
209
|
|
|
182
|
-
|
|
210
|
+
const logged = new LoggingMiddleware(registry);
|
|
211
|
+
```
|
|
183
212
|
|
|
184
|
-
|
|
185
|
-
- `mirror?: string` - Mirror URL for remote fetch
|
|
186
|
-
- `types?: BundledType[]` - Custom resource types
|
|
187
|
-
- `isolator?: IsolatorType` - Sandbox isolation level
|
|
213
|
+
### DomainValidation
|
|
188
214
|
|
|
189
|
-
|
|
215
|
+
Built-in middleware that validates resource registry matches a trusted registry.
|
|
190
216
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
- `isolator?: IsolatorType` - Sandbox isolation level
|
|
217
|
+
```typescript
|
|
218
|
+
import { withDomainValidation } from "@resourcexjs/registry";
|
|
194
219
|
|
|
195
|
-
|
|
220
|
+
const validated = withDomainValidation(registry, "deepractice.ai");
|
|
196
221
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const registry = createRegistry({
|
|
200
|
-
path: "~/.resourcex",
|
|
201
|
-
mirror: "https://registry.deepractice.ai/v1",
|
|
202
|
-
types: [promptType],
|
|
203
|
-
isolator: "srt",
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
// Server mode
|
|
207
|
-
const registry = createRegistry({
|
|
208
|
-
storage: new LocalStorage({ path: "./data" }),
|
|
209
|
-
types: [promptType],
|
|
210
|
-
});
|
|
222
|
+
// Throws RegistryError if resource.manifest.registry !== "deepractice.ai"
|
|
223
|
+
await validated.get(rxl);
|
|
211
224
|
```
|
|
212
225
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
Discover registry endpoints for a domain via well-known.
|
|
226
|
+
**Use cases:**
|
|
216
227
|
|
|
217
|
-
|
|
228
|
+
- Server-side validation to prevent registry impersonation
|
|
229
|
+
- Ensuring resources are from trusted sources
|
|
218
230
|
|
|
219
|
-
|
|
231
|
+
## Discovery
|
|
220
232
|
|
|
221
|
-
|
|
233
|
+
Discover registry endpoints via well-known URL.
|
|
222
234
|
|
|
223
235
|
```typescript
|
|
236
|
+
import { discoverRegistry } from "@resourcexjs/registry";
|
|
237
|
+
|
|
224
238
|
const result = await discoverRegistry("deepractice.ai");
|
|
225
|
-
// {
|
|
239
|
+
// {
|
|
240
|
+
// domain: "deepractice.ai",
|
|
241
|
+
// registries: ["https://registry.deepractice.ai/api/v1"]
|
|
242
|
+
// }
|
|
226
243
|
```
|
|
227
244
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
#### `supportType(type: BundledType): void`
|
|
231
|
-
|
|
232
|
-
Add support for a custom resource type at runtime.
|
|
233
|
-
|
|
234
|
-
#### `link(path: string): Promise<void>`
|
|
235
|
-
|
|
236
|
-
Create a symlink for development. Changes in the source directory are immediately reflected.
|
|
237
|
-
|
|
238
|
-
#### `add(source: string | RXR): Promise<void>`
|
|
239
|
-
|
|
240
|
-
Add resource to storage. Accepts a folder path or RXR object.
|
|
241
|
-
|
|
242
|
-
#### `get(locator: string): Promise<RXR>`
|
|
245
|
+
**Well-known format** (`https://{domain}/.well-known/resourcex`):
|
|
243
246
|
|
|
244
|
-
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"version": "1.0",
|
|
250
|
+
"registries": ["https://registry.example.com/api/v1"]
|
|
251
|
+
}
|
|
252
|
+
```
|
|
245
253
|
|
|
246
|
-
|
|
254
|
+
## Storage Structure
|
|
247
255
|
|
|
248
|
-
|
|
256
|
+
```
|
|
257
|
+
~/.resourcex/
|
|
258
|
+
├── local/ # LocalRegistry - local resources
|
|
259
|
+
│ └── {path/}{name}/
|
|
260
|
+
│ └── {tag}/
|
|
261
|
+
│ ├── manifest.json
|
|
262
|
+
│ └── archive.tar.gz
|
|
263
|
+
│
|
|
264
|
+
├── cache/ # MirrorRegistry - cached remote
|
|
265
|
+
│ └── {registry}/
|
|
266
|
+
│ └── {path/}{name}/
|
|
267
|
+
│ └── {tag}/
|
|
268
|
+
│ ├── manifest.json
|
|
269
|
+
│ └── archive.tar.gz
|
|
270
|
+
│
|
|
271
|
+
└── linked/ # LinkedRegistry - dev symlinks
|
|
272
|
+
└── {registry}/
|
|
273
|
+
└── {path/}{name}/
|
|
274
|
+
└── {tag} -> /path/to/dev/folder
|
|
275
|
+
```
|
|
249
276
|
|
|
250
|
-
|
|
277
|
+
## Error Handling
|
|
251
278
|
|
|
252
|
-
|
|
279
|
+
```typescript
|
|
280
|
+
import { RegistryError } from "@resourcexjs/registry";
|
|
253
281
|
|
|
254
|
-
|
|
282
|
+
try {
|
|
283
|
+
await registry.get(rxl);
|
|
284
|
+
} catch (error) {
|
|
285
|
+
if (error instanceof RegistryError) {
|
|
286
|
+
console.error("Registry error:", error.message);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
```
|
|
255
290
|
|
|
256
|
-
|
|
291
|
+
**Common errors:**
|
|
257
292
|
|
|
258
|
-
|
|
293
|
+
- `Resource not found: {locator}`
|
|
294
|
+
- `Resource not found in cache: {locator}`
|
|
295
|
+
- `Linked resource not found: {locator}`
|
|
296
|
+
- `LinkedRegistry does not support put(). Use link() instead.`
|
|
297
|
+
- `Well-known discovery failed for {domain}: {status}`
|
|
298
|
+
- `Untrusted registry: resource claims "{claimed}" but registry only trusts "{trusted}"`
|
|
259
299
|
|
|
260
|
-
|
|
300
|
+
## API Reference
|
|
261
301
|
|
|
262
|
-
|
|
263
|
-
- `limit?: number` - Max results
|
|
264
|
-
- `offset?: number` - Skip first N results
|
|
302
|
+
### Registries
|
|
265
303
|
|
|
266
|
-
|
|
304
|
+
#### `LocalRegistry`
|
|
267
305
|
|
|
268
306
|
```typescript
|
|
269
|
-
|
|
270
|
-
readonly type: string;
|
|
271
|
-
get(locator: string): Promise<RXR>;
|
|
272
|
-
put(rxr: RXR): Promise<void>;
|
|
273
|
-
exists(locator: string): Promise<boolean>;
|
|
274
|
-
delete(locator: string): Promise<void>;
|
|
275
|
-
search(options?: SearchOptions): Promise<RXL[]>;
|
|
276
|
-
}
|
|
307
|
+
new LocalRegistry(storage: Storage)
|
|
277
308
|
```
|
|
278
309
|
|
|
279
|
-
|
|
310
|
+
Registry for local resources without registry domain.
|
|
280
311
|
|
|
281
|
-
|
|
312
|
+
#### `MirrorRegistry`
|
|
282
313
|
|
|
283
314
|
```typescript
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const storage = new LocalStorage({
|
|
287
|
-
path: "~/.resourcex", // optional, defaults to ~/.resourcex
|
|
288
|
-
});
|
|
315
|
+
new MirrorRegistry(storage: Storage)
|
|
289
316
|
```
|
|
290
317
|
|
|
291
|
-
|
|
318
|
+
Registry for cached remote resources with registry domain.
|
|
292
319
|
|
|
293
|
-
|
|
320
|
+
**Additional methods:**
|
|
294
321
|
|
|
295
|
-
|
|
296
|
-
~/.resourcex/
|
|
297
|
-
├── local/ # Development resources
|
|
298
|
-
│ └── {name}.{type}/
|
|
299
|
-
│ └── {version}/
|
|
300
|
-
│ ├── manifest.json
|
|
301
|
-
│ └── archive.tar.gz
|
|
302
|
-
│
|
|
303
|
-
└── cache/ # Remote cached resources
|
|
304
|
-
└── {domain}/
|
|
305
|
-
└── {path}/
|
|
306
|
-
└── {name}.{type}/
|
|
307
|
-
└── {version}/
|
|
308
|
-
├── manifest.json
|
|
309
|
-
└── archive.tar.gz
|
|
310
|
-
```
|
|
322
|
+
- `clear(registry?: string): Promise<void>` - Clear cached resources
|
|
311
323
|
|
|
312
|
-
|
|
324
|
+
#### `LinkedRegistry`
|
|
313
325
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
326
|
+
```typescript
|
|
327
|
+
new LinkedRegistry(basePath: string)
|
|
328
|
+
```
|
|
317
329
|
|
|
318
|
-
|
|
330
|
+
Registry for development symlinks.
|
|
319
331
|
|
|
320
|
-
|
|
332
|
+
**Additional methods:**
|
|
321
333
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
3. Discover source via `https://{domain}/.well-known/resourcex`
|
|
325
|
-
4. Fetch from discovered endpoint
|
|
326
|
-
5. Cache to local storage
|
|
334
|
+
- `link(devPath: string): Promise<RXL>` - Create symlink to development directory
|
|
335
|
+
- `unlink(rxl: RXL): Promise<void>` - Remove symlink (alias for `remove`)
|
|
327
336
|
|
|
328
|
-
|
|
337
|
+
### Access Chain
|
|
329
338
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}
|
|
339
|
+
#### `RegistryAccessChain`
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
new RegistryAccessChain(accessors: RegistryAccessor[], options?: { memCache?: boolean })
|
|
335
343
|
```
|
|
336
344
|
|
|
337
|
-
|
|
345
|
+
**Methods:**
|
|
338
346
|
|
|
339
|
-
|
|
347
|
+
- `get(rxl: RXL): Promise<RXR>` - Get resource through chain
|
|
348
|
+
- `has(rxl: RXL): Promise<boolean>` - Check existence
|
|
349
|
+
- `clearCache(): void` - Clear memory cache
|
|
350
|
+
- `invalidate(rxl: RXL): void` - Remove specific resource from memory cache
|
|
340
351
|
|
|
341
|
-
|
|
352
|
+
#### `RegistryAccessor`
|
|
342
353
|
|
|
343
354
|
```typescript
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
console.log("Getting:", locator);
|
|
349
|
-
return this.inner.get(locator);
|
|
350
|
-
}
|
|
355
|
+
interface RegistryAccessor {
|
|
356
|
+
readonly name: string;
|
|
357
|
+
canHandle(rxl: RXL): Promise<boolean>;
|
|
358
|
+
get(rxl: RXL): Promise<RXR>;
|
|
351
359
|
}
|
|
352
360
|
```
|
|
353
361
|
|
|
354
|
-
###
|
|
362
|
+
### Middleware
|
|
355
363
|
|
|
356
|
-
|
|
364
|
+
#### `RegistryMiddleware`
|
|
357
365
|
|
|
358
366
|
```typescript
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
// Throws if resource.manifest.domain !== "deepractice.ai"
|
|
364
|
-
await validatedRegistry.get("deepractice.ai/assistant.text@1.0.0");
|
|
367
|
+
abstract class RegistryMiddleware implements Registry {
|
|
368
|
+
constructor(protected readonly inner: Registry)
|
|
369
|
+
}
|
|
365
370
|
```
|
|
366
371
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
Sandbox isolation for resolver execution:
|
|
370
|
-
|
|
371
|
-
| Type | Description | Latency |
|
|
372
|
-
| -------------- | -------------------------- | ------- |
|
|
373
|
-
| `"none"` | No isolation (development) | ~10ms |
|
|
374
|
-
| `"srt"` | OS-level isolation | ~50ms |
|
|
375
|
-
| `"cloudflare"` | Container isolation | ~100ms |
|
|
376
|
-
| `"e2b"` | MicroVM isolation | ~150ms |
|
|
372
|
+
#### `DomainValidation`
|
|
377
373
|
|
|
378
374
|
```typescript
|
|
379
|
-
|
|
380
|
-
isolator: "srt",
|
|
381
|
-
});
|
|
375
|
+
new DomainValidation(inner: Registry, trustedRegistry: string)
|
|
382
376
|
```
|
|
383
377
|
|
|
384
|
-
|
|
378
|
+
Middleware class that validates resource registry matches the trusted registry.
|
|
385
379
|
|
|
386
|
-
|
|
387
|
-
import { RegistryError } from "@resourcexjs/registry";
|
|
380
|
+
#### `withDomainValidation`
|
|
388
381
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
} catch (error) {
|
|
392
|
-
if (error instanceof RegistryError) {
|
|
393
|
-
console.error("Registry error:", error.message);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
382
|
+
```typescript
|
|
383
|
+
withDomainValidation(registry: Registry, trustedRegistry: string): Registry
|
|
396
384
|
```
|
|
397
385
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
- `Resource not found: {locator}`
|
|
401
|
-
- `Unsupported resource type: {type}`
|
|
402
|
-
- `Well-known discovery failed for {domain}: {status}`
|
|
403
|
-
- `{storage} is read-only: {operation} not supported`
|
|
386
|
+
Factory function to create `DomainValidation` middleware.
|
|
404
387
|
|
|
405
|
-
|
|
388
|
+
### Discovery
|
|
406
389
|
|
|
407
|
-
|
|
390
|
+
#### `discoverRegistry`
|
|
408
391
|
|
|
409
392
|
```typescript
|
|
410
|
-
|
|
411
|
-
import { createRegistry } from "@resourcexjs/registry";
|
|
412
|
-
|
|
413
|
-
// 1. Create registry
|
|
414
|
-
const registry = createRegistry();
|
|
415
|
-
|
|
416
|
-
// 2. Load and add resource
|
|
417
|
-
const rxr = await loadResource("./my-prompts/assistant");
|
|
418
|
-
await registry.add(rxr);
|
|
419
|
-
|
|
420
|
-
// 3. Resolve and execute
|
|
421
|
-
const resolved = await registry.resolve("localhost/assistant.text@1.0.0");
|
|
422
|
-
const text = await resolved.execute();
|
|
423
|
-
console.log(text);
|
|
393
|
+
discoverRegistry(domain: string): Promise<DiscoveryResult>
|
|
424
394
|
```
|
|
425
395
|
|
|
426
|
-
|
|
396
|
+
**Types:**
|
|
427
397
|
|
|
428
398
|
```typescript
|
|
429
|
-
|
|
430
|
-
|
|
399
|
+
interface DiscoveryResult {
|
|
400
|
+
domain: string;
|
|
401
|
+
registries: string[];
|
|
402
|
+
}
|
|
431
403
|
|
|
432
|
-
|
|
433
|
-
|
|
404
|
+
interface WellKnownResponse {
|
|
405
|
+
version?: string;
|
|
406
|
+
registries: string[];
|
|
407
|
+
}
|
|
408
|
+
```
|
|
434
409
|
|
|
435
|
-
|
|
436
|
-
const registry = createRegistry({
|
|
437
|
-
types: [promptType],
|
|
438
|
-
});
|
|
410
|
+
### Error
|
|
439
411
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
412
|
+
#### `RegistryError`
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
class RegistryError extends ResourceXError {
|
|
416
|
+
constructor(message: string, options?: ErrorOptions);
|
|
417
|
+
}
|
|
444
418
|
```
|
|
445
419
|
|
|
446
420
|
## License
|
|
447
421
|
|
|
448
|
-
|
|
422
|
+
Apache-2.0
|