@resourcexjs/registry 2.2.0 → 2.3.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.js CHANGED
@@ -23447,13 +23447,6 @@ var require_ours = __commonJS((exports, module) => {
23447
23447
  module.exports.default = module.exports;
23448
23448
  });
23449
23449
 
23450
- // src/types.ts
23451
- function isRemoteConfig(config) {
23452
- return config !== undefined && "endpoint" in config;
23453
- }
23454
- function isGitConfig(config) {
23455
- return config !== undefined && "type" in config && config.type === "git";
23456
- }
23457
23450
  // ../core/dist/index.js
23458
23451
  import { gzip, gunzip } from "node:zlib";
23459
23452
  import { promisify } from "node:util";
@@ -24679,9 +24672,81 @@ async function unpackTar(archive, options = {}) {
24679
24672
  var gzipAsync2 = promisify2(gzip2);
24680
24673
  var gunzipAsync2 = promisify2(gunzip2);
24681
24674
 
24682
- class RXCImpl {
24675
+ class RXPImpl {
24676
+ _files;
24677
+ _pathsCache = null;
24678
+ _treeCache = null;
24679
+ constructor(files) {
24680
+ this._files = files;
24681
+ }
24682
+ paths() {
24683
+ if (this._pathsCache) {
24684
+ return this._pathsCache;
24685
+ }
24686
+ this._pathsCache = Array.from(this._files.keys()).sort();
24687
+ return this._pathsCache;
24688
+ }
24689
+ tree() {
24690
+ if (this._treeCache) {
24691
+ return this._treeCache;
24692
+ }
24693
+ const root = new Map;
24694
+ for (const path of this._files.keys()) {
24695
+ const parts = path.split("/");
24696
+ let currentLevel = root;
24697
+ for (let i = 0;i < parts.length; i++) {
24698
+ const part = parts[i];
24699
+ const isFile = i === parts.length - 1;
24700
+ if (!currentLevel.has(part)) {
24701
+ const treeNode2 = {
24702
+ node: {
24703
+ name: part,
24704
+ type: isFile ? "file" : "directory",
24705
+ children: isFile ? undefined : []
24706
+ },
24707
+ children: new Map
24708
+ };
24709
+ currentLevel.set(part, treeNode2);
24710
+ }
24711
+ const treeNode = currentLevel.get(part);
24712
+ if (!isFile) {
24713
+ currentLevel = treeNode.children;
24714
+ }
24715
+ }
24716
+ }
24717
+ const convertToPathNodes = (level) => {
24718
+ return Array.from(level.values()).map((treeNode) => {
24719
+ if (treeNode.node.type === "directory" && treeNode.children.size > 0) {
24720
+ treeNode.node.children = convertToPathNodes(treeNode.children);
24721
+ }
24722
+ return treeNode.node;
24723
+ });
24724
+ };
24725
+ this._treeCache = convertToPathNodes(root);
24726
+ return this._treeCache;
24727
+ }
24728
+ async file(path) {
24729
+ const content = this._files.get(path);
24730
+ if (!content) {
24731
+ throw new ContentError(`file not found: ${path}`);
24732
+ }
24733
+ return content;
24734
+ }
24735
+ async files() {
24736
+ return new Map(this._files);
24737
+ }
24738
+ async pack() {
24739
+ const filesRecord = {};
24740
+ for (const [path, content] of this._files) {
24741
+ filesRecord[path] = content;
24742
+ }
24743
+ return createRXA(filesRecord);
24744
+ }
24745
+ }
24746
+
24747
+ class RXAImpl {
24683
24748
  _buffer;
24684
- _filesCache = null;
24749
+ _rxpCache = null;
24685
24750
  constructor(buffer) {
24686
24751
  this._buffer = buffer;
24687
24752
  }
@@ -24697,17 +24762,9 @@ class RXCImpl {
24697
24762
  async buffer() {
24698
24763
  return this._buffer;
24699
24764
  }
24700
- async file(path) {
24701
- const filesMap = await this.files();
24702
- const content = filesMap.get(path);
24703
- if (!content) {
24704
- throw new ContentError(`file not found: ${path}`);
24705
- }
24706
- return content;
24707
- }
24708
- async files() {
24709
- if (this._filesCache) {
24710
- return this._filesCache;
24765
+ async extract() {
24766
+ if (this._rxpCache) {
24767
+ return this._rxpCache;
24711
24768
  }
24712
24769
  const tarBuffer = await gunzipAsync2(this._buffer);
24713
24770
  const entries = await unpackTar(tarBuffer);
@@ -24717,16 +24774,16 @@ class RXCImpl {
24717
24774
  filesMap.set(entry.header.name, Buffer.from(entry.data));
24718
24775
  }
24719
24776
  }
24720
- this._filesCache = filesMap;
24721
- return filesMap;
24777
+ this._rxpCache = new RXPImpl(filesMap);
24778
+ return this._rxpCache;
24722
24779
  }
24723
24780
  }
24724
- function isArchiveInput(input) {
24725
- return "archive" in input && Buffer.isBuffer(input.archive);
24781
+ function isBufferInput(input) {
24782
+ return "buffer" in input && Buffer.isBuffer(input.buffer);
24726
24783
  }
24727
- async function createRXC(input) {
24728
- if (isArchiveInput(input)) {
24729
- return new RXCImpl(input.archive);
24784
+ async function createRXA(input) {
24785
+ if (isBufferInput(input)) {
24786
+ return new RXAImpl(input.buffer);
24730
24787
  }
24731
24788
  const entries = Object.entries(input).map(([name, content]) => {
24732
24789
  const body = typeof content === "string" ? content : content instanceof Uint8Array ? content : new Uint8Array(content);
@@ -24738,7 +24795,7 @@ async function createRXC(input) {
24738
24795
  });
24739
24796
  const tarBuffer = await packTar(entries);
24740
24797
  const gzipBuffer = await gzipAsync2(Buffer.from(tarBuffer));
24741
- return new RXCImpl(gzipBuffer);
24798
+ return new RXAImpl(gzipBuffer);
24742
24799
  }
24743
24800
 
24744
24801
  class ResourceTypeError extends ResourceXError2 {
@@ -24749,13 +24806,13 @@ class ResourceTypeError extends ResourceXError2 {
24749
24806
  }
24750
24807
  var textSerializer = {
24751
24808
  async serialize(rxr) {
24752
- return rxr.content.buffer();
24809
+ return rxr.archive.buffer();
24753
24810
  },
24754
24811
  async deserialize(data, manifest) {
24755
24812
  return {
24756
24813
  locator: parseRXL2(manifest.toLocator()),
24757
24814
  manifest,
24758
- content: await createRXC({ archive: data })
24815
+ archive: await createRXA({ buffer: data })
24759
24816
  };
24760
24817
  }
24761
24818
  };
@@ -24766,7 +24823,8 @@ var textResolver = {
24766
24823
  resource: rxr,
24767
24824
  schema: undefined,
24768
24825
  execute: async () => {
24769
- const buffer = await rxr.content.file("content");
24826
+ const pkg = await rxr.archive.extract();
24827
+ const buffer = await pkg.file("content");
24770
24828
  return buffer.toString("utf-8");
24771
24829
  }
24772
24830
  };
@@ -24781,13 +24839,13 @@ var textType = {
24781
24839
  };
24782
24840
  var jsonSerializer = {
24783
24841
  async serialize(rxr) {
24784
- return rxr.content.buffer();
24842
+ return rxr.archive.buffer();
24785
24843
  },
24786
24844
  async deserialize(data, manifest) {
24787
24845
  return {
24788
24846
  locator: parseRXL2(manifest.toLocator()),
24789
24847
  manifest,
24790
- content: await createRXC({ archive: data })
24848
+ archive: await createRXA({ buffer: data })
24791
24849
  };
24792
24850
  }
24793
24851
  };
@@ -24798,7 +24856,8 @@ var jsonResolver = {
24798
24856
  resource: rxr,
24799
24857
  schema: undefined,
24800
24858
  execute: async () => {
24801
- const buffer = await rxr.content.file("content");
24859
+ const pkg = await rxr.archive.extract();
24860
+ const buffer = await pkg.file("content");
24802
24861
  return JSON.parse(buffer.toString("utf-8"));
24803
24862
  }
24804
24863
  };
@@ -24813,13 +24872,13 @@ var jsonType = {
24813
24872
  };
24814
24873
  var binarySerializer = {
24815
24874
  async serialize(rxr) {
24816
- return rxr.content.buffer();
24875
+ return rxr.archive.buffer();
24817
24876
  },
24818
24877
  async deserialize(data, manifest) {
24819
24878
  return {
24820
24879
  locator: parseRXL2(manifest.toLocator()),
24821
24880
  manifest,
24822
- content: await createRXC({ archive: data })
24881
+ archive: await createRXA({ buffer: data })
24823
24882
  };
24824
24883
  }
24825
24884
  };
@@ -24830,7 +24889,8 @@ var binaryResolver = {
24830
24889
  resource: rxr,
24831
24890
  schema: undefined,
24832
24891
  execute: async () => {
24833
- return rxr.content.file("content");
24892
+ const pkg = await rxr.archive.extract();
24893
+ return pkg.file("content");
24834
24894
  }
24835
24895
  };
24836
24896
  }
@@ -26563,9 +26623,81 @@ async function unpackTar2(archive, options = {}) {
26563
26623
  var gzipAsync3 = promisify3(gzip3);
26564
26624
  var gunzipAsync3 = promisify3(gunzip3);
26565
26625
 
26566
- class RXCImpl2 {
26626
+ class RXPImpl2 {
26627
+ _files;
26628
+ _pathsCache = null;
26629
+ _treeCache = null;
26630
+ constructor(files) {
26631
+ this._files = files;
26632
+ }
26633
+ paths() {
26634
+ if (this._pathsCache) {
26635
+ return this._pathsCache;
26636
+ }
26637
+ this._pathsCache = Array.from(this._files.keys()).sort();
26638
+ return this._pathsCache;
26639
+ }
26640
+ tree() {
26641
+ if (this._treeCache) {
26642
+ return this._treeCache;
26643
+ }
26644
+ const root = new Map;
26645
+ for (const path of this._files.keys()) {
26646
+ const parts = path.split("/");
26647
+ let currentLevel = root;
26648
+ for (let i = 0;i < parts.length; i++) {
26649
+ const part = parts[i];
26650
+ const isFile = i === parts.length - 1;
26651
+ if (!currentLevel.has(part)) {
26652
+ const treeNode2 = {
26653
+ node: {
26654
+ name: part,
26655
+ type: isFile ? "file" : "directory",
26656
+ children: isFile ? undefined : []
26657
+ },
26658
+ children: new Map
26659
+ };
26660
+ currentLevel.set(part, treeNode2);
26661
+ }
26662
+ const treeNode = currentLevel.get(part);
26663
+ if (!isFile) {
26664
+ currentLevel = treeNode.children;
26665
+ }
26666
+ }
26667
+ }
26668
+ const convertToPathNodes = (level) => {
26669
+ return Array.from(level.values()).map((treeNode) => {
26670
+ if (treeNode.node.type === "directory" && treeNode.children.size > 0) {
26671
+ treeNode.node.children = convertToPathNodes(treeNode.children);
26672
+ }
26673
+ return treeNode.node;
26674
+ });
26675
+ };
26676
+ this._treeCache = convertToPathNodes(root);
26677
+ return this._treeCache;
26678
+ }
26679
+ async file(path) {
26680
+ const content = this._files.get(path);
26681
+ if (!content) {
26682
+ throw new ContentError2(`file not found: ${path}`);
26683
+ }
26684
+ return content;
26685
+ }
26686
+ async files() {
26687
+ return new Map(this._files);
26688
+ }
26689
+ async pack() {
26690
+ const filesRecord = {};
26691
+ for (const [path, content] of this._files) {
26692
+ filesRecord[path] = content;
26693
+ }
26694
+ return createRXA2(filesRecord);
26695
+ }
26696
+ }
26697
+
26698
+ class RXAImpl2 {
26567
26699
  _buffer;
26568
- _filesCache = null;
26700
+ _rxpCache = null;
26569
26701
  constructor(buffer) {
26570
26702
  this._buffer = buffer;
26571
26703
  }
@@ -26581,17 +26713,9 @@ class RXCImpl2 {
26581
26713
  async buffer() {
26582
26714
  return this._buffer;
26583
26715
  }
26584
- async file(path) {
26585
- const filesMap = await this.files();
26586
- const content = filesMap.get(path);
26587
- if (!content) {
26588
- throw new ContentError2(`file not found: ${path}`);
26589
- }
26590
- return content;
26591
- }
26592
- async files() {
26593
- if (this._filesCache) {
26594
- return this._filesCache;
26716
+ async extract() {
26717
+ if (this._rxpCache) {
26718
+ return this._rxpCache;
26595
26719
  }
26596
26720
  const tarBuffer = await gunzipAsync3(this._buffer);
26597
26721
  const entries = await unpackTar2(tarBuffer);
@@ -26601,16 +26725,16 @@ class RXCImpl2 {
26601
26725
  filesMap.set(entry.header.name, Buffer.from(entry.data));
26602
26726
  }
26603
26727
  }
26604
- this._filesCache = filesMap;
26605
- return filesMap;
26728
+ this._rxpCache = new RXPImpl2(filesMap);
26729
+ return this._rxpCache;
26606
26730
  }
26607
26731
  }
26608
- function isArchiveInput2(input) {
26609
- return "archive" in input && Buffer.isBuffer(input.archive);
26732
+ function isBufferInput2(input) {
26733
+ return "buffer" in input && Buffer.isBuffer(input.buffer);
26610
26734
  }
26611
- async function createRXC2(input) {
26612
- if (isArchiveInput2(input)) {
26613
- return new RXCImpl2(input.archive);
26735
+ async function createRXA2(input) {
26736
+ if (isBufferInput2(input)) {
26737
+ return new RXAImpl2(input.buffer);
26614
26738
  }
26615
26739
  const entries = Object.entries(input).map(([name, content]) => {
26616
26740
  const body = typeof content === "string" ? content : content instanceof Uint8Array ? content : new Uint8Array(content);
@@ -26622,7 +26746,7 @@ async function createRXC2(input) {
26622
26746
  });
26623
26747
  const tarBuffer = await packTar2(entries);
26624
26748
  const gzipBuffer = await gzipAsync3(Buffer.from(tarBuffer));
26625
- return new RXCImpl2(gzipBuffer);
26749
+ return new RXAImpl2(gzipBuffer);
26626
26750
  }
26627
26751
 
26628
26752
  class FolderLoader {
@@ -26673,12 +26797,12 @@ class FolderLoader {
26673
26797
  if (Object.keys(files).length === 0) {
26674
26798
  throw new ResourceXError3("No content files found in resource folder");
26675
26799
  }
26676
- const content = await createRXC2(files);
26800
+ const archive = await createRXA2(files);
26677
26801
  const locator = parseRXL3(manifest.toLocator());
26678
26802
  return {
26679
26803
  locator,
26680
26804
  manifest,
26681
- content
26805
+ archive
26682
26806
  };
26683
26807
  }
26684
26808
  async readFolderFiles(folderPath, basePath = folderPath) {
@@ -26786,7 +26910,7 @@ class LocalRegistry {
26786
26910
  const manifestContent = manifestResource.content.toString("utf-8");
26787
26911
  const manifestData = JSON.parse(manifestContent);
26788
26912
  const manifest = createRXM(manifestData);
26789
- const contentPath = join3(resourcePath, "content.tar.gz");
26913
+ const contentPath = join3(resourcePath, "archive.tar.gz");
26790
26914
  const contentArl = this.arp.parse(this.toArpUrl(contentPath));
26791
26915
  const contentResource = await contentArl.resolve();
26792
26916
  const data = contentResource.content;
@@ -26822,7 +26946,7 @@ class LocalRegistry {
26822
26946
  const manifestArl = this.arp.parse(this.toArpUrl(manifestPath));
26823
26947
  const manifestContent = Buffer.from(JSON.stringify(resource.manifest.toJSON(), null, 2), "utf-8");
26824
26948
  await manifestArl.deposit(manifestContent);
26825
- const contentPath = join3(resourcePath, "content.tar.gz");
26949
+ const contentPath = join3(resourcePath, "archive.tar.gz");
26826
26950
  const contentArl = this.arp.parse(this.toArpUrl(contentPath));
26827
26951
  const serialized = await this.typeHandler.serialize(resource);
26828
26952
  await contentArl.deposit(serialized);
@@ -26966,6 +27090,12 @@ class LocalRegistry {
26966
27090
  }
26967
27091
  // src/RemoteRegistry.ts
26968
27092
  class RemoteRegistry {
27093
+ static canHandle(url) {
27094
+ return url.startsWith("https://") || url.startsWith("http://");
27095
+ }
27096
+ static create(config) {
27097
+ return new RemoteRegistry({ endpoint: config.url });
27098
+ }
26969
27099
  endpoint;
26970
27100
  typeHandler;
26971
27101
  constructor(config) {
@@ -27061,6 +27191,20 @@ async function discoverRegistry(domain) {
27061
27191
  throw new RegistryError(`Failed to discover registry for ${domain}: ${error.message}`);
27062
27192
  }
27063
27193
  }
27194
+ // src/types.ts
27195
+ function isUrlConfig(config) {
27196
+ return config !== undefined && "url" in config && !("type" in config) && !("endpoint" in config);
27197
+ }
27198
+ function isRemoteConfig(config) {
27199
+ return config !== undefined && "endpoint" in config;
27200
+ }
27201
+ function isGitConfig(config) {
27202
+ return config !== undefined && "type" in config && config.type === "git";
27203
+ }
27204
+ function isGitHubConfig(config) {
27205
+ return config !== undefined && "type" in config && config.type === "github";
27206
+ }
27207
+
27064
27208
  // src/GitRegistry.ts
27065
27209
  var import_isomorphic_git = __toESM(require_isomorphic_git(), 1);
27066
27210
  import { homedir as homedir2 } from "node:os";
@@ -27224,6 +27368,71 @@ async function request({
27224
27368
  var index = { request };
27225
27369
  var node_default = index;
27226
27370
 
27371
+ // src/middleware/RegistryMiddleware.ts
27372
+ class RegistryMiddleware {
27373
+ inner;
27374
+ constructor(inner) {
27375
+ this.inner = inner;
27376
+ }
27377
+ supportType(type) {
27378
+ this.inner.supportType(type);
27379
+ }
27380
+ link(path) {
27381
+ return this.inner.link(path);
27382
+ }
27383
+ add(source) {
27384
+ return this.inner.add(source);
27385
+ }
27386
+ pull(locator, options) {
27387
+ return this.inner.pull(locator, options);
27388
+ }
27389
+ publish(source, options) {
27390
+ return this.inner.publish(source, options);
27391
+ }
27392
+ get(locator) {
27393
+ return this.inner.get(locator);
27394
+ }
27395
+ resolve(locator) {
27396
+ return this.inner.resolve(locator);
27397
+ }
27398
+ exists(locator) {
27399
+ return this.inner.exists(locator);
27400
+ }
27401
+ delete(locator) {
27402
+ return this.inner.delete(locator);
27403
+ }
27404
+ search(options) {
27405
+ return this.inner.search(options);
27406
+ }
27407
+ }
27408
+
27409
+ // src/middleware/DomainValidation.ts
27410
+ class DomainValidation extends RegistryMiddleware {
27411
+ trustedDomain;
27412
+ constructor(inner, trustedDomain) {
27413
+ super(inner);
27414
+ this.trustedDomain = trustedDomain;
27415
+ }
27416
+ validateDomain(rxr) {
27417
+ if (rxr.manifest.domain !== this.trustedDomain) {
27418
+ throw new RegistryError(`Untrusted domain: resource claims "${rxr.manifest.domain}" but registry only trusts "${this.trustedDomain}"`);
27419
+ }
27420
+ }
27421
+ async get(locator) {
27422
+ const rxr = await this.inner.get(locator);
27423
+ this.validateDomain(rxr);
27424
+ return rxr;
27425
+ }
27426
+ async resolve(locator) {
27427
+ const rxr = await this.inner.get(locator);
27428
+ this.validateDomain(rxr);
27429
+ return this.inner.resolve(locator);
27430
+ }
27431
+ }
27432
+ function withDomainValidation(registry, trustedDomain) {
27433
+ return new DomainValidation(registry, trustedDomain);
27434
+ }
27435
+
27227
27436
  // src/GitRegistry.ts
27228
27437
  var DEFAULT_GIT_CACHE = `${homedir2()}/.resourcex/.git-cache`;
27229
27438
  var MAX_RETRIES = 2;
@@ -27232,6 +27441,19 @@ function isLocalPath(url) {
27232
27441
  }
27233
27442
 
27234
27443
  class GitRegistry {
27444
+ static canHandle(url) {
27445
+ return url.startsWith("git@") || url.endsWith(".git");
27446
+ }
27447
+ static create(config) {
27448
+ const registry = new GitRegistry({
27449
+ type: "git",
27450
+ url: config.url,
27451
+ ref: config.ref,
27452
+ basePath: config.basePath,
27453
+ domain: config.domain
27454
+ });
27455
+ return config.domain ? withDomainValidation(registry, config.domain) : registry;
27456
+ }
27235
27457
  url;
27236
27458
  ref;
27237
27459
  basePath;
@@ -27364,7 +27586,7 @@ class GitRegistry {
27364
27586
  const manifestContent = manifestResource.content.toString("utf-8");
27365
27587
  const manifestData = JSON.parse(manifestContent);
27366
27588
  const manifest = createRXM(manifestData);
27367
- const contentPath = join4(resourcePath, "content.tar.gz");
27589
+ const contentPath = join4(resourcePath, "archive.tar.gz");
27368
27590
  const contentArl = this.arp.parse(this.toArpUrl(contentPath));
27369
27591
  const contentResource = await contentArl.resolve();
27370
27592
  const data = contentResource.content;
@@ -27465,109 +27687,333 @@ class GitRegistry {
27465
27687
  throw new RegistryError("GitRegistry is read-only - use LocalRegistry.delete()");
27466
27688
  }
27467
27689
  }
27468
- // src/middleware/RegistryMiddleware.ts
27469
- class RegistryMiddleware {
27470
- inner;
27471
- constructor(inner) {
27472
- this.inner = inner;
27690
+
27691
+ // src/GitHubRegistry.ts
27692
+ import { homedir as homedir3 } from "node:os";
27693
+ import { join as join5 } from "node:path";
27694
+ import { gunzipSync } from "node:zlib";
27695
+ var DEFAULT_GITHUB_CACHE = `${homedir3()}/.resourcex/.github-cache`;
27696
+ function parseGitHubUrl(url) {
27697
+ if (!url.startsWith("https://github.com/")) {
27698
+ throw new RegistryError(`Invalid GitHub URL: ${url}. Expected format: https://github.com/owner/repo`);
27699
+ }
27700
+ const path = url.slice("https://github.com/".length);
27701
+ const parts = path.split("/");
27702
+ if (parts.length < 2) {
27703
+ throw new RegistryError(`Invalid GitHub URL: ${url}. Expected format: https://github.com/owner/repo`);
27704
+ }
27705
+ const owner = parts[0];
27706
+ const repo = parts[1];
27707
+ let branch = "main";
27708
+ if (parts.length >= 4 && parts[2] === "tree") {
27709
+ branch = parts[3];
27710
+ }
27711
+ return { owner, repo, branch };
27712
+ }
27713
+ function isGitHubUrl(url) {
27714
+ return url.startsWith("https://github.com/");
27715
+ }
27716
+
27717
+ class GitHubRegistry {
27718
+ static canHandle(url) {
27719
+ return isGitHubUrl(url);
27720
+ }
27721
+ static create(config) {
27722
+ const registry = new GitHubRegistry({
27723
+ url: config.url,
27724
+ ref: config.ref,
27725
+ basePath: config.basePath
27726
+ });
27727
+ return config.domain ? withDomainValidation(registry, config.domain) : registry;
27473
27728
  }
27474
- supportType(type) {
27475
- this.inner.supportType(type);
27729
+ url;
27730
+ owner;
27731
+ repo;
27732
+ ref;
27733
+ basePath;
27734
+ cacheDir;
27735
+ typeHandler;
27736
+ arp;
27737
+ tarballDownloaded = false;
27738
+ constructor(config) {
27739
+ this.url = config.url;
27740
+ const parsed = parseGitHubUrl(config.url);
27741
+ this.owner = parsed.owner;
27742
+ this.repo = parsed.repo;
27743
+ this.ref = config.ref ?? parsed.branch;
27744
+ this.basePath = config.basePath ?? ".resourcex";
27745
+ this.typeHandler = TypeHandlerChain.create();
27746
+ this.arp = createARP();
27747
+ this.cacheDir = this.buildCacheDir();
27476
27748
  }
27477
- link(path) {
27478
- return this.inner.link(path);
27749
+ buildCacheDir() {
27750
+ const dirName = `github.com-${this.owner}-${this.repo}`;
27751
+ return join5(DEFAULT_GITHUB_CACHE, dirName);
27479
27752
  }
27480
- add(source) {
27481
- return this.inner.add(source);
27753
+ supportType(type) {
27754
+ this.typeHandler.register(type);
27482
27755
  }
27483
- pull(locator, options) {
27484
- return this.inner.pull(locator, options);
27756
+ toArpUrl(filePath) {
27757
+ return `arp:binary:file://${filePath}`;
27485
27758
  }
27486
- publish(source, options) {
27487
- return this.inner.publish(source, options);
27759
+ getTarballUrl() {
27760
+ return `https://github.com/${this.owner}/${this.repo}/archive/refs/heads/${this.ref}.tar.gz`;
27488
27761
  }
27489
- get(locator) {
27490
- return this.inner.get(locator);
27762
+ async ensureDownloaded() {
27763
+ if (this.tarballDownloaded) {
27764
+ return;
27765
+ }
27766
+ const tarballUrl = this.getTarballUrl();
27767
+ const response = await fetch(tarballUrl, {
27768
+ headers: {
27769
+ "User-Agent": "ResourceX/1.0"
27770
+ }
27771
+ });
27772
+ if (!response.ok) {
27773
+ throw new RegistryError(`Failed to download tarball from ${tarballUrl}: ${response.status} ${response.statusText}`);
27774
+ }
27775
+ const tarballBuffer = Buffer.from(await response.arrayBuffer());
27776
+ await this.extractTarball(tarballBuffer);
27777
+ this.tarballDownloaded = true;
27778
+ }
27779
+ async extractTarball(tarballBuffer) {
27780
+ const tarBuffer = gunzipSync(tarballBuffer);
27781
+ const files = this.parseTar(tarBuffer);
27782
+ const cacheArl = this.arp.parse(this.toArpUrl(this.cacheDir));
27783
+ await cacheArl.mkdir();
27784
+ for (const [path, content] of files) {
27785
+ const parts = path.split("/");
27786
+ if (parts.length < 2)
27787
+ continue;
27788
+ const relativePath = parts.slice(1).join("/");
27789
+ if (!relativePath)
27790
+ continue;
27791
+ const fullPath = join5(this.cacheDir, relativePath);
27792
+ const parentDir = fullPath.substring(0, fullPath.lastIndexOf("/"));
27793
+ if (parentDir) {
27794
+ const parentArl = this.arp.parse(this.toArpUrl(parentDir));
27795
+ await parentArl.mkdir();
27796
+ }
27797
+ const fileArl = this.arp.parse(this.toArpUrl(fullPath));
27798
+ await fileArl.deposit(content);
27799
+ }
27491
27800
  }
27492
- resolve(locator) {
27493
- return this.inner.resolve(locator);
27801
+ parseTar(tarBuffer) {
27802
+ const files = new Map;
27803
+ let offset = 0;
27804
+ while (offset < tarBuffer.length) {
27805
+ const header = tarBuffer.subarray(offset, offset + 512);
27806
+ if (header[0] === 0) {
27807
+ break;
27808
+ }
27809
+ let filename = "";
27810
+ for (let i = 0;i < 100 && header[i] !== 0; i++) {
27811
+ filename += String.fromCharCode(header[i]);
27812
+ }
27813
+ let prefix = "";
27814
+ for (let i = 345;i < 500 && header[i] !== 0; i++) {
27815
+ prefix += String.fromCharCode(header[i]);
27816
+ }
27817
+ if (prefix) {
27818
+ filename = prefix + "/" + filename;
27819
+ }
27820
+ let sizeStr = "";
27821
+ for (let i = 124;i < 136 && header[i] !== 0 && header[i] !== 32; i++) {
27822
+ sizeStr += String.fromCharCode(header[i]);
27823
+ }
27824
+ const size = parseInt(sizeStr, 8) || 0;
27825
+ const typeFlag = header[156];
27826
+ offset += 512;
27827
+ if (typeFlag === 48 || typeFlag === 0) {
27828
+ if (size > 0) {
27829
+ const content = tarBuffer.subarray(offset, offset + size);
27830
+ files.set(filename, Buffer.from(content));
27831
+ }
27832
+ }
27833
+ offset += Math.ceil(size / 512) * 512;
27834
+ }
27835
+ return files;
27494
27836
  }
27495
- exists(locator) {
27496
- return this.inner.exists(locator);
27837
+ buildResourcePath(locator) {
27838
+ const rxl = parseRXL(locator);
27839
+ const domain = rxl.domain ?? "localhost";
27840
+ const version = rxl.version ?? "latest";
27841
+ let path = join5(this.cacheDir, this.basePath, domain);
27842
+ if (rxl.path) {
27843
+ path = join5(path, rxl.path);
27844
+ }
27845
+ const resourceName = rxl.type ? `${rxl.name}.${rxl.type}` : rxl.name;
27846
+ return join5(path, resourceName, version);
27497
27847
  }
27498
- delete(locator) {
27499
- return this.inner.delete(locator);
27848
+ async get(locator) {
27849
+ await this.ensureDownloaded();
27850
+ const resourcePath = this.buildResourcePath(locator);
27851
+ const manifestPath = join5(resourcePath, "manifest.json");
27852
+ const manifestArl = this.arp.parse(this.toArpUrl(manifestPath));
27853
+ if (!await manifestArl.exists()) {
27854
+ throw new RegistryError(`Resource not found: ${locator}`);
27855
+ }
27856
+ const manifestResource = await manifestArl.resolve();
27857
+ const manifestContent = manifestResource.content.toString("utf-8");
27858
+ const manifestData = JSON.parse(manifestContent);
27859
+ const manifest = createRXM(manifestData);
27860
+ const contentPath = join5(resourcePath, "archive.tar.gz");
27861
+ const contentArl = this.arp.parse(this.toArpUrl(contentPath));
27862
+ const contentResource = await contentArl.resolve();
27863
+ const data = contentResource.content;
27864
+ return this.typeHandler.deserialize(data, manifest);
27500
27865
  }
27501
- search(options) {
27502
- return this.inner.search(options);
27866
+ async resolve(locator) {
27867
+ const rxr = await this.get(locator);
27868
+ return this.typeHandler.resolve(rxr);
27503
27869
  }
27504
- }
27505
-
27506
- // src/middleware/DomainValidation.ts
27507
- class DomainValidation extends RegistryMiddleware {
27508
- trustedDomain;
27509
- constructor(inner, trustedDomain) {
27510
- super(inner);
27511
- this.trustedDomain = trustedDomain;
27870
+ async exists(locator) {
27871
+ try {
27872
+ await this.ensureDownloaded();
27873
+ const resourcePath = this.buildResourcePath(locator);
27874
+ const manifestPath = join5(resourcePath, "manifest.json");
27875
+ const manifestArl = this.arp.parse(this.toArpUrl(manifestPath));
27876
+ return await manifestArl.exists();
27877
+ } catch {
27878
+ return false;
27879
+ }
27512
27880
  }
27513
- validateDomain(rxr) {
27514
- if (rxr.manifest.domain !== this.trustedDomain) {
27515
- throw new RegistryError(`Untrusted domain: resource claims "${rxr.manifest.domain}" but registry only trusts "${this.trustedDomain}"`);
27881
+ async search(options) {
27882
+ await this.ensureDownloaded();
27883
+ const { query, limit, offset = 0 } = options ?? {};
27884
+ const locators = [];
27885
+ const baseDir = join5(this.cacheDir, this.basePath);
27886
+ try {
27887
+ const baseArl = this.arp.parse(this.toArpUrl(baseDir));
27888
+ const entries = await baseArl.list({ recursive: true, pattern: "*.json" });
27889
+ for (const entry of entries) {
27890
+ if (!entry.endsWith("manifest.json"))
27891
+ continue;
27892
+ const rxl = this.parseEntryToRXL(entry);
27893
+ if (rxl)
27894
+ locators.push(rxl);
27895
+ }
27896
+ } catch {
27897
+ return [];
27898
+ }
27899
+ let filtered = locators;
27900
+ if (query) {
27901
+ const lowerQuery = query.toLowerCase();
27902
+ filtered = locators.filter((rxl) => {
27903
+ const searchText = `${rxl.domain ?? ""} ${rxl.path ?? ""} ${rxl.name} ${rxl.type ?? ""}`.toLowerCase();
27904
+ return searchText.includes(lowerQuery);
27905
+ });
27906
+ }
27907
+ let result = filtered.slice(offset);
27908
+ if (limit !== undefined) {
27909
+ result = result.slice(0, limit);
27516
27910
  }
27911
+ return result;
27517
27912
  }
27518
- async get(locator) {
27519
- const rxr = await this.inner.get(locator);
27520
- this.validateDomain(rxr);
27521
- return rxr;
27913
+ parseEntryToRXL(entry) {
27914
+ const dirPath = entry.replace(/[/\\]manifest\.json$/, "");
27915
+ const parts = dirPath.split(/[/\\]/);
27916
+ if (parts.length < 3)
27917
+ return null;
27918
+ const version = parts.pop();
27919
+ const nameTypePart = parts.pop();
27920
+ const domain = parts.shift();
27921
+ const path = parts.length > 0 ? parts.join("/") : undefined;
27922
+ const dotIndex = nameTypePart.lastIndexOf(".");
27923
+ let name;
27924
+ let type;
27925
+ if (dotIndex !== -1) {
27926
+ name = nameTypePart.substring(0, dotIndex);
27927
+ type = nameTypePart.substring(dotIndex + 1);
27928
+ } else {
27929
+ name = nameTypePart;
27930
+ type = undefined;
27931
+ }
27932
+ let locatorStr = domain;
27933
+ if (path)
27934
+ locatorStr += `/${path}`;
27935
+ locatorStr += `/${name}`;
27936
+ if (type)
27937
+ locatorStr += `.${type}`;
27938
+ locatorStr += `@${version}`;
27939
+ try {
27940
+ return parseRXL(locatorStr);
27941
+ } catch {
27942
+ return null;
27943
+ }
27522
27944
  }
27523
- async resolve(locator) {
27524
- const rxr = await this.inner.get(locator);
27525
- this.validateDomain(rxr);
27526
- return this.inner.resolve(locator);
27945
+ async link(_path) {
27946
+ throw new RegistryError("GitHubRegistry is read-only - use LocalRegistry.link()");
27947
+ }
27948
+ async add(_source) {
27949
+ throw new RegistryError("GitHubRegistry is read-only - use LocalRegistry.add()");
27950
+ }
27951
+ async pull(_locator, _options) {
27952
+ throw new RegistryError("GitHubRegistry is read-only - use LocalRegistry.pull()");
27953
+ }
27954
+ async publish(_source, _options) {
27955
+ throw new RegistryError("GitHubRegistry is read-only - use LocalRegistry.publish()");
27956
+ }
27957
+ async delete(_locator) {
27958
+ throw new RegistryError("GitHubRegistry is read-only - use LocalRegistry.delete()");
27527
27959
  }
27528
- }
27529
- function withDomainValidation(registry, trustedDomain) {
27530
- return new DomainValidation(registry, trustedDomain);
27531
27960
  }
27532
27961
 
27533
27962
  // src/createRegistry.ts
27534
- function isRemoteGitUrl(url) {
27963
+ var URL_HANDLERS = [
27964
+ GitHubRegistry,
27965
+ GitRegistry,
27966
+ RemoteRegistry
27967
+ ];
27968
+ function isRemoteUrl(url) {
27535
27969
  return url.startsWith("git@") || url.startsWith("https://") || url.startsWith("http://");
27536
27970
  }
27537
27971
  function createRegistry(config) {
27538
27972
  if (isRemoteConfig(config)) {
27539
27973
  return new RemoteRegistry(config);
27540
27974
  }
27975
+ if (isGitHubConfig(config)) {
27976
+ const registry = new GitHubRegistry(config);
27977
+ return config.domain ? withDomainValidation(registry, config.domain) : registry;
27978
+ }
27541
27979
  if (isGitConfig(config)) {
27542
- if (isRemoteGitUrl(config.url) && !config.domain) {
27980
+ if (isRemoteUrl(config.url) && !config.domain) {
27543
27981
  throw new RegistryError(`Remote git registry requires a trusted domain.
27544
27982
 
27545
- ` + `Either:
27546
- ` + `1. Use discoverRegistry("your-domain.com") to auto-bind domain
27547
- ` + `2. Explicitly set domain: createRegistry({ type: "git", url: "...", domain: "your-domain.com" })
27548
-
27549
- ` + `This ensures resources from untrusted sources cannot impersonate your domain.`);
27550
- }
27551
- const gitRegistry = new GitRegistry(config);
27552
- if (config.domain) {
27553
- return withDomainValidation(gitRegistry, config.domain);
27983
+ ` + `Use discoverRegistry() or explicitly set domain:
27984
+ ` + `createRegistry({ type: "git", url: "...", domain: "your-domain.com" })`);
27554
27985
  }
27555
- return gitRegistry;
27986
+ const registry = new GitRegistry(config);
27987
+ return config.domain ? withDomainValidation(registry, config.domain) : registry;
27988
+ }
27989
+ if (isUrlConfig(config)) {
27990
+ return createFromUrl(config);
27556
27991
  }
27557
27992
  return new LocalRegistry(config);
27558
27993
  }
27994
+ function createFromUrl(config) {
27995
+ const { url, domain } = config;
27996
+ if (isRemoteUrl(url) && !domain) {
27997
+ throw new RegistryError(`Remote registry URL requires a trusted domain.
27998
+
27999
+ ` + `Use discoverRegistry() or explicitly set domain:
28000
+ ` + `createRegistry({ url: "...", domain: "your-domain.com" })`);
28001
+ }
28002
+ for (const Handler of URL_HANDLERS) {
28003
+ if (Handler.canHandle(url)) {
28004
+ return Handler.create(config);
28005
+ }
28006
+ }
28007
+ throw new RegistryError(`No handler found for URL: ${url}`);
28008
+ }
27559
28009
  export {
27560
28010
  withDomainValidation,
27561
- isRemoteConfig,
27562
- isGitConfig,
27563
28011
  discoverRegistry,
27564
28012
  createRegistry,
27565
- RemoteRegistry,
27566
28013
  RegistryMiddleware,
27567
28014
  RegistryError,
27568
28015
  LocalRegistry,
27569
- GitRegistry,
27570
28016
  DomainValidation
27571
28017
  };
27572
28018
 
27573
- //# debugId=FE38D995FCCB453564756E2164756E21
28019
+ //# debugId=4521F4F0C842396664756E2164756E21