@resourcexjs/core 2.4.1 → 2.5.1
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 +279 -178
- package/dist/index.d.ts +127 -90
- package/dist/index.js +197 -226
- package/dist/index.js.map +14 -8
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @resourcexjs/core
|
|
2
2
|
|
|
3
|
-
Core
|
|
3
|
+
Core primitives and types for ResourceX - the resource management protocol for AI Agents.
|
|
4
4
|
|
|
5
|
-
> **Note**: For most use cases, use the main [`resourcexjs`](https://www.npmjs.com/package/resourcexjs) package. This package is for
|
|
5
|
+
> **Note**: For most use cases, use the main [`resourcexjs`](https://www.npmjs.com/package/resourcexjs) package. This package is for low-level operations.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -12,269 +12,370 @@ npm install @resourcexjs/core
|
|
|
12
12
|
bun add @resourcexjs/core
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Core Concepts
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
ResourceX uses a layered architecture with five core primitives:
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
| Primitive | Description |
|
|
20
|
+
| --------- | -------------------------------------------------------- |
|
|
21
|
+
| **RXD** | Resource Definition - content of `resource.json` |
|
|
22
|
+
| **RXL** | Resource Locator - unique identifier for a resource |
|
|
23
|
+
| **RXM** | Resource Manifest - metadata stored within the resource |
|
|
24
|
+
| **RXA** | Resource Archive - tar.gz container for storage/transfer |
|
|
25
|
+
| **RXR** | Resource - complete resource (RXL + RXM + RXA) |
|
|
26
|
+
|
|
27
|
+
### Locator Format
|
|
28
|
+
|
|
29
|
+
Docker-style format: `[registry/][path/]name[:tag]`
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
hello -> name=hello, tag=latest
|
|
33
|
+
hello:1.0.0 -> name=hello, tag=1.0.0
|
|
34
|
+
prompts/hello:stable -> path=prompts, name=hello, tag=stable
|
|
35
|
+
localhost:3098/hello:1.0.0 -> registry=localhost:3098, name=hello, tag=1.0.0
|
|
36
|
+
registry.example.com/org/hello -> registry=registry.example.com, path=org, name=hello, tag=latest
|
|
37
|
+
```
|
|
25
38
|
|
|
26
39
|
## API
|
|
27
40
|
|
|
28
|
-
###
|
|
41
|
+
### `parse(locator: string): RXL`
|
|
29
42
|
|
|
30
|
-
Parse
|
|
43
|
+
Parse a locator string into an RXL object.
|
|
31
44
|
|
|
32
45
|
```typescript
|
|
33
|
-
import {
|
|
46
|
+
import { parse } from "@resourcexjs/core";
|
|
47
|
+
|
|
48
|
+
const rxl = parse("registry.example.com/prompts/hello:1.0.0");
|
|
49
|
+
// {
|
|
50
|
+
// registry: "registry.example.com",
|
|
51
|
+
// path: "prompts",
|
|
52
|
+
// name: "hello",
|
|
53
|
+
// tag: "1.0.0"
|
|
54
|
+
// }
|
|
55
|
+
|
|
56
|
+
const simple = parse("hello");
|
|
57
|
+
// { registry: undefined, path: undefined, name: "hello", tag: "latest" }
|
|
58
|
+
```
|
|
34
59
|
|
|
35
|
-
|
|
60
|
+
### `format(rxl: RXL): string`
|
|
36
61
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
62
|
+
Format an RXL object back to a locator string.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { format } from "@resourcexjs/core";
|
|
66
|
+
|
|
67
|
+
format({ name: "hello", tag: "latest" });
|
|
68
|
+
// "hello"
|
|
69
|
+
|
|
70
|
+
format({ name: "hello", tag: "1.0.0" });
|
|
71
|
+
// "hello:1.0.0"
|
|
72
|
+
|
|
73
|
+
format({ registry: "localhost:3098", name: "hello", tag: "1.0.0" });
|
|
74
|
+
// "localhost:3098/hello:1.0.0"
|
|
43
75
|
```
|
|
44
76
|
|
|
45
|
-
|
|
77
|
+
### `define(input: unknown): RXD`
|
|
78
|
+
|
|
79
|
+
Parse and validate a resource definition (from `resource.json`).
|
|
46
80
|
|
|
47
81
|
```typescript
|
|
48
|
-
|
|
49
|
-
|
|
82
|
+
import { define } from "@resourcexjs/core";
|
|
83
|
+
|
|
84
|
+
const rxd = define({
|
|
85
|
+
name: "my-prompt",
|
|
86
|
+
type: "text",
|
|
87
|
+
tag: "1.0.0",
|
|
88
|
+
description: "A helpful prompt",
|
|
89
|
+
author: "Alice",
|
|
90
|
+
});
|
|
91
|
+
// {
|
|
92
|
+
// name: "my-prompt",
|
|
93
|
+
// type: "text",
|
|
94
|
+
// tag: "1.0.0",
|
|
95
|
+
// description: "A helpful prompt",
|
|
96
|
+
// author: "Alice"
|
|
97
|
+
// }
|
|
50
98
|
```
|
|
51
99
|
|
|
52
|
-
|
|
100
|
+
**Required fields**: `name`, `type`
|
|
101
|
+
|
|
102
|
+
**Optional fields**: `tag` (defaults to "latest"), `registry`, `path`, `description`, `author`, `license`, `keywords`, `repository`
|
|
103
|
+
|
|
104
|
+
> Note: `version` is accepted as an alias for `tag` for backward compatibility.
|
|
53
105
|
|
|
54
|
-
|
|
106
|
+
### `manifest(rxd: RXD): RXM`
|
|
107
|
+
|
|
108
|
+
Create a manifest from a definition. Extracts core metadata fields.
|
|
55
109
|
|
|
56
110
|
```typescript
|
|
57
|
-
import {
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
version: "1.0.0",
|
|
65
|
-
description: "Optional description", // optional
|
|
66
|
-
tags: ["optional", "tags"], // optional
|
|
111
|
+
import { define, manifest } from "@resourcexjs/core";
|
|
112
|
+
|
|
113
|
+
const rxd = define({
|
|
114
|
+
name: "my-prompt",
|
|
115
|
+
type: "text",
|
|
116
|
+
tag: "1.0.0",
|
|
117
|
+
description: "A helpful prompt", // not included in manifest
|
|
67
118
|
});
|
|
68
119
|
|
|
69
|
-
manifest
|
|
70
|
-
|
|
120
|
+
const rxm = manifest(rxd);
|
|
121
|
+
// { name: "my-prompt", type: "text", tag: "1.0.0" }
|
|
71
122
|
```
|
|
72
123
|
|
|
73
|
-
|
|
124
|
+
### `locate(rxm: RXM): RXL`
|
|
74
125
|
|
|
75
|
-
|
|
126
|
+
Create a locator from a manifest.
|
|
76
127
|
|
|
77
|
-
|
|
128
|
+
```typescript
|
|
129
|
+
import { locate } from "@resourcexjs/core";
|
|
130
|
+
|
|
131
|
+
const rxl = locate({
|
|
132
|
+
registry: "example.com",
|
|
133
|
+
path: "prompts",
|
|
134
|
+
name: "hello",
|
|
135
|
+
type: "text",
|
|
136
|
+
tag: "1.0.0",
|
|
137
|
+
});
|
|
138
|
+
// { registry: "example.com", path: "prompts", name: "hello", tag: "1.0.0" }
|
|
139
|
+
```
|
|
78
140
|
|
|
79
|
-
|
|
141
|
+
### `archive(files: Record<string, Buffer>): Promise<RXA>`
|
|
142
|
+
|
|
143
|
+
Create an archive from files. Output is in tar.gz format.
|
|
80
144
|
|
|
81
145
|
```typescript
|
|
82
|
-
import {
|
|
146
|
+
import { archive } from "@resourcexjs/core";
|
|
83
147
|
|
|
84
148
|
// Single file
|
|
85
|
-
const
|
|
149
|
+
const rxa = await archive({
|
|
150
|
+
content: Buffer.from("Hello, World!"),
|
|
151
|
+
});
|
|
86
152
|
|
|
87
153
|
// Multiple files
|
|
88
|
-
const
|
|
89
|
-
"
|
|
90
|
-
"
|
|
154
|
+
const rxa = await archive({
|
|
155
|
+
"prompt.md": Buffer.from("# System Prompt\nYou are..."),
|
|
156
|
+
"config.json": Buffer.from('{"temperature": 0.7}'),
|
|
91
157
|
});
|
|
92
158
|
|
|
93
159
|
// Nested directories
|
|
94
|
-
const
|
|
95
|
-
"src/index.ts": "main code",
|
|
96
|
-
"src/utils/helper.ts": "helper code",
|
|
160
|
+
const rxa = await archive({
|
|
161
|
+
"src/index.ts": Buffer.from("main code"),
|
|
162
|
+
"src/utils/helper.ts": Buffer.from("helper code"),
|
|
97
163
|
});
|
|
98
164
|
|
|
99
|
-
//
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
// Extract to package for file access
|
|
103
|
-
const pkg = await content.extract();
|
|
104
|
-
const buffer = await pkg.file("content"); // → Buffer
|
|
105
|
-
const buffer = await pkg.file("src/index.ts"); // → Buffer
|
|
106
|
-
const files = await pkg.files(); // → Map<string, Buffer>
|
|
107
|
-
const paths = pkg.paths(); // → string[]
|
|
108
|
-
const tree = pkg.tree(); // → PathNode[]
|
|
109
|
-
|
|
110
|
-
// Archive methods
|
|
111
|
-
const archiveBuffer = await content.buffer(); // → raw tar.gz Buffer
|
|
112
|
-
const stream = content.stream; // → ReadableStream (tar.gz)
|
|
165
|
+
// Access raw archive data
|
|
166
|
+
const buffer = await rxa.buffer(); // tar.gz Buffer
|
|
167
|
+
const stream = rxa.stream; // ReadableStream<Uint8Array>
|
|
113
168
|
```
|
|
114
169
|
|
|
115
|
-
###
|
|
170
|
+
### `extract(rxa: RXA): Promise<Record<string, Buffer>>`
|
|
116
171
|
|
|
117
|
-
|
|
172
|
+
Extract files from an archive.
|
|
118
173
|
|
|
119
174
|
```typescript
|
|
120
|
-
import
|
|
175
|
+
import { archive, extract } from "@resourcexjs/core";
|
|
121
176
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
177
|
+
const rxa = await archive({
|
|
178
|
+
"hello.txt": Buffer.from("Hello!"),
|
|
179
|
+
"world.txt": Buffer.from("World!"),
|
|
180
|
+
});
|
|
127
181
|
|
|
128
|
-
|
|
129
|
-
|
|
182
|
+
const files = await extract(rxa);
|
|
183
|
+
// {
|
|
184
|
+
// "hello.txt": Buffer<...>,
|
|
185
|
+
// "world.txt": Buffer<...>
|
|
186
|
+
// }
|
|
130
187
|
```
|
|
131
188
|
|
|
132
|
-
|
|
189
|
+
### `wrap(buffer: Buffer): RXA`
|
|
133
190
|
|
|
134
|
-
|
|
191
|
+
Wrap an existing tar.gz buffer as an RXA. Useful for deserializing archives.
|
|
135
192
|
|
|
136
193
|
```typescript
|
|
137
|
-
import {
|
|
194
|
+
import { wrap, extract } from "@resourcexjs/core";
|
|
138
195
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
} catch (error) {
|
|
142
|
-
if (error instanceof LocatorError) {
|
|
143
|
-
console.error("Invalid locator format");
|
|
144
|
-
}
|
|
145
|
-
}
|
|
196
|
+
// From storage or network
|
|
197
|
+
const tarGzBuffer = await fetchFromStorage();
|
|
146
198
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
} catch (error) {
|
|
150
|
-
if (error instanceof ManifestError) {
|
|
151
|
-
console.error("Invalid manifest");
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
try {
|
|
156
|
-
const pkg = await archive.extract();
|
|
157
|
-
await pkg.file("nonexistent");
|
|
158
|
-
} catch (error) {
|
|
159
|
-
if (error instanceof ContentError) {
|
|
160
|
-
console.error("File not found in archive");
|
|
161
|
-
}
|
|
162
|
-
}
|
|
199
|
+
const rxa = wrap(tarGzBuffer);
|
|
200
|
+
const files = await extract(rxa);
|
|
163
201
|
```
|
|
164
202
|
|
|
165
|
-
###
|
|
203
|
+
### `resource(rxm: RXM, rxa: RXA): RXR`
|
|
166
204
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
205
|
+
Create a complete resource from manifest and archive.
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import { define, manifest, archive, resource } from "@resourcexjs/core";
|
|
209
|
+
|
|
210
|
+
const rxd = define({ name: "hello", type: "text", tag: "1.0.0" });
|
|
211
|
+
const rxm = manifest(rxd);
|
|
212
|
+
const rxa = await archive({ content: Buffer.from("Hello!") });
|
|
213
|
+
const rxr = resource(rxm, rxa);
|
|
214
|
+
// {
|
|
215
|
+
// locator: { name: "hello", tag: "1.0.0" },
|
|
216
|
+
// manifest: { name: "hello", type: "text", tag: "1.0.0" },
|
|
217
|
+
// archive: RXA
|
|
218
|
+
// }
|
|
172
219
|
```
|
|
173
220
|
|
|
174
|
-
##
|
|
221
|
+
## Types
|
|
175
222
|
|
|
176
|
-
###
|
|
223
|
+
### RXD - Resource Definition
|
|
177
224
|
|
|
178
|
-
|
|
179
|
-
import { parseRXL, createRXM, createRXA } from "@resourcexjs/core";
|
|
180
|
-
import type { RXR } from "@resourcexjs/core";
|
|
225
|
+
The content of `resource.json` file. Contains all metadata for a resource in development.
|
|
181
226
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
227
|
+
```typescript
|
|
228
|
+
interface RXD {
|
|
229
|
+
readonly name: string; // Required
|
|
230
|
+
readonly type: string; // Required
|
|
231
|
+
readonly tag?: string; // Optional (defaults to "latest")
|
|
232
|
+
readonly registry?: string;
|
|
233
|
+
readonly path?: string;
|
|
234
|
+
readonly description?: string;
|
|
235
|
+
readonly author?: string;
|
|
236
|
+
readonly license?: string;
|
|
237
|
+
readonly keywords?: string[];
|
|
238
|
+
readonly repository?: string;
|
|
239
|
+
readonly [key: string]: unknown; // Additional fields allowed
|
|
240
|
+
}
|
|
241
|
+
```
|
|
189
242
|
|
|
190
|
-
|
|
191
|
-
const locator = parseRXL(manifest.toLocator());
|
|
243
|
+
### RXL - Resource Locator
|
|
192
244
|
|
|
193
|
-
|
|
194
|
-
const archive = await createRXA({ content: "You are a helpful assistant." });
|
|
245
|
+
Unique identifier for a resource.
|
|
195
246
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
247
|
+
```typescript
|
|
248
|
+
interface RXL {
|
|
249
|
+
readonly registry?: string; // e.g., "localhost:3098", "registry.example.com"
|
|
250
|
+
readonly path?: string; // e.g., "org", "prompts"
|
|
251
|
+
readonly name: string; // Resource name
|
|
252
|
+
readonly tag: string; // Tag (defaults to "latest")
|
|
253
|
+
}
|
|
202
254
|
```
|
|
203
255
|
|
|
204
|
-
###
|
|
256
|
+
### RXM - Resource Manifest
|
|
257
|
+
|
|
258
|
+
Resource metadata stored within the resource.
|
|
205
259
|
|
|
206
260
|
```typescript
|
|
207
|
-
|
|
261
|
+
interface RXM {
|
|
262
|
+
readonly registry?: string;
|
|
263
|
+
readonly path?: string;
|
|
264
|
+
readonly name: string;
|
|
265
|
+
readonly type: string;
|
|
266
|
+
readonly tag: string;
|
|
267
|
+
readonly files?: string[]; // Package file structure
|
|
268
|
+
}
|
|
269
|
+
```
|
|
208
270
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
271
|
+
### RXA - Resource Archive
|
|
272
|
+
|
|
273
|
+
Archive container (tar.gz format) for storage and transfer.
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
interface RXA {
|
|
277
|
+
readonly stream: ReadableStream<Uint8Array>;
|
|
278
|
+
buffer(): Promise<Buffer>;
|
|
279
|
+
}
|
|
280
|
+
```
|
|
214
281
|
|
|
215
|
-
|
|
216
|
-
const pkg = await archive.extract();
|
|
282
|
+
### RXR - Resource
|
|
217
283
|
|
|
218
|
-
|
|
219
|
-
const promptBuffer = await pkg.file("prompt.md");
|
|
220
|
-
const configBuffer = await pkg.file("config.json");
|
|
284
|
+
Complete resource object combining locator, manifest, and archive.
|
|
221
285
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
286
|
+
```typescript
|
|
287
|
+
interface RXR {
|
|
288
|
+
readonly locator: RXL;
|
|
289
|
+
readonly manifest: RXM;
|
|
290
|
+
readonly archive: RXA;
|
|
226
291
|
}
|
|
227
292
|
```
|
|
228
293
|
|
|
229
|
-
|
|
294
|
+
## Error Handling
|
|
230
295
|
|
|
231
296
|
```typescript
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
297
|
+
import {
|
|
298
|
+
ResourceXError,
|
|
299
|
+
LocatorError,
|
|
300
|
+
ManifestError,
|
|
301
|
+
ContentError,
|
|
302
|
+
DefinitionError,
|
|
303
|
+
} from "@resourcexjs/core";
|
|
237
304
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
{
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
"version": "1.0.0"
|
|
305
|
+
try {
|
|
306
|
+
parse("invalid@locator");
|
|
307
|
+
} catch (error) {
|
|
308
|
+
if (error instanceof LocatorError) {
|
|
309
|
+
console.error("Invalid locator format:", error.message);
|
|
310
|
+
console.error("Locator:", error.locator);
|
|
311
|
+
}
|
|
246
312
|
}
|
|
247
|
-
*/
|
|
248
313
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
314
|
+
try {
|
|
315
|
+
define({ name: "test" }); // Missing required 'type' field
|
|
316
|
+
} catch (error) {
|
|
317
|
+
if (error instanceof DefinitionError) {
|
|
318
|
+
console.error("Invalid definition:", error.message);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
252
321
|
```
|
|
253
322
|
|
|
254
|
-
|
|
323
|
+
### Error Hierarchy
|
|
255
324
|
|
|
256
|
-
|
|
325
|
+
```
|
|
326
|
+
ResourceXError (base)
|
|
327
|
+
├── LocatorError - RXL parsing errors
|
|
328
|
+
├── ManifestError - RXM validation errors
|
|
329
|
+
├── ContentError - RXA operations errors
|
|
330
|
+
└── DefinitionError - RXD validation errors
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Complete Example
|
|
257
334
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
- **[@resourcexjs/arp](../arp)** - Low-level I/O
|
|
262
|
-
- **[resourcexjs](../resourcex)** - Main package (all-in-one)
|
|
335
|
+
```typescript
|
|
336
|
+
import { define, manifest, archive, resource, extract, format, parse } from "@resourcexjs/core";
|
|
337
|
+
import type { RXR } from "@resourcexjs/core";
|
|
263
338
|
|
|
264
|
-
|
|
339
|
+
// 1. Define resource metadata
|
|
340
|
+
const rxd = define({
|
|
341
|
+
name: "assistant-prompt",
|
|
342
|
+
type: "text",
|
|
343
|
+
tag: "1.0.0",
|
|
344
|
+
description: "A helpful AI assistant prompt",
|
|
345
|
+
author: "Example Team",
|
|
346
|
+
});
|
|
265
347
|
|
|
266
|
-
|
|
348
|
+
// 2. Create manifest from definition
|
|
349
|
+
const rxm = manifest(rxd);
|
|
267
350
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
351
|
+
// 3. Create archive from files
|
|
352
|
+
const rxa = await archive({
|
|
353
|
+
content: Buffer.from("You are a helpful AI assistant."),
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// 4. Combine into complete resource
|
|
357
|
+
const rxr: RXR = resource(rxm, rxa);
|
|
358
|
+
|
|
359
|
+
// 5. Access locator string
|
|
360
|
+
const locatorStr = format(rxr.locator);
|
|
361
|
+
console.log(locatorStr); // "assistant-prompt:1.0.0"
|
|
271
362
|
|
|
272
|
-
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
const resource: RXR = { locator, manifest, archive };
|
|
363
|
+
// 6. Extract files when needed
|
|
364
|
+
const files = await extract(rxr.archive);
|
|
365
|
+
console.log(files.content.toString()); // "You are a helpful AI assistant."
|
|
276
366
|
```
|
|
277
367
|
|
|
368
|
+
## Related Packages
|
|
369
|
+
|
|
370
|
+
| Package | Description |
|
|
371
|
+
| ---------------------------------------------------------------------------- | -------------------------- |
|
|
372
|
+
| [resourcexjs](https://www.npmjs.com/package/resourcexjs) | Main package (recommended) |
|
|
373
|
+
| [@resourcexjs/storage](https://www.npmjs.com/package/@resourcexjs/storage) | Storage layer |
|
|
374
|
+
| [@resourcexjs/registry](https://www.npmjs.com/package/@resourcexjs/registry) | Registry layer |
|
|
375
|
+
| [@resourcexjs/type](https://www.npmjs.com/package/@resourcexjs/type) | Type system |
|
|
376
|
+
| [@resourcexjs/loader](https://www.npmjs.com/package/@resourcexjs/loader) | Resource loading |
|
|
377
|
+
| [@resourcexjs/arp](https://www.npmjs.com/package/@resourcexjs/arp) | Low-level I/O |
|
|
378
|
+
|
|
278
379
|
## License
|
|
279
380
|
|
|
280
|
-
|
|
381
|
+
Apache-2.0
|