@resourcexjs/core 2.14.1 → 2.16.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 CHANGED
@@ -72,18 +72,21 @@ interface RXI {
72
72
  readonly path?: string;
73
73
  /** Resource name */
74
74
  readonly name: string;
75
- /** Tag (version or label). Defaults to "latest" if not specified. */
75
+ /** Tag (mutable pointer). Defaults to "latest" if not specified. */
76
76
  readonly tag: string;
77
+ /** Content digest (immutable hash, e.g., "sha256:abc123"). */
78
+ readonly digest?: string;
77
79
  }
78
80
  /**
79
81
  * Format RXI to locator string.
80
82
  *
81
- * Docker-style format: [registry/][path/]name[:tag]
83
+ * Docker-style format: [registry/][path/]name[:tag][@digest]
82
84
  *
83
85
  * Examples:
84
86
  * - { name: "hello", tag: "latest" } → "hello" (omit :latest)
85
87
  * - { name: "hello", tag: "1.0.0" } → "hello:1.0.0"
86
- * - { registry: "localhost:3098", name: "hello", tag: "1.0.0" } → "localhost:3098/hello:1.0.0"
88
+ * - { name: "hello", tag: "latest", digest: "sha256:abc" } → "hello@sha256:abc"
89
+ * - { name: "hello", tag: "beta", digest: "sha256:abc" } → "hello:beta@sha256:abc"
87
90
  *
88
91
  * @param rxi - Resource identifier
89
92
  * @returns Locator string
@@ -113,9 +116,11 @@ interface RXMDefinition {
113
116
  }
114
117
  /**
115
118
  * RXM Archive — packaging metadata.
116
- * Placeholder for future fields (digest, size, md5, etc.)
117
119
  */
118
- type RXMArchive = {};
120
+ interface RXMArchive {
121
+ /** Deterministic content digest computed from file-level digests. Format: sha256:<hex> */
122
+ readonly digest?: string;
123
+ }
119
124
  /**
120
125
  * File entry with metadata.
121
126
  */
@@ -242,7 +247,7 @@ interface TypeDetectionResult {
242
247
  readonly type: string;
243
248
  /** Detected resource name */
244
249
  readonly name: string;
245
- /** Tag/version (defaults to "latest" if not provided) */
250
+ /** Tag (defaults to "latest" if not provided) */
246
251
  readonly tag?: string;
247
252
  /** Description extracted from content */
248
253
  readonly description?: string;
@@ -293,6 +298,17 @@ interface TypeDetector {
293
298
  */
294
299
  declare function generateDefinition(result: TypeDetectionResult): RXD;
295
300
  /**
301
+ * PrototypeDetector - Detects prototype resources from prototype.json.
302
+ *
303
+ * Pattern:
304
+ * - Required: prototype.json file
305
+ * - Optional: *.feature files (referenced via @filename)
306
+ */
307
+ declare class PrototypeDetector implements TypeDetector {
308
+ readonly name = "prototype";
309
+ detect(files: Record<string, Buffer>, source: string): TypeDetectionResult | null;
310
+ }
311
+ /**
296
312
  * ResourceJsonDetector - Detects resources with an explicit resource.json.
297
313
  *
298
314
  * Highest-priority detector. When resource.json exists,
@@ -330,7 +346,8 @@ declare class SkillDetector implements TypeDetector {
330
346
  * Detection order:
331
347
  * 1. ResourceJsonDetector (explicit resource.json always wins)
332
348
  * 2. SkillDetector (SKILL.md pattern)
333
- * 3. Custom detectors (registered in order)
349
+ * 3. PrototypeDetector (prototype.json pattern)
350
+ * 4. Custom detectors (registered in order)
334
351
  */
335
352
  declare class TypeDetectorChain {
336
353
  private readonly detectors;
@@ -415,21 +432,6 @@ interface SourceLoader {
415
432
  * @throws ResourceXError if loading fails
416
433
  */
417
434
  load(source: string): Promise<RXS>;
418
- /**
419
- * Check if cached content for this source is still fresh.
420
- *
421
- * Each loader implements its own strategy:
422
- * - FolderSourceLoader: compare file mtime against cachedAt
423
- * - GitHubSourceLoader: not implemented (always stale)
424
- *
425
- * Loaders that don't implement this are treated as always stale,
426
- * causing a full reload on every ingest.
427
- *
428
- * @param source - Source path or identifier
429
- * @param cachedAt - When the resource was last cached
430
- * @returns true if cache is still fresh, false if stale
431
- */
432
- isFresh?(source: string, cachedAt: Date): Promise<boolean>;
433
435
  }
434
436
  /**
435
437
  * Default ResourceLoader implementation for loading resources from folders.
@@ -446,8 +448,8 @@ interface SourceLoader {
446
448
  * {
447
449
  * "name": "resource-name", // required
448
450
  * "type": "text", // required
449
- * "version": "1.0.0", // required
450
- * "domain": "localhost", // optional, defaults to "localhost"
451
+ * "tag": "1.0.0", // optional, defaults to "latest"
452
+ * "registry": "localhost", // optional
451
453
  * "path": "optional/path" // optional
452
454
  * }
453
455
  * ```
@@ -473,15 +475,6 @@ declare class FolderSourceLoader implements SourceLoader {
473
475
  canLoad(source: string): Promise<boolean>;
474
476
  load(source: string): Promise<RXS>;
475
477
  /**
476
- * Check if cached content is still fresh by comparing file mtimes.
477
- * Returns true only if no file in the directory has been modified since cachedAt.
478
- */
479
- isFresh(source: string, cachedAt: Date): Promise<boolean>;
480
- /**
481
- * Get the most recent mtime across all files in a folder (recursive).
482
- */
483
- private getMaxMtime;
484
- /**
485
478
  * Recursively read all files in a folder.
486
479
  */
487
480
  private readFolderFiles;
@@ -584,17 +577,6 @@ declare class SourceLoaderChain {
584
577
  * @throws ResourceXError if no loader matches
585
578
  */
586
579
  load(source: string): Promise<RXS>;
587
- /**
588
- * Check if cached content for a source is still fresh.
589
- *
590
- * Delegates to the matching loader's isFresh method.
591
- * Returns false if the loader doesn't implement isFresh (always reload).
592
- *
593
- * @param source - Source path or identifier
594
- * @param cachedAt - When the resource was last cached
595
- * @returns true if cache is still fresh
596
- */
597
- isFresh(source: string, cachedAt: Date): Promise<boolean>;
598
580
  }
599
581
  /**
600
582
  * Configuration for resolveSource.
@@ -684,6 +666,7 @@ interface StoredRXM {
684
666
  readonly license?: string;
685
667
  readonly keywords?: string[];
686
668
  readonly repository?: string;
669
+ readonly digest?: string;
687
670
  readonly files: Record<string, string>;
688
671
  readonly createdAt?: Date;
689
672
  readonly updatedAt?: Date;
@@ -778,19 +761,6 @@ interface ProviderStores {
778
761
  rxmStore: RXMStore;
779
762
  }
780
763
  /**
781
- * Resource loader interface for loading from directories/archives.
782
- */
783
- interface ResourceLoader2 {
784
- /**
785
- * Check if this loader can handle the given source.
786
- */
787
- canLoad(source: string): boolean | Promise<boolean>;
788
- /**
789
- * Load resource from source.
790
- */
791
- load(source: string): Promise<unknown>;
792
- }
793
- /**
794
764
  * Platform-specific defaults resolved from environment variables and config files.
795
765
  */
796
766
  interface ProviderDefaults {
@@ -823,11 +793,6 @@ interface ResourceXProvider {
823
793
  */
824
794
  createStores(config: ProviderConfig): ProviderStores;
825
795
  /**
826
- * Create resource loader (optional).
827
- * Not all platforms support loading from filesystem.
828
- */
829
- createLoader?(config: ProviderConfig): ResourceLoader2;
830
- /**
831
796
  * Create source loader for auto-detection pipeline (optional).
832
797
  */
833
798
  createSourceLoader?(config: ProviderConfig): SourceLoader;
@@ -915,8 +880,9 @@ interface Registry {
915
880
  get(rxi: RXI): Promise<RXR>;
916
881
  /**
917
882
  * Store resource.
883
+ * @returns The stored manifest (with computed digest).
918
884
  */
919
- put(rxr: RXR): Promise<void>;
885
+ put(rxr: RXR): Promise<RXM>;
920
886
  /**
921
887
  * Check if resource exists.
922
888
  */
@@ -936,7 +902,7 @@ declare class CASRegistry implements Registry {
936
902
  constructor(rxaStore: RXAStore, rxmStore: RXMStore);
937
903
  private resolveTag;
938
904
  get(rxi: RXI): Promise<RXR>;
939
- put(rxr: RXR): Promise<void>;
905
+ put(rxr: RXR): Promise<RXM>;
940
906
  has(rxi: RXI): Promise<boolean>;
941
907
  remove(rxi: RXI): Promise<void>;
942
908
  list(options?: SearchOptions): Promise<RXI[]>;
@@ -994,7 +960,7 @@ declare class LinkedRegistry implements Registry {
994
960
  * Put is not typically used for LinkedRegistry.
995
961
  * Use link() instead to create symlinks.
996
962
  */
997
- put(_rxr: RXR): Promise<void>;
963
+ put(_rxr: RXR): Promise<RXM>;
998
964
  has(rxi: RXI): Promise<boolean>;
999
965
  remove(rxi: RXI): Promise<void>;
1000
966
  list(options?: SearchOptions): Promise<RXI[]>;
@@ -1030,7 +996,7 @@ declare abstract class RegistryMiddleware implements Registry {
1030
996
  protected readonly inner: Registry;
1031
997
  constructor(inner: Registry);
1032
998
  get(rxi: RXI): Promise<RXR>;
1033
- put(rxr: RXR): Promise<void>;
999
+ put(rxr: RXR): Promise<RXM>;
1034
1000
  has(rxi: RXI): Promise<boolean>;
1035
1001
  remove(rxi: RXI): Promise<void>;
1036
1002
  list(options?: SearchOptions): Promise<RXI[]>;
@@ -1129,7 +1095,7 @@ interface ResolveContext {
1129
1095
  path?: string
1130
1096
  name: string
1131
1097
  type: string
1132
- version: string
1098
+ tag: string
1133
1099
  };
1134
1100
  /**
1135
1101
  * Extracted files from archive.
@@ -1265,6 +1231,14 @@ declare const jsonType: BundledType;
1265
1231
  */
1266
1232
  declare const binaryType: BundledType;
1267
1233
  /**
1234
+ * Skill content (SKILL.md + optional references)
1235
+ */
1236
+ declare const skillType: BundledType;
1237
+ /**
1238
+ * Prototype instruction set (prototype.json + @filename references)
1239
+ */
1240
+ declare const prototypeType: BundledType;
1241
+ /**
1268
1242
  * All built-in types as an array.
1269
1243
  */
1270
1244
  declare const builtinTypes: BundledType[];
@@ -1337,4 +1311,4 @@ declare class TypeHandlerChain {
1337
1311
  */
1338
1312
  clear(): void;
1339
1313
  }
1340
- export { wrap, withDomainValidation, textType, resource, resolveSource, parse, manifest, locate, loadResource, jsonType, isValidDigest, generateDefinition, format, extract, discoverRegistry, define, computeDigest, bundleResourceType, builtinTypes, binaryType, archive, WellKnownResponse, TypeHandlerChain, TypeDetectorChain, TypeDetector, TypeDetectionResult, StoredRXM, SourceLoaderChain, SourceLoader, SkillDetector, SearchOptions, ResourceXProvider, ResourceXError, ResourceTypeError, ResourceType, ResourceResolver, ResourceLoader, ResourceJsonDetector, ResolvedResource, ResolveSourceConfig, ResolveContext, RegistryMiddleware, RegistryError, RegistryEntry, Registry, RXS, RXR, RXMStore, RXMSource, RXMSearchOptions, RXMDefinition, RXMArchive, RXM, RXL, RXI, RXD, RXAStore, RXA, ProviderStores, ProviderDefaults, ProviderConfig, MemoryRXMStore, MemoryRXAStore, ManifestError, LocatorError, LoadResourceConfig, LinkedRegistry, JSONSchemaProperty, JSONSchema, IsolatorType, GitHubSourceLoader, FolderSourceLoader, FolderLoader, FileTree, FileEntry, DomainValidation, DiscoveryResult, DefinitionError, ContentError, CASRegistry, BundledType };
1314
+ export { wrap, withDomainValidation, textType, skillType, resource, resolveSource, prototypeType, parse, manifest, locate, loadResource, jsonType, isValidDigest, generateDefinition, format, extract, discoverRegistry, define, computeDigest, bundleResourceType, builtinTypes, binaryType, archive, WellKnownResponse, TypeHandlerChain, TypeDetectorChain, TypeDetector, TypeDetectionResult, StoredRXM, SourceLoaderChain, SourceLoader, SkillDetector, SearchOptions, ResourceXProvider, ResourceXError, ResourceTypeError, ResourceType, ResourceResolver, ResourceLoader, ResourceJsonDetector, ResolvedResource, ResolveSourceConfig, ResolveContext, RegistryMiddleware, RegistryError, RegistryEntry, Registry, RXS, RXR, RXMStore, RXMSource, RXMSearchOptions, RXMDefinition, RXMArchive, RXM, RXL, RXI, RXD, RXAStore, RXA, ProviderStores, ProviderDefaults, ProviderConfig, PrototypeDetector, MemoryRXMStore, MemoryRXAStore, ManifestError, LocatorError, LoadResourceConfig, LinkedRegistry, JSONSchemaProperty, JSONSchema, IsolatorType, GitHubSourceLoader, FolderSourceLoader, FolderLoader, FileTree, FileEntry, DomainValidation, DiscoveryResult, DefinitionError, ContentError, CASRegistry, BundledType };
package/dist/index.js CHANGED
@@ -14597,7 +14597,6 @@ var RXDSchema = exports_external.object({
14597
14597
  name: exports_external.string().min(1).max(128),
14598
14598
  type: exports_external.string().min(1).max(64),
14599
14599
  tag: exports_external.string().max(64).optional(),
14600
- version: exports_external.string().max(64).optional(),
14601
14600
  registry: exports_external.string().max(256).optional(),
14602
14601
  path: exports_external.string().max(256).optional(),
14603
14602
  description: exports_external.string().max(1024).optional(),
@@ -14619,7 +14618,7 @@ function define(input) {
14619
14618
  const rxd = Object.assign(Object.create(null), {
14620
14619
  name: validated.name,
14621
14620
  type: validated.type,
14622
- tag: validated.tag ?? validated.version ?? undefined,
14621
+ tag: validated.tag ?? undefined,
14623
14622
  registry: validated.registry,
14624
14623
  path: validated.path,
14625
14624
  description: validated.description,
@@ -14659,6 +14658,9 @@ function format(rxi) {
14659
14658
  if (rxi.tag && rxi.tag !== "latest") {
14660
14659
  result += `:${rxi.tag}`;
14661
14660
  }
14661
+ if (rxi.digest) {
14662
+ result += `@${rxi.digest}`;
14663
+ }
14662
14664
  return result;
14663
14665
  }
14664
14666
  // src/model/locate.ts
@@ -14737,15 +14739,25 @@ function parse5(locator) {
14737
14739
  throw new LocatorError("Locator must be a non-empty string", locator);
14738
14740
  }
14739
14741
  validateLocatorSecurity(locator);
14740
- if (locator.includes("@")) {
14741
- throw new LocatorError("Invalid locator format. Use name:tag instead of name@version", locator);
14742
+ let digest;
14743
+ let locatorWithoutDigest = locator;
14744
+ const atIndex = locator.indexOf("@");
14745
+ if (atIndex !== -1) {
14746
+ if (atIndex === 0) {
14747
+ throw new LocatorError("Invalid locator format. Name is required before @", locator);
14748
+ }
14749
+ digest = locator.substring(atIndex + 1);
14750
+ locatorWithoutDigest = locator.substring(0, atIndex);
14751
+ if (!digest || digest.includes("@")) {
14752
+ throw new LocatorError("Invalid digest format after @", locator);
14753
+ }
14742
14754
  }
14743
- const lastSlashIndex = locator.lastIndexOf("/");
14755
+ const lastSlashIndex = locatorWithoutDigest.lastIndexOf("/");
14744
14756
  let beforeSlash = "";
14745
- let afterSlash = locator;
14757
+ let afterSlash = locatorWithoutDigest;
14746
14758
  if (lastSlashIndex !== -1) {
14747
- beforeSlash = locator.substring(0, lastSlashIndex);
14748
- afterSlash = locator.substring(lastSlashIndex + 1);
14759
+ beforeSlash = locatorWithoutDigest.substring(0, lastSlashIndex);
14760
+ afterSlash = locatorWithoutDigest.substring(lastSlashIndex + 1);
14749
14761
  }
14750
14762
  const colonIndex = afterSlash.lastIndexOf(":");
14751
14763
  let name;
@@ -14768,7 +14780,8 @@ function parse5(locator) {
14768
14780
  registry: undefined,
14769
14781
  path: undefined,
14770
14782
  name,
14771
- tag
14783
+ tag,
14784
+ digest
14772
14785
  };
14773
14786
  }
14774
14787
  const parts = beforeSlash.split("/");
@@ -14779,14 +14792,16 @@ function parse5(locator) {
14779
14792
  registry: registry2,
14780
14793
  path,
14781
14794
  name,
14782
- tag
14795
+ tag,
14796
+ digest
14783
14797
  };
14784
14798
  }
14785
14799
  return {
14786
14800
  registry: undefined,
14787
14801
  path: beforeSlash,
14788
14802
  name,
14789
- tag
14803
+ tag,
14804
+ digest
14790
14805
  };
14791
14806
  }
14792
14807
  // src/model/resource.ts
@@ -14844,6 +14859,23 @@ function generateDefinition(result) {
14844
14859
  input.repository = result.repository;
14845
14860
  return define(input);
14846
14861
  }
14862
+ // src/detector/PrototypeDetector.ts
14863
+ import { basename } from "node:path";
14864
+
14865
+ class PrototypeDetector {
14866
+ name = "prototype";
14867
+ detect(files, source) {
14868
+ if (!files["prototype.json"]) {
14869
+ return null;
14870
+ }
14871
+ const name = basename(source);
14872
+ return {
14873
+ type: "prototype",
14874
+ name,
14875
+ description: `Prototype instruction set: ${name}`
14876
+ };
14877
+ }
14878
+ }
14847
14879
  // src/detector/ResourceJsonDetector.ts
14848
14880
  class ResourceJsonDetector {
14849
14881
  name = "resource-json";
@@ -14864,7 +14896,7 @@ class ResourceJsonDetector {
14864
14896
  return {
14865
14897
  type: json2.type,
14866
14898
  name: json2.name,
14867
- tag: json2.tag ?? json2.version ?? undefined,
14899
+ tag: json2.tag,
14868
14900
  description: json2.description,
14869
14901
  registry: json2.registry,
14870
14902
  path: json2.path,
@@ -14877,7 +14909,7 @@ class ResourceJsonDetector {
14877
14909
  }
14878
14910
  }
14879
14911
  // src/detector/SkillDetector.ts
14880
- import { basename } from "node:path";
14912
+ import { basename as basename2 } from "node:path";
14881
14913
 
14882
14914
  class SkillDetector {
14883
14915
  name = "skill";
@@ -14885,7 +14917,7 @@ class SkillDetector {
14885
14917
  if (!files["SKILL.md"]) {
14886
14918
  return null;
14887
14919
  }
14888
- const name = basename(source);
14920
+ const name = basename2(source);
14889
14921
  const content = files["SKILL.md"].toString("utf-8");
14890
14922
  const description = this.extractDescription(content);
14891
14923
  return {
@@ -14914,6 +14946,7 @@ class TypeDetectorChain {
14914
14946
  const chain = new TypeDetectorChain;
14915
14947
  chain.detectors.push(new ResourceJsonDetector);
14916
14948
  chain.detectors.push(new SkillDetector);
14949
+ chain.detectors.push(new PrototypeDetector);
14917
14950
  return chain;
14918
14951
  }
14919
14952
  register(detector) {
@@ -15008,31 +15041,6 @@ class FolderSourceLoader {
15008
15041
  const files = await this.readFolderFiles(source);
15009
15042
  return { source, files };
15010
15043
  }
15011
- async isFresh(source, cachedAt) {
15012
- try {
15013
- const maxMtime = await this.getMaxMtime(source);
15014
- return maxMtime <= cachedAt;
15015
- } catch {
15016
- return false;
15017
- }
15018
- }
15019
- async getMaxMtime(folderPath) {
15020
- let max = new Date(0);
15021
- const entries = await readdir2(folderPath, { withFileTypes: true });
15022
- for (const entry of entries) {
15023
- const fullPath = join2(folderPath, entry.name);
15024
- if (entry.isFile()) {
15025
- const stats = await stat2(fullPath);
15026
- if (stats.mtime > max)
15027
- max = stats.mtime;
15028
- } else if (entry.isDirectory()) {
15029
- const subMax = await this.getMaxMtime(fullPath);
15030
- if (subMax > max)
15031
- max = subMax;
15032
- }
15033
- }
15034
- return max;
15035
- }
15036
15044
  async readFolderFiles(folderPath, basePath = folderPath) {
15037
15045
  const files = {};
15038
15046
  const entries = await readdir2(folderPath, { withFileTypes: true });
@@ -15154,17 +15162,6 @@ class SourceLoaderChain {
15154
15162
  }
15155
15163
  throw new ResourceXError(`Cannot load source: ${source}`);
15156
15164
  }
15157
- async isFresh(source, cachedAt) {
15158
- for (const loader of this.loaders) {
15159
- if (await loader.canLoad(source)) {
15160
- if (loader.isFresh) {
15161
- return loader.isFresh(source, cachedAt);
15162
- }
15163
- return false;
15164
- }
15165
- }
15166
- return false;
15167
- }
15168
15165
  }
15169
15166
 
15170
15167
  // src/loader/resolveSource.ts
@@ -15278,6 +15275,22 @@ function withRegistryValidation(registry2, trustedRegistry) {
15278
15275
  }
15279
15276
  var DomainValidation = RegistryValidation;
15280
15277
  var withDomainValidation = withRegistryValidation;
15278
+ // src/registry/store/digest.ts
15279
+ import { createHash } from "node:crypto";
15280
+ function computeDigest(data) {
15281
+ const hash2 = createHash("sha256").update(data).digest("hex");
15282
+ return `sha256:${hash2}`;
15283
+ }
15284
+ function computeArchiveDigest(files) {
15285
+ const entries = Object.keys(files).sort().map((name) => `${name}:${files[name]}`).join(`
15286
+ `);
15287
+ const hash2 = createHash("sha256").update(entries).digest("hex");
15288
+ return `sha256:${hash2}`;
15289
+ }
15290
+ function isValidDigest(digest) {
15291
+ return /^sha256:[a-f0-9]{64}$/.test(digest);
15292
+ }
15293
+
15281
15294
  // src/registry/registries/CASRegistry.ts
15282
15295
  class CASRegistry {
15283
15296
  rxaStore;
@@ -15320,7 +15333,9 @@ class CASRegistry {
15320
15333
  keywords: storedRxm.keywords,
15321
15334
  repository: storedRxm.repository
15322
15335
  },
15323
- archive: {},
15336
+ archive: {
15337
+ digest: storedRxm.digest ?? computeArchiveDigest(storedRxm.files)
15338
+ },
15324
15339
  source: {}
15325
15340
  };
15326
15341
  const rxa = await archive(files);
@@ -15330,9 +15345,10 @@ class CASRegistry {
15330
15345
  const files = await extract(rxr.archive);
15331
15346
  const fileDigests = {};
15332
15347
  for (const [filename, content] of Object.entries(files)) {
15333
- const digest = await this.rxaStore.put(content);
15334
- fileDigests[filename] = digest;
15348
+ const digest2 = await this.rxaStore.put(content);
15349
+ fileDigests[filename] = digest2;
15335
15350
  }
15351
+ const digest = computeArchiveDigest(fileDigests);
15336
15352
  const storedRxm = {
15337
15353
  registry: rxr.manifest.definition.registry,
15338
15354
  path: rxr.manifest.definition.path,
@@ -15344,12 +15360,18 @@ class CASRegistry {
15344
15360
  license: rxr.manifest.definition.license,
15345
15361
  keywords: rxr.manifest.definition.keywords,
15346
15362
  repository: rxr.manifest.definition.repository,
15363
+ digest,
15347
15364
  files: fileDigests,
15348
15365
  createdAt: new Date,
15349
15366
  updatedAt: new Date
15350
15367
  };
15351
15368
  await this.rxmStore.put(storedRxm);
15352
15369
  await this.rxmStore.setLatest(rxr.manifest.definition.name, rxr.manifest.definition.tag, rxr.manifest.definition.registry);
15370
+ return {
15371
+ definition: rxr.manifest.definition,
15372
+ archive: { digest },
15373
+ source: rxr.manifest.source
15374
+ };
15353
15375
  }
15354
15376
  async has(rxi) {
15355
15377
  const tag = await this.resolveTag(rxi.name, rxi.tag ?? "latest", rxi.registry);
@@ -15548,15 +15570,6 @@ class LinkedRegistry {
15548
15570
  }
15549
15571
  }
15550
15572
  }
15551
- // src/registry/store/digest.ts
15552
- import { createHash } from "node:crypto";
15553
- function computeDigest(data) {
15554
- const hash2 = createHash("sha256").update(data).digest("hex");
15555
- return `sha256:${hash2}`;
15556
- }
15557
- function isValidDigest(digest) {
15558
- return /^sha256:[a-f0-9]{64}$/.test(digest);
15559
- }
15560
15573
  // src/registry/store/MemoryRXAStore.ts
15561
15574
  class MemoryRXAStore {
15562
15575
  blobs = new Map;
@@ -15760,7 +15773,47 @@ var skill_type_default = {
15760
15773
  }
15761
15774
  }
15762
15775
  };
15763
- var builtinTypes = [textType, jsonType, binaryType, skillType];
15776
+ var prototypeType = {
15777
+ name: "prototype",
15778
+ description: "Instruction set for materializing roles and organizations",
15779
+ code: `// @resolver: prototype_type_default
15780
+ var prototype_type_default = {
15781
+ async resolve(ctx) {
15782
+ var protoFile = ctx.files["prototype.json"];
15783
+ if (!protoFile) throw new Error("Prototype resource must contain a prototype.json file");
15784
+ var decoder = new TextDecoder();
15785
+ var instructions = JSON.parse(decoder.decode(protoFile));
15786
+ if (!Array.isArray(instructions)) {
15787
+ throw new Error("prototype.json must be a JSON array of instructions");
15788
+ }
15789
+ var resolved = instructions.map(function(instr) {
15790
+ var resolvedArgs = {};
15791
+ var keys = Object.keys(instr.args || {});
15792
+ for (var i = 0; i < keys.length; i++) {
15793
+ var key = keys[i];
15794
+ var value = instr.args[key];
15795
+ if (typeof value === "string" && value.startsWith("@")) {
15796
+ var filename = value.slice(1);
15797
+ var file = ctx.files[filename];
15798
+ if (!file) throw new Error("Referenced file not found: " + filename);
15799
+ resolvedArgs[key] = decoder.decode(file);
15800
+ } else {
15801
+ resolvedArgs[key] = value;
15802
+ }
15803
+ }
15804
+ return { op: instr.op, args: resolvedArgs };
15805
+ });
15806
+ return { id: ctx.manifest.name, instructions: resolved };
15807
+ }
15808
+ };`
15809
+ };
15810
+ var builtinTypes = [
15811
+ textType,
15812
+ jsonType,
15813
+ binaryType,
15814
+ skillType,
15815
+ prototypeType
15816
+ ];
15764
15817
  // src/type/bundler.ts
15765
15818
  import { readFile as readFile3 } from "node:fs/promises";
15766
15819
  import { isAbsolute, resolve } from "node:path";
@@ -15863,8 +15916,10 @@ export {
15863
15916
  wrap,
15864
15917
  withDomainValidation,
15865
15918
  textType,
15919
+ skillType,
15866
15920
  resource,
15867
15921
  resolveSource,
15922
+ prototypeType,
15868
15923
  parse5 as parse,
15869
15924
  manifest,
15870
15925
  locate,
@@ -15890,6 +15945,7 @@ export {
15890
15945
  ResourceJsonDetector,
15891
15946
  RegistryMiddleware,
15892
15947
  RegistryError,
15948
+ PrototypeDetector,
15893
15949
  MemoryRXMStore,
15894
15950
  MemoryRXAStore,
15895
15951
  ManifestError,
@@ -15904,4 +15960,4 @@ export {
15904
15960
  CASRegistry
15905
15961
  };
15906
15962
 
15907
- //# debugId=B122DF8CA052DA8364756E2164756E21
15963
+ //# debugId=0BF8A796080AFBD664756E2164756E21