capdag 0.88.20458
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 +131 -0
- package/RULES.md +111 -0
- package/capdag.js +4322 -0
- package/capdag.test.js +2874 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Cap URN - JavaScript Implementation
|
|
2
|
+
|
|
3
|
+
JavaScript implementation of Cap URN (Capability Uniform Resource Names), built on [Tagged URN](https://github.com/machinefabric/tagged-urn-js).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Required Direction Specifiers** - `in`/`out` tags for input/output media types
|
|
8
|
+
- **Media URN Validation** - Validates direction spec values are valid Media URNs
|
|
9
|
+
- **Special Pattern Values** - `*` (must-have-any), `?` (unspecified), `!` (must-not-have)
|
|
10
|
+
- **Graded Specificity** - Exact values score higher than wildcards
|
|
11
|
+
- **Cross-Language Compatible** - Identical behavior to Rust, Go, and Objective-C implementations
|
|
12
|
+
- **Production Ready** - No fallbacks, fails hard on invalid input
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install capdag
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
const { CapUrn, CapUrnBuilder, CapMatcher } = require('capdag');
|
|
24
|
+
|
|
25
|
+
// Create from string (with required direction specifiers)
|
|
26
|
+
const cap = CapUrn.fromString('cap:in="media:binary";op=extract;out="media:object"');
|
|
27
|
+
console.log(cap.toString());
|
|
28
|
+
|
|
29
|
+
// Use builder pattern
|
|
30
|
+
const built = new CapUrnBuilder()
|
|
31
|
+
.inSpec('media:void')
|
|
32
|
+
.outSpec('media:object')
|
|
33
|
+
.tag('op', 'generate')
|
|
34
|
+
.tag('target', 'thumbnail')
|
|
35
|
+
.build();
|
|
36
|
+
|
|
37
|
+
// Matching
|
|
38
|
+
const request = CapUrn.fromString('cap:in="media:binary";op=extract;out="media:object"');
|
|
39
|
+
console.log(cap.accepts(request)); // true
|
|
40
|
+
|
|
41
|
+
// Find best match by specificity
|
|
42
|
+
const caps = [
|
|
43
|
+
CapUrn.fromString('cap:in=*;op=extract;out=*'),
|
|
44
|
+
CapUrn.fromString('cap:in="media:binary";op=extract;out="media:object"'),
|
|
45
|
+
CapUrn.fromString('cap:ext=pdf;in="media:binary";op=extract;out="media:object"')
|
|
46
|
+
];
|
|
47
|
+
const best = CapMatcher.findBestMatch(caps, request);
|
|
48
|
+
console.log(best.toString()); // Most specific match
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## API Reference
|
|
52
|
+
|
|
53
|
+
### CapUrn Class
|
|
54
|
+
|
|
55
|
+
#### Static Methods
|
|
56
|
+
- `CapUrn.fromString(s)` - Parse Cap URN from string
|
|
57
|
+
- Throws `CapUrnError` on invalid format or missing direction specifiers
|
|
58
|
+
|
|
59
|
+
#### Instance Methods
|
|
60
|
+
- `toString()` - Get canonical string representation
|
|
61
|
+
- `getTag(key)` - Get tag value (case-insensitive)
|
|
62
|
+
- `getInSpec()` - Get input media type
|
|
63
|
+
- `getOutSpec()` - Get output media type
|
|
64
|
+
- `hasTag(key, value)` - Check if tag exists with value
|
|
65
|
+
- `withTag(key, value)` - Add/update tag (returns new instance)
|
|
66
|
+
- `withoutTag(key)` - Remove tag (returns new instance)
|
|
67
|
+
- `accepts(request)` - Check if this cap (as pattern) accepts a request
|
|
68
|
+
- `specificity()` - Get specificity score for matching
|
|
69
|
+
- `isMoreSpecificThan(other)` - Compare specificity
|
|
70
|
+
- `equals(other)` - Check equality
|
|
71
|
+
|
|
72
|
+
### CapUrnBuilder Class
|
|
73
|
+
|
|
74
|
+
Fluent builder for constructing Cap URNs:
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
const cap = new CapUrnBuilder()
|
|
78
|
+
.inSpec('media:binary')
|
|
79
|
+
.outSpec('media:object')
|
|
80
|
+
.tag('op', 'extract')
|
|
81
|
+
.tag('target', 'metadata')
|
|
82
|
+
.build();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### CapMatcher Class
|
|
86
|
+
|
|
87
|
+
Utility for matching sets of caps:
|
|
88
|
+
|
|
89
|
+
- `CapMatcher.findBestMatch(caps, request)` - Find most specific match
|
|
90
|
+
- `CapMatcher.findAllMatches(caps, request)` - Find all matches (sorted by specificity)
|
|
91
|
+
|
|
92
|
+
### Error Handling
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
const { CapUrnError, ErrorCodes } = require('capdag');
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const cap = CapUrn.fromString('cap:op=extract'); // Missing in/out
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (error instanceof CapUrnError) {
|
|
101
|
+
console.log(`Error code: ${error.code}`); // MISSING_IN_SPEC
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Cap-specific error codes:
|
|
107
|
+
- `ErrorCodes.MISSING_IN_SPEC` - Missing required `in` tag
|
|
108
|
+
- `ErrorCodes.MISSING_OUT_SPEC` - Missing required `out` tag
|
|
109
|
+
- `ErrorCodes.INVALID_MEDIA_URN` - Invalid Media URN in direction spec
|
|
110
|
+
|
|
111
|
+
For base Tagged URN error codes, see [Tagged URN documentation](https://github.com/machinefabric/tagged-urn-js).
|
|
112
|
+
|
|
113
|
+
## Documentation
|
|
114
|
+
|
|
115
|
+
- [RULES.md](./RULES.md) - Cap-specific rules
|
|
116
|
+
- [Tagged URN RULES.md](https://github.com/machinefabric/tagged-urn-js/blob/main/RULES.md) - Base format rules (case, quoting, wildcards, etc.)
|
|
117
|
+
|
|
118
|
+
## Testing
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npm test
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Cross-Language Compatibility
|
|
125
|
+
|
|
126
|
+
This JavaScript implementation produces identical results to:
|
|
127
|
+
- [Rust reference implementation](https://github.com/machinefabric/capdag)
|
|
128
|
+
- [Go implementation](https://github.com/machinefabric/capdag-go)
|
|
129
|
+
- [Objective-C implementation](https://github.com/machinefabric/capdag-objc)
|
|
130
|
+
|
|
131
|
+
All implementations pass the same test cases and follow identical rules.
|
package/RULES.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Cap URN Rules (JavaScript)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Cap URNs extend Tagged URNs with capability-specific requirements. For base Tagged URN format rules (case handling, tag ordering, special values, quoting, value-less tags, etc.), see [Tagged URN RULES.md](https://github.com/machinefabric/tagged-urn-js/blob/main/RULES.md).
|
|
6
|
+
|
|
7
|
+
This document covers only cap-specific rules.
|
|
8
|
+
|
|
9
|
+
## Cap-Specific Rules
|
|
10
|
+
|
|
11
|
+
### 1. Required Direction Specifiers
|
|
12
|
+
|
|
13
|
+
Cap URNs **must** include `in` and `out` tags that specify input/output media types:
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
// Valid cap URN with direction specifiers
|
|
17
|
+
const cap = CapUrn.fromString('cap:in="media:binary";op=extract;out="media:object"');
|
|
18
|
+
|
|
19
|
+
// Invalid - missing direction specifiers
|
|
20
|
+
CapUrn.fromString('cap:op=extract'); // throws ErrorCodes.MISSING_IN_SPEC
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 2. Media URN Validation
|
|
24
|
+
|
|
25
|
+
Direction specifier values must be valid Media URNs or special pattern values:
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
// Valid: Media URN value
|
|
29
|
+
'cap:in="media:binary";op=extract;out="media:object"'
|
|
30
|
+
|
|
31
|
+
// Valid: Must-have-any (any media type)
|
|
32
|
+
'cap:in=*;op=extract;out=*'
|
|
33
|
+
|
|
34
|
+
// Invalid: Not a Media URN or special value
|
|
35
|
+
'cap:in=binary;op=extract;out=object' // throws ErrorCodes.INVALID_MEDIA_URN
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 3. Matching Semantics
|
|
39
|
+
|
|
40
|
+
Cap matching uses Tagged URN matching semantics:
|
|
41
|
+
|
|
42
|
+
| Pattern Value | Meaning | Instance Missing | Instance=v | Instance=x≠v |
|
|
43
|
+
|---------------|---------|------------------|------------|--------------|
|
|
44
|
+
| (missing) | No constraint | OK | OK | OK |
|
|
45
|
+
| `K=?` | No constraint (explicit) | OK | OK | OK |
|
|
46
|
+
| `K=!` | Must-not-have | OK | NO | NO |
|
|
47
|
+
| `K=*` | Must-have, any value | NO | OK | OK |
|
|
48
|
+
| `K=v` | Must-have, exact value | NO | OK | NO |
|
|
49
|
+
|
|
50
|
+
### 4. Graded Specificity
|
|
51
|
+
|
|
52
|
+
Specificity uses graded scoring:
|
|
53
|
+
|
|
54
|
+
| Value Type | Score |
|
|
55
|
+
|------------|-------|
|
|
56
|
+
| Exact value (K=v) | 3 |
|
|
57
|
+
| Must-have-any (K=*) | 2 |
|
|
58
|
+
| Must-not-have (K=!) | 1 |
|
|
59
|
+
| Unspecified (K=?) or missing | 0 |
|
|
60
|
+
|
|
61
|
+
Examples:
|
|
62
|
+
- `cap:in="media:binary";op=extract;out="media:object"` → 3+3+3 = 9
|
|
63
|
+
- `cap:in=*;op=extract;out=*` → 2+3+2 = 7
|
|
64
|
+
|
|
65
|
+
## Cap-Specific Error Codes
|
|
66
|
+
|
|
67
|
+
| Code | Name | Description |
|
|
68
|
+
|------|------|-------------|
|
|
69
|
+
| 10 | MISSING_IN_SPEC | Cap URN missing required `in` tag |
|
|
70
|
+
| 11 | MISSING_OUT_SPEC | Cap URN missing required `out` tag |
|
|
71
|
+
| 12 | INVALID_MEDIA_URN | Direction spec value is not a valid Media URN |
|
|
72
|
+
|
|
73
|
+
## Validation Rules
|
|
74
|
+
|
|
75
|
+
### XV5: No Redefinition of Registry Media Specs
|
|
76
|
+
|
|
77
|
+
Inline media specs in a capability's `media_specs` table must not redefine media specs that already exist in the global registry or built-in specs.
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
const { validateNoMediaSpecRedefinitionSync, MEDIA_STRING } = require('capdag');
|
|
81
|
+
|
|
82
|
+
// This will fail - MEDIA_STRING is a built-in spec
|
|
83
|
+
const mediaSpecs = {
|
|
84
|
+
[MEDIA_STRING]: { media_type: 'text/plain', title: 'My String' }
|
|
85
|
+
};
|
|
86
|
+
const result = validateNoMediaSpecRedefinitionSync(mediaSpecs);
|
|
87
|
+
// result: { valid: false, error: 'XV5: ...', redefines: ['media:textable'] }
|
|
88
|
+
|
|
89
|
+
// This is allowed - custom spec that doesn't exist
|
|
90
|
+
const customSpecs = {
|
|
91
|
+
'media:my-custom-type': { media_type: 'application/json', title: 'My Type' }
|
|
92
|
+
};
|
|
93
|
+
const customResult = validateNoMediaSpecRedefinitionSync(customSpecs);
|
|
94
|
+
// result: { valid: true }
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
For server-side validation with registry access, use the async version:
|
|
98
|
+
```javascript
|
|
99
|
+
const { validateNoMediaSpecRedefinition } = require('capdag');
|
|
100
|
+
|
|
101
|
+
const result = await validateNoMediaSpecRedefinition(mediaSpecs, {
|
|
102
|
+
registryLookup: async (urn) => await mediaStore.get(urn) !== null
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Cross-Language Compatibility
|
|
107
|
+
|
|
108
|
+
This JavaScript implementation follows the same rules as:
|
|
109
|
+
- [Rust reference implementation](https://github.com/machinefabric/capdag)
|
|
110
|
+
- [Go implementation](https://github.com/machinefabric/capdag-go)
|
|
111
|
+
- [Objective-C implementation](https://github.com/machinefabric/capdag-objc)
|