@resourcexjs/cli 2.18.0 → 2.20.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
@@ -774,18 +774,394 @@ var init_prompt = __esm(() => {
774
774
  });
775
775
 
776
776
  // ../../packages/core/dist/index.js
777
- import { promisify } from "util";
778
- import { gzip } from "zlib";
779
- import { promisify as promisify2 } from "util";
780
- import { gunzip } from "zlib";
781
- import { readdir as readdir2, readFile as readFile2, stat as stat2 } from "fs/promises";
782
- import { join as join2, relative as relative2 } from "path";
783
- import { dirname, join as join3 } from "path";
784
- import { fileURLToPath, pathToFileURL } from "url";
785
- import { createHash } from "crypto";
786
777
  function __exportSetter2(name, newValue) {
787
778
  this[name] = __returnValue2.bind(null, newValue);
788
779
  }
780
+ function writeString(view, offset, size, value) {
781
+ if (value)
782
+ encoder.encodeInto(value, view.subarray(offset, offset + size));
783
+ }
784
+ function writeOctal(view, offset, size, value) {
785
+ if (value === undefined)
786
+ return;
787
+ const octalString = value.toString(8).padStart(size - 1, "0");
788
+ encoder.encodeInto(octalString, view.subarray(offset, offset + size - 1));
789
+ }
790
+ async function normalizeBody(body) {
791
+ if (body === null || body === undefined)
792
+ return EMPTY;
793
+ if (body instanceof Uint8Array)
794
+ return body;
795
+ if (typeof body === "string")
796
+ return encoder.encode(body);
797
+ if (body instanceof ArrayBuffer)
798
+ return new Uint8Array(body);
799
+ if (body instanceof Blob)
800
+ return new Uint8Array(await body.arrayBuffer());
801
+ throw new TypeError("Unsupported content type for entry body.");
802
+ }
803
+ function writeChecksum(block) {
804
+ block.fill(CHECKSUM_SPACE, USTAR_CHECKSUM_OFFSET, USTAR_CHECKSUM_OFFSET + USTAR_CHECKSUM_SIZE);
805
+ let checksum = 0;
806
+ for (const byte of block)
807
+ checksum += byte;
808
+ for (let i2 = USTAR_CHECKSUM_OFFSET + 6 - 1;i2 >= USTAR_CHECKSUM_OFFSET; i2--) {
809
+ block[i2] = (checksum & 7) + ASCII_ZERO;
810
+ checksum >>= 3;
811
+ }
812
+ block[USTAR_CHECKSUM_OFFSET + 6] = 0;
813
+ block[USTAR_CHECKSUM_OFFSET + 7] = CHECKSUM_SPACE;
814
+ }
815
+ function generatePax(header) {
816
+ const paxRecords = {};
817
+ if (header.name.length > USTAR_NAME_SIZE) {
818
+ if (findUstarSplit(header.name) === null)
819
+ paxRecords.path = header.name;
820
+ }
821
+ if (header.linkname && header.linkname.length > USTAR_NAME_SIZE)
822
+ paxRecords.linkpath = header.linkname;
823
+ if (header.uname && header.uname.length > USTAR_UNAME_SIZE)
824
+ paxRecords.uname = header.uname;
825
+ if (header.gname && header.gname.length > USTAR_GNAME_SIZE)
826
+ paxRecords.gname = header.gname;
827
+ if (header.uid != null && header.uid > USTAR_MAX_UID_GID)
828
+ paxRecords.uid = String(header.uid);
829
+ if (header.gid != null && header.gid > USTAR_MAX_UID_GID)
830
+ paxRecords.gid = String(header.gid);
831
+ if (header.size != null && header.size > USTAR_MAX_SIZE)
832
+ paxRecords.size = String(header.size);
833
+ if (header.pax)
834
+ Object.assign(paxRecords, header.pax);
835
+ const paxEntries = Object.entries(paxRecords);
836
+ if (paxEntries.length === 0)
837
+ return null;
838
+ const paxBody = encoder.encode(paxEntries.map(([key, value]) => {
839
+ const record = `${key}=${value}
840
+ `;
841
+ const partLength = encoder.encode(record).length + 1;
842
+ let totalLength = partLength + String(partLength).length;
843
+ totalLength = partLength + String(totalLength).length;
844
+ return `${totalLength} ${record}`;
845
+ }).join(""));
846
+ return {
847
+ paxHeader: createTarHeader({
848
+ name: decoder.decode(encoder.encode(`PaxHeader/${header.name}`).slice(0, 100)),
849
+ size: paxBody.length,
850
+ type: "pax-header",
851
+ mode: 420,
852
+ mtime: header.mtime,
853
+ uname: header.uname,
854
+ gname: header.gname,
855
+ uid: header.uid,
856
+ gid: header.gid
857
+ }),
858
+ paxBody
859
+ };
860
+ }
861
+ function findUstarSplit(path) {
862
+ if (path.length <= USTAR_NAME_SIZE)
863
+ return null;
864
+ const minSlashIndex = path.length - USTAR_NAME_SIZE - 1;
865
+ const slashIndex = path.lastIndexOf("/", USTAR_PREFIX_SIZE);
866
+ if (slashIndex > 0 && slashIndex >= minSlashIndex)
867
+ return {
868
+ prefix: path.slice(0, slashIndex),
869
+ name: path.slice(slashIndex + 1)
870
+ };
871
+ return null;
872
+ }
873
+ function createTarHeader(header) {
874
+ const view = new Uint8Array(BLOCK_SIZE);
875
+ const size = isBodyless(header) ? 0 : header.size ?? 0;
876
+ let name = header.name;
877
+ let prefix = "";
878
+ if (!header.pax?.path) {
879
+ const split = findUstarSplit(name);
880
+ if (split) {
881
+ name = split.name;
882
+ prefix = split.prefix;
883
+ }
884
+ }
885
+ writeString(view, USTAR_NAME_OFFSET, USTAR_NAME_SIZE, name);
886
+ writeOctal(view, USTAR_MODE_OFFSET, USTAR_MODE_SIZE, header.mode ?? (header.type === DIRECTORY ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE));
887
+ writeOctal(view, USTAR_UID_OFFSET, USTAR_UID_SIZE, header.uid ?? 0);
888
+ writeOctal(view, USTAR_GID_OFFSET, USTAR_GID_SIZE, header.gid ?? 0);
889
+ writeOctal(view, USTAR_SIZE_OFFSET, USTAR_SIZE_SIZE, size);
890
+ writeOctal(view, USTAR_MTIME_OFFSET, USTAR_MTIME_SIZE, Math.floor((header.mtime?.getTime() ?? Date.now()) / 1000));
891
+ writeString(view, USTAR_TYPEFLAG_OFFSET, USTAR_TYPEFLAG_SIZE, TYPEFLAG[header.type ?? FILE]);
892
+ writeString(view, USTAR_LINKNAME_OFFSET, USTAR_LINKNAME_SIZE, header.linkname);
893
+ writeString(view, USTAR_MAGIC_OFFSET, USTAR_MAGIC_SIZE, "ustar\x00");
894
+ writeString(view, USTAR_VERSION_OFFSET, USTAR_VERSION_SIZE, USTAR_VERSION);
895
+ writeString(view, USTAR_UNAME_OFFSET, USTAR_UNAME_SIZE, header.uname);
896
+ writeString(view, USTAR_GNAME_OFFSET, USTAR_GNAME_SIZE, header.gname);
897
+ writeString(view, USTAR_PREFIX_OFFSET, USTAR_PREFIX_SIZE, prefix);
898
+ writeChecksum(view);
899
+ return view;
900
+ }
901
+ function getHeaderBlocks(header) {
902
+ const base = createTarHeader(header);
903
+ const pax = generatePax(header);
904
+ if (!pax)
905
+ return [base];
906
+ const paxPadding = -pax.paxBody.length & BLOCK_SIZE_MASK;
907
+ const paddingBlocks = paxPadding > 0 ? [ZERO_BLOCK.subarray(0, paxPadding)] : [];
908
+ return [
909
+ pax.paxHeader,
910
+ pax.paxBody,
911
+ ...paddingBlocks,
912
+ base
913
+ ];
914
+ }
915
+ function createTarPacker(onData, onError, onFinalize) {
916
+ let currentHeader = null;
917
+ let bytesWritten = 0;
918
+ let finalized = false;
919
+ return {
920
+ add(header) {
921
+ if (finalized) {
922
+ const error = /* @__PURE__ */ new Error("No new tar entries after finalize.");
923
+ onError(error);
924
+ throw error;
925
+ }
926
+ if (currentHeader !== null) {
927
+ const error = /* @__PURE__ */ new Error("Previous entry must be completed before adding a new one");
928
+ onError(error);
929
+ throw error;
930
+ }
931
+ try {
932
+ const size = isBodyless(header) ? 0 : header.size ?? 0;
933
+ const headerBlocks = getHeaderBlocks({
934
+ ...header,
935
+ size
936
+ });
937
+ for (const block of headerBlocks)
938
+ onData(block);
939
+ currentHeader = {
940
+ ...header,
941
+ size
942
+ };
943
+ bytesWritten = 0;
944
+ } catch (error) {
945
+ onError(error);
946
+ }
947
+ },
948
+ write(chunk) {
949
+ if (!currentHeader) {
950
+ const error = /* @__PURE__ */ new Error("No active tar entry.");
951
+ onError(error);
952
+ throw error;
953
+ }
954
+ if (finalized) {
955
+ const error = /* @__PURE__ */ new Error("Cannot write data after finalize.");
956
+ onError(error);
957
+ throw error;
958
+ }
959
+ const newTotal = bytesWritten + chunk.length;
960
+ if (newTotal > currentHeader.size) {
961
+ const error = /* @__PURE__ */ new Error(`"${currentHeader.name}" exceeds given size of ${currentHeader.size} bytes.`);
962
+ onError(error);
963
+ throw error;
964
+ }
965
+ try {
966
+ bytesWritten = newTotal;
967
+ onData(chunk);
968
+ } catch (error) {
969
+ onError(error);
970
+ }
971
+ },
972
+ endEntry() {
973
+ if (!currentHeader) {
974
+ const error = /* @__PURE__ */ new Error("No active entry to end.");
975
+ onError(error);
976
+ throw error;
977
+ }
978
+ if (finalized) {
979
+ const error = /* @__PURE__ */ new Error("Cannot end entry after finalize.");
980
+ onError(error);
981
+ throw error;
982
+ }
983
+ try {
984
+ if (bytesWritten !== currentHeader.size) {
985
+ const error = /* @__PURE__ */ new Error(`Size mismatch for "${currentHeader.name}".`);
986
+ onError(error);
987
+ throw error;
988
+ }
989
+ const paddingSize = -currentHeader.size & BLOCK_SIZE_MASK;
990
+ if (paddingSize > 0)
991
+ onData(new Uint8Array(paddingSize));
992
+ currentHeader = null;
993
+ bytesWritten = 0;
994
+ } catch (error) {
995
+ onError(error);
996
+ throw error;
997
+ }
998
+ },
999
+ finalize() {
1000
+ if (finalized) {
1001
+ const error = /* @__PURE__ */ new Error("Archive has already been finalized");
1002
+ onError(error);
1003
+ throw error;
1004
+ }
1005
+ if (currentHeader !== null) {
1006
+ const error = /* @__PURE__ */ new Error("Cannot finalize while an entry is still active");
1007
+ onError(error);
1008
+ throw error;
1009
+ }
1010
+ try {
1011
+ onData(EOF_BUFFER);
1012
+ finalized = true;
1013
+ if (onFinalize)
1014
+ onFinalize();
1015
+ } catch (error) {
1016
+ onError(error);
1017
+ }
1018
+ }
1019
+ };
1020
+ }
1021
+ function createTarPacker2() {
1022
+ let streamController;
1023
+ let packer;
1024
+ return {
1025
+ readable: new ReadableStream({ start(controller) {
1026
+ streamController = controller;
1027
+ packer = createTarPacker(controller.enqueue.bind(controller), controller.error.bind(controller), controller.close.bind(controller));
1028
+ } }),
1029
+ controller: {
1030
+ add(header) {
1031
+ const bodyless = isBodyless(header);
1032
+ const h2 = { ...header };
1033
+ if (bodyless)
1034
+ h2.size = 0;
1035
+ packer.add(h2);
1036
+ if (bodyless)
1037
+ packer.endEntry();
1038
+ return new WritableStream({
1039
+ write(chunk) {
1040
+ packer.write(chunk);
1041
+ },
1042
+ close() {
1043
+ if (!bodyless)
1044
+ packer.endEntry();
1045
+ },
1046
+ abort(reason) {
1047
+ streamController.error(reason);
1048
+ }
1049
+ });
1050
+ },
1051
+ finalize() {
1052
+ packer.finalize();
1053
+ },
1054
+ error(err) {
1055
+ streamController.error(err);
1056
+ }
1057
+ }
1058
+ };
1059
+ }
1060
+ async function streamToBuffer(stream) {
1061
+ const chunks = [];
1062
+ const reader = stream.getReader();
1063
+ let totalLength = 0;
1064
+ try {
1065
+ while (true) {
1066
+ const { done, value } = await reader.read();
1067
+ if (done)
1068
+ break;
1069
+ chunks.push(value);
1070
+ totalLength += value.length;
1071
+ }
1072
+ const result = new Uint8Array(totalLength);
1073
+ let offset = 0;
1074
+ for (const chunk of chunks) {
1075
+ result.set(chunk, offset);
1076
+ offset += chunk.length;
1077
+ }
1078
+ return result;
1079
+ } finally {
1080
+ reader.releaseLock();
1081
+ }
1082
+ }
1083
+ async function packTar(entries) {
1084
+ const { readable, controller } = createTarPacker2();
1085
+ await (async () => {
1086
+ for (const entry of entries) {
1087
+ const entryStream = controller.add(entry.header);
1088
+ const body = "body" in entry ? entry.body : entry.data;
1089
+ if (!body) {
1090
+ await entryStream.close();
1091
+ continue;
1092
+ }
1093
+ if (body instanceof ReadableStream)
1094
+ await body.pipeTo(entryStream);
1095
+ else if (body instanceof Blob)
1096
+ await body.stream().pipeTo(entryStream);
1097
+ else
1098
+ try {
1099
+ const chunk = await normalizeBody(body);
1100
+ if (chunk.length > 0) {
1101
+ const writer = entryStream.getWriter();
1102
+ await writer.write(chunk);
1103
+ await writer.close();
1104
+ } else
1105
+ await entryStream.close();
1106
+ } catch {
1107
+ throw new TypeError(`Unsupported content type for entry "${entry.header.name}".`);
1108
+ }
1109
+ }
1110
+ })().then(() => controller.finalize()).catch((err) => controller.error(err));
1111
+ return new Uint8Array(await streamToBuffer(readable));
1112
+ }
1113
+ async function gzipCompress(data) {
1114
+ const cs = new CompressionStream("gzip");
1115
+ const writer = cs.writable.getWriter();
1116
+ writer.write(data);
1117
+ writer.close();
1118
+ const reader = cs.readable.getReader();
1119
+ const chunks = [];
1120
+ while (true) {
1121
+ const { done, value } = await reader.read();
1122
+ if (done)
1123
+ break;
1124
+ chunks.push(value);
1125
+ }
1126
+ const totalLength = chunks.reduce((sum, c3) => sum + c3.length, 0);
1127
+ const result = new Uint8Array(totalLength);
1128
+ let offset = 0;
1129
+ for (const chunk of chunks) {
1130
+ result.set(chunk, offset);
1131
+ offset += chunk.length;
1132
+ }
1133
+ return Buffer.from(result);
1134
+ }
1135
+
1136
+ class RXAImpl {
1137
+ _buffer;
1138
+ constructor(buffer) {
1139
+ this._buffer = buffer;
1140
+ }
1141
+ get stream() {
1142
+ const buffer = this._buffer;
1143
+ return new ReadableStream({
1144
+ start(controller) {
1145
+ controller.enqueue(new Uint8Array(buffer));
1146
+ controller.close();
1147
+ }
1148
+ });
1149
+ }
1150
+ async buffer() {
1151
+ return this._buffer;
1152
+ }
1153
+ }
1154
+ async function archive(files) {
1155
+ const entries = Object.entries(files).map(([name, content]) => {
1156
+ return {
1157
+ header: { name, size: content.length, type: "file" },
1158
+ body: new Uint8Array(content)
1159
+ };
1160
+ });
1161
+ const tarBuffer = await packTar(entries);
1162
+ const gzipBuffer = await gzipCompress(new Uint8Array(tarBuffer));
1163
+ return new RXAImpl(gzipBuffer);
1164
+ }
789
1165
  function $constructor(name, initializer, params) {
790
1166
  function init2(inst, def) {
791
1167
  if (!inst._zod) {
@@ -4473,72 +4849,215 @@ function bigint3(params) {
4473
4849
  function date4(params) {
4474
4850
  return _coercedDate(ZodDate, params);
4475
4851
  }
4476
-
4477
- class FolderSourceLoader {
4478
- async canLoad(source) {
4479
- try {
4480
- const stats = await stat2(source);
4481
- return stats.isDirectory();
4482
- } catch {
4483
- return false;
4484
- }
4852
+ function define(input) {
4853
+ if (input === null || typeof input !== "object") {
4854
+ throw new DefinitionError("definition must be an object");
4485
4855
  }
4486
- async load(source) {
4487
- const canLoad = await this.canLoad(source);
4488
- if (!canLoad) {
4489
- throw new ResourceXError(`Source is not a directory: ${source}`);
4490
- }
4491
- const files = await this.readFolderFiles(source);
4492
- return { source, files };
4856
+ let validated;
4857
+ try {
4858
+ validated = RXDSchema.parse(input);
4859
+ } catch (e2) {
4860
+ throw new DefinitionError(`Invalid definition: ${e2 instanceof Error ? e2.message : String(e2)}`);
4861
+ }
4862
+ const rxd = Object.assign(Object.create(null), {
4863
+ name: validated.name,
4864
+ type: validated.type,
4865
+ tag: validated.tag ?? undefined,
4866
+ registry: validated.registry,
4867
+ path: validated.path,
4868
+ description: validated.description,
4869
+ author: validated.author,
4870
+ license: validated.license,
4871
+ keywords: validated.keywords,
4872
+ repository: validated.repository
4873
+ });
4874
+ return rxd;
4875
+ }
4876
+ function format(rxi) {
4877
+ let result = "";
4878
+ if (rxi.registry) {
4879
+ result += `${rxi.registry}/`;
4880
+ }
4881
+ if (rxi.path) {
4882
+ result += `${rxi.path}/`;
4883
+ }
4884
+ result += rxi.name;
4885
+ if (rxi.tag && rxi.tag !== "latest") {
4886
+ result += `:${rxi.tag}`;
4887
+ }
4888
+ if (rxi.digest) {
4889
+ result += `@${rxi.digest}`;
4493
4890
  }
4494
- async readFolderFiles(folderPath, basePath = folderPath) {
4495
- const files = {};
4496
- const entries = await readdir2(folderPath, { withFileTypes: true });
4497
- for (const entry of entries) {
4498
- const fullPath = join2(folderPath, entry.name);
4499
- const relativePath = relative2(basePath, fullPath);
4500
- if (entry.isFile()) {
4501
- files[relativePath] = await readFile2(fullPath);
4502
- } else if (entry.isDirectory()) {
4503
- const subFiles = await this.readFolderFiles(fullPath, basePath);
4504
- Object.assign(files, subFiles);
4891
+ return result;
4892
+ }
4893
+ function locate(rxm) {
4894
+ return {
4895
+ registry: rxm.definition.registry,
4896
+ path: rxm.definition.path,
4897
+ name: rxm.definition.name,
4898
+ tag: rxm.definition.tag
4899
+ };
4900
+ }
4901
+ function manifest(rxd) {
4902
+ return {
4903
+ definition: {
4904
+ name: rxd.name,
4905
+ type: rxd.type,
4906
+ tag: rxd.tag ?? "latest",
4907
+ registry: rxd.registry,
4908
+ path: rxd.path,
4909
+ description: rxd.description,
4910
+ author: rxd.author,
4911
+ license: rxd.license,
4912
+ keywords: rxd.keywords,
4913
+ repository: rxd.repository
4914
+ },
4915
+ archive: {},
4916
+ source: {}
4917
+ };
4918
+ }
4919
+ function validateLocatorSecurity(locator) {
4920
+ if (locator.length > MAX_LOCATOR_LENGTH) {
4921
+ throw new LocatorError("Locator too long", locator);
4922
+ }
4923
+ if (locator.startsWith("/") || locator.startsWith("\\")) {
4924
+ throw new LocatorError("Absolute paths not allowed", locator);
4925
+ }
4926
+ const normalized = locator.normalize("NFC");
4927
+ let decoded;
4928
+ try {
4929
+ decoded = decodeURIComponent(normalized);
4930
+ } catch {
4931
+ decoded = normalized;
4932
+ }
4933
+ const lastColonIndex = decoded.lastIndexOf(":");
4934
+ const namePathPart = lastColonIndex !== -1 ? decoded.substring(0, lastColonIndex) : decoded;
4935
+ if (namePathPart.includes("..")) {
4936
+ throw new LocatorError("Path traversal detected", locator);
4937
+ }
4938
+ const segments = decoded.split("/");
4939
+ const stack = [];
4940
+ for (const seg of segments) {
4941
+ if (seg === "..") {
4942
+ if (stack.length > 0)
4943
+ stack.pop();
4944
+ else {
4945
+ throw new LocatorError("Path traversal detected", locator);
4505
4946
  }
4947
+ } else if (seg !== "." && seg !== "") {
4948
+ stack.push(seg);
4506
4949
  }
4507
- return files;
4950
+ }
4951
+ const pathNormalized = stack.join("/");
4952
+ if (pathNormalized.startsWith("/") || pathNormalized.startsWith("\\")) {
4953
+ throw new LocatorError("Path traversal detected", locator);
4954
+ }
4955
+ if (DANGEROUS_PATTERNS.test(decoded)) {
4956
+ throw new LocatorError("Invalid characters detected", locator);
4508
4957
  }
4509
4958
  }
4510
-
4511
- class NpmSourceLoader {
4512
- folder = new FolderSourceLoader;
4513
- canLoad(source) {
4514
- return source.startsWith(NPM_PREFIX);
4959
+ function looksLikeRegistry(str) {
4960
+ if (str.includes(":") && !str.includes("/")) {
4961
+ return true;
4515
4962
  }
4516
- async load(source) {
4517
- if (!this.canLoad(source)) {
4518
- throw new ResourceXError(`Not an npm source: ${source}`);
4963
+ if (str.includes(".")) {
4964
+ return true;
4965
+ }
4966
+ if (str === "localhost") {
4967
+ return true;
4968
+ }
4969
+ return false;
4970
+ }
4971
+ function parse5(locator) {
4972
+ if (!locator || typeof locator !== "string") {
4973
+ throw new LocatorError("Locator must be a non-empty string", locator);
4974
+ }
4975
+ validateLocatorSecurity(locator);
4976
+ let digest;
4977
+ let locatorWithoutDigest = locator;
4978
+ const atIndex = locator.indexOf("@");
4979
+ if (atIndex !== -1) {
4980
+ if (atIndex === 0) {
4981
+ throw new LocatorError("Invalid locator format. Name is required before @", locator);
4519
4982
  }
4520
- const packageName = source.slice(NPM_PREFIX.length);
4521
- if (!packageName) {
4522
- throw new ResourceXError(`Empty package name in npm source: ${source}`);
4983
+ digest = locator.substring(atIndex + 1);
4984
+ locatorWithoutDigest = locator.substring(0, atIndex);
4985
+ if (!digest || digest.includes("@")) {
4986
+ throw new LocatorError("Invalid digest format after @", locator);
4523
4987
  }
4524
- const packageDir = this.resolvePackageDir(packageName);
4525
- const rxs = await this.folder.load(packageDir);
4526
- return { source, files: rxs.files };
4527
4988
  }
4528
- resolvePackageDir(packageName) {
4529
- const entry = globalThis.Bun?.main ?? process.argv[1] ?? join3(process.cwd(), "noop.js");
4530
- const parent = pathToFileURL(entry).href;
4531
- try {
4532
- const url2 = import.meta.resolve(`${packageName}/package.json`, parent);
4533
- return dirname(fileURLToPath(url2));
4534
- } catch {
4535
- throw new ResourceXError(`Cannot resolve npm package: ${packageName}`);
4536
- }
4989
+ const lastSlashIndex = locatorWithoutDigest.lastIndexOf("/");
4990
+ let beforeSlash = "";
4991
+ let afterSlash = locatorWithoutDigest;
4992
+ if (lastSlashIndex !== -1) {
4993
+ beforeSlash = locatorWithoutDigest.substring(0, lastSlashIndex);
4994
+ afterSlash = locatorWithoutDigest.substring(lastSlashIndex + 1);
4995
+ }
4996
+ const colonIndex = afterSlash.lastIndexOf(":");
4997
+ let name;
4998
+ let tag;
4999
+ if (colonIndex === -1) {
5000
+ name = afterSlash;
5001
+ tag = "latest";
5002
+ } else {
5003
+ name = afterSlash.substring(0, colonIndex);
5004
+ tag = afterSlash.substring(colonIndex + 1);
5005
+ }
5006
+ if (!name) {
5007
+ throw new LocatorError("Name is required", locator);
5008
+ }
5009
+ if (!tag) {
5010
+ throw new LocatorError("Tag cannot be empty. Use name:tag format or omit tag for :latest", locator);
5011
+ }
5012
+ if (lastSlashIndex === -1) {
5013
+ return {
5014
+ registry: undefined,
5015
+ path: undefined,
5016
+ name,
5017
+ tag,
5018
+ digest
5019
+ };
5020
+ }
5021
+ const parts = beforeSlash.split("/");
5022
+ if (looksLikeRegistry(parts[0])) {
5023
+ const registry2 = parts[0];
5024
+ const path = parts.length > 1 ? parts.slice(1).join("/") : undefined;
5025
+ return {
5026
+ registry: registry2,
5027
+ path,
5028
+ name,
5029
+ tag,
5030
+ digest
5031
+ };
4537
5032
  }
5033
+ return {
5034
+ registry: undefined,
5035
+ path: beforeSlash,
5036
+ name,
5037
+ tag,
5038
+ digest
5039
+ };
5040
+ }
5041
+ function resource(rxm, rxa) {
5042
+ const rxi = locate(rxm);
5043
+ return {
5044
+ identifier: rxi,
5045
+ manifest: rxm,
5046
+ archive: rxa
5047
+ };
4538
5048
  }
4539
- function computeDigest(data) {
4540
- const hash2 = createHash("sha256").update(data).digest("hex");
4541
- return `sha256:${hash2}`;
5049
+ async function loadResource(source, config2) {
5050
+ const loader = config2.loader;
5051
+ const canLoad = await loader.canLoad(source);
5052
+ if (!canLoad) {
5053
+ throw new ResourceXError(`Cannot load resource from: ${source}`);
5054
+ }
5055
+ return loader.load(source);
5056
+ }
5057
+ async function computeDigest(data) {
5058
+ const hash2 = await crypto.subtle.digest("SHA-256", data);
5059
+ const hex3 = [...new Uint8Array(hash2)].map((b2) => b2.toString(16).padStart(2, "0")).join("");
5060
+ return `sha256:${hex3}`;
4542
5061
  }
4543
5062
  function isValidDigest(digest) {
4544
5063
  return /^sha256:[a-f0-9]{64}$/.test(digest);
@@ -4554,7 +5073,7 @@ class MemoryRXAStore {
4554
5073
  return blob;
4555
5074
  }
4556
5075
  async put(data) {
4557
- const digest = computeDigest(data);
5076
+ const digest = await computeDigest(data);
4558
5077
  if (!this.blobs.has(digest)) {
4559
5078
  this.blobs.set(digest, data);
4560
5079
  }
@@ -4720,7 +5239,7 @@ var __defProp2, __returnValue2 = (v2) => v2, __export2 = (target, all) => {
4720
5239
  configurable: true,
4721
5240
  set: __exportSetter2.bind(all, name)
4722
5241
  });
4723
- }, BLOCK_SIZE = 512, ZERO_BLOCK, EMPTY, encoder, decoder, EOF_BUFFER, gzipAsync, exports_external, exports_core2, NEVER, $brand, $ZodAsyncError, $ZodEncodeError, globalConfig, exports_util, EVALUATING, captureStackTrace, allowsEval, getParsedType = (data) => {
5242
+ }, BLOCK_SIZE = 512, BLOCK_SIZE_MASK = 511, DEFAULT_FILE_MODE = 420, DEFAULT_DIR_MODE = 493, USTAR_NAME_OFFSET = 0, USTAR_NAME_SIZE = 100, USTAR_MODE_OFFSET = 100, USTAR_MODE_SIZE = 8, USTAR_UID_OFFSET = 108, USTAR_UID_SIZE = 8, USTAR_GID_OFFSET = 116, USTAR_GID_SIZE = 8, USTAR_SIZE_OFFSET = 124, USTAR_SIZE_SIZE = 12, USTAR_MTIME_OFFSET = 136, USTAR_MTIME_SIZE = 12, USTAR_CHECKSUM_OFFSET = 148, USTAR_CHECKSUM_SIZE = 8, USTAR_TYPEFLAG_OFFSET = 156, USTAR_TYPEFLAG_SIZE = 1, USTAR_LINKNAME_OFFSET = 157, USTAR_LINKNAME_SIZE = 100, USTAR_MAGIC_OFFSET = 257, USTAR_MAGIC_SIZE = 6, USTAR_VERSION_OFFSET = 263, USTAR_VERSION_SIZE = 2, USTAR_UNAME_OFFSET = 265, USTAR_UNAME_SIZE = 32, USTAR_GNAME_OFFSET = 297, USTAR_GNAME_SIZE = 32, USTAR_PREFIX_OFFSET = 345, USTAR_PREFIX_SIZE = 155, USTAR_VERSION = "00", USTAR_MAX_UID_GID = 2097151, USTAR_MAX_SIZE = 8589934591, FILE = "file", LINK = "link", SYMLINK = "symlink", DIRECTORY = "directory", TYPEFLAG, ZERO_BLOCK, EMPTY, encoder, decoder, isBodyless = (header) => header.type === DIRECTORY || header.type === SYMLINK || header.type === LINK, CHECKSUM_SPACE = 32, ASCII_ZERO = 48, EOF_BUFFER, exports_external, exports_core2, NEVER, $brand, $ZodAsyncError, $ZodEncodeError, globalConfig, exports_util, EVALUATING, captureStackTrace, allowsEval, getParsedType = (data) => {
4724
5243
  const t2 = typeof data;
4725
5244
  switch (t2) {
4726
5245
  case "undefined":
@@ -10398,15 +10917,27 @@ var __defProp2, __returnValue2 = (v2) => v2, __export2 = (target, all) => {
10398
10917
  Codec: ZodCodec,
10399
10918
  Boolean: ZodBoolean,
10400
10919
  String: ZodString
10401
- }, ...args), ZodIssueCode, ZodFirstPartyTypeKind, z2, RECOGNIZED_KEYS, exports_coerce, ResourceXError, RXDSchema, gunzipAsync, NPM_PREFIX = "npm:", RegistryError, textType, jsonType, binaryType, skillType, prototypeType, builtinTypes, ResourceTypeError;
10920
+ }, ...args), ZodIssueCode, ZodFirstPartyTypeKind, z2, RECOGNIZED_KEYS, exports_coerce, ResourceXError, LocatorError, DefinitionError, RXDSchema, MAX_LOCATOR_LENGTH = 256, DANGEROUS_PATTERNS, RegistryError, textType, jsonType, binaryType, skillType, prototypeType, builtinTypes, ResourceTypeError;
10402
10921
  var init_dist = __esm(() => {
10403
10922
  __defProp2 = Object.defineProperty;
10923
+ TYPEFLAG = {
10924
+ file: "0",
10925
+ link: "1",
10926
+ symlink: "2",
10927
+ "character-device": "3",
10928
+ "block-device": "4",
10929
+ directory: "5",
10930
+ fifo: "6",
10931
+ "pax-header": "x",
10932
+ "pax-global-header": "g",
10933
+ "gnu-long-name": "L",
10934
+ "gnu-long-link-name": "K"
10935
+ };
10404
10936
  ZERO_BLOCK = new Uint8Array(BLOCK_SIZE);
10405
10937
  EMPTY = new Uint8Array(0);
10406
10938
  encoder = new TextEncoder;
10407
10939
  decoder = new TextDecoder;
10408
10940
  EOF_BUFFER = new Uint8Array(BLOCK_SIZE * 2);
10409
- gzipAsync = promisify(gzip);
10410
10941
  exports_external = {};
10411
10942
  __export2(exports_external, {
10412
10943
  xor: () => xor,
@@ -14374,6 +14905,20 @@ var init_dist = __esm(() => {
14374
14905
  this.name = "ResourceXError";
14375
14906
  }
14376
14907
  };
14908
+ LocatorError = class LocatorError extends ResourceXError {
14909
+ locator;
14910
+ constructor(message, locator) {
14911
+ super(message);
14912
+ this.locator = locator;
14913
+ this.name = "LocatorError";
14914
+ }
14915
+ };
14916
+ DefinitionError = class DefinitionError extends ResourceXError {
14917
+ constructor(message) {
14918
+ super(message);
14919
+ this.name = "DefinitionError";
14920
+ }
14921
+ };
14377
14922
  RXDSchema = exports_external.object({
14378
14923
  name: exports_external.string().min(1).max(128),
14379
14924
  type: exports_external.string().min(1).max(64),
@@ -14386,7 +14931,7 @@ var init_dist = __esm(() => {
14386
14931
  keywords: exports_external.array(exports_external.string().max(64)).max(20).optional(),
14387
14932
  repository: exports_external.string().max(256).optional()
14388
14933
  }).strict();
14389
- gunzipAsync = promisify2(gunzip);
14934
+ DANGEROUS_PATTERNS = /[;|&$`\n\0\r]/;
14390
14935
  RegistryError = class RegistryError extends ResourceXError {
14391
14936
  constructor(message, options) {
14392
14937
  super(message, options);
@@ -14531,19 +15076,69 @@ var prototype_type_default = {
14531
15076
  // ../../packages/node-provider/dist/index.js
14532
15077
  var exports_dist = {};
14533
15078
  __export(exports_dist, {
15079
+ bundleResourceType: () => bundleResourceType,
15080
+ NpmSourceLoader: () => NpmSourceLoader,
14534
15081
  NodeProvider: () => NodeProvider,
15082
+ LinkedRegistry: () => LinkedRegistry,
15083
+ FolderSourceLoader: () => FolderSourceLoader,
15084
+ FolderLoader: () => FolderLoader,
14535
15085
  FileSystemRXMStore: () => FileSystemRXMStore,
14536
15086
  FileSystemRXAStore: () => FileSystemRXAStore
14537
15087
  });
14538
- import { createHash as createHash2 } from "crypto";
15088
+ import { readFile } from "fs/promises";
15089
+ import { isAbsolute, resolve } from "path";
15090
+ import { createHash } from "crypto";
14539
15091
  import { createReadStream } from "fs";
14540
15092
  import { mkdir, readdir, stat, unlink, writeFile } from "fs/promises";
14541
15093
  import { join } from "path";
14542
- import { mkdir as mkdir2, readdir as readdir22, readFile, rm, stat as stat22, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
14543
- import { join as join22 } from "path";
15094
+ import { mkdir as mkdir2, readdir as readdir2, readFile as readFile2, rm, stat as stat2, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
15095
+ import { join as join2 } from "path";
15096
+ import { readdir as readdir3, readFile as readFile3, stat as stat3 } from "fs/promises";
15097
+ import { join as join3, relative } from "path";
15098
+ import { readdir as readdir4, readFile as readFile4, stat as stat4 } from "fs/promises";
15099
+ import { join as join4, relative as relative2 } from "path";
15100
+ import { lstat, mkdir as mkdir3, readdir as readdir5, readlink, rm as rm2, symlink } from "fs/promises";
15101
+ import { join as join5, resolve as resolvePath } from "path";
14544
15102
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
14545
15103
  import { homedir } from "os";
14546
- import { dirname as dirname2, join as join32 } from "path";
15104
+ import { dirname as dirname2, join as join7 } from "path";
15105
+ import { dirname, join as join6 } from "path";
15106
+ import { fileURLToPath, pathToFileURL } from "url";
15107
+ async function bundleResourceType(sourcePath, basePath) {
15108
+ const fullPath = isAbsolute(sourcePath) ? sourcePath : resolve(basePath ?? process.cwd(), sourcePath);
15109
+ const source = await readFile(fullPath, "utf-8");
15110
+ const result = await Bun.build({
15111
+ stdin: {
15112
+ contents: source,
15113
+ resolveDir: resolve(fullPath, ".."),
15114
+ loader: "ts"
15115
+ },
15116
+ target: "bun",
15117
+ format: "esm",
15118
+ minify: false
15119
+ });
15120
+ if (!result.success) {
15121
+ const errors = result.logs.map((log) => log.message).join(`
15122
+ `);
15123
+ throw new Error(`Failed to bundle ${sourcePath}: ${errors}`);
15124
+ }
15125
+ const bundledCode = await result.outputs[0].text();
15126
+ const tempModule = await import(fullPath);
15127
+ const typeSource = tempModule.default;
15128
+ if (!typeSource.name) {
15129
+ throw new Error(`Resource type at ${sourcePath} must have a name`);
15130
+ }
15131
+ if (typeof typeSource.resolve !== "function") {
15132
+ throw new Error(`Resource type at ${sourcePath} must have a resolve function`);
15133
+ }
15134
+ return {
15135
+ name: typeSource.name,
15136
+ aliases: typeSource.aliases,
15137
+ description: typeSource.description ?? "",
15138
+ schema: typeSource.schema,
15139
+ code: bundledCode
15140
+ };
15141
+ }
14547
15142
 
14548
15143
  class FileSystemRXAStore {
14549
15144
  basePath;
@@ -14560,16 +15155,16 @@ class FileSystemRXAStore {
14560
15155
  }
14561
15156
  const path = this.getPath(digest);
14562
15157
  const chunks = [];
14563
- const hash2 = createHash2("sha256");
15158
+ const hash2 = createHash("sha256");
14564
15159
  const readStream = createReadStream(path);
14565
- await new Promise((resolve, reject) => {
15160
+ await new Promise((resolve2, reject) => {
14566
15161
  readStream.on("data", (chunk) => {
14567
15162
  if (typeof chunk === "string")
14568
15163
  chunk = Buffer.from(chunk);
14569
15164
  chunks.push(chunk);
14570
15165
  hash2.update(chunk);
14571
15166
  });
14572
- readStream.on("end", resolve);
15167
+ readStream.on("end", resolve2);
14573
15168
  readStream.on("error", () => reject(new RegistryError(`Blob not found: ${digest}`)));
14574
15169
  });
14575
15170
  const actualDigest = `sha256:${hash2.digest("hex")}`;
@@ -14579,7 +15174,7 @@ class FileSystemRXAStore {
14579
15174
  return Buffer.concat(chunks);
14580
15175
  }
14581
15176
  async put(data) {
14582
- const digest = computeDigest(data);
15177
+ const digest = await computeDigest(data);
14583
15178
  const path = this.getPath(digest);
14584
15179
  if (await this.has(digest)) {
14585
15180
  return digest;
@@ -14631,30 +15226,30 @@ class FileSystemRXMStore {
14631
15226
  }
14632
15227
  getDir(name, registry2) {
14633
15228
  const registryDir = registry2 ?? LOCAL_DIR;
14634
- return join22(this.basePath, registryDir, name);
15229
+ return join2(this.basePath, registryDir, name);
14635
15230
  }
14636
15231
  getPath(name, tag, registry2) {
14637
- return join22(this.getDir(name, registry2), `${tag}.json`);
15232
+ return join2(this.getDir(name, registry2), `${tag}.json`);
14638
15233
  }
14639
15234
  async get(name, tag, registry2) {
14640
15235
  const path = this.getPath(name, tag, registry2);
14641
15236
  try {
14642
- const data = await readFile(path, "utf-8");
15237
+ const data = await readFile2(path, "utf-8");
14643
15238
  return JSON.parse(data);
14644
15239
  } catch {
14645
15240
  return null;
14646
15241
  }
14647
15242
  }
14648
- async put(manifest) {
14649
- const path = this.getPath(manifest.name, manifest.tag, manifest.registry);
14650
- const dir = join22(path, "..");
15243
+ async put(manifest2) {
15244
+ const path = this.getPath(manifest2.name, manifest2.tag, manifest2.registry);
15245
+ const dir = join2(path, "..");
14651
15246
  await mkdir2(dir, { recursive: true });
14652
- await writeFile2(path, JSON.stringify(manifest, null, 2), "utf-8");
15247
+ await writeFile2(path, JSON.stringify(manifest2, null, 2), "utf-8");
14653
15248
  }
14654
15249
  async has(name, tag, registry2) {
14655
15250
  const path = this.getPath(name, tag, registry2);
14656
15251
  try {
14657
- await stat22(path);
15252
+ await stat2(path);
14658
15253
  return true;
14659
15254
  } catch {
14660
15255
  return false;
@@ -14670,7 +15265,7 @@ class FileSystemRXMStore {
14670
15265
  const dir = this.getDir(name, registry2);
14671
15266
  const tags = [];
14672
15267
  try {
14673
- const files = await readdir22(dir);
15268
+ const files = await readdir2(dir);
14674
15269
  for (const file2 of files) {
14675
15270
  if (file2.endsWith(".json")) {
14676
15271
  tags.push(file2.replace(".json", ""));
@@ -14682,26 +15277,26 @@ class FileSystemRXMStore {
14682
15277
  async setLatest(name, tag, registry2) {
14683
15278
  const dir = this.getDir(name, registry2);
14684
15279
  await mkdir2(dir, { recursive: true });
14685
- await writeFile2(join22(dir, LATEST_FILE), tag, "utf-8");
15280
+ await writeFile2(join2(dir, LATEST_FILE), tag, "utf-8");
14686
15281
  }
14687
15282
  async getLatest(name, registry2) {
14688
15283
  const dir = this.getDir(name, registry2);
14689
15284
  try {
14690
- return (await readFile(join22(dir, LATEST_FILE), "utf-8")).trim();
15285
+ return (await readFile2(join2(dir, LATEST_FILE), "utf-8")).trim();
14691
15286
  } catch {
14692
15287
  return null;
14693
15288
  }
14694
15289
  }
14695
15290
  async listNames(registry2, query) {
14696
15291
  const registryDir = registry2 ?? LOCAL_DIR;
14697
- const basePath = join22(this.basePath, registryDir);
15292
+ const basePath = join2(this.basePath, registryDir);
14698
15293
  const names = [];
14699
15294
  try {
14700
- const entries = await readdir22(basePath);
15295
+ const entries = await readdir2(basePath);
14701
15296
  for (const entry of entries) {
14702
- const entryPath = join22(basePath, entry);
15297
+ const entryPath = join2(basePath, entry);
14703
15298
  try {
14704
- const entryStat = await stat22(entryPath);
15299
+ const entryStat = await stat2(entryPath);
14705
15300
  if (entryStat.isDirectory()) {
14706
15301
  if (!query || entry.toLowerCase().includes(query.toLowerCase())) {
14707
15302
  names.push(entry);
@@ -14722,28 +15317,28 @@ class FileSystemRXMStore {
14722
15317
  registryDirs = [registry2];
14723
15318
  } else {
14724
15319
  try {
14725
- registryDirs = await readdir22(this.basePath);
15320
+ registryDirs = await readdir2(this.basePath);
14726
15321
  } catch {
14727
15322
  return [];
14728
15323
  }
14729
15324
  }
14730
15325
  for (const regDir of registryDirs) {
14731
- const regPath = join22(this.basePath, regDir);
15326
+ const regPath = join2(this.basePath, regDir);
14732
15327
  try {
14733
- const names = await readdir22(regPath);
15328
+ const names = await readdir2(regPath);
14734
15329
  for (const name of names) {
14735
15330
  if (query && !name.toLowerCase().includes(query.toLowerCase())) {
14736
15331
  continue;
14737
15332
  }
14738
- const namePath = join22(regPath, name);
15333
+ const namePath = join2(regPath, name);
14739
15334
  try {
14740
- const files = await readdir22(namePath);
15335
+ const files = await readdir2(namePath);
14741
15336
  for (const file2 of files) {
14742
15337
  if (file2.endsWith(".json")) {
14743
- const filePath = join22(namePath, file2);
14744
- const data = await readFile(filePath, "utf-8");
14745
- const manifest = JSON.parse(data);
14746
- results.push(manifest);
15338
+ const filePath = join2(namePath, file2);
15339
+ const data = await readFile2(filePath, "utf-8");
15340
+ const manifest2 = JSON.parse(data);
15341
+ results.push(manifest2);
14747
15342
  }
14748
15343
  }
14749
15344
  } catch {}
@@ -14757,24 +15352,273 @@ class FileSystemRXMStore {
14757
15352
  return paginated;
14758
15353
  }
14759
15354
  async deleteByRegistry(registry2) {
14760
- const regPath = join22(this.basePath, registry2);
15355
+ const regPath = join2(this.basePath, registry2);
14761
15356
  try {
14762
15357
  await rm(regPath, { recursive: true });
14763
15358
  } catch {}
14764
15359
  }
14765
15360
  }
14766
15361
 
15362
+ class FolderLoader {
15363
+ async canLoad(source) {
15364
+ try {
15365
+ const stats = await stat3(source);
15366
+ if (!stats.isDirectory()) {
15367
+ return false;
15368
+ }
15369
+ const manifestPath = join3(source, "resource.json");
15370
+ const manifestStats = await stat3(manifestPath);
15371
+ return manifestStats.isFile();
15372
+ } catch {
15373
+ return false;
15374
+ }
15375
+ }
15376
+ async load(folderPath) {
15377
+ const resourceJsonPath = join3(folderPath, "resource.json");
15378
+ let resourceJson;
15379
+ try {
15380
+ resourceJson = await readFile3(resourceJsonPath, "utf-8");
15381
+ } catch (error48) {
15382
+ throw new ResourceXError(`Failed to read resource.json: ${error48 instanceof Error ? error48.message : String(error48)}`);
15383
+ }
15384
+ let json2;
15385
+ try {
15386
+ json2 = JSON.parse(resourceJson);
15387
+ } catch (error48) {
15388
+ throw new ResourceXError(`Invalid JSON in resource.json: ${error48 instanceof Error ? error48.message : String(error48)}`);
15389
+ }
15390
+ const rxd = define(json2);
15391
+ const files = await this.readFolderFiles(folderPath);
15392
+ if (Object.keys(files).length === 0) {
15393
+ throw new ResourceXError("No content files found in resource folder");
15394
+ }
15395
+ const rxm = manifest(rxd);
15396
+ const rxa = await archive(files);
15397
+ return resource(rxm, rxa);
15398
+ }
15399
+ async readFolderFiles(folderPath, basePath = folderPath) {
15400
+ const files = {};
15401
+ const entries = await readdir3(folderPath, { withFileTypes: true });
15402
+ for (const entry of entries) {
15403
+ const fullPath = join3(folderPath, entry.name);
15404
+ const relativePath = relative(basePath, fullPath);
15405
+ if (relativePath === "resource.json") {
15406
+ continue;
15407
+ }
15408
+ if (entry.isFile()) {
15409
+ files[relativePath] = await readFile3(fullPath);
15410
+ } else if (entry.isDirectory()) {
15411
+ const subFiles = await this.readFolderFiles(fullPath, basePath);
15412
+ Object.assign(files, subFiles);
15413
+ }
15414
+ }
15415
+ return files;
15416
+ }
15417
+ }
15418
+
15419
+ class FolderSourceLoader {
15420
+ async canLoad(source) {
15421
+ try {
15422
+ const stats = await stat4(source);
15423
+ return stats.isDirectory();
15424
+ } catch {
15425
+ return false;
15426
+ }
15427
+ }
15428
+ async load(source) {
15429
+ const canLoad = await this.canLoad(source);
15430
+ if (!canLoad) {
15431
+ throw new ResourceXError(`Source is not a directory: ${source}`);
15432
+ }
15433
+ const files = await this.readFolderFiles(source);
15434
+ return { source, files };
15435
+ }
15436
+ async readFolderFiles(folderPath, basePath = folderPath) {
15437
+ const files = {};
15438
+ const entries = await readdir4(folderPath, { withFileTypes: true });
15439
+ for (const entry of entries) {
15440
+ const fullPath = join4(folderPath, entry.name);
15441
+ const relativePath = relative2(basePath, fullPath);
15442
+ if (entry.isFile()) {
15443
+ files[relativePath] = await readFile4(fullPath);
15444
+ } else if (entry.isDirectory()) {
15445
+ const subFiles = await this.readFolderFiles(fullPath, basePath);
15446
+ Object.assign(files, subFiles);
15447
+ }
15448
+ }
15449
+ return files;
15450
+ }
15451
+ }
15452
+
15453
+ class LinkedRegistry {
15454
+ basePath;
15455
+ constructor(basePath) {
15456
+ this.basePath = basePath;
15457
+ }
15458
+ buildLinkPath(rxi) {
15459
+ const registry2 = rxi.registry ?? "localhost";
15460
+ const tag = rxi.tag ?? "latest";
15461
+ let linkPath = join5(this.basePath, registry2);
15462
+ if (rxi.path) {
15463
+ linkPath = join5(linkPath, rxi.path);
15464
+ }
15465
+ return join5(linkPath, rxi.name, tag);
15466
+ }
15467
+ async isSymlink(path) {
15468
+ try {
15469
+ const stats = await lstat(path);
15470
+ return stats.isSymbolicLink();
15471
+ } catch {
15472
+ return false;
15473
+ }
15474
+ }
15475
+ async get(rxi) {
15476
+ const linkPath = this.buildLinkPath(rxi);
15477
+ if (!await this.isSymlink(linkPath)) {
15478
+ throw new RegistryError(`Linked resource not found: ${format(rxi)}`);
15479
+ }
15480
+ const targetPath = await readlink(linkPath);
15481
+ return loadResource(targetPath, { loader: folderLoader });
15482
+ }
15483
+ async put(_rxr) {
15484
+ throw new RegistryError("LinkedRegistry does not support put(). Use link() instead.");
15485
+ }
15486
+ async has(rxi) {
15487
+ const linkPath = this.buildLinkPath(rxi);
15488
+ return this.isSymlink(linkPath);
15489
+ }
15490
+ async remove(rxi) {
15491
+ const linkPath = this.buildLinkPath(rxi);
15492
+ if (await this.isSymlink(linkPath)) {
15493
+ await rm2(linkPath);
15494
+ }
15495
+ }
15496
+ async list(options) {
15497
+ const { query, limit, offset = 0 } = options ?? {};
15498
+ const identifiers = [];
15499
+ try {
15500
+ await this.scanSymlinks(this.basePath, "", identifiers);
15501
+ } catch {
15502
+ return [];
15503
+ }
15504
+ let filtered = identifiers;
15505
+ if (query) {
15506
+ const lowerQuery = query.toLowerCase();
15507
+ filtered = identifiers.filter((rxi) => {
15508
+ const searchText = `${rxi.registry ?? ""} ${rxi.path ?? ""} ${rxi.name}`.toLowerCase();
15509
+ return searchText.includes(lowerQuery);
15510
+ });
15511
+ }
15512
+ let result = filtered.slice(offset);
15513
+ if (limit !== undefined) {
15514
+ result = result.slice(0, limit);
15515
+ }
15516
+ return result;
15517
+ }
15518
+ async link(devPath) {
15519
+ const rxr = await loadResource(devPath, { loader: folderLoader });
15520
+ const linkPath = this.buildLinkPath(rxr.identifier);
15521
+ try {
15522
+ const stats = await lstat(linkPath);
15523
+ if (stats.isSymbolicLink() || stats.isDirectory()) {
15524
+ await rm2(linkPath, { recursive: true });
15525
+ }
15526
+ } catch {}
15527
+ const parentPath = join5(linkPath, "..");
15528
+ await mkdir3(parentPath, { recursive: true });
15529
+ const absolutePath = resolvePath(devPath);
15530
+ await symlink(absolutePath, linkPath);
15531
+ return rxr.identifier;
15532
+ }
15533
+ async unlink(rxi) {
15534
+ return this.remove(rxi);
15535
+ }
15536
+ async scanSymlinks(dirPath, relativePath, identifiers) {
15537
+ let entries;
15538
+ try {
15539
+ entries = await readdir5(dirPath);
15540
+ } catch {
15541
+ return;
15542
+ }
15543
+ for (const entry of entries) {
15544
+ const fullPath = join5(dirPath, entry);
15545
+ const relPath = relativePath ? `${relativePath}/${entry}` : entry;
15546
+ try {
15547
+ const stats = await lstat(fullPath);
15548
+ if (stats.isSymbolicLink()) {
15549
+ const rxi = this.parsePathToRXI(relPath);
15550
+ if (rxi) {
15551
+ identifiers.push(rxi);
15552
+ }
15553
+ } else if (stats.isDirectory()) {
15554
+ await this.scanSymlinks(fullPath, relPath, identifiers);
15555
+ }
15556
+ } catch {}
15557
+ }
15558
+ }
15559
+ parsePathToRXI(relPath) {
15560
+ const parts = relPath.split("/");
15561
+ if (parts.length < 3) {
15562
+ return null;
15563
+ }
15564
+ const tag = parts.pop();
15565
+ const name = parts.pop();
15566
+ const registry2 = parts.shift();
15567
+ const path = parts.length > 0 ? parts.join("/") : undefined;
15568
+ let locatorStr = registry2;
15569
+ if (path)
15570
+ locatorStr += `/${path}`;
15571
+ locatorStr += `/${name}`;
15572
+ if (tag !== "latest")
15573
+ locatorStr += `:${tag}`;
15574
+ try {
15575
+ return parse5(locatorStr);
15576
+ } catch {
15577
+ return null;
15578
+ }
15579
+ }
15580
+ }
15581
+
15582
+ class NpmSourceLoader {
15583
+ folder = new FolderSourceLoader;
15584
+ canLoad(source) {
15585
+ return source.startsWith(NPM_PREFIX);
15586
+ }
15587
+ async load(source) {
15588
+ if (!this.canLoad(source)) {
15589
+ throw new ResourceXError(`Not an npm source: ${source}`);
15590
+ }
15591
+ const packageName = source.slice(NPM_PREFIX.length);
15592
+ if (!packageName) {
15593
+ throw new ResourceXError(`Empty package name in npm source: ${source}`);
15594
+ }
15595
+ const packageDir = this.resolvePackageDir(packageName);
15596
+ const rxs = await this.folder.load(packageDir);
15597
+ return { source, files: rxs.files };
15598
+ }
15599
+ resolvePackageDir(packageName) {
15600
+ const entry = globalThis.Bun?.main ?? process.argv[1] ?? join6(process.cwd(), "noop.js");
15601
+ const parent = pathToFileURL(entry).href;
15602
+ try {
15603
+ const url2 = import.meta.resolve(`${packageName}/package.json`, parent);
15604
+ return dirname(fileURLToPath(url2));
15605
+ } catch {
15606
+ throw new ResourceXError(`Cannot resolve npm package: ${packageName}`);
15607
+ }
15608
+ }
15609
+ }
15610
+
14767
15611
  class NodeProvider {
14768
15612
  platform = "node";
14769
15613
  createStores(config2) {
14770
15614
  const basePath = config2.path ?? DEFAULT_BASE_PATH;
14771
15615
  return {
14772
- rxaStore: new FileSystemRXAStore(join32(basePath, "blobs")),
14773
- rxmStore: new FileSystemRXMStore(join32(basePath, "manifests"))
15616
+ rxaStore: new FileSystemRXAStore(join7(basePath, "blobs")),
15617
+ rxmStore: new FileSystemRXMStore(join7(basePath, "manifests"))
14774
15618
  };
14775
15619
  }
14776
- createSourceLoader(_config) {
14777
- return new FolderSourceLoader;
15620
+ createSourceLoaders(_config) {
15621
+ return [new FolderSourceLoader, new NpmSourceLoader];
14778
15622
  }
14779
15623
  getDefaults(config2) {
14780
15624
  const envRegistry = process.env.RESOURCEX_REGISTRY;
@@ -14782,7 +15626,7 @@ class NodeProvider {
14782
15626
  return { registry: envRegistry };
14783
15627
  }
14784
15628
  const basePath = config2.path ?? DEFAULT_BASE_PATH;
14785
- const configPath = join32(basePath, "config.json");
15629
+ const configPath = join7(basePath, "config.json");
14786
15630
  try {
14787
15631
  if (existsSync(configPath)) {
14788
15632
  const raw = JSON.parse(readFileSync(configPath, "utf-8"));
@@ -14799,7 +15643,7 @@ class NodeProvider {
14799
15643
  }
14800
15644
  configPath(config2) {
14801
15645
  const basePath = config2.path ?? DEFAULT_BASE_PATH;
14802
- return join32(basePath, "config.json");
15646
+ return join7(basePath, "config.json");
14803
15647
  }
14804
15648
  readConfig(config2) {
14805
15649
  const configPath = this.configPath(config2);
@@ -14862,10 +15706,14 @@ class NodeProvider {
14862
15706
  this.writeConfig(config2, data);
14863
15707
  }
14864
15708
  }
14865
- var LOCAL_DIR = "_local", LATEST_FILE = ".latest", DEFAULT_BASE_PATH;
15709
+ var LOCAL_DIR = "_local", LATEST_FILE = ".latest", folderLoader, NPM_PREFIX = "npm:", DEFAULT_BASE_PATH;
14866
15710
  var init_dist2 = __esm(() => {
14867
15711
  init_dist();
14868
15712
  init_dist();
15713
+ init_dist();
15714
+ init_dist();
15715
+ init_dist();
15716
+ folderLoader = new FolderLoader;
14869
15717
  DEFAULT_BASE_PATH = `${homedir()}/.deepractice/resourcex`;
14870
15718
  });
14871
15719
 
@@ -14881,7 +15729,7 @@ import { createServer as createServerHTTP } from "http";
14881
15729
  import { Http2ServerRequest as Http2ServerRequest2 } from "http2";
14882
15730
  import { Http2ServerRequest } from "http2";
14883
15731
  import { Readable } from "stream";
14884
- import crypto from "crypto";
15732
+ import crypto2 from "crypto";
14885
15733
  async function readWithoutBlocking(readPromise) {
14886
15734
  return Promise.race([readPromise, Promise.resolve().then(() => Promise.resolve(undefined))]);
14887
15735
  }
@@ -15117,7 +15965,7 @@ var RequestError, toRequestError = (e2) => {
15117
15965
  });
15118
15966
  if (!chunk) {
15119
15967
  if (i2 === 1) {
15120
- await new Promise((resolve) => setTimeout(resolve));
15968
+ await new Promise((resolve2) => setTimeout(resolve2));
15121
15969
  maxReadCount = 3;
15122
15970
  continue;
15123
15971
  }
@@ -15392,7 +16240,7 @@ var init_dist3 = __esm(() => {
15392
16240
  Object.setPrototypeOf(Response2, GlobalResponse);
15393
16241
  Object.setPrototypeOf(Response2.prototype, GlobalResponse.prototype);
15394
16242
  if (typeof global.crypto === "undefined") {
15395
- global.crypto = crypto;
16243
+ global.crypto = crypto2;
15396
16244
  }
15397
16245
  outgoingEnded = Symbol("outgoingEnded");
15398
16246
  });
@@ -16802,14 +17650,14 @@ import { createResourceX, setProvider } from "resourcexjs";
16802
17650
 
16803
17651
  // src/lib/paths.ts
16804
17652
  import { homedir as homedir2 } from "os";
16805
- import { join as join4 } from "path";
16806
- var RX_HOME = process.env.RESOURCEX_HOME || process.env.RX_HOME || join4(homedir2(), ".deepractice", "resourcex");
17653
+ import { join as join8 } from "path";
17654
+ var RX_HOME = process.env.RESOURCEX_HOME || process.env.RX_HOME || join8(homedir2(), ".deepractice", "resourcex");
16807
17655
  var PATHS = {
16808
17656
  root: RX_HOME,
16809
- config: join4(RX_HOME, "config.json"),
16810
- local: join4(RX_HOME, "local"),
16811
- cache: join4(RX_HOME, "cache"),
16812
- linked: join4(RX_HOME, "linked")
17657
+ config: join8(RX_HOME, "config.json"),
17658
+ local: join8(RX_HOME, "local"),
17659
+ cache: join8(RX_HOME, "cache"),
17660
+ linked: join8(RX_HOME, "linked")
16813
17661
  };
16814
17662
 
16815
17663
  // src/lib/config.ts
@@ -16939,13 +17787,13 @@ var add = defineCommand({
16939
17787
  async run({ args }) {
16940
17788
  try {
16941
17789
  const rx = await getClient();
16942
- const resource = await rx.add(args.path);
17790
+ const resource2 = await rx.add(args.path);
16943
17791
  consola.success(`Added resource:
16944
17792
  `);
16945
- console.log(` Locator: ${resource.locator}`);
16946
- console.log(` Name: ${resource.definition.name}`);
16947
- console.log(` Type: ${resource.definition.type}`);
16948
- console.log(` Tag: ${resource.definition.tag}`);
17793
+ console.log(` Locator: ${resource2.locator}`);
17794
+ console.log(` Name: ${resource2.definition.name}`);
17795
+ console.log(` Type: ${resource2.definition.type}`);
17796
+ console.log(` Tag: ${resource2.definition.tag}`);
16949
17797
  } catch (error48) {
16950
17798
  consola.error(error48 instanceof Error ? error48.message : "Failed to add resource");
16951
17799
  process.exit(1);
@@ -17059,13 +17907,13 @@ var info = defineCommand({
17059
17907
  async run({ args }) {
17060
17908
  try {
17061
17909
  const rx = await getClient();
17062
- const resource = await rx.info(args.locator);
17063
- const { definition, source } = resource;
17910
+ const resource2 = await rx.info(args.locator);
17911
+ const { definition, source } = resource2;
17064
17912
  console.log();
17065
17913
  console.log(` ${definition.name}:${definition.tag}`);
17066
17914
  console.log(` ${"\u2500".repeat(40)}`);
17067
17915
  console.log();
17068
- console.log(` Locator: ${resource.locator}`);
17916
+ console.log(` Locator: ${resource2.locator}`);
17069
17917
  if (definition.registry) {
17070
17918
  console.log(` Registry: ${definition.registry}`);
17071
17919
  }
@@ -17436,7 +18284,7 @@ var search = defineCommand({
17436
18284
  });
17437
18285
 
17438
18286
  // src/commands/server.ts
17439
- import { join as join6 } from "path";
18287
+ import { join as join10 } from "path";
17440
18288
  var server = defineCommand({
17441
18289
  meta: {
17442
18290
  name: "server",
@@ -17460,8 +18308,8 @@ var server = defineCommand({
17460
18308
  const { FileSystemRXAStore: FileSystemRXAStore2, FileSystemRXMStore: FileSystemRXMStore2 } = await Promise.resolve().then(() => (init_dist2(), exports_dist));
17461
18309
  const { serve: serve2 } = await Promise.resolve().then(() => (init_dist3(), exports_dist2));
17462
18310
  const app = createRegistryServer({
17463
- rxaStore: new FileSystemRXAStore2(join6(storagePath, "blobs")),
17464
- rxmStore: new FileSystemRXMStore2(join6(storagePath, "manifests"))
18311
+ rxaStore: new FileSystemRXAStore2(join10(storagePath, "blobs")),
18312
+ rxmStore: new FileSystemRXMStore2(join10(storagePath, "manifests"))
17465
18313
  });
17466
18314
  consola.info(`Starting registry server...`);
17467
18315
  console.log();
@@ -17538,4 +18386,4 @@ var main = defineCommand({
17538
18386
  });
17539
18387
  runMain(main);
17540
18388
 
17541
- //# debugId=D37BF1EEFFFDA7D564756E2164756E21
18389
+ //# debugId=F1DFD9AFB5D18EC864756E2164756E21