@resourcexjs/registry 1.5.0 → 1.7.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/dist/index.d.ts +82 -13
- package/dist/index.js +161 -526
- package/dist/index.js.map +7 -6
- package/package.json +3 -4
package/dist/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
function isRemoteConfig(config) {
|
|
3
|
+
return config !== undefined && "endpoint" in config;
|
|
4
|
+
}
|
|
1
5
|
// ../core/dist/index.js
|
|
2
6
|
import { gzip, gunzip } from "node:zlib";
|
|
3
7
|
import { promisify } from "node:util";
|
|
@@ -156,8 +160,10 @@ class RegistryError extends ResourceXError {
|
|
|
156
160
|
this.name = "RegistryError";
|
|
157
161
|
}
|
|
158
162
|
}
|
|
159
|
-
// src/
|
|
163
|
+
// src/LocalRegistry.ts
|
|
160
164
|
import { homedir } from "node:os";
|
|
165
|
+
import { join } from "node:path";
|
|
166
|
+
import { readFile, writeFile, mkdir, rm, stat, readdir } from "node:fs/promises";
|
|
161
167
|
|
|
162
168
|
// ../type/dist/index.js
|
|
163
169
|
import { gzip as gzip2, gunzip as gunzip2 } from "node:zlib";
|
|
@@ -1459,487 +1465,13 @@ class TypeHandlerChain {
|
|
|
1459
1465
|
}
|
|
1460
1466
|
}
|
|
1461
1467
|
|
|
1462
|
-
//
|
|
1463
|
-
import { readFile, writeFile, readdir, mkdir, rm, access, stat } from "node:fs/promises";
|
|
1464
|
-
import { resolve, dirname, join } from "node:path";
|
|
1465
|
-
|
|
1466
|
-
class ARPError extends Error {
|
|
1467
|
-
constructor(message, options) {
|
|
1468
|
-
super(message, options);
|
|
1469
|
-
this.name = "ARPError";
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
class ParseError extends ARPError {
|
|
1474
|
-
url;
|
|
1475
|
-
constructor(message, url) {
|
|
1476
|
-
super(message);
|
|
1477
|
-
this.url = url;
|
|
1478
|
-
this.name = "ParseError";
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
class TransportError extends ARPError {
|
|
1483
|
-
transport;
|
|
1484
|
-
constructor(message, transport, options) {
|
|
1485
|
-
super(message, options);
|
|
1486
|
-
this.transport = transport;
|
|
1487
|
-
this.name = "TransportError";
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
|
|
1491
|
-
class SemanticError extends ARPError {
|
|
1492
|
-
semantic;
|
|
1493
|
-
constructor(message, semantic, options) {
|
|
1494
|
-
super(message, options);
|
|
1495
|
-
this.semantic = semantic;
|
|
1496
|
-
this.name = "SemanticError";
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
class ARL {
|
|
1501
|
-
semantic;
|
|
1502
|
-
transport;
|
|
1503
|
-
location;
|
|
1504
|
-
resolver;
|
|
1505
|
-
constructor(semantic, transport, location, resolver) {
|
|
1506
|
-
this.semantic = semantic;
|
|
1507
|
-
this.transport = transport;
|
|
1508
|
-
this.location = location;
|
|
1509
|
-
this.resolver = resolver;
|
|
1510
|
-
}
|
|
1511
|
-
createContext(params) {
|
|
1512
|
-
return {
|
|
1513
|
-
url: this.toString(),
|
|
1514
|
-
semantic: this.semantic,
|
|
1515
|
-
transport: this.transport,
|
|
1516
|
-
location: this.location,
|
|
1517
|
-
timestamp: new Date,
|
|
1518
|
-
params
|
|
1519
|
-
};
|
|
1520
|
-
}
|
|
1521
|
-
async resolve(params) {
|
|
1522
|
-
const transport = this.resolver.getTransportHandler(this.transport);
|
|
1523
|
-
const semantic = this.resolver.getSemanticHandler(this.semantic);
|
|
1524
|
-
const context = this.createContext(params);
|
|
1525
|
-
return semantic.resolve(transport, this.location, context);
|
|
1526
|
-
}
|
|
1527
|
-
async deposit(data, params) {
|
|
1528
|
-
const transport = this.resolver.getTransportHandler(this.transport);
|
|
1529
|
-
const semantic = this.resolver.getSemanticHandler(this.semantic);
|
|
1530
|
-
const context = this.createContext(params);
|
|
1531
|
-
if (!semantic.deposit) {
|
|
1532
|
-
throw new SemanticError(`Semantic "${semantic.name}" does not support deposit operation`, this.semantic);
|
|
1533
|
-
}
|
|
1534
|
-
await semantic.deposit(transport, this.location, data, context);
|
|
1535
|
-
}
|
|
1536
|
-
async exists() {
|
|
1537
|
-
const transport = this.resolver.getTransportHandler(this.transport);
|
|
1538
|
-
const semantic = this.resolver.getSemanticHandler(this.semantic);
|
|
1539
|
-
const context = this.createContext();
|
|
1540
|
-
if (semantic.exists) {
|
|
1541
|
-
return semantic.exists(transport, this.location, context);
|
|
1542
|
-
}
|
|
1543
|
-
return transport.exists(this.location);
|
|
1544
|
-
}
|
|
1545
|
-
async delete() {
|
|
1546
|
-
const transport = this.resolver.getTransportHandler(this.transport);
|
|
1547
|
-
const semantic = this.resolver.getSemanticHandler(this.semantic);
|
|
1548
|
-
const context = this.createContext();
|
|
1549
|
-
if (semantic.delete) {
|
|
1550
|
-
return semantic.delete(transport, this.location, context);
|
|
1551
|
-
}
|
|
1552
|
-
await transport.delete(this.location);
|
|
1553
|
-
}
|
|
1554
|
-
toString() {
|
|
1555
|
-
return `arp:${this.semantic}:${this.transport}://${this.location}`;
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
|
-
class FileTransportHandler {
|
|
1560
|
-
name = "file";
|
|
1561
|
-
resolvePath(location) {
|
|
1562
|
-
return resolve(process.cwd(), location);
|
|
1563
|
-
}
|
|
1564
|
-
async get(location, params) {
|
|
1565
|
-
const filePath = this.resolvePath(location);
|
|
1566
|
-
try {
|
|
1567
|
-
const stats = await stat(filePath);
|
|
1568
|
-
if (stats.isDirectory()) {
|
|
1569
|
-
return this.getDirectory(filePath, stats, params);
|
|
1570
|
-
} else {
|
|
1571
|
-
return this.getFile(filePath, stats);
|
|
1572
|
-
}
|
|
1573
|
-
} catch (error) {
|
|
1574
|
-
const err = error;
|
|
1575
|
-
throw new TransportError(`File get error: ${err.code} - ${filePath}`, this.name, {
|
|
1576
|
-
cause: err
|
|
1577
|
-
});
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
async getFile(filePath, stats) {
|
|
1581
|
-
const content = await readFile(filePath);
|
|
1582
|
-
return {
|
|
1583
|
-
content,
|
|
1584
|
-
metadata: {
|
|
1585
|
-
type: "file",
|
|
1586
|
-
size: Number(stats.size),
|
|
1587
|
-
modifiedAt: stats.mtime
|
|
1588
|
-
}
|
|
1589
|
-
};
|
|
1590
|
-
}
|
|
1591
|
-
async getDirectory(dirPath, stats, params) {
|
|
1592
|
-
const recursive = params?.recursive === "true";
|
|
1593
|
-
const pattern = params?.pattern;
|
|
1594
|
-
let entries;
|
|
1595
|
-
if (recursive) {
|
|
1596
|
-
entries = await this.listRecursive(dirPath, dirPath);
|
|
1597
|
-
} else {
|
|
1598
|
-
entries = await readdir(dirPath);
|
|
1599
|
-
}
|
|
1600
|
-
if (pattern) {
|
|
1601
|
-
entries = this.filterByPattern(entries, pattern);
|
|
1602
|
-
}
|
|
1603
|
-
const content = Buffer.from(JSON.stringify(entries));
|
|
1604
|
-
return {
|
|
1605
|
-
content,
|
|
1606
|
-
metadata: {
|
|
1607
|
-
type: "directory",
|
|
1608
|
-
modifiedAt: stats.mtime
|
|
1609
|
-
}
|
|
1610
|
-
};
|
|
1611
|
-
}
|
|
1612
|
-
async listRecursive(basePath, currentPath) {
|
|
1613
|
-
const entries = await readdir(currentPath, { withFileTypes: true });
|
|
1614
|
-
const results = [];
|
|
1615
|
-
for (const entry of entries) {
|
|
1616
|
-
const fullPath = join(currentPath, entry.name);
|
|
1617
|
-
const relativePath = fullPath.substring(basePath.length + 1);
|
|
1618
|
-
if (entry.isDirectory()) {
|
|
1619
|
-
const subEntries = await this.listRecursive(basePath, fullPath);
|
|
1620
|
-
results.push(...subEntries);
|
|
1621
|
-
} else {
|
|
1622
|
-
results.push(relativePath);
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
return results;
|
|
1626
|
-
}
|
|
1627
|
-
filterByPattern(entries, pattern) {
|
|
1628
|
-
const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
1629
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
1630
|
-
return entries.filter((entry) => {
|
|
1631
|
-
const filename = entry.split("/").pop() || entry;
|
|
1632
|
-
return regex.test(filename);
|
|
1633
|
-
});
|
|
1634
|
-
}
|
|
1635
|
-
async set(location, content, _params) {
|
|
1636
|
-
const filePath = this.resolvePath(location);
|
|
1637
|
-
try {
|
|
1638
|
-
await mkdir(dirname(filePath), { recursive: true });
|
|
1639
|
-
await writeFile(filePath, content);
|
|
1640
|
-
} catch (error) {
|
|
1641
|
-
const err = error;
|
|
1642
|
-
throw new TransportError(`File set error: ${err.code} - ${filePath}`, this.name, {
|
|
1643
|
-
cause: err
|
|
1644
|
-
});
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
async exists(location) {
|
|
1648
|
-
const filePath = this.resolvePath(location);
|
|
1649
|
-
try {
|
|
1650
|
-
await access(filePath);
|
|
1651
|
-
return true;
|
|
1652
|
-
} catch {
|
|
1653
|
-
return false;
|
|
1654
|
-
}
|
|
1655
|
-
}
|
|
1656
|
-
async delete(location) {
|
|
1657
|
-
const filePath = this.resolvePath(location);
|
|
1658
|
-
try {
|
|
1659
|
-
await rm(filePath, { recursive: true });
|
|
1660
|
-
} catch (error) {
|
|
1661
|
-
const err = error;
|
|
1662
|
-
if (err.code === "ENOENT") {
|
|
1663
|
-
return;
|
|
1664
|
-
}
|
|
1665
|
-
throw new TransportError(`File delete error: ${err.code} - ${filePath}`, this.name, {
|
|
1666
|
-
cause: err
|
|
1667
|
-
});
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1670
|
-
}
|
|
1671
|
-
var fileTransport = new FileTransportHandler;
|
|
1672
|
-
|
|
1673
|
-
class HttpTransportHandler {
|
|
1674
|
-
name;
|
|
1675
|
-
protocol;
|
|
1676
|
-
constructor(protocol = "https") {
|
|
1677
|
-
this.protocol = protocol;
|
|
1678
|
-
this.name = protocol;
|
|
1679
|
-
}
|
|
1680
|
-
async get(location, params) {
|
|
1681
|
-
const url = this.buildUrl(location, params);
|
|
1682
|
-
try {
|
|
1683
|
-
const response = await fetch(url);
|
|
1684
|
-
if (!response.ok) {
|
|
1685
|
-
throw new TransportError(`HTTP ${response.status}: ${response.statusText} - ${url}`, this.name);
|
|
1686
|
-
}
|
|
1687
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
1688
|
-
const content = Buffer.from(arrayBuffer);
|
|
1689
|
-
const contentType = response.headers.get("content-type");
|
|
1690
|
-
const contentLength = response.headers.get("content-length");
|
|
1691
|
-
const lastModified = response.headers.get("last-modified");
|
|
1692
|
-
return {
|
|
1693
|
-
content,
|
|
1694
|
-
metadata: {
|
|
1695
|
-
type: "file",
|
|
1696
|
-
size: contentLength ? parseInt(contentLength, 10) : content.length,
|
|
1697
|
-
modifiedAt: lastModified ? new Date(lastModified) : undefined,
|
|
1698
|
-
contentType
|
|
1699
|
-
}
|
|
1700
|
-
};
|
|
1701
|
-
} catch (error) {
|
|
1702
|
-
if (error instanceof TransportError) {
|
|
1703
|
-
throw error;
|
|
1704
|
-
}
|
|
1705
|
-
throw new TransportError(`Network error: ${url}`, this.name, {
|
|
1706
|
-
cause: error
|
|
1707
|
-
});
|
|
1708
|
-
}
|
|
1709
|
-
}
|
|
1710
|
-
buildUrl(location, params) {
|
|
1711
|
-
const url = new URL(`${this.protocol}://${location}`);
|
|
1712
|
-
if (params) {
|
|
1713
|
-
for (const [key, value] of Object.entries(params)) {
|
|
1714
|
-
url.searchParams.set(key, value);
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
return url.toString();
|
|
1718
|
-
}
|
|
1719
|
-
async set(_location, _content, _params) {
|
|
1720
|
-
throw new TransportError("HTTP transport is read-only, set not supported", this.name);
|
|
1721
|
-
}
|
|
1722
|
-
async exists(location) {
|
|
1723
|
-
const url = `${this.protocol}://${location}`;
|
|
1724
|
-
try {
|
|
1725
|
-
const response = await fetch(url, { method: "HEAD" });
|
|
1726
|
-
return response.ok;
|
|
1727
|
-
} catch {
|
|
1728
|
-
return false;
|
|
1729
|
-
}
|
|
1730
|
-
}
|
|
1731
|
-
async delete(_location) {
|
|
1732
|
-
throw new TransportError("HTTP transport is read-only, delete not supported", this.name);
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
1735
|
-
var httpsTransport = new HttpTransportHandler("https");
|
|
1736
|
-
var httpTransport = new HttpTransportHandler("http");
|
|
1737
|
-
|
|
1738
|
-
class TextSemanticHandler {
|
|
1739
|
-
name = "text";
|
|
1740
|
-
async resolve(transport, location, context) {
|
|
1741
|
-
const result = await transport.get(location, context.params);
|
|
1742
|
-
if (result.metadata?.type === "directory") {
|
|
1743
|
-
const meta2 = {
|
|
1744
|
-
url: context.url,
|
|
1745
|
-
semantic: context.semantic,
|
|
1746
|
-
transport: context.transport,
|
|
1747
|
-
location: context.location,
|
|
1748
|
-
size: result.content.length,
|
|
1749
|
-
encoding: "utf-8",
|
|
1750
|
-
mimeType: "application/json",
|
|
1751
|
-
resolvedAt: context.timestamp.toISOString(),
|
|
1752
|
-
type: "directory"
|
|
1753
|
-
};
|
|
1754
|
-
return {
|
|
1755
|
-
type: "text",
|
|
1756
|
-
content: result.content.toString("utf-8"),
|
|
1757
|
-
meta: meta2
|
|
1758
|
-
};
|
|
1759
|
-
}
|
|
1760
|
-
const text = result.content.toString("utf-8");
|
|
1761
|
-
const meta = {
|
|
1762
|
-
url: context.url,
|
|
1763
|
-
semantic: context.semantic,
|
|
1764
|
-
transport: context.transport,
|
|
1765
|
-
location: context.location,
|
|
1766
|
-
size: result.metadata?.size ?? result.content.length,
|
|
1767
|
-
encoding: "utf-8",
|
|
1768
|
-
mimeType: "text/plain",
|
|
1769
|
-
resolvedAt: context.timestamp.toISOString(),
|
|
1770
|
-
type: "file"
|
|
1771
|
-
};
|
|
1772
|
-
return {
|
|
1773
|
-
type: "text",
|
|
1774
|
-
content: text,
|
|
1775
|
-
meta
|
|
1776
|
-
};
|
|
1777
|
-
}
|
|
1778
|
-
async deposit(transport, location, data, context) {
|
|
1779
|
-
const buffer = Buffer.from(data, "utf-8");
|
|
1780
|
-
try {
|
|
1781
|
-
await transport.set(location, buffer, context.params);
|
|
1782
|
-
} catch (error) {
|
|
1783
|
-
throw new SemanticError(`Failed to deposit text to "${location}": ${error.message}`, this.name, { cause: error });
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
async exists(transport, location, _context) {
|
|
1787
|
-
return transport.exists(location);
|
|
1788
|
-
}
|
|
1789
|
-
async delete(transport, location, _context) {
|
|
1790
|
-
try {
|
|
1791
|
-
await transport.delete(location);
|
|
1792
|
-
} catch (error) {
|
|
1793
|
-
throw new SemanticError(`Failed to delete "${location}": ${error.message}`, this.name, { cause: error });
|
|
1794
|
-
}
|
|
1795
|
-
}
|
|
1796
|
-
}
|
|
1797
|
-
var textSemantic = new TextSemanticHandler;
|
|
1798
|
-
function toBuffer(data) {
|
|
1799
|
-
if (Buffer.isBuffer(data)) {
|
|
1800
|
-
return data;
|
|
1801
|
-
}
|
|
1802
|
-
if (data instanceof Uint8Array) {
|
|
1803
|
-
return Buffer.from(data);
|
|
1804
|
-
}
|
|
1805
|
-
if (data instanceof ArrayBuffer) {
|
|
1806
|
-
return Buffer.from(data);
|
|
1807
|
-
}
|
|
1808
|
-
if (Array.isArray(data)) {
|
|
1809
|
-
return Buffer.from(data);
|
|
1810
|
-
}
|
|
1811
|
-
throw new SemanticError(`Unsupported binary input type`, "binary");
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
|
-
class BinarySemanticHandler {
|
|
1815
|
-
name = "binary";
|
|
1816
|
-
async resolve(transport, location, context) {
|
|
1817
|
-
const result = await transport.get(location, context.params);
|
|
1818
|
-
const meta = {
|
|
1819
|
-
url: context.url,
|
|
1820
|
-
semantic: context.semantic,
|
|
1821
|
-
transport: context.transport,
|
|
1822
|
-
location: context.location,
|
|
1823
|
-
size: result.metadata?.size ?? result.content.length,
|
|
1824
|
-
resolvedAt: context.timestamp.toISOString(),
|
|
1825
|
-
type: result.metadata?.type
|
|
1826
|
-
};
|
|
1827
|
-
return {
|
|
1828
|
-
type: "binary",
|
|
1829
|
-
content: result.content,
|
|
1830
|
-
meta
|
|
1831
|
-
};
|
|
1832
|
-
}
|
|
1833
|
-
async deposit(transport, location, data, context) {
|
|
1834
|
-
const buffer = toBuffer(data);
|
|
1835
|
-
try {
|
|
1836
|
-
await transport.set(location, buffer, context.params);
|
|
1837
|
-
} catch (error) {
|
|
1838
|
-
throw new SemanticError(`Failed to deposit binary to "${location}": ${error.message}`, this.name, { cause: error });
|
|
1839
|
-
}
|
|
1840
|
-
}
|
|
1841
|
-
async exists(transport, location, _context) {
|
|
1842
|
-
return transport.exists(location);
|
|
1843
|
-
}
|
|
1844
|
-
async delete(transport, location, _context) {
|
|
1845
|
-
try {
|
|
1846
|
-
await transport.delete(location);
|
|
1847
|
-
} catch (error) {
|
|
1848
|
-
throw new SemanticError(`Failed to delete "${location}": ${error.message}`, this.name, { cause: error });
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1851
|
-
}
|
|
1852
|
-
var binarySemantic = new BinarySemanticHandler;
|
|
1853
|
-
|
|
1854
|
-
class ARP {
|
|
1855
|
-
transports;
|
|
1856
|
-
semantics;
|
|
1857
|
-
constructor(config = {}) {
|
|
1858
|
-
this.transports = new Map;
|
|
1859
|
-
this.semantics = new Map;
|
|
1860
|
-
const defaultTransports = [fileTransport, httpTransport, httpsTransport];
|
|
1861
|
-
const defaultSemantics = [textSemantic, binarySemantic];
|
|
1862
|
-
for (const handler of defaultTransports) {
|
|
1863
|
-
this.transports.set(handler.name, handler);
|
|
1864
|
-
}
|
|
1865
|
-
for (const handler of defaultSemantics) {
|
|
1866
|
-
this.semantics.set(handler.name, handler);
|
|
1867
|
-
}
|
|
1868
|
-
if (config.transports) {
|
|
1869
|
-
for (const handler of config.transports) {
|
|
1870
|
-
this.transports.set(handler.name, handler);
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
if (config.semantics) {
|
|
1874
|
-
for (const handler of config.semantics) {
|
|
1875
|
-
this.semantics.set(handler.name, handler);
|
|
1876
|
-
}
|
|
1877
|
-
}
|
|
1878
|
-
}
|
|
1879
|
-
registerTransport(handler) {
|
|
1880
|
-
this.transports.set(handler.name, handler);
|
|
1881
|
-
}
|
|
1882
|
-
registerSemantic(handler) {
|
|
1883
|
-
this.semantics.set(handler.name, handler);
|
|
1884
|
-
}
|
|
1885
|
-
getTransportHandler(name) {
|
|
1886
|
-
const handler = this.transports.get(name);
|
|
1887
|
-
if (!handler) {
|
|
1888
|
-
throw new TransportError(`Unsupported transport type: ${name}`, name);
|
|
1889
|
-
}
|
|
1890
|
-
return handler;
|
|
1891
|
-
}
|
|
1892
|
-
getSemanticHandler(name) {
|
|
1893
|
-
const handler = this.semantics.get(name);
|
|
1894
|
-
if (!handler) {
|
|
1895
|
-
throw new SemanticError(`Unsupported semantic type: ${name}`, name);
|
|
1896
|
-
}
|
|
1897
|
-
return handler;
|
|
1898
|
-
}
|
|
1899
|
-
parse(url) {
|
|
1900
|
-
if (!url.startsWith("arp:")) {
|
|
1901
|
-
throw new ParseError(`Invalid ARP URL: must start with "arp:"`, url);
|
|
1902
|
-
}
|
|
1903
|
-
const content = url.substring(4);
|
|
1904
|
-
const separatorIndex = content.indexOf("://");
|
|
1905
|
-
if (separatorIndex === -1) {
|
|
1906
|
-
throw new ParseError(`Invalid ARP URL: missing "://"`, url);
|
|
1907
|
-
}
|
|
1908
|
-
const typePart = content.substring(0, separatorIndex);
|
|
1909
|
-
const location = content.substring(separatorIndex + 3);
|
|
1910
|
-
const colonIndex = typePart.indexOf(":");
|
|
1911
|
-
if (colonIndex === -1) {
|
|
1912
|
-
throw new ParseError(`Invalid ARP URL: must have exactly 2 types (semantic:transport)`, url);
|
|
1913
|
-
}
|
|
1914
|
-
const semantic = typePart.substring(0, colonIndex);
|
|
1915
|
-
const transport = typePart.substring(colonIndex + 1);
|
|
1916
|
-
if (!semantic) {
|
|
1917
|
-
throw new ParseError(`Invalid ARP URL: semantic type cannot be empty`, url);
|
|
1918
|
-
}
|
|
1919
|
-
if (!transport) {
|
|
1920
|
-
throw new ParseError(`Invalid ARP URL: transport type cannot be empty`, url);
|
|
1921
|
-
}
|
|
1922
|
-
if (!location) {
|
|
1923
|
-
throw new ParseError(`Invalid ARP URL: location cannot be empty`, url);
|
|
1924
|
-
}
|
|
1925
|
-
this.getTransportHandler(transport);
|
|
1926
|
-
this.getSemanticHandler(semantic);
|
|
1927
|
-
return new ARL(semantic, transport, location, this);
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
function createARP(config) {
|
|
1931
|
-
return new ARP(config);
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
|
-
// src/ARPRegistry.ts
|
|
1468
|
+
// src/LocalRegistry.ts
|
|
1935
1469
|
var DEFAULT_PATH = `${homedir()}/.resourcex`;
|
|
1936
1470
|
|
|
1937
|
-
class
|
|
1938
|
-
arp;
|
|
1471
|
+
class LocalRegistry {
|
|
1939
1472
|
basePath;
|
|
1940
1473
|
typeHandler;
|
|
1941
1474
|
constructor(config) {
|
|
1942
|
-
this.arp = createARP();
|
|
1943
1475
|
this.basePath = config?.path ?? DEFAULT_PATH;
|
|
1944
1476
|
this.typeHandler = TypeHandlerChain.create();
|
|
1945
1477
|
if (config?.types) {
|
|
@@ -1951,78 +1483,79 @@ class ARPRegistry {
|
|
|
1951
1483
|
supportType(type) {
|
|
1952
1484
|
this.typeHandler.register(type);
|
|
1953
1485
|
}
|
|
1954
|
-
|
|
1486
|
+
buildPath(locator) {
|
|
1955
1487
|
const rxl = typeof locator === "string" ? parseRXL(locator) : locator;
|
|
1956
1488
|
const domain = rxl.domain ?? "localhost";
|
|
1957
|
-
|
|
1489
|
+
const version = rxl.version ?? "latest";
|
|
1490
|
+
let path = join(this.basePath, domain);
|
|
1958
1491
|
if (rxl.path) {
|
|
1959
|
-
path
|
|
1492
|
+
path = join(path, rxl.path);
|
|
1960
1493
|
}
|
|
1961
|
-
const
|
|
1962
|
-
return
|
|
1494
|
+
const resourceName = rxl.type ? `${rxl.name}.${rxl.type}` : rxl.name;
|
|
1495
|
+
return join(path, resourceName, version);
|
|
1963
1496
|
}
|
|
1964
1497
|
async publish(_resource) {
|
|
1965
1498
|
throw new RegistryError("Remote publish not implemented yet");
|
|
1966
1499
|
}
|
|
1967
1500
|
async link(resource) {
|
|
1968
1501
|
const locator = resource.manifest.toLocator();
|
|
1969
|
-
const
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
const
|
|
1502
|
+
const resourcePath = this.buildPath(locator);
|
|
1503
|
+
await mkdir(resourcePath, { recursive: true });
|
|
1504
|
+
const manifestPath = join(resourcePath, "manifest.json");
|
|
1505
|
+
await writeFile(manifestPath, JSON.stringify(resource.manifest.toJSON(), null, 2), "utf-8");
|
|
1506
|
+
const contentPath = join(resourcePath, "content.tar.gz");
|
|
1974
1507
|
const serialized = await this.typeHandler.serialize(resource);
|
|
1975
|
-
await
|
|
1508
|
+
await writeFile(contentPath, serialized);
|
|
1976
1509
|
}
|
|
1977
|
-
async
|
|
1510
|
+
async get(locator) {
|
|
1978
1511
|
if (!await this.exists(locator)) {
|
|
1979
1512
|
throw new RegistryError(`Resource not found: ${locator}`);
|
|
1980
1513
|
}
|
|
1981
|
-
const
|
|
1982
|
-
const
|
|
1983
|
-
const
|
|
1984
|
-
const manifestData = JSON.parse(
|
|
1514
|
+
const resourcePath = this.buildPath(locator);
|
|
1515
|
+
const manifestPath = join(resourcePath, "manifest.json");
|
|
1516
|
+
const manifestContent = await readFile(manifestPath, "utf-8");
|
|
1517
|
+
const manifestData = JSON.parse(manifestContent);
|
|
1985
1518
|
const manifest = createRXM(manifestData);
|
|
1986
|
-
const
|
|
1987
|
-
const
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1519
|
+
const contentPath = join(resourcePath, "content.tar.gz");
|
|
1520
|
+
const data = await readFile(contentPath);
|
|
1521
|
+
return this.typeHandler.deserialize(data, manifest);
|
|
1522
|
+
}
|
|
1523
|
+
async resolve(locator) {
|
|
1524
|
+
const rxr = await this.get(locator);
|
|
1991
1525
|
return this.typeHandler.resolve(rxr);
|
|
1992
1526
|
}
|
|
1993
1527
|
async exists(locator) {
|
|
1994
|
-
const
|
|
1995
|
-
const
|
|
1996
|
-
|
|
1528
|
+
const resourcePath = this.buildPath(locator);
|
|
1529
|
+
const manifestPath = join(resourcePath, "manifest.json");
|
|
1530
|
+
try {
|
|
1531
|
+
await stat(manifestPath);
|
|
1532
|
+
return true;
|
|
1533
|
+
} catch {
|
|
1534
|
+
return false;
|
|
1535
|
+
}
|
|
1997
1536
|
}
|
|
1998
1537
|
async delete(locator) {
|
|
1999
1538
|
if (!await this.exists(locator)) {
|
|
2000
1539
|
return;
|
|
2001
1540
|
}
|
|
2002
|
-
const
|
|
2003
|
-
|
|
2004
|
-
await manifestArl.delete();
|
|
2005
|
-
const contentUrl = this.buildUrl(locator, "content").replace("arp:text:", "arp:binary:");
|
|
2006
|
-
const contentArl = this.arp.parse(contentUrl);
|
|
2007
|
-
await contentArl.delete();
|
|
1541
|
+
const resourcePath = this.buildPath(locator);
|
|
1542
|
+
await rm(resourcePath, { recursive: true, force: true });
|
|
2008
1543
|
}
|
|
2009
1544
|
async search(options) {
|
|
2010
1545
|
const { query, limit, offset = 0 } = options ?? {};
|
|
2011
|
-
const baseUrl = `arp:text:file://${this.basePath}`;
|
|
2012
|
-
const baseArl = this.arp.parse(baseUrl);
|
|
2013
1546
|
let entries;
|
|
2014
1547
|
try {
|
|
2015
|
-
|
|
2016
|
-
entries = JSON.parse(result2.content);
|
|
1548
|
+
entries = await this.listRecursive(this.basePath);
|
|
2017
1549
|
} catch {
|
|
2018
1550
|
return [];
|
|
2019
1551
|
}
|
|
2020
1552
|
const locators = [];
|
|
2021
1553
|
for (const entry of entries) {
|
|
2022
|
-
if (!entry.endsWith("
|
|
1554
|
+
if (!entry.endsWith("manifest.json")) {
|
|
2023
1555
|
continue;
|
|
2024
1556
|
}
|
|
2025
|
-
const
|
|
1557
|
+
const relativePath = entry.slice(this.basePath.length + 1);
|
|
1558
|
+
const rxl = this.parseEntryToRXL(relativePath);
|
|
2026
1559
|
if (rxl) {
|
|
2027
1560
|
locators.push(rxl);
|
|
2028
1561
|
}
|
|
@@ -2041,21 +1574,32 @@ class ARPRegistry {
|
|
|
2041
1574
|
}
|
|
2042
1575
|
return result;
|
|
2043
1576
|
}
|
|
1577
|
+
async listRecursive(dir) {
|
|
1578
|
+
const results = [];
|
|
1579
|
+
try {
|
|
1580
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
1581
|
+
for (const entry of entries) {
|
|
1582
|
+
const fullPath = join(dir, entry.name);
|
|
1583
|
+
if (entry.isDirectory()) {
|
|
1584
|
+
const subEntries = await this.listRecursive(fullPath);
|
|
1585
|
+
results.push(...subEntries);
|
|
1586
|
+
} else {
|
|
1587
|
+
results.push(fullPath);
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
} catch {}
|
|
1591
|
+
return results;
|
|
1592
|
+
}
|
|
2044
1593
|
parseEntryToRXL(entry) {
|
|
2045
|
-
const dirPath = entry.replace(
|
|
2046
|
-
const parts = dirPath.split(
|
|
2047
|
-
if (parts.length <
|
|
1594
|
+
const dirPath = entry.replace(/[/\\]manifest\.json$/, "");
|
|
1595
|
+
const parts = dirPath.split(/[/\\]/);
|
|
1596
|
+
if (parts.length < 3) {
|
|
2048
1597
|
return null;
|
|
2049
1598
|
}
|
|
2050
|
-
const
|
|
1599
|
+
const version = parts.pop();
|
|
1600
|
+
const nameTypePart = parts.pop();
|
|
2051
1601
|
const domain = parts.shift();
|
|
2052
1602
|
const path = parts.length > 0 ? parts.join("/") : undefined;
|
|
2053
|
-
const atIndex = resourceDir.lastIndexOf("@");
|
|
2054
|
-
if (atIndex === -1) {
|
|
2055
|
-
return null;
|
|
2056
|
-
}
|
|
2057
|
-
const nameTypePart = resourceDir.substring(0, atIndex);
|
|
2058
|
-
const version = resourceDir.substring(atIndex + 1);
|
|
2059
1603
|
const dotIndex = nameTypePart.lastIndexOf(".");
|
|
2060
1604
|
let name;
|
|
2061
1605
|
let type;
|
|
@@ -2082,14 +1626,105 @@ class ARPRegistry {
|
|
|
2082
1626
|
}
|
|
2083
1627
|
}
|
|
2084
1628
|
}
|
|
1629
|
+
// src/RemoteRegistry.ts
|
|
1630
|
+
class RemoteRegistry {
|
|
1631
|
+
endpoint;
|
|
1632
|
+
typeHandler;
|
|
1633
|
+
constructor(config) {
|
|
1634
|
+
this.endpoint = config.endpoint.replace(/\/$/, "");
|
|
1635
|
+
this.typeHandler = TypeHandlerChain.create();
|
|
1636
|
+
}
|
|
1637
|
+
supportType(type) {
|
|
1638
|
+
this.typeHandler.register(type);
|
|
1639
|
+
}
|
|
1640
|
+
async publish(_resource) {
|
|
1641
|
+
throw new RegistryError("Remote registry publish not implemented yet");
|
|
1642
|
+
}
|
|
1643
|
+
async link(_resource) {
|
|
1644
|
+
throw new RegistryError("Cannot link to remote registry - use local registry for linking");
|
|
1645
|
+
}
|
|
1646
|
+
async get(locator) {
|
|
1647
|
+
const manifestUrl = `${this.endpoint}/resource?locator=${encodeURIComponent(locator)}`;
|
|
1648
|
+
const manifestResponse = await fetch(manifestUrl);
|
|
1649
|
+
if (!manifestResponse.ok) {
|
|
1650
|
+
if (manifestResponse.status === 404) {
|
|
1651
|
+
throw new RegistryError(`Resource not found: ${locator}`);
|
|
1652
|
+
}
|
|
1653
|
+
throw new RegistryError(`Failed to fetch resource: ${manifestResponse.statusText}`);
|
|
1654
|
+
}
|
|
1655
|
+
const manifestData = await manifestResponse.json();
|
|
1656
|
+
const manifest = createRXM(manifestData);
|
|
1657
|
+
const contentUrl = `${this.endpoint}/content?locator=${encodeURIComponent(locator)}`;
|
|
1658
|
+
const contentResponse = await fetch(contentUrl);
|
|
1659
|
+
if (!contentResponse.ok) {
|
|
1660
|
+
throw new RegistryError(`Failed to fetch content: ${contentResponse.statusText}`);
|
|
1661
|
+
}
|
|
1662
|
+
const contentBuffer = Buffer.from(await contentResponse.arrayBuffer());
|
|
1663
|
+
return this.typeHandler.deserialize(contentBuffer, manifest);
|
|
1664
|
+
}
|
|
1665
|
+
async resolve(locator) {
|
|
1666
|
+
const rxr = await this.get(locator);
|
|
1667
|
+
return this.typeHandler.resolve(rxr);
|
|
1668
|
+
}
|
|
1669
|
+
async exists(locator) {
|
|
1670
|
+
const url = `${this.endpoint}/exists?locator=${encodeURIComponent(locator)}`;
|
|
1671
|
+
const response = await fetch(url);
|
|
1672
|
+
if (!response.ok) {
|
|
1673
|
+
return false;
|
|
1674
|
+
}
|
|
1675
|
+
const data = await response.json();
|
|
1676
|
+
return data.exists === true;
|
|
1677
|
+
}
|
|
1678
|
+
async delete(_locator) {
|
|
1679
|
+
throw new RegistryError("Cannot delete from remote registry - use local registry for deletion");
|
|
1680
|
+
}
|
|
1681
|
+
async search(options) {
|
|
1682
|
+
const params = new URLSearchParams;
|
|
1683
|
+
if (options?.query)
|
|
1684
|
+
params.set("query", options.query);
|
|
1685
|
+
if (options?.limit !== undefined)
|
|
1686
|
+
params.set("limit", String(options.limit));
|
|
1687
|
+
if (options?.offset !== undefined)
|
|
1688
|
+
params.set("offset", String(options.offset));
|
|
1689
|
+
const url = `${this.endpoint}/search?${params.toString()}`;
|
|
1690
|
+
const response = await fetch(url);
|
|
1691
|
+
if (!response.ok) {
|
|
1692
|
+
throw new RegistryError(`Search failed: ${response.statusText}`);
|
|
1693
|
+
}
|
|
1694
|
+
const data = await response.json();
|
|
1695
|
+
return (data.results || []).map((locator) => parseRXL(locator));
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
async function discoverRegistry(domain) {
|
|
1699
|
+
const wellKnownUrl = `https://${domain}/.well-known/resourcex`;
|
|
1700
|
+
try {
|
|
1701
|
+
const response = await fetch(wellKnownUrl);
|
|
1702
|
+
if (!response.ok) {
|
|
1703
|
+
throw new RegistryError(`Well-known discovery failed for ${domain}: ${response.statusText}`);
|
|
1704
|
+
}
|
|
1705
|
+
const data = await response.json();
|
|
1706
|
+
return data.registry;
|
|
1707
|
+
} catch (error) {
|
|
1708
|
+
if (error instanceof RegistryError) {
|
|
1709
|
+
throw error;
|
|
1710
|
+
}
|
|
1711
|
+
throw new RegistryError(`Failed to discover registry for ${domain}: ${error.message}`);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
2085
1714
|
// src/createRegistry.ts
|
|
2086
1715
|
function createRegistry(config) {
|
|
2087
|
-
|
|
1716
|
+
if (isRemoteConfig(config)) {
|
|
1717
|
+
return new RemoteRegistry(config);
|
|
1718
|
+
}
|
|
1719
|
+
return new LocalRegistry(config);
|
|
2088
1720
|
}
|
|
2089
1721
|
export {
|
|
1722
|
+
isRemoteConfig,
|
|
1723
|
+
discoverRegistry,
|
|
2090
1724
|
createRegistry,
|
|
1725
|
+
RemoteRegistry,
|
|
2091
1726
|
RegistryError,
|
|
2092
|
-
|
|
1727
|
+
LocalRegistry
|
|
2093
1728
|
};
|
|
2094
1729
|
|
|
2095
|
-
//# debugId=
|
|
1730
|
+
//# debugId=E606A28A0E6E3E5464756E2164756E21
|