@resourcexjs/arp 1.7.0 → 2.0.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/README.md +52 -10
- package/dist/index.d.ts +5 -1
- package/dist/index.js +356 -62
- package/dist/index.js.map +4 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ arp:{semantic}:{transport}://{location}
|
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
- **semantic**: Content interpretation (text, binary)
|
|
22
|
-
- **transport**: Storage backend (file, http, https,
|
|
22
|
+
- **transport**: Storage backend (file, http, https, rxr)
|
|
23
23
|
- **location**: Resource location (path, URL)
|
|
24
24
|
|
|
25
25
|
### Examples
|
|
@@ -27,9 +27,24 @@ arp:{semantic}:{transport}://{location}
|
|
|
27
27
|
```
|
|
28
28
|
arp:text:file://~/data.txt
|
|
29
29
|
arp:binary:https://example.com/image.png
|
|
30
|
-
arp:text:
|
|
30
|
+
arp:text:rxr://localhost/my-prompt.text@1.0.0/content
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
### Built-in Handlers
|
|
34
|
+
|
|
35
|
+
**createARP()** auto-registers all built-in handlers:
|
|
36
|
+
|
|
37
|
+
**Transports:**
|
|
38
|
+
|
|
39
|
+
- `file` - Local filesystem (read-write)
|
|
40
|
+
- `http`, `https` - Network resources (read-only)
|
|
41
|
+
- `rxr` - Files inside resources (read-only, auto-creates Registry)
|
|
42
|
+
|
|
43
|
+
**Semantics:**
|
|
44
|
+
|
|
45
|
+
- `text` - UTF-8 text → string
|
|
46
|
+
- `binary` - Raw bytes → Buffer
|
|
47
|
+
|
|
33
48
|
## Usage
|
|
34
49
|
|
|
35
50
|
### Basic Operations
|
|
@@ -289,21 +304,48 @@ const result = await arl.resolve({ lang: "en" });
|
|
|
289
304
|
// Fetches: https://api.example.com/data?format=json&lang=en
|
|
290
305
|
```
|
|
291
306
|
|
|
292
|
-
###
|
|
307
|
+
### RXR Transport (`rxr`)
|
|
293
308
|
|
|
294
|
-
|
|
309
|
+
Access files inside ResourceX resources (read-only).
|
|
295
310
|
|
|
296
311
|
```typescript
|
|
297
|
-
arp
|
|
298
|
-
|
|
312
|
+
// Format: arp:{semantic}:rxr://{rxl}/{internal-path}
|
|
313
|
+
arp.parse("arp:text:rxr://localhost/my-prompt.text@1.0.0/content");
|
|
314
|
+
arp.parse("arp:text:rxr://deepractice.ai/nuwa.text@1.0.0/thought/first-principles.md");
|
|
299
315
|
```
|
|
300
316
|
|
|
301
317
|
**Operations:**
|
|
302
318
|
|
|
303
|
-
- ✅ get (read)
|
|
304
|
-
-
|
|
305
|
-
- ✅ exists
|
|
306
|
-
-
|
|
319
|
+
- ✅ get (read file from resource)
|
|
320
|
+
- ❌ set (read-only, throws error)
|
|
321
|
+
- ✅ exists (check if file exists in resource)
|
|
322
|
+
- ❌ delete (read-only, throws error)
|
|
323
|
+
|
|
324
|
+
**Auto-creates Registry:**
|
|
325
|
+
|
|
326
|
+
- `localhost` domain → LocalRegistry (filesystem)
|
|
327
|
+
- Other domains → RemoteRegistry (via well-known discovery)
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
// No manual setup needed - works out of the box!
|
|
331
|
+
const arp = createARP();
|
|
332
|
+
const arl = arp.parse("arp:text:rxr://localhost/hello.text@1.0.0/content");
|
|
333
|
+
const { content } = await arl.resolve();
|
|
334
|
+
// RxrTransport automatically creates LocalRegistry for localhost
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**Manual Registry injection (optional):**
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
import { createRegistry } from "@resourcexjs/registry";
|
|
341
|
+
import { createARP, RxrTransport } from "@resourcexjs/arp";
|
|
342
|
+
|
|
343
|
+
const registry = createRegistry({ path: "./custom-path" });
|
|
344
|
+
const rxrTransport = new RxrTransport(registry);
|
|
345
|
+
|
|
346
|
+
const arp = createARP();
|
|
347
|
+
arp.registerTransport(rxrTransport); // Override default rxr transport
|
|
348
|
+
```
|
|
307
349
|
|
|
308
350
|
## Error Handling
|
|
309
351
|
|
package/dist/index.d.ts
CHANGED
|
@@ -455,10 +455,14 @@ declare class RxrTransport implements TransportHandler {
|
|
|
455
455
|
* Get or create a registry for the given domain.
|
|
456
456
|
* - If a registry was provided in constructor, use it
|
|
457
457
|
* - localhost: create LocalRegistry
|
|
458
|
-
* - Other domains: discover endpoint via well-known and create
|
|
458
|
+
* - Other domains: discover endpoint via well-known and create appropriate registry
|
|
459
459
|
*/
|
|
460
460
|
private getRegistry;
|
|
461
461
|
/**
|
|
462
|
+
* Check if URL is a git repository URL.
|
|
463
|
+
*/
|
|
464
|
+
private isGitUrl;
|
|
465
|
+
/**
|
|
462
466
|
* Parse location into domain, RXL and internal path.
|
|
463
467
|
* Format: {domain}/{path}/{name}.{type}@{version}/{internal-path}
|
|
464
468
|
* Example: deepractice.ai/nuwa.role@1.0.0/thought/first-principles.md
|
package/dist/index.js
CHANGED
|
@@ -282,9 +282,16 @@ import { join as join2 } from "node:path";
|
|
|
282
282
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rm as rm2, stat as stat2, readdir as readdir2 } from "node:fs/promises";
|
|
283
283
|
import { gzip as gzip2, gunzip as gunzip2 } from "node:zlib";
|
|
284
284
|
import { promisify as promisify2 } from "node:util";
|
|
285
|
+
import { homedir as homedir2 } from "node:os";
|
|
286
|
+
import { join as join22 } from "node:path";
|
|
287
|
+
import { readFile as readFile22, stat as stat22, readdir as readdir22, mkdir as mkdir22 } from "node:fs/promises";
|
|
288
|
+
import { execSync } from "node:child_process";
|
|
285
289
|
function isRemoteConfig(config) {
|
|
286
290
|
return config !== undefined && "endpoint" in config;
|
|
287
291
|
}
|
|
292
|
+
function isGitConfig(config) {
|
|
293
|
+
return config !== undefined && "type" in config && config.type === "git";
|
|
294
|
+
}
|
|
288
295
|
|
|
289
296
|
class ResourceXError extends Error {
|
|
290
297
|
constructor(message, options) {
|
|
@@ -1754,23 +1761,63 @@ class LocalRegistry {
|
|
|
1754
1761
|
supportType(type) {
|
|
1755
1762
|
this.typeHandler.register(type);
|
|
1756
1763
|
}
|
|
1757
|
-
buildPath(locator) {
|
|
1764
|
+
buildPath(locator, area) {
|
|
1758
1765
|
const rxl = typeof locator === "string" ? parseRXL(locator) : locator;
|
|
1759
|
-
const
|
|
1766
|
+
const resourceName = rxl.type ? `${rxl.name}.${rxl.type}` : rxl.name;
|
|
1760
1767
|
const version = rxl.version ?? "latest";
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1768
|
+
if (area === "local") {
|
|
1769
|
+
return join2(this.basePath, "local", resourceName, version);
|
|
1770
|
+
} else {
|
|
1771
|
+
const domain = rxl.domain ?? "localhost";
|
|
1772
|
+
let path = join2(this.basePath, "cache", domain);
|
|
1773
|
+
if (rxl.path) {
|
|
1774
|
+
path = join2(path, rxl.path);
|
|
1775
|
+
}
|
|
1776
|
+
return join2(path, resourceName, version);
|
|
1764
1777
|
}
|
|
1765
|
-
const resourceName = rxl.type ? `${rxl.name}.${rxl.type}` : rxl.name;
|
|
1766
|
-
return join2(path, resourceName, version);
|
|
1767
1778
|
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1779
|
+
isLocalOnlyLocator(locator) {
|
|
1780
|
+
const rxl = typeof locator === "string" ? parseRXL(locator) : locator;
|
|
1781
|
+
return !rxl.domain || rxl.domain === "localhost";
|
|
1782
|
+
}
|
|
1783
|
+
async existsAt(resourcePath) {
|
|
1784
|
+
const manifestPath = join2(resourcePath, "manifest.json");
|
|
1785
|
+
try {
|
|
1786
|
+
await stat2(manifestPath);
|
|
1787
|
+
return true;
|
|
1788
|
+
} catch {
|
|
1789
|
+
return false;
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
async findArea(locator) {
|
|
1793
|
+
const localPath = this.buildPath(locator, "local");
|
|
1794
|
+
if (await this.existsAt(localPath)) {
|
|
1795
|
+
return "local";
|
|
1796
|
+
}
|
|
1797
|
+
const cachePath = this.buildPath(locator, "cache");
|
|
1798
|
+
if (await this.existsAt(cachePath)) {
|
|
1799
|
+
return "cache";
|
|
1800
|
+
}
|
|
1801
|
+
return null;
|
|
1802
|
+
}
|
|
1803
|
+
async loadFrom(resourcePath) {
|
|
1804
|
+
const manifestPath = join2(resourcePath, "manifest.json");
|
|
1805
|
+
const manifestContent = await readFile2(manifestPath, "utf-8");
|
|
1806
|
+
const manifestData = JSON.parse(manifestContent);
|
|
1807
|
+
const manifest = createRXM(manifestData);
|
|
1808
|
+
const contentPath = join2(resourcePath, "content.tar.gz");
|
|
1809
|
+
const data = await readFile2(contentPath);
|
|
1810
|
+
return this.typeHandler.deserialize(data, manifest);
|
|
1811
|
+
}
|
|
1812
|
+
async pull(_locator, _options) {
|
|
1813
|
+
throw new RegistryError("Pull not implemented yet - see issue #018");
|
|
1814
|
+
}
|
|
1815
|
+
async publish(_resource, _options) {
|
|
1816
|
+
throw new RegistryError("Publish not implemented yet - see issue #018");
|
|
1770
1817
|
}
|
|
1771
1818
|
async link(resource) {
|
|
1772
1819
|
const locator = resource.manifest.toLocator();
|
|
1773
|
-
const resourcePath = this.buildPath(locator);
|
|
1820
|
+
const resourcePath = this.buildPath(locator, "local");
|
|
1774
1821
|
await mkdir2(resourcePath, { recursive: true });
|
|
1775
1822
|
const manifestPath = join2(resourcePath, "manifest.json");
|
|
1776
1823
|
await writeFile2(manifestPath, JSON.stringify(resource.manifest.toJSON(), null, 2), "utf-8");
|
|
@@ -1779,58 +1826,62 @@ class LocalRegistry {
|
|
|
1779
1826
|
await writeFile2(contentPath, serialized);
|
|
1780
1827
|
}
|
|
1781
1828
|
async get(locator) {
|
|
1782
|
-
|
|
1829
|
+
const area = await this.findArea(locator);
|
|
1830
|
+
if (!area) {
|
|
1783
1831
|
throw new RegistryError(`Resource not found: ${locator}`);
|
|
1784
1832
|
}
|
|
1785
|
-
const resourcePath = this.buildPath(locator);
|
|
1786
|
-
|
|
1787
|
-
const manifestContent = await readFile2(manifestPath, "utf-8");
|
|
1788
|
-
const manifestData = JSON.parse(manifestContent);
|
|
1789
|
-
const manifest = createRXM(manifestData);
|
|
1790
|
-
const contentPath = join2(resourcePath, "content.tar.gz");
|
|
1791
|
-
const data = await readFile2(contentPath);
|
|
1792
|
-
return this.typeHandler.deserialize(data, manifest);
|
|
1833
|
+
const resourcePath = this.buildPath(locator, area);
|
|
1834
|
+
return this.loadFrom(resourcePath);
|
|
1793
1835
|
}
|
|
1794
1836
|
async resolve(locator) {
|
|
1795
1837
|
const rxr = await this.get(locator);
|
|
1796
1838
|
return this.typeHandler.resolve(rxr);
|
|
1797
1839
|
}
|
|
1798
1840
|
async exists(locator) {
|
|
1799
|
-
const
|
|
1800
|
-
|
|
1801
|
-
try {
|
|
1802
|
-
await stat2(manifestPath);
|
|
1803
|
-
return true;
|
|
1804
|
-
} catch {
|
|
1805
|
-
return false;
|
|
1806
|
-
}
|
|
1841
|
+
const area = await this.findArea(locator);
|
|
1842
|
+
return area !== null;
|
|
1807
1843
|
}
|
|
1808
1844
|
async delete(locator) {
|
|
1809
|
-
|
|
1810
|
-
|
|
1845
|
+
const isLocal = this.isLocalOnlyLocator(locator);
|
|
1846
|
+
if (isLocal) {
|
|
1847
|
+
const localPath = this.buildPath(locator, "local");
|
|
1848
|
+
if (await this.existsAt(localPath)) {
|
|
1849
|
+
await rm2(localPath, { recursive: true, force: true });
|
|
1850
|
+
}
|
|
1851
|
+
} else {
|
|
1852
|
+
const cachePath = this.buildPath(locator, "cache");
|
|
1853
|
+
if (await this.existsAt(cachePath)) {
|
|
1854
|
+
await rm2(cachePath, { recursive: true, force: true });
|
|
1855
|
+
}
|
|
1811
1856
|
}
|
|
1812
|
-
const resourcePath = this.buildPath(locator);
|
|
1813
|
-
await rm2(resourcePath, { recursive: true, force: true });
|
|
1814
1857
|
}
|
|
1815
1858
|
async search(options) {
|
|
1816
1859
|
const { query, limit, offset = 0 } = options ?? {};
|
|
1817
|
-
let entries;
|
|
1818
|
-
try {
|
|
1819
|
-
entries = await this.listRecursive(this.basePath);
|
|
1820
|
-
} catch {
|
|
1821
|
-
return [];
|
|
1822
|
-
}
|
|
1823
1860
|
const locators = [];
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1861
|
+
const localDir = join2(this.basePath, "local");
|
|
1862
|
+
try {
|
|
1863
|
+
const localEntries = await this.listRecursive(localDir);
|
|
1864
|
+
for (const entry of localEntries) {
|
|
1865
|
+
if (!entry.endsWith("manifest.json"))
|
|
1866
|
+
continue;
|
|
1867
|
+
const relativePath = entry.slice(localDir.length + 1);
|
|
1868
|
+
const rxl = this.parseLocalEntry(relativePath);
|
|
1869
|
+
if (rxl)
|
|
1870
|
+
locators.push(rxl);
|
|
1827
1871
|
}
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1872
|
+
} catch {}
|
|
1873
|
+
const cacheDir = join2(this.basePath, "cache");
|
|
1874
|
+
try {
|
|
1875
|
+
const cacheEntries = await this.listRecursive(cacheDir);
|
|
1876
|
+
for (const entry of cacheEntries) {
|
|
1877
|
+
if (!entry.endsWith("manifest.json"))
|
|
1878
|
+
continue;
|
|
1879
|
+
const relativePath = entry.slice(cacheDir.length + 1);
|
|
1880
|
+
const rxl = this.parseCacheEntry(relativePath);
|
|
1881
|
+
if (rxl)
|
|
1882
|
+
locators.push(rxl);
|
|
1832
1883
|
}
|
|
1833
|
-
}
|
|
1884
|
+
} catch {}
|
|
1834
1885
|
let filtered = locators;
|
|
1835
1886
|
if (query) {
|
|
1836
1887
|
const lowerQuery = query.toLowerCase();
|
|
@@ -1861,7 +1912,27 @@ class LocalRegistry {
|
|
|
1861
1912
|
} catch {}
|
|
1862
1913
|
return results;
|
|
1863
1914
|
}
|
|
1864
|
-
|
|
1915
|
+
parseLocalEntry(entry) {
|
|
1916
|
+
const dirPath = entry.replace(/[/\\]manifest\.json$/, "");
|
|
1917
|
+
const parts = dirPath.split(/[/\\]/);
|
|
1918
|
+
if (parts.length < 2) {
|
|
1919
|
+
return null;
|
|
1920
|
+
}
|
|
1921
|
+
const version = parts.pop();
|
|
1922
|
+
const nameTypePart = parts.shift();
|
|
1923
|
+
const { name, type } = this.parseNameType(nameTypePart);
|
|
1924
|
+
let locatorStr = name;
|
|
1925
|
+
if (type) {
|
|
1926
|
+
locatorStr += `.${type}`;
|
|
1927
|
+
}
|
|
1928
|
+
locatorStr += `@${version}`;
|
|
1929
|
+
try {
|
|
1930
|
+
return parseRXL(locatorStr);
|
|
1931
|
+
} catch {
|
|
1932
|
+
return null;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
parseCacheEntry(entry) {
|
|
1865
1936
|
const dirPath = entry.replace(/[/\\]manifest\.json$/, "");
|
|
1866
1937
|
const parts = dirPath.split(/[/\\]/);
|
|
1867
1938
|
if (parts.length < 3) {
|
|
@@ -1871,16 +1942,7 @@ class LocalRegistry {
|
|
|
1871
1942
|
const nameTypePart = parts.pop();
|
|
1872
1943
|
const domain = parts.shift();
|
|
1873
1944
|
const path = parts.length > 0 ? parts.join("/") : undefined;
|
|
1874
|
-
const
|
|
1875
|
-
let name;
|
|
1876
|
-
let type;
|
|
1877
|
-
if (dotIndex !== -1) {
|
|
1878
|
-
name = nameTypePart.substring(0, dotIndex);
|
|
1879
|
-
type = nameTypePart.substring(dotIndex + 1);
|
|
1880
|
-
} else {
|
|
1881
|
-
name = nameTypePart;
|
|
1882
|
-
type = undefined;
|
|
1883
|
-
}
|
|
1945
|
+
const { name, type } = this.parseNameType(nameTypePart);
|
|
1884
1946
|
let locatorStr = domain;
|
|
1885
1947
|
if (path) {
|
|
1886
1948
|
locatorStr += `/${path}`;
|
|
@@ -1896,6 +1958,17 @@ class LocalRegistry {
|
|
|
1896
1958
|
return null;
|
|
1897
1959
|
}
|
|
1898
1960
|
}
|
|
1961
|
+
parseNameType(nameTypePart) {
|
|
1962
|
+
const dotIndex = nameTypePart.lastIndexOf(".");
|
|
1963
|
+
if (dotIndex !== -1) {
|
|
1964
|
+
return {
|
|
1965
|
+
name: nameTypePart.substring(0, dotIndex),
|
|
1966
|
+
type: nameTypePart.substring(dotIndex + 1)
|
|
1967
|
+
};
|
|
1968
|
+
} else {
|
|
1969
|
+
return { name: nameTypePart, type: undefined };
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1899
1972
|
}
|
|
1900
1973
|
|
|
1901
1974
|
class RemoteRegistry {
|
|
@@ -1908,7 +1981,10 @@ class RemoteRegistry {
|
|
|
1908
1981
|
supportType(type) {
|
|
1909
1982
|
this.typeHandler.register(type);
|
|
1910
1983
|
}
|
|
1911
|
-
async
|
|
1984
|
+
async pull(_locator, _options) {
|
|
1985
|
+
throw new RegistryError("Cannot pull to remote registry - use local registry for pulling");
|
|
1986
|
+
}
|
|
1987
|
+
async publish(_resource, _options) {
|
|
1912
1988
|
throw new RegistryError("Remote registry publish not implemented yet");
|
|
1913
1989
|
}
|
|
1914
1990
|
async link(_resource) {
|
|
@@ -1974,7 +2050,13 @@ async function discoverRegistry(domain) {
|
|
|
1974
2050
|
throw new RegistryError(`Well-known discovery failed for ${domain}: ${response.statusText}`);
|
|
1975
2051
|
}
|
|
1976
2052
|
const data = await response.json();
|
|
1977
|
-
|
|
2053
|
+
if (!data.registries || !Array.isArray(data.registries) || data.registries.length === 0) {
|
|
2054
|
+
throw new RegistryError(`Invalid well-known response for ${domain}: missing or empty registries`);
|
|
2055
|
+
}
|
|
2056
|
+
return {
|
|
2057
|
+
domain,
|
|
2058
|
+
registries: data.registries
|
|
2059
|
+
};
|
|
1978
2060
|
} catch (error) {
|
|
1979
2061
|
if (error instanceof RegistryError) {
|
|
1980
2062
|
throw error;
|
|
@@ -1982,10 +2064,210 @@ async function discoverRegistry(domain) {
|
|
|
1982
2064
|
throw new RegistryError(`Failed to discover registry for ${domain}: ${error.message}`);
|
|
1983
2065
|
}
|
|
1984
2066
|
}
|
|
2067
|
+
var DEFAULT_GIT_CACHE = `${homedir2()}/.resourcex/.git-cache`;
|
|
2068
|
+
|
|
2069
|
+
class GitRegistry {
|
|
2070
|
+
url;
|
|
2071
|
+
ref;
|
|
2072
|
+
basePath;
|
|
2073
|
+
cacheDir;
|
|
2074
|
+
typeHandler;
|
|
2075
|
+
trustedDomain;
|
|
2076
|
+
constructor(config) {
|
|
2077
|
+
this.url = config.url;
|
|
2078
|
+
this.ref = config.ref ?? "main";
|
|
2079
|
+
this.basePath = config.basePath ?? ".resourcex";
|
|
2080
|
+
this.typeHandler = TypeHandlerChain.create();
|
|
2081
|
+
this.trustedDomain = config.domain;
|
|
2082
|
+
if (this.isRemoteUrl(config.url) && !config.domain) {
|
|
2083
|
+
throw new RegistryError(`Remote git registry requires a trusted domain.
|
|
2084
|
+
|
|
2085
|
+
` + `Either:
|
|
2086
|
+
` + `1. Use discoverRegistry("your-domain.com") to auto-bind domain
|
|
2087
|
+
` + `2. Explicitly set domain: createRegistry({ type: "git", url: "...", domain: "your-domain.com" })
|
|
2088
|
+
|
|
2089
|
+
` + `This ensures resources from untrusted sources cannot impersonate your domain.`);
|
|
2090
|
+
}
|
|
2091
|
+
this.cacheDir = this.buildCacheDir(config.url);
|
|
2092
|
+
}
|
|
2093
|
+
isRemoteUrl(url) {
|
|
2094
|
+
return url.startsWith("git@") || url.startsWith("https://") || url.startsWith("http://");
|
|
2095
|
+
}
|
|
2096
|
+
buildCacheDir(url) {
|
|
2097
|
+
let normalized = url;
|
|
2098
|
+
if (url.startsWith("git@")) {
|
|
2099
|
+
normalized = url.slice(4).replace(":", "/");
|
|
2100
|
+
}
|
|
2101
|
+
if (normalized.endsWith(".git")) {
|
|
2102
|
+
normalized = normalized.slice(0, -4);
|
|
2103
|
+
}
|
|
2104
|
+
const dirName = normalized.replace(/\//g, "-");
|
|
2105
|
+
return join22(DEFAULT_GIT_CACHE, dirName);
|
|
2106
|
+
}
|
|
2107
|
+
supportType(type) {
|
|
2108
|
+
this.typeHandler.register(type);
|
|
2109
|
+
}
|
|
2110
|
+
async ensureCloned() {
|
|
2111
|
+
const gitDir = join22(this.cacheDir, ".git");
|
|
2112
|
+
try {
|
|
2113
|
+
await stat22(gitDir);
|
|
2114
|
+
this.gitExec(`fetch origin ${this.ref}`);
|
|
2115
|
+
this.gitExec(`checkout FETCH_HEAD`);
|
|
2116
|
+
} catch {
|
|
2117
|
+
await mkdir22(DEFAULT_GIT_CACHE, { recursive: true });
|
|
2118
|
+
execSync(`git clone --depth 1 --branch ${this.ref} ${this.url} ${this.cacheDir}`, {
|
|
2119
|
+
stdio: "pipe"
|
|
2120
|
+
});
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
gitExec(command) {
|
|
2124
|
+
execSync(`git -C ${this.cacheDir} ${command}`, { stdio: "pipe" });
|
|
2125
|
+
}
|
|
2126
|
+
buildResourcePath(locator) {
|
|
2127
|
+
const rxl = parseRXL(locator);
|
|
2128
|
+
const domain = rxl.domain ?? "localhost";
|
|
2129
|
+
const version = rxl.version ?? "latest";
|
|
2130
|
+
let path = join22(this.cacheDir, this.basePath, domain);
|
|
2131
|
+
if (rxl.path) {
|
|
2132
|
+
path = join22(path, rxl.path);
|
|
2133
|
+
}
|
|
2134
|
+
const resourceName = rxl.type ? `${rxl.name}.${rxl.type}` : rxl.name;
|
|
2135
|
+
return join22(path, resourceName, version);
|
|
2136
|
+
}
|
|
2137
|
+
async get(locator) {
|
|
2138
|
+
await this.ensureCloned();
|
|
2139
|
+
const resourcePath = this.buildResourcePath(locator);
|
|
2140
|
+
const manifestPath = join22(resourcePath, "manifest.json");
|
|
2141
|
+
try {
|
|
2142
|
+
await stat22(manifestPath);
|
|
2143
|
+
} catch {
|
|
2144
|
+
throw new RegistryError(`Resource not found: ${locator}`);
|
|
2145
|
+
}
|
|
2146
|
+
const manifestContent = await readFile22(manifestPath, "utf-8");
|
|
2147
|
+
const manifestData = JSON.parse(manifestContent);
|
|
2148
|
+
const manifest = createRXM(manifestData);
|
|
2149
|
+
if (this.trustedDomain && manifest.domain !== this.trustedDomain) {
|
|
2150
|
+
throw new RegistryError(`Untrusted domain: resource claims "${manifest.domain}" but registry only trusts "${this.trustedDomain}"`);
|
|
2151
|
+
}
|
|
2152
|
+
const contentPath = join22(resourcePath, "content.tar.gz");
|
|
2153
|
+
const data = await readFile22(contentPath);
|
|
2154
|
+
return this.typeHandler.deserialize(data, manifest);
|
|
2155
|
+
}
|
|
2156
|
+
async resolve(locator) {
|
|
2157
|
+
const rxr = await this.get(locator);
|
|
2158
|
+
return this.typeHandler.resolve(rxr);
|
|
2159
|
+
}
|
|
2160
|
+
async exists(locator) {
|
|
2161
|
+
try {
|
|
2162
|
+
await this.ensureCloned();
|
|
2163
|
+
const resourcePath = this.buildResourcePath(locator);
|
|
2164
|
+
const manifestPath = join22(resourcePath, "manifest.json");
|
|
2165
|
+
await stat22(manifestPath);
|
|
2166
|
+
return true;
|
|
2167
|
+
} catch {
|
|
2168
|
+
return false;
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
async search(options) {
|
|
2172
|
+
await this.ensureCloned();
|
|
2173
|
+
const { query, limit, offset = 0 } = options ?? {};
|
|
2174
|
+
const locators = [];
|
|
2175
|
+
const baseDir = join22(this.cacheDir, this.basePath);
|
|
2176
|
+
try {
|
|
2177
|
+
const entries = await this.listRecursive(baseDir);
|
|
2178
|
+
for (const entry of entries) {
|
|
2179
|
+
if (!entry.endsWith("manifest.json"))
|
|
2180
|
+
continue;
|
|
2181
|
+
const relativePath = entry.slice(baseDir.length + 1);
|
|
2182
|
+
const rxl = this.parseEntryToRXL(relativePath);
|
|
2183
|
+
if (rxl)
|
|
2184
|
+
locators.push(rxl);
|
|
2185
|
+
}
|
|
2186
|
+
} catch {
|
|
2187
|
+
return [];
|
|
2188
|
+
}
|
|
2189
|
+
let filtered = locators;
|
|
2190
|
+
if (query) {
|
|
2191
|
+
const lowerQuery = query.toLowerCase();
|
|
2192
|
+
filtered = locators.filter((rxl) => {
|
|
2193
|
+
const searchText = `${rxl.domain ?? ""} ${rxl.path ?? ""} ${rxl.name} ${rxl.type ?? ""}`.toLowerCase();
|
|
2194
|
+
return searchText.includes(lowerQuery);
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
let result = filtered.slice(offset);
|
|
2198
|
+
if (limit !== undefined) {
|
|
2199
|
+
result = result.slice(0, limit);
|
|
2200
|
+
}
|
|
2201
|
+
return result;
|
|
2202
|
+
}
|
|
2203
|
+
async listRecursive(dir) {
|
|
2204
|
+
const results = [];
|
|
2205
|
+
try {
|
|
2206
|
+
const entries = await readdir22(dir, { withFileTypes: true });
|
|
2207
|
+
for (const entry of entries) {
|
|
2208
|
+
const fullPath = join22(dir, entry.name);
|
|
2209
|
+
if (entry.isDirectory()) {
|
|
2210
|
+
const subEntries = await this.listRecursive(fullPath);
|
|
2211
|
+
results.push(...subEntries);
|
|
2212
|
+
} else {
|
|
2213
|
+
results.push(fullPath);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
} catch {}
|
|
2217
|
+
return results;
|
|
2218
|
+
}
|
|
2219
|
+
parseEntryToRXL(entry) {
|
|
2220
|
+
const dirPath = entry.replace(/[/\\]manifest\.json$/, "");
|
|
2221
|
+
const parts = dirPath.split(/[/\\]/);
|
|
2222
|
+
if (parts.length < 3)
|
|
2223
|
+
return null;
|
|
2224
|
+
const version = parts.pop();
|
|
2225
|
+
const nameTypePart = parts.pop();
|
|
2226
|
+
const domain = parts.shift();
|
|
2227
|
+
const path = parts.length > 0 ? parts.join("/") : undefined;
|
|
2228
|
+
const dotIndex = nameTypePart.lastIndexOf(".");
|
|
2229
|
+
let name;
|
|
2230
|
+
let type;
|
|
2231
|
+
if (dotIndex !== -1) {
|
|
2232
|
+
name = nameTypePart.substring(0, dotIndex);
|
|
2233
|
+
type = nameTypePart.substring(dotIndex + 1);
|
|
2234
|
+
} else {
|
|
2235
|
+
name = nameTypePart;
|
|
2236
|
+
type = undefined;
|
|
2237
|
+
}
|
|
2238
|
+
let locatorStr = domain;
|
|
2239
|
+
if (path)
|
|
2240
|
+
locatorStr += `/${path}`;
|
|
2241
|
+
locatorStr += `/${name}`;
|
|
2242
|
+
if (type)
|
|
2243
|
+
locatorStr += `.${type}`;
|
|
2244
|
+
locatorStr += `@${version}`;
|
|
2245
|
+
try {
|
|
2246
|
+
return parseRXL(locatorStr);
|
|
2247
|
+
} catch {
|
|
2248
|
+
return null;
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
async pull(_locator, _options) {
|
|
2252
|
+
throw new RegistryError("GitRegistry is read-only - use LocalRegistry.pull()");
|
|
2253
|
+
}
|
|
2254
|
+
async publish(_resource, _options) {
|
|
2255
|
+
throw new RegistryError("GitRegistry is read-only - use LocalRegistry.publish()");
|
|
2256
|
+
}
|
|
2257
|
+
async link(_resource) {
|
|
2258
|
+
throw new RegistryError("GitRegistry is read-only - use LocalRegistry.link()");
|
|
2259
|
+
}
|
|
2260
|
+
async delete(_locator) {
|
|
2261
|
+
throw new RegistryError("GitRegistry is read-only - use LocalRegistry.delete()");
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
1985
2264
|
function createRegistry(config) {
|
|
1986
2265
|
if (isRemoteConfig(config)) {
|
|
1987
2266
|
return new RemoteRegistry(config);
|
|
1988
2267
|
}
|
|
2268
|
+
if (isGitConfig(config)) {
|
|
2269
|
+
return new GitRegistry(config);
|
|
2270
|
+
}
|
|
1989
2271
|
return new LocalRegistry(config);
|
|
1990
2272
|
}
|
|
1991
2273
|
|
|
@@ -2041,8 +2323,17 @@ class RxrTransport {
|
|
|
2041
2323
|
registry = createRegistry();
|
|
2042
2324
|
} else {
|
|
2043
2325
|
try {
|
|
2044
|
-
const
|
|
2045
|
-
|
|
2326
|
+
const discovery = await discoverRegistry(domain);
|
|
2327
|
+
const registryUrl = discovery.registries[0];
|
|
2328
|
+
if (this.isGitUrl(registryUrl)) {
|
|
2329
|
+
registry = createRegistry({
|
|
2330
|
+
type: "git",
|
|
2331
|
+
url: registryUrl,
|
|
2332
|
+
domain: discovery.domain
|
|
2333
|
+
});
|
|
2334
|
+
} else {
|
|
2335
|
+
registry = createRegistry({ endpoint: registryUrl });
|
|
2336
|
+
}
|
|
2046
2337
|
} catch (error) {
|
|
2047
2338
|
throw new TransportError(`Failed to discover registry for domain ${domain}: ${error.message}`, this.name);
|
|
2048
2339
|
}
|
|
@@ -2050,6 +2341,9 @@ class RxrTransport {
|
|
|
2050
2341
|
registryCache.set(domain, registry);
|
|
2051
2342
|
return registry;
|
|
2052
2343
|
}
|
|
2344
|
+
isGitUrl(url) {
|
|
2345
|
+
return url.startsWith("git@") || url.endsWith(".git");
|
|
2346
|
+
}
|
|
2053
2347
|
parseLocation(location) {
|
|
2054
2348
|
const atIndex = location.indexOf("@");
|
|
2055
2349
|
if (atIndex === -1) {
|
|
@@ -2270,7 +2564,7 @@ function createARP(config) {
|
|
|
2270
2564
|
}
|
|
2271
2565
|
|
|
2272
2566
|
// src/index.ts
|
|
2273
|
-
var VERSION = "
|
|
2567
|
+
var VERSION = "2.0.0";
|
|
2274
2568
|
export {
|
|
2275
2569
|
textSemantic,
|
|
2276
2570
|
httpsTransport,
|
|
@@ -2292,4 +2586,4 @@ export {
|
|
|
2292
2586
|
ARP
|
|
2293
2587
|
};
|
|
2294
2588
|
|
|
2295
|
-
//# debugId=
|
|
2589
|
+
//# debugId=1847C08CED2B25BC64756E2164756E21
|