@resourcexjs/core 0.9.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +124 -122
- package/dist/index.d.ts +1 -208
- package/dist/index.js +1 -241
- package/dist/index.js.map +5 -10
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @resourcexjs/core
|
|
2
2
|
|
|
3
|
-
Core
|
|
3
|
+
Core data structures for ResourceX.
|
|
4
4
|
|
|
5
5
|
> **Note**: For most use cases, use the main [`resourcexjs`](https://www.npmjs.com/package/resourcexjs) package. This package is for advanced usage.
|
|
6
6
|
|
|
@@ -14,14 +14,12 @@ bun add @resourcexjs/core
|
|
|
14
14
|
|
|
15
15
|
## What's Inside
|
|
16
16
|
|
|
17
|
-
Core building blocks
|
|
17
|
+
Core building blocks - pure data structures:
|
|
18
18
|
|
|
19
|
-
- **RXL** (Locator) -
|
|
20
|
-
- **RXM** (Manifest) -
|
|
19
|
+
- **RXL** (Locator) - Resource locator string parser
|
|
20
|
+
- **RXM** (Manifest) - Resource metadata
|
|
21
21
|
- **RXC** (Content) - Stream-based content
|
|
22
22
|
- **RXR** (Resource) - Complete resource type
|
|
23
|
-
- **ResourceType** - Type system with serializer & resolver
|
|
24
|
-
- **TypeHandlerChain** - Responsibility chain for type handling
|
|
25
23
|
- **Errors** - Error hierarchy
|
|
26
24
|
|
|
27
25
|
## API
|
|
@@ -43,6 +41,13 @@ rxl.version; // "1.0.0"
|
|
|
43
41
|
rxl.toString(); // "deepractice.ai/sean/assistant.prompt@1.0.0"
|
|
44
42
|
```
|
|
45
43
|
|
|
44
|
+
**Minimal locator:**
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
parseRXL("name.text@1.0.0");
|
|
48
|
+
// → domain: "localhost", path: undefined, name: "name", type: "text", version: "1.0.0"
|
|
49
|
+
```
|
|
50
|
+
|
|
46
51
|
### RXM - Resource Manifest
|
|
47
52
|
|
|
48
53
|
Create and validate resource metadata:
|
|
@@ -56,13 +61,17 @@ const manifest = createRXM({
|
|
|
56
61
|
name: "assistant",
|
|
57
62
|
type: "prompt",
|
|
58
63
|
version: "1.0.0",
|
|
64
|
+
description: "Optional description", // optional
|
|
65
|
+
tags: ["optional", "tags"], // optional
|
|
59
66
|
});
|
|
60
67
|
|
|
61
68
|
manifest.toLocator(); // → "deepractice.ai/sean/assistant.prompt@1.0.0"
|
|
62
69
|
manifest.toJSON(); // → plain object
|
|
63
70
|
```
|
|
64
71
|
|
|
65
|
-
Required fields
|
|
72
|
+
**Required fields**: `name`, `type`, `version`
|
|
73
|
+
|
|
74
|
+
**Optional fields**: `domain` (default: "localhost"), `path`, `description`, `tags`
|
|
66
75
|
|
|
67
76
|
### RXC - Resource Content
|
|
68
77
|
|
|
@@ -87,9 +96,17 @@ const json = await content.json<T>(); // → T
|
|
|
87
96
|
const stream = content.stream; // → ReadableStream<Uint8Array>
|
|
88
97
|
```
|
|
89
98
|
|
|
99
|
+
**Important**: Content can only be consumed once!
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
const content = createRXC("Hello");
|
|
103
|
+
await content.text(); // ✅ "Hello"
|
|
104
|
+
await content.text(); // ❌ ContentError: already consumed
|
|
105
|
+
```
|
|
106
|
+
|
|
90
107
|
### RXR - Resource
|
|
91
108
|
|
|
92
|
-
Complete resource object (pure interface
|
|
109
|
+
Complete resource object (pure interface):
|
|
93
110
|
|
|
94
111
|
```typescript
|
|
95
112
|
import type { RXR } from "@resourcexjs/core";
|
|
@@ -101,156 +118,141 @@ interface RXR {
|
|
|
101
118
|
}
|
|
102
119
|
|
|
103
120
|
// Create from literals
|
|
104
|
-
const rxr: RXR = {
|
|
105
|
-
locator: parseRXL("localhost/test.text@1.0.0"),
|
|
106
|
-
manifest: createRXM({ domain: "localhost", name: "test", type: "text", version: "1.0.0" }),
|
|
107
|
-
content: createRXC("content"),
|
|
108
|
-
};
|
|
121
|
+
const rxr: RXR = { locator, manifest, content };
|
|
109
122
|
```
|
|
110
123
|
|
|
111
|
-
|
|
124
|
+
RXR is a pure DTO (Data Transfer Object) - no factory function needed.
|
|
112
125
|
|
|
113
|
-
|
|
126
|
+
## Error Handling
|
|
114
127
|
|
|
115
128
|
```typescript
|
|
116
|
-
import {
|
|
129
|
+
import { ResourceXError, LocatorError, ManifestError, ContentError } from "@resourcexjs/core";
|
|
117
130
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
131
|
+
try {
|
|
132
|
+
parseRXL("invalid-locator");
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (error instanceof LocatorError) {
|
|
135
|
+
console.error("Invalid locator format");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
123
138
|
|
|
124
|
-
|
|
139
|
+
try {
|
|
140
|
+
createRXM({ name: "test" }); // Missing required fields
|
|
141
|
+
} catch (error) {
|
|
142
|
+
if (error instanceof ManifestError) {
|
|
143
|
+
console.error("Invalid manifest");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
125
146
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
async serialize(rxr: RXR): Promise<Buffer> {
|
|
135
|
-
// Convert RXR to Buffer for storage
|
|
136
|
-
const text = await rxr.content.text();
|
|
137
|
-
return Buffer.from(JSON.stringify({ template: text }));
|
|
138
|
-
},
|
|
139
|
-
async deserialize(data: Buffer, manifest: RXM): Promise<RXR> {
|
|
140
|
-
// Convert Buffer back to RXR
|
|
141
|
-
const obj = JSON.parse(data.toString());
|
|
142
|
-
return {
|
|
143
|
-
locator: parseRXL(manifest.toLocator()),
|
|
144
|
-
manifest,
|
|
145
|
-
content: createRXC(obj.template),
|
|
146
|
-
};
|
|
147
|
-
},
|
|
148
|
-
},
|
|
149
|
-
resolver: {
|
|
150
|
-
async resolve(rxr: RXR): Promise<PromptTemplate> {
|
|
151
|
-
// Convert RXR to usable object
|
|
152
|
-
return {
|
|
153
|
-
template: await rxr.content.text(),
|
|
154
|
-
compile: (vars) => {
|
|
155
|
-
/* ... */
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
});
|
|
147
|
+
try {
|
|
148
|
+
await content.text();
|
|
149
|
+
await content.text(); // Second consumption
|
|
150
|
+
} catch (error) {
|
|
151
|
+
if (error instanceof ContentError) {
|
|
152
|
+
console.error("Content already consumed");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
161
155
|
```
|
|
162
156
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
import { getResourceType, clearResourceTypes } from "@resourcexjs/core";
|
|
167
|
-
|
|
168
|
-
const type = getResourceType("text");
|
|
169
|
-
const type = getResourceType("txt"); // Works with aliases
|
|
157
|
+
### Error Hierarchy
|
|
170
158
|
|
|
171
|
-
|
|
159
|
+
```
|
|
160
|
+
ResourceXError (base)
|
|
161
|
+
├── LocatorError
|
|
162
|
+
├── ManifestError
|
|
163
|
+
└── ContentError
|
|
172
164
|
```
|
|
173
165
|
|
|
174
|
-
|
|
166
|
+
## Examples
|
|
175
167
|
|
|
176
|
-
|
|
168
|
+
### Complete Resource Creation
|
|
177
169
|
|
|
178
170
|
```typescript
|
|
179
|
-
import {
|
|
171
|
+
import { parseRXL, createRXM, createRXC } from "@resourcexjs/core";
|
|
172
|
+
import type { RXR } from "@resourcexjs/core";
|
|
180
173
|
|
|
181
|
-
// Create
|
|
182
|
-
const
|
|
174
|
+
// Create manifest
|
|
175
|
+
const manifest = createRXM({
|
|
176
|
+
domain: "deepractice.ai",
|
|
177
|
+
name: "assistant",
|
|
178
|
+
type: "prompt",
|
|
179
|
+
version: "1.0.0",
|
|
180
|
+
});
|
|
183
181
|
|
|
184
|
-
//
|
|
185
|
-
const
|
|
186
|
-
chain.register(customType);
|
|
187
|
-
chain.registerAll([type1, type2]);
|
|
182
|
+
// Create locator from manifest
|
|
183
|
+
const locator = parseRXL(manifest.toLocator());
|
|
188
184
|
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
chain.getHandler("txt"); // → ResourceType (via alias)
|
|
185
|
+
// Create content
|
|
186
|
+
const content = createRXC("You are a helpful assistant.");
|
|
192
187
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
188
|
+
// Assemble RXR
|
|
189
|
+
const rxr: RXR = {
|
|
190
|
+
locator,
|
|
191
|
+
manifest,
|
|
192
|
+
content,
|
|
193
|
+
};
|
|
196
194
|
```
|
|
197
195
|
|
|
198
|
-
|
|
196
|
+
### Load Content from File
|
|
199
197
|
|
|
200
198
|
```typescript
|
|
201
|
-
import {
|
|
202
|
-
ResourceXError,
|
|
203
|
-
LocatorError,
|
|
204
|
-
ManifestError,
|
|
205
|
-
ContentError,
|
|
206
|
-
ResourceTypeError,
|
|
207
|
-
} from "@resourcexjs/core";
|
|
199
|
+
import { loadRXC } from "@resourcexjs/core";
|
|
208
200
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
201
|
+
// Load from local file
|
|
202
|
+
const content = await loadRXC("./prompt.txt");
|
|
203
|
+
const text = await content.text();
|
|
204
|
+
|
|
205
|
+
// Load from URL
|
|
206
|
+
const remoteContent = await loadRXC("https://example.com/prompt.txt");
|
|
207
|
+
const remoteText = await remoteContent.text();
|
|
216
208
|
```
|
|
217
209
|
|
|
218
|
-
|
|
210
|
+
### Manifest Serialization
|
|
219
211
|
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
212
|
+
```typescript
|
|
213
|
+
const manifest = createRXM({
|
|
214
|
+
name: "assistant",
|
|
215
|
+
type: "prompt",
|
|
216
|
+
version: "1.0.0",
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// To JSON (for storage)
|
|
220
|
+
const json = manifest.toJSON();
|
|
221
|
+
/*
|
|
222
|
+
{
|
|
223
|
+
"domain": "localhost",
|
|
224
|
+
"name": "assistant",
|
|
225
|
+
"type": "prompt",
|
|
226
|
+
"version": "1.0.0"
|
|
227
|
+
}
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
// To locator string
|
|
231
|
+
const locator = manifest.toLocator();
|
|
232
|
+
// "localhost/assistant.prompt@1.0.0"
|
|
227
233
|
```
|
|
228
234
|
|
|
229
|
-
##
|
|
235
|
+
## Related Packages
|
|
230
236
|
|
|
231
|
-
|
|
232
|
-
// Errors
|
|
233
|
-
export { ResourceXError, LocatorError, ManifestError, ContentError, ResourceTypeError };
|
|
237
|
+
This package provides only data structures. For full functionality:
|
|
234
238
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
239
|
+
- **[@resourcexjs/type](../type)** - Type system and handlers
|
|
240
|
+
- **[@resourcexjs/loader](../loader)** - Resource loading
|
|
241
|
+
- **[@resourcexjs/registry](../registry)** - Storage and retrieval
|
|
242
|
+
- **[@resourcexjs/arp](../arp)** - Low-level I/O
|
|
243
|
+
- **[resourcexjs](../resourcex)** - Main package (all-in-one)
|
|
238
244
|
|
|
239
|
-
|
|
240
|
-
export { createRXM };
|
|
241
|
-
export type { RXM, ManifestData };
|
|
245
|
+
## Type Safety
|
|
242
246
|
|
|
243
|
-
|
|
244
|
-
export { createRXC, loadRXC };
|
|
245
|
-
export type { RXC };
|
|
247
|
+
All types are fully typed with TypeScript:
|
|
246
248
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
+
```typescript
|
|
250
|
+
import type { RXL, RXM, RXC, RXR } from "@resourcexjs/core";
|
|
249
251
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
const locator: RXL = parseRXL("...");
|
|
253
|
+
const manifest: RXM = createRXM({ ... });
|
|
254
|
+
const content: RXC = createRXC("...");
|
|
255
|
+
const resource: RXR = { locator, manifest, content };
|
|
254
256
|
```
|
|
255
257
|
|
|
256
258
|
## License
|
package/dist/index.d.ts
CHANGED
|
@@ -14,9 +14,6 @@ declare class ManifestError extends ResourceXError {
|
|
|
14
14
|
declare class ContentError extends ResourceXError {
|
|
15
15
|
constructor(message: string);
|
|
16
16
|
}
|
|
17
|
-
declare class ResourceTypeError extends ResourceXError {
|
|
18
|
-
constructor(message: string);
|
|
19
|
-
}
|
|
20
17
|
/**
|
|
21
18
|
* RXL - ResourceX Locator
|
|
22
19
|
*
|
|
@@ -91,208 +88,4 @@ interface RXR {
|
|
|
91
88
|
manifest: RXM;
|
|
92
89
|
content: RXC;
|
|
93
90
|
}
|
|
94
|
-
|
|
95
|
-
* ResourceSerializer - Handles RXR serialization/deserialization for storage.
|
|
96
|
-
*/
|
|
97
|
-
interface ResourceSerializer {
|
|
98
|
-
/**
|
|
99
|
-
* Serialize RXR to storage format.
|
|
100
|
-
*/
|
|
101
|
-
serialize(rxr: RXR): Promise<Buffer>;
|
|
102
|
-
/**
|
|
103
|
-
* Deserialize storage data to RXR.
|
|
104
|
-
*/
|
|
105
|
-
deserialize(data: Buffer, manifest: RXM): Promise<RXR>;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* ResourceResolver - Transforms RXR into usable object.
|
|
109
|
-
*/
|
|
110
|
-
interface ResourceResolver<T = unknown> {
|
|
111
|
-
/**
|
|
112
|
-
* Resolve RXR content into a usable object.
|
|
113
|
-
*/
|
|
114
|
-
resolve(rxr: RXR): Promise<T>;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* ResourceType - Defines how a resource type is handled.
|
|
118
|
-
*/
|
|
119
|
-
interface ResourceType<T = unknown> {
|
|
120
|
-
/**
|
|
121
|
-
* Type name (e.g., "text", "json", "binary").
|
|
122
|
-
*/
|
|
123
|
-
name: string;
|
|
124
|
-
/**
|
|
125
|
-
* Alternative names for this type (e.g., ["txt", "plaintext"]).
|
|
126
|
-
*/
|
|
127
|
-
aliases?: string[];
|
|
128
|
-
/**
|
|
129
|
-
* Human-readable description.
|
|
130
|
-
*/
|
|
131
|
-
description: string;
|
|
132
|
-
/**
|
|
133
|
-
* Serializer for storage operations.
|
|
134
|
-
*/
|
|
135
|
-
serializer: ResourceSerializer;
|
|
136
|
-
/**
|
|
137
|
-
* Resolver to transform RXR into usable object.
|
|
138
|
-
*/
|
|
139
|
-
resolver: ResourceResolver<T>;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* ResourceLoader - Strategy interface for loading resources from different sources.
|
|
143
|
-
*/
|
|
144
|
-
interface ResourceLoader {
|
|
145
|
-
/**
|
|
146
|
-
* Check if this loader can handle the given source.
|
|
147
|
-
*
|
|
148
|
-
* @param source - Source path or identifier
|
|
149
|
-
* @returns true if this loader can handle the source
|
|
150
|
-
*/
|
|
151
|
-
canLoad(source: string): boolean | Promise<boolean>;
|
|
152
|
-
/**
|
|
153
|
-
* Load a resource from the given source.
|
|
154
|
-
*
|
|
155
|
-
* @param source - Source path or identifier
|
|
156
|
-
* @returns Complete RXR object
|
|
157
|
-
* @throws ResourceXError if loading fails
|
|
158
|
-
*/
|
|
159
|
-
load(source: string): Promise<RXR>;
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Define and register a resource type.
|
|
163
|
-
*
|
|
164
|
-
* @throws ResourceTypeError if type is already registered
|
|
165
|
-
*/
|
|
166
|
-
declare function defineResourceType<T>(config: ResourceType<T>): ResourceType<T>;
|
|
167
|
-
/**
|
|
168
|
-
* Get a registered resource type by name.
|
|
169
|
-
*
|
|
170
|
-
* @returns ResourceType or undefined if not found
|
|
171
|
-
*/
|
|
172
|
-
declare function getResourceType<T = unknown>(name: string): ResourceType<T> | undefined;
|
|
173
|
-
/**
|
|
174
|
-
* Clear all registered resource types (for testing).
|
|
175
|
-
*/
|
|
176
|
-
declare function clearResourceTypes(): void;
|
|
177
|
-
/**
|
|
178
|
-
* Text resource type
|
|
179
|
-
*/
|
|
180
|
-
declare const textType: ResourceType<string>;
|
|
181
|
-
/**
|
|
182
|
-
* JSON resource type
|
|
183
|
-
*/
|
|
184
|
-
declare const jsonType: ResourceType<unknown>;
|
|
185
|
-
/**
|
|
186
|
-
* Binary resource type
|
|
187
|
-
*/
|
|
188
|
-
declare const binaryType: ResourceType<Buffer>;
|
|
189
|
-
/**
|
|
190
|
-
* All built-in types
|
|
191
|
-
*/
|
|
192
|
-
declare const builtinTypes: ResourceType[];
|
|
193
|
-
/**
|
|
194
|
-
* TypeHandlerChain - Responsibility chain for resource type handling.
|
|
195
|
-
* Manages type registration and delegates serialization/deserialization.
|
|
196
|
-
*/
|
|
197
|
-
declare class TypeHandlerChain {
|
|
198
|
-
private handlers;
|
|
199
|
-
/**
|
|
200
|
-
* Register a resource type handler.
|
|
201
|
-
* Registers both the type name and its aliases.
|
|
202
|
-
*/
|
|
203
|
-
register(type: ResourceType): void;
|
|
204
|
-
/**
|
|
205
|
-
* Register multiple type handlers.
|
|
206
|
-
*/
|
|
207
|
-
registerAll(types: ResourceType[]): void;
|
|
208
|
-
/**
|
|
209
|
-
* Check if a type is supported.
|
|
210
|
-
*/
|
|
211
|
-
canHandle(typeName: string): boolean;
|
|
212
|
-
/**
|
|
213
|
-
* Get handler for a type.
|
|
214
|
-
*/
|
|
215
|
-
getHandler(typeName: string): ResourceType | undefined;
|
|
216
|
-
/**
|
|
217
|
-
* Serialize RXR content using the appropriate type handler.
|
|
218
|
-
*/
|
|
219
|
-
serialize(rxr: RXR): Promise<Buffer>;
|
|
220
|
-
/**
|
|
221
|
-
* Deserialize content into RXR using the appropriate type handler.
|
|
222
|
-
*/
|
|
223
|
-
deserialize(data: Buffer, manifest: RXM): Promise<RXR>;
|
|
224
|
-
/**
|
|
225
|
-
* Resolve RXR content into usable object using the appropriate type handler.
|
|
226
|
-
*/
|
|
227
|
-
resolve<T = unknown>(rxr: RXR): Promise<T>;
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Create a new TypeHandlerChain with optional initial types.
|
|
231
|
-
*/
|
|
232
|
-
declare function createTypeHandlerChain(types?: ResourceType[]): TypeHandlerChain;
|
|
233
|
-
/**
|
|
234
|
-
* Default ResourceLoader implementation for loading resources from folders.
|
|
235
|
-
*
|
|
236
|
-
* Expected folder structure:
|
|
237
|
-
* ```
|
|
238
|
-
* folder/
|
|
239
|
-
* ├── resource.json # Resource metadata (required)
|
|
240
|
-
* └── content # Resource content (required)
|
|
241
|
-
* ```
|
|
242
|
-
*
|
|
243
|
-
* resource.json format:
|
|
244
|
-
* ```json
|
|
245
|
-
* {
|
|
246
|
-
* "name": "resource-name", // required
|
|
247
|
-
* "type": "text", // required
|
|
248
|
-
* "version": "1.0.0", // required
|
|
249
|
-
* "domain": "localhost", // optional, defaults to "localhost"
|
|
250
|
-
* "path": "optional/path" // optional
|
|
251
|
-
* }
|
|
252
|
-
* ```
|
|
253
|
-
*/
|
|
254
|
-
declare class FolderLoader implements ResourceLoader {
|
|
255
|
-
canLoad(source: string): Promise<boolean>;
|
|
256
|
-
load(folderPath: string): Promise<RXR>;
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Configuration options for loadResource.
|
|
260
|
-
*/
|
|
261
|
-
interface LoadResourceConfig {
|
|
262
|
-
/**
|
|
263
|
-
* Custom loader to use. If not provided, defaults to FolderLoader.
|
|
264
|
-
*/
|
|
265
|
-
loader?: ResourceLoader;
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Load a resource from a given source using a ResourceLoader.
|
|
269
|
-
*
|
|
270
|
-
* By default, uses FolderLoader which expects:
|
|
271
|
-
* ```
|
|
272
|
-
* folder/
|
|
273
|
-
* ├── resource.json # Resource metadata
|
|
274
|
-
* └── content # Resource content
|
|
275
|
-
* ```
|
|
276
|
-
*
|
|
277
|
-
* You can provide a custom loader via config.loader to support other formats
|
|
278
|
-
* (e.g., zip, tar.gz, URLs).
|
|
279
|
-
*
|
|
280
|
-
* @param source - Source path or identifier
|
|
281
|
-
* @param config - Optional configuration
|
|
282
|
-
* @returns Complete RXR object ready for registry.link()
|
|
283
|
-
* @throws ResourceXError if the source cannot be loaded
|
|
284
|
-
*
|
|
285
|
-
* @example
|
|
286
|
-
* ```typescript
|
|
287
|
-
* // Load from folder (default)
|
|
288
|
-
* const rxr = await loadResource("./my-resource");
|
|
289
|
-
* await registry.link(rxr);
|
|
290
|
-
*
|
|
291
|
-
* // Load with custom loader
|
|
292
|
-
* const rxr = await loadResource("resource.zip", {
|
|
293
|
-
* loader: new ZipLoader()
|
|
294
|
-
* });
|
|
295
|
-
* ```
|
|
296
|
-
*/
|
|
297
|
-
declare function loadResource(source: string, config?: LoadResourceConfig): Promise<RXR>;
|
|
298
|
-
export { textType, parseRXL, loadResource, loadRXC, jsonType, getResourceType, defineResourceType, createTypeHandlerChain, createRXM, createRXC, clearResourceTypes, builtinTypes, binaryType, TypeHandlerChain, ResourceXError, ResourceTypeError, ResourceType, ResourceSerializer, ResourceResolver, ResourceLoader, RXR, RXM, RXL, RXC, ManifestError, ManifestData, LocatorError, LoadResourceConfig, FolderLoader, ContentError };
|
|
91
|
+
export { parseRXL, loadRXC, createRXM, createRXC, ResourceXError, RXR, RXM, RXL, RXC, ManifestError, ManifestData, LocatorError, ContentError };
|
package/dist/index.js
CHANGED
|
@@ -28,13 +28,6 @@ class ContentError extends ResourceXError {
|
|
|
28
28
|
this.name = "ContentError";
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
class ResourceTypeError extends ResourceXError {
|
|
33
|
-
constructor(message) {
|
|
34
|
-
super(message);
|
|
35
|
-
this.name = "ResourceTypeError";
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
31
|
// src/locator/parseRXL.ts
|
|
39
32
|
class RXLImpl {
|
|
40
33
|
domain;
|
|
@@ -240,248 +233,15 @@ async function loadRXC(source) {
|
|
|
240
233
|
const webStream = Readable.toWeb(nodeStream);
|
|
241
234
|
return createRXC(webStream);
|
|
242
235
|
}
|
|
243
|
-
// src/resource/defineResourceType.ts
|
|
244
|
-
var resourceTypes = new Map;
|
|
245
|
-
function defineResourceType(config) {
|
|
246
|
-
if (resourceTypes.has(config.name)) {
|
|
247
|
-
throw new ResourceTypeError(`Resource type "${config.name}" is already registered`);
|
|
248
|
-
}
|
|
249
|
-
resourceTypes.set(config.name, config);
|
|
250
|
-
return config;
|
|
251
|
-
}
|
|
252
|
-
function getResourceType(name) {
|
|
253
|
-
return resourceTypes.get(name);
|
|
254
|
-
}
|
|
255
|
-
function clearResourceTypes() {
|
|
256
|
-
resourceTypes.clear();
|
|
257
|
-
}
|
|
258
|
-
// src/resource/builtinTypes.ts
|
|
259
|
-
var textSerializer = {
|
|
260
|
-
async serialize(rxr) {
|
|
261
|
-
const text = await rxr.content.text();
|
|
262
|
-
return Buffer.from(text, "utf-8");
|
|
263
|
-
},
|
|
264
|
-
async deserialize(data, manifest) {
|
|
265
|
-
const text = data.toString("utf-8");
|
|
266
|
-
return {
|
|
267
|
-
locator: parseRXL(manifest.toLocator()),
|
|
268
|
-
manifest,
|
|
269
|
-
content: createRXC(text)
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
var textResolver = {
|
|
274
|
-
async resolve(rxr) {
|
|
275
|
-
return rxr.content.text();
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
var textType = {
|
|
279
|
-
name: "text",
|
|
280
|
-
aliases: ["txt", "plaintext"],
|
|
281
|
-
description: "Plain text content",
|
|
282
|
-
serializer: textSerializer,
|
|
283
|
-
resolver: textResolver
|
|
284
|
-
};
|
|
285
|
-
var jsonSerializer = {
|
|
286
|
-
async serialize(rxr) {
|
|
287
|
-
const json = await rxr.content.json();
|
|
288
|
-
return Buffer.from(JSON.stringify(json, null, 2), "utf-8");
|
|
289
|
-
},
|
|
290
|
-
async deserialize(data, manifest) {
|
|
291
|
-
const text = data.toString("utf-8");
|
|
292
|
-
return {
|
|
293
|
-
locator: parseRXL(manifest.toLocator()),
|
|
294
|
-
manifest,
|
|
295
|
-
content: createRXC(text)
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
var jsonResolver = {
|
|
300
|
-
async resolve(rxr) {
|
|
301
|
-
return rxr.content.json();
|
|
302
|
-
}
|
|
303
|
-
};
|
|
304
|
-
var jsonType = {
|
|
305
|
-
name: "json",
|
|
306
|
-
aliases: ["config", "manifest"],
|
|
307
|
-
description: "JSON content",
|
|
308
|
-
serializer: jsonSerializer,
|
|
309
|
-
resolver: jsonResolver
|
|
310
|
-
};
|
|
311
|
-
var binarySerializer = {
|
|
312
|
-
async serialize(rxr) {
|
|
313
|
-
return rxr.content.buffer();
|
|
314
|
-
},
|
|
315
|
-
async deserialize(data, manifest) {
|
|
316
|
-
return {
|
|
317
|
-
locator: parseRXL(manifest.toLocator()),
|
|
318
|
-
manifest,
|
|
319
|
-
content: createRXC(data)
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
};
|
|
323
|
-
var binaryResolver = {
|
|
324
|
-
async resolve(rxr) {
|
|
325
|
-
return rxr.content.buffer();
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
var binaryType = {
|
|
329
|
-
name: "binary",
|
|
330
|
-
aliases: ["bin", "blob", "raw"],
|
|
331
|
-
description: "Binary content",
|
|
332
|
-
serializer: binarySerializer,
|
|
333
|
-
resolver: binaryResolver
|
|
334
|
-
};
|
|
335
|
-
var builtinTypes = [textType, jsonType, binaryType];
|
|
336
|
-
// src/resource/TypeHandlerChain.ts
|
|
337
|
-
class TypeHandlerChain {
|
|
338
|
-
handlers = new Map;
|
|
339
|
-
register(type) {
|
|
340
|
-
this.handlers.set(type.name, type);
|
|
341
|
-
if (type.aliases) {
|
|
342
|
-
for (const alias of type.aliases) {
|
|
343
|
-
this.handlers.set(alias, type);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
registerAll(types) {
|
|
348
|
-
for (const type of types) {
|
|
349
|
-
this.register(type);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
canHandle(typeName) {
|
|
353
|
-
return this.handlers.has(typeName);
|
|
354
|
-
}
|
|
355
|
-
getHandler(typeName) {
|
|
356
|
-
return this.handlers.get(typeName);
|
|
357
|
-
}
|
|
358
|
-
async serialize(rxr) {
|
|
359
|
-
const typeName = rxr.manifest.type;
|
|
360
|
-
const handler = this.handlers.get(typeName);
|
|
361
|
-
if (!handler) {
|
|
362
|
-
throw new ResourceTypeError(`Unsupported resource type: ${typeName}`);
|
|
363
|
-
}
|
|
364
|
-
return handler.serializer.serialize(rxr);
|
|
365
|
-
}
|
|
366
|
-
async deserialize(data, manifest) {
|
|
367
|
-
const typeName = manifest.type;
|
|
368
|
-
const handler = this.handlers.get(typeName);
|
|
369
|
-
if (!handler) {
|
|
370
|
-
throw new ResourceTypeError(`Unsupported resource type: ${typeName}`);
|
|
371
|
-
}
|
|
372
|
-
return handler.serializer.deserialize(data, manifest);
|
|
373
|
-
}
|
|
374
|
-
async resolve(rxr) {
|
|
375
|
-
const typeName = rxr.manifest.type;
|
|
376
|
-
const handler = this.handlers.get(typeName);
|
|
377
|
-
if (!handler) {
|
|
378
|
-
throw new ResourceTypeError(`Unsupported resource type: ${typeName}`);
|
|
379
|
-
}
|
|
380
|
-
return handler.resolver.resolve(rxr);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
function createTypeHandlerChain(types) {
|
|
384
|
-
const chain = new TypeHandlerChain;
|
|
385
|
-
if (types) {
|
|
386
|
-
chain.registerAll(types);
|
|
387
|
-
}
|
|
388
|
-
return chain;
|
|
389
|
-
}
|
|
390
|
-
// src/resource/FolderLoader.ts
|
|
391
|
-
import { join } from "node:path";
|
|
392
|
-
import { stat, readFile } from "node:fs/promises";
|
|
393
|
-
class FolderLoader {
|
|
394
|
-
async canLoad(source) {
|
|
395
|
-
try {
|
|
396
|
-
const stats = await stat(source);
|
|
397
|
-
if (!stats.isDirectory()) {
|
|
398
|
-
return false;
|
|
399
|
-
}
|
|
400
|
-
const manifestPath = join(source, "resource.json");
|
|
401
|
-
const contentPath = join(source, "content");
|
|
402
|
-
const manifestStats = await stat(manifestPath);
|
|
403
|
-
const contentStats = await stat(contentPath);
|
|
404
|
-
return manifestStats.isFile() && contentStats.isFile();
|
|
405
|
-
} catch {
|
|
406
|
-
return false;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
async load(folderPath) {
|
|
410
|
-
const manifestPath = join(folderPath, "resource.json");
|
|
411
|
-
let manifestJson;
|
|
412
|
-
try {
|
|
413
|
-
manifestJson = await readFile(manifestPath, "utf-8");
|
|
414
|
-
} catch (error) {
|
|
415
|
-
throw new ResourceXError(`Failed to read resource.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
416
|
-
}
|
|
417
|
-
let manifestData;
|
|
418
|
-
try {
|
|
419
|
-
manifestData = JSON.parse(manifestJson);
|
|
420
|
-
} catch (error) {
|
|
421
|
-
throw new ResourceXError(`Invalid JSON in resource.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
422
|
-
}
|
|
423
|
-
if (!manifestData.name) {
|
|
424
|
-
throw new ResourceXError("Invalid resource.json: missing required field 'name'");
|
|
425
|
-
}
|
|
426
|
-
if (!manifestData.type) {
|
|
427
|
-
throw new ResourceXError("Invalid resource.json: missing required field 'type'");
|
|
428
|
-
}
|
|
429
|
-
if (!manifestData.version) {
|
|
430
|
-
throw new ResourceXError("Invalid resource.json: missing required field 'version'");
|
|
431
|
-
}
|
|
432
|
-
const manifest = createRXM({
|
|
433
|
-
domain: manifestData.domain ?? "localhost",
|
|
434
|
-
path: manifestData.path,
|
|
435
|
-
name: manifestData.name,
|
|
436
|
-
type: manifestData.type,
|
|
437
|
-
version: manifestData.version
|
|
438
|
-
});
|
|
439
|
-
const contentPath = join(folderPath, "content");
|
|
440
|
-
let contentBuffer;
|
|
441
|
-
try {
|
|
442
|
-
contentBuffer = await readFile(contentPath);
|
|
443
|
-
} catch (error) {
|
|
444
|
-
throw new ResourceXError(`Failed to read content file: ${error instanceof Error ? error.message : String(error)}`);
|
|
445
|
-
}
|
|
446
|
-
const content = createRXC(contentBuffer);
|
|
447
|
-
const locator = parseRXL(manifest.toLocator());
|
|
448
|
-
return {
|
|
449
|
-
locator,
|
|
450
|
-
manifest,
|
|
451
|
-
content
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
// src/resource/loadResource.ts
|
|
456
|
-
async function loadResource(source, config) {
|
|
457
|
-
const loader = config?.loader ?? new FolderLoader;
|
|
458
|
-
const canLoad = await loader.canLoad(source);
|
|
459
|
-
if (!canLoad) {
|
|
460
|
-
throw new ResourceXError(`Cannot load resource from: ${source}`);
|
|
461
|
-
}
|
|
462
|
-
return loader.load(source);
|
|
463
|
-
}
|
|
464
236
|
export {
|
|
465
|
-
textType,
|
|
466
237
|
parseRXL,
|
|
467
|
-
loadResource,
|
|
468
238
|
loadRXC,
|
|
469
|
-
jsonType,
|
|
470
|
-
getResourceType,
|
|
471
|
-
defineResourceType,
|
|
472
|
-
createTypeHandlerChain,
|
|
473
239
|
createRXM,
|
|
474
240
|
createRXC,
|
|
475
|
-
clearResourceTypes,
|
|
476
|
-
builtinTypes,
|
|
477
|
-
binaryType,
|
|
478
|
-
TypeHandlerChain,
|
|
479
241
|
ResourceXError,
|
|
480
|
-
ResourceTypeError,
|
|
481
242
|
ManifestError,
|
|
482
243
|
LocatorError,
|
|
483
|
-
FolderLoader,
|
|
484
244
|
ContentError
|
|
485
245
|
};
|
|
486
246
|
|
|
487
|
-
//# debugId=
|
|
247
|
+
//# debugId=2AAEFC45892A016E64756E2164756E21
|
package/dist/index.js.map
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/errors.ts", "../src/locator/parseRXL.ts", "../src/manifest/createRXM.ts", "../src/content/createRXC.ts", "../src/content/loadRXC.ts"
|
|
3
|
+
"sources": ["../src/errors.ts", "../src/locator/parseRXL.ts", "../src/manifest/createRXM.ts", "../src/content/createRXC.ts", "../src/content/loadRXC.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * ResourceX Error hierarchy\n */\n\nexport class ResourceXError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ResourceXError\";\n }\n}\n\nexport class LocatorError extends ResourceXError {\n constructor(\n message: string,\n public readonly locator?: string\n ) {\n super(message);\n this.name = \"LocatorError\";\n }\n}\n\nexport class ManifestError extends ResourceXError {\n constructor(message: string) {\n super(message);\n this.name = \"ManifestError\";\n }\n}\n\nexport class ContentError extends ResourceXError {\n constructor(message: string) {\n super(message);\n this.name = \"ContentError\";\n }\n}\n
|
|
5
|
+
"/**\n * ResourceX Error hierarchy\n */\n\nexport class ResourceXError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ResourceXError\";\n }\n}\n\nexport class LocatorError extends ResourceXError {\n constructor(\n message: string,\n public readonly locator?: string\n ) {\n super(message);\n this.name = \"LocatorError\";\n }\n}\n\nexport class ManifestError extends ResourceXError {\n constructor(message: string) {\n super(message);\n this.name = \"ManifestError\";\n }\n}\n\nexport class ContentError extends ResourceXError {\n constructor(message: string) {\n super(message);\n this.name = \"ContentError\";\n }\n}\n",
|
|
6
6
|
"import type { RXL } from \"./types.js\";\n\nclass RXLImpl implements RXL {\n readonly domain?: string;\n readonly path?: string;\n readonly name: string;\n readonly type?: string;\n readonly version?: string;\n\n constructor(parts: {\n domain?: string;\n path?: string;\n name: string;\n type?: string;\n version?: string;\n }) {\n this.domain = parts.domain;\n this.path = parts.path;\n this.name = parts.name;\n this.type = parts.type;\n this.version = parts.version;\n }\n\n toString(): string {\n let result = \"\";\n if (this.domain) {\n result += this.domain + \"/\";\n if (this.path) {\n result += this.path + \"/\";\n }\n }\n result += this.name;\n if (this.type) {\n result += \".\" + this.type;\n }\n if (this.version) {\n result += \"@\" + this.version;\n }\n return result;\n }\n}\n\nfunction isDomain(str: string): boolean {\n if (str === \"localhost\") return true;\n return str.includes(\".\");\n}\n\n/**\n * Parse a resource locator string into RXL object.\n *\n * Format: [domain/path/]name[.type][@version]\n */\nexport function parseRXL(locator: string): RXL {\n let remaining = locator;\n let version: string | undefined;\n let type: string | undefined;\n let domain: string | undefined;\n let path: string | undefined;\n let name: string;\n\n // 1. Extract version (after @)\n const atIndex = remaining.indexOf(\"@\");\n if (atIndex !== -1) {\n version = remaining.slice(atIndex + 1);\n remaining = remaining.slice(0, atIndex);\n }\n\n // 2. Split by / to get segments\n const segments = remaining.split(\"/\");\n\n // 3. Handle domain and path\n if (segments.length > 1 && isDomain(segments[0])) {\n domain = segments[0];\n const lastSegment = segments[segments.length - 1];\n if (segments.length > 2) {\n path = segments.slice(1, -1).join(\"/\");\n }\n remaining = lastSegment;\n } else {\n remaining = segments.join(\"/\");\n }\n\n // 4. Extract type (after last .)\n const dotIndex = remaining.lastIndexOf(\".\");\n if (dotIndex !== -1) {\n type = remaining.slice(dotIndex + 1);\n name = remaining.slice(0, dotIndex);\n } else {\n name = remaining;\n }\n\n return new RXLImpl({ domain, path, name, type, version });\n}\n",
|
|
7
7
|
"import type { RXM, ManifestData } from \"./types.js\";\nimport { ManifestError } from \"~/errors.js\";\n\nclass RXMImpl implements RXM {\n readonly domain: string;\n readonly path?: string;\n readonly name: string;\n readonly type: string;\n readonly version: string;\n\n constructor(data: {\n domain: string;\n path?: string;\n name: string;\n type: string;\n version: string;\n }) {\n this.domain = data.domain;\n this.path = data.path;\n this.name = data.name;\n this.type = data.type;\n this.version = data.version;\n }\n\n toLocator(): string {\n let result = this.domain + \"/\";\n if (this.path) {\n result += this.path + \"/\";\n }\n result += this.name;\n result += \".\" + this.type;\n result += \"@\" + this.version;\n return result;\n }\n\n toJSON(): ManifestData {\n const json: ManifestData = {\n domain: this.domain,\n name: this.name,\n type: this.type,\n version: this.version,\n };\n if (this.path !== undefined) {\n json.path = this.path;\n }\n return json;\n }\n}\n\n/**\n * Create a manifest from data object.\n */\nexport function createRXM(data: ManifestData): RXM {\n if (!data.domain) {\n throw new ManifestError(\"domain is required\");\n }\n if (!data.name) {\n throw new ManifestError(\"name is required\");\n }\n if (!data.type) {\n throw new ManifestError(\"type is required\");\n }\n if (!data.version) {\n throw new ManifestError(\"version is required\");\n }\n\n return new RXMImpl({\n domain: data.domain,\n path: data.path,\n name: data.name,\n type: data.type,\n version: data.version,\n });\n}\n",
|
|
8
8
|
"import type { RXC } from \"./types.js\";\nimport { ContentError } from \"~/errors.js\";\n\nclass RXCImpl implements RXC {\n private _stream: ReadableStream<Uint8Array>;\n private _consumed = false;\n\n constructor(stream: ReadableStream<Uint8Array>) {\n this._stream = stream;\n }\n\n get stream(): ReadableStream<Uint8Array> {\n if (this._consumed) {\n throw new ContentError(\"Content has already been consumed\");\n }\n this._consumed = true;\n return this._stream;\n }\n\n async text(): Promise<string> {\n const buffer = await this.buffer();\n return buffer.toString(\"utf-8\");\n }\n\n async buffer(): Promise<Buffer> {\n if (this._consumed) {\n throw new ContentError(\"Content has already been consumed\");\n }\n this._consumed = true;\n\n const reader = this._stream.getReader();\n const chunks: Uint8Array[] = [];\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n\n return Buffer.concat(chunks);\n }\n\n async json<T>(): Promise<T> {\n const text = await this.text();\n return JSON.parse(text) as T;\n }\n}\n\n/**\n * Create RXC from string, Buffer, or ReadableStream.\n */\nexport function createRXC(data: string | Buffer | ReadableStream<Uint8Array>): RXC {\n let stream: ReadableStream<Uint8Array>;\n\n if (typeof data === \"string\") {\n const encoded = new TextEncoder().encode(data);\n stream = new ReadableStream({\n start(controller) {\n controller.enqueue(encoded);\n controller.close();\n },\n });\n } else if (Buffer.isBuffer(data)) {\n stream = new ReadableStream({\n start(controller) {\n controller.enqueue(new Uint8Array(data));\n controller.close();\n },\n });\n } else {\n // Already a ReadableStream\n stream = data;\n }\n\n return new RXCImpl(stream);\n}\n",
|
|
9
|
-
"import { createReadStream } from \"node:fs\";\nimport { Readable } from \"node:stream\";\nimport type { RXC } from \"./types.js\";\nimport { createRXC } from \"./createRXC.js\";\n\n/**\n * Load RXC from file path or URL.\n */\nexport async function loadRXC(source: string): Promise<RXC> {\n // Check if it's a URL\n if (source.startsWith(\"http://\") || source.startsWith(\"https://\")) {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Failed to fetch ${source}: ${response.statusText}`);\n }\n if (!response.body) {\n throw new Error(`No body in response from ${source}`);\n }\n return createRXC(response.body);\n }\n\n // Otherwise, treat as file path\n const nodeStream = createReadStream(source);\n const webStream = Readable.toWeb(nodeStream) as ReadableStream<Uint8Array>;\n return createRXC(webStream);\n}\n"
|
|
10
|
-
"import type { ResourceType } from \"./types.js\";\nimport { ResourceTypeError } from \"~/errors.js\";\n\n// Registry of resource types\nconst resourceTypes = new Map<string, ResourceType>();\n\n/**\n * Define and register a resource type.\n *\n * @throws ResourceTypeError if type is already registered\n */\nexport function defineResourceType<T>(config: ResourceType<T>): ResourceType<T> {\n if (resourceTypes.has(config.name)) {\n throw new ResourceTypeError(`Resource type \"${config.name}\" is already registered`);\n }\n resourceTypes.set(config.name, config as ResourceType);\n return config;\n}\n\n/**\n * Get a registered resource type by name.\n *\n * @returns ResourceType or undefined if not found\n */\nexport function getResourceType<T = unknown>(name: string): ResourceType<T> | undefined {\n return resourceTypes.get(name) as ResourceType<T> | undefined;\n}\n\n/**\n * Clear all registered resource types (for testing).\n */\nexport function clearResourceTypes(): void {\n resourceTypes.clear();\n}\n",
|
|
11
|
-
"import type { ResourceType, ResourceSerializer, ResourceResolver, RXR } from \"./types.js\";\nimport type { RXM } from \"~/manifest/types.js\";\nimport { createRXC } from \"~/content/createRXC.js\";\nimport { parseRXL } from \"~/locator/parseRXL.js\";\n\n/**\n * Text serializer - stores content as UTF-8 text\n */\nconst textSerializer: ResourceSerializer = {\n async serialize(rxr: RXR): Promise<Buffer> {\n const text = await rxr.content.text();\n return Buffer.from(text, \"utf-8\");\n },\n\n async deserialize(data: Buffer, manifest: RXM): Promise<RXR> {\n const text = data.toString(\"utf-8\");\n return {\n locator: parseRXL(manifest.toLocator()),\n manifest,\n content: createRXC(text),\n };\n },\n};\n\n/**\n * Text resolver - returns content as string\n */\nconst textResolver: ResourceResolver<string> = {\n async resolve(rxr: RXR): Promise<string> {\n return rxr.content.text();\n },\n};\n\n/**\n * Text resource type\n */\nexport const textType: ResourceType<string> = {\n name: \"text\",\n aliases: [\"txt\", \"plaintext\"],\n description: \"Plain text content\",\n serializer: textSerializer,\n resolver: textResolver,\n};\n\n/**\n * JSON serializer - stores content as JSON string\n */\nconst jsonSerializer: ResourceSerializer = {\n async serialize(rxr: RXR): Promise<Buffer> {\n const json = await rxr.content.json();\n return Buffer.from(JSON.stringify(json, null, 2), \"utf-8\");\n },\n\n async deserialize(data: Buffer, manifest: RXM): Promise<RXR> {\n const text = data.toString(\"utf-8\");\n return {\n locator: parseRXL(manifest.toLocator()),\n manifest,\n content: createRXC(text),\n };\n },\n};\n\n/**\n * JSON resolver - returns content as parsed object\n */\nconst jsonResolver: ResourceResolver<unknown> = {\n async resolve(rxr: RXR): Promise<unknown> {\n return rxr.content.json();\n },\n};\n\n/**\n * JSON resource type\n */\nexport const jsonType: ResourceType<unknown> = {\n name: \"json\",\n aliases: [\"config\", \"manifest\"],\n description: \"JSON content\",\n serializer: jsonSerializer,\n resolver: jsonResolver,\n};\n\n/**\n * Binary serializer - stores content as raw bytes\n */\nconst binarySerializer: ResourceSerializer = {\n async serialize(rxr: RXR): Promise<Buffer> {\n return rxr.content.buffer();\n },\n\n async deserialize(data: Buffer, manifest: RXM): Promise<RXR> {\n return {\n locator: parseRXL(manifest.toLocator()),\n manifest,\n content: createRXC(data),\n };\n },\n};\n\n/**\n * Binary resolver - returns content as Buffer\n */\nconst binaryResolver: ResourceResolver<Buffer> = {\n async resolve(rxr: RXR): Promise<Buffer> {\n return rxr.content.buffer();\n },\n};\n\n/**\n * Binary resource type\n */\nexport const binaryType: ResourceType<Buffer> = {\n name: \"binary\",\n aliases: [\"bin\", \"blob\", \"raw\"],\n description: \"Binary content\",\n serializer: binarySerializer,\n resolver: binaryResolver,\n};\n\n/**\n * All built-in types\n */\nexport const builtinTypes: ResourceType[] = [textType, jsonType, binaryType];\n",
|
|
12
|
-
"import type { ResourceType, RXR } from \"./types.js\";\nimport type { RXM } from \"~/manifest/types.js\";\nimport { ResourceTypeError } from \"~/errors.js\";\n\n/**\n * TypeHandlerChain - Responsibility chain for resource type handling.\n * Manages type registration and delegates serialization/deserialization.\n */\nexport class TypeHandlerChain {\n private handlers: Map<string, ResourceType> = new Map();\n\n /**\n * Register a resource type handler.\n * Registers both the type name and its aliases.\n */\n register(type: ResourceType): void {\n this.handlers.set(type.name, type);\n if (type.aliases) {\n for (const alias of type.aliases) {\n this.handlers.set(alias, type);\n }\n }\n }\n\n /**\n * Register multiple type handlers.\n */\n registerAll(types: ResourceType[]): void {\n for (const type of types) {\n this.register(type);\n }\n }\n\n /**\n * Check if a type is supported.\n */\n canHandle(typeName: string): boolean {\n return this.handlers.has(typeName);\n }\n\n /**\n * Get handler for a type.\n */\n getHandler(typeName: string): ResourceType | undefined {\n return this.handlers.get(typeName);\n }\n\n /**\n * Serialize RXR content using the appropriate type handler.\n */\n async serialize(rxr: RXR): Promise<Buffer> {\n const typeName = rxr.manifest.type;\n const handler = this.handlers.get(typeName);\n\n if (!handler) {\n throw new ResourceTypeError(`Unsupported resource type: ${typeName}`);\n }\n\n return handler.serializer.serialize(rxr);\n }\n\n /**\n * Deserialize content into RXR using the appropriate type handler.\n */\n async deserialize(data: Buffer, manifest: RXM): Promise<RXR> {\n const typeName = manifest.type;\n const handler = this.handlers.get(typeName);\n\n if (!handler) {\n throw new ResourceTypeError(`Unsupported resource type: ${typeName}`);\n }\n\n return handler.serializer.deserialize(data, manifest);\n }\n\n /**\n * Resolve RXR content into usable object using the appropriate type handler.\n */\n async resolve<T = unknown>(rxr: RXR): Promise<T> {\n const typeName = rxr.manifest.type;\n const handler = this.handlers.get(typeName);\n\n if (!handler) {\n throw new ResourceTypeError(`Unsupported resource type: ${typeName}`);\n }\n\n return handler.resolver.resolve(rxr) as Promise<T>;\n }\n}\n\n/**\n * Create a new TypeHandlerChain with optional initial types.\n */\nexport function createTypeHandlerChain(types?: ResourceType[]): TypeHandlerChain {\n const chain = new TypeHandlerChain();\n if (types) {\n chain.registerAll(types);\n }\n return chain;\n}\n",
|
|
13
|
-
"import { join } from \"node:path\";\nimport { stat, readFile } from \"node:fs/promises\";\nimport type { ResourceLoader, RXR } from \"./types.js\";\nimport { createRXM } from \"~/manifest/index.js\";\nimport { createRXC } from \"~/content/index.js\";\nimport { parseRXL } from \"~/locator/index.js\";\nimport { ResourceXError } from \"~/errors.js\";\n\n/**\n * Default ResourceLoader implementation for loading resources from folders.\n *\n * Expected folder structure:\n * ```\n * folder/\n * ├── resource.json # Resource metadata (required)\n * └── content # Resource content (required)\n * ```\n *\n * resource.json format:\n * ```json\n * {\n * \"name\": \"resource-name\", // required\n * \"type\": \"text\", // required\n * \"version\": \"1.0.0\", // required\n * \"domain\": \"localhost\", // optional, defaults to \"localhost\"\n * \"path\": \"optional/path\" // optional\n * }\n * ```\n */\nexport class FolderLoader implements ResourceLoader {\n async canLoad(source: string): Promise<boolean> {\n try {\n const stats = await stat(source);\n if (!stats.isDirectory()) {\n return false;\n }\n\n // Check if required files exist\n const manifestPath = join(source, \"resource.json\");\n const contentPath = join(source, \"content\");\n\n const manifestStats = await stat(manifestPath);\n const contentStats = await stat(contentPath);\n\n return manifestStats.isFile() && contentStats.isFile();\n } catch {\n return false;\n }\n }\n\n async load(folderPath: string): Promise<RXR> {\n // 1. Read resource.json\n const manifestPath = join(folderPath, \"resource.json\");\n let manifestJson: string;\n try {\n manifestJson = await readFile(manifestPath, \"utf-8\");\n } catch (error) {\n throw new ResourceXError(\n `Failed to read resource.json: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n // 2. Parse JSON\n let manifestData: any;\n try {\n manifestData = JSON.parse(manifestJson);\n } catch (error) {\n throw new ResourceXError(\n `Invalid JSON in resource.json: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n // 3. Validate required fields\n if (!manifestData.name) {\n throw new ResourceXError(\"Invalid resource.json: missing required field 'name'\");\n }\n if (!manifestData.type) {\n throw new ResourceXError(\"Invalid resource.json: missing required field 'type'\");\n }\n if (!manifestData.version) {\n throw new ResourceXError(\"Invalid resource.json: missing required field 'version'\");\n }\n\n // 4. Create RXM with defaults\n const manifest = createRXM({\n domain: manifestData.domain ?? \"localhost\",\n path: manifestData.path,\n name: manifestData.name,\n type: manifestData.type,\n version: manifestData.version,\n });\n\n // 5. Read content file\n const contentPath = join(folderPath, \"content\");\n let contentBuffer: Buffer;\n try {\n contentBuffer = await readFile(contentPath);\n } catch (error) {\n throw new ResourceXError(\n `Failed to read content file: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n // 6. Create RXC\n const content = createRXC(contentBuffer);\n\n // 7. Assemble RXR\n const locator = parseRXL(manifest.toLocator());\n\n return {\n locator,\n manifest,\n content,\n };\n }\n}\n",
|
|
14
|
-
"import type { ResourceLoader, RXR } from \"./types.js\";\nimport { FolderLoader } from \"./FolderLoader.js\";\nimport { ResourceXError } from \"~/errors.js\";\n\n/**\n * Configuration options for loadResource.\n */\nexport interface LoadResourceConfig {\n /**\n * Custom loader to use. If not provided, defaults to FolderLoader.\n */\n loader?: ResourceLoader;\n}\n\n/**\n * Load a resource from a given source using a ResourceLoader.\n *\n * By default, uses FolderLoader which expects:\n * ```\n * folder/\n * ├── resource.json # Resource metadata\n * └── content # Resource content\n * ```\n *\n * You can provide a custom loader via config.loader to support other formats\n * (e.g., zip, tar.gz, URLs).\n *\n * @param source - Source path or identifier\n * @param config - Optional configuration\n * @returns Complete RXR object ready for registry.link()\n * @throws ResourceXError if the source cannot be loaded\n *\n * @example\n * ```typescript\n * // Load from folder (default)\n * const rxr = await loadResource(\"./my-resource\");\n * await registry.link(rxr);\n *\n * // Load with custom loader\n * const rxr = await loadResource(\"resource.zip\", {\n * loader: new ZipLoader()\n * });\n * ```\n */\nexport async function loadResource(source: string, config?: LoadResourceConfig): Promise<RXR> {\n const loader = config?.loader ?? new FolderLoader();\n\n // Check if loader can handle this source\n const canLoad = await loader.canLoad(source);\n if (!canLoad) {\n throw new ResourceXError(`Cannot load resource from: ${source}`);\n }\n\n // Load the resource\n return loader.load(source);\n}\n"
|
|
9
|
+
"import { createReadStream } from \"node:fs\";\nimport { Readable } from \"node:stream\";\nimport type { RXC } from \"./types.js\";\nimport { createRXC } from \"./createRXC.js\";\n\n/**\n * Load RXC from file path or URL.\n */\nexport async function loadRXC(source: string): Promise<RXC> {\n // Check if it's a URL\n if (source.startsWith(\"http://\") || source.startsWith(\"https://\")) {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Failed to fetch ${source}: ${response.statusText}`);\n }\n if (!response.body) {\n throw new Error(`No body in response from ${source}`);\n }\n return createRXC(response.body);\n }\n\n // Otherwise, treat as file path\n const nodeStream = createReadStream(source);\n const webStream = Readable.toWeb(nodeStream) as ReadableStream<Uint8Array>;\n return createRXC(webStream);\n}\n"
|
|
15
10
|
],
|
|
16
|
-
"mappings": ";AAIO,MAAM,uBAAuB,MAAM;AAAA,EACxC,WAAW,CAAC,SAAiB,SAAwB;AAAA,IACnD,MAAM,SAAS,OAAO;AAAA,IACtB,KAAK,OAAO;AAAA;AAEhB;AAAA;AAEO,MAAM,qBAAqB,eAAe;AAAA,EAG7B;AAAA,EAFlB,WAAW,CACT,SACgB,SAChB;AAAA,IACA,MAAM,OAAO;AAAA,IAFG;AAAA,IAGhB,KAAK,OAAO;AAAA;AAEhB;AAAA;AAEO,MAAM,sBAAsB,eAAe;AAAA,EAChD,WAAW,CAAC,SAAiB;AAAA,IAC3B,MAAM,OAAO;AAAA,IACb,KAAK,OAAO;AAAA;AAEhB;AAAA;AAEO,MAAM,qBAAqB,eAAe;AAAA,EAC/C,WAAW,CAAC,SAAiB;AAAA,IAC3B,MAAM,OAAO;AAAA,IACb,KAAK,OAAO;AAAA;AAEhB
|
|
17
|
-
"debugId": "
|
|
11
|
+
"mappings": "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",
|
|
12
|
+
"debugId": "2AAEFC45892A016E64756E2164756E21",
|
|
18
13
|
"names": []
|
|
19
14
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@resourcexjs/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "ResourceX Core - Resource management layer",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"resourcex",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"clean": "rm -rf dist"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@resourcexjs/arp": "^0.
|
|
40
|
+
"@resourcexjs/arp": "^1.0.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {},
|
|
43
43
|
"publishConfig": {
|