tagged-urn 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 ADDED
@@ -0,0 +1,158 @@
1
+ # Tagged URN - JavaScript Implementation
2
+
3
+ Production-ready JavaScript implementation of Tagged URN with strict validation and matching rules.
4
+
5
+ ## Features
6
+
7
+ - **Strict Rule Enforcement** - Follows exact same rules as Rust, Go, and Objective-C implementations
8
+ - **Case Insensitive** - All input normalized to lowercase
9
+ - **Tag Order Independent** - Canonical alphabetical sorting
10
+ - **Wildcard Support** - `*` matching in values only
11
+ - **Value-less Tags** - Tags without values (`tag`) are wildcards (`tag=*`)
12
+ - **Extended Characters** - Support for `/` and `:` in tag components
13
+ - **Production Ready** - No fallbacks, fails hard on invalid input
14
+ - **Comprehensive Tests** - Full test suite verifying all rules
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install tagged-urn
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```javascript
25
+ const { TaggedUrn, TaggedUrnBuilder, UrnMatcher } = require('tagged-urn');
26
+
27
+ // Create from string
28
+ const urn = TaggedUrn.fromString('cap:op=generate;ext=pdf');
29
+ console.log(urn.toString()); // "cap:ext=pdf;op=generate"
30
+
31
+ // Use builder pattern
32
+ const built = new TaggedUrnBuilder()
33
+ .tag('op', 'extract')
34
+ .tag('target', 'metadata')
35
+ .build();
36
+
37
+ // Matching
38
+ const request = TaggedUrn.fromString('cap:op=generate');
39
+ console.log(urn.matches(request)); // true
40
+
41
+ // Find best match
42
+ const urns = [
43
+ TaggedUrn.fromString('cap:op=*'),
44
+ TaggedUrn.fromString('cap:op=generate'),
45
+ TaggedUrn.fromString('cap:op=generate;ext=pdf')
46
+ ];
47
+ const best = UrnMatcher.findBestMatch(urns, request);
48
+ console.log(best.toString()); // "cap:ext=pdf;op=generate" (most specific)
49
+ ```
50
+
51
+ ## API Reference
52
+
53
+ ### TaggedUrn Class
54
+
55
+ #### Static Methods
56
+ - `TaggedUrn.fromString(s)` - Parse Tagged URN from string
57
+ - Throws `TaggedUrnError` on invalid format
58
+
59
+ #### Instance Methods
60
+ - `toString()` - Get canonical string representation
61
+ - `getTag(key)` - Get tag value (case-insensitive)
62
+ - `hasTag(key, value)` - Check if tag exists with value
63
+ - `withTag(key, value)` - Add/update tag (returns new instance)
64
+ - `withoutTag(key)` - Remove tag (returns new instance)
65
+ - `matches(other)` - Check if this URN matches another
66
+ - `canHandle(request)` - Check if this URN can handle a request
67
+ - `specificity()` - Get specificity score for matching
68
+ - `isMoreSpecificThan(other)` - Compare specificity
69
+ - `isCompatibleWith(other)` - Check compatibility
70
+ - `equals(other)` - Check equality
71
+
72
+ ### TaggedUrnBuilder Class
73
+
74
+ Fluent builder for constructing Tagged URNs:
75
+
76
+ ```javascript
77
+ const urn = new TaggedUrnBuilder()
78
+ .tag('op', 'generate')
79
+ .tag('format', 'json')
80
+ .build();
81
+ ```
82
+
83
+ ### UrnMatcher Class
84
+
85
+ Utility for matching sets of Tagged URNs:
86
+
87
+ - `UrnMatcher.findBestMatch(urns, request)` - Find most specific match
88
+ - `UrnMatcher.findAllMatches(urns, request)` - Find all matches (sorted by specificity)
89
+ - `UrnMatcher.areCompatible(urns1, urns2)` - Check if URN sets are compatible
90
+
91
+ ### Error Handling
92
+
93
+ ```javascript
94
+ const { TaggedUrnError, ErrorCodes } = require('tagged-urn');
95
+
96
+ try {
97
+ const urn = TaggedUrn.fromString('invalid:format');
98
+ } catch (error) {
99
+ if (error instanceof TaggedUrnError) {
100
+ console.log(`Error code: ${error.code}`);
101
+ console.log(`Message: ${error.message}`);
102
+ }
103
+ }
104
+ ```
105
+
106
+ Error codes:
107
+ - `ErrorCodes.INVALID_FORMAT` - General format error
108
+ - `ErrorCodes.MISSING_CAP_PREFIX` - Missing "cap:" prefix
109
+ - `ErrorCodes.INVALID_CHARACTER` - Invalid characters in tags
110
+ - `ErrorCodes.DUPLICATE_KEY` - Duplicate tag keys
111
+ - `ErrorCodes.NUMERIC_KEY` - Pure numeric tag keys
112
+ - `ErrorCodes.EMPTY_TAG` - Empty tag components
113
+
114
+ ## Rules
115
+
116
+ This implementation strictly follows the 21 Tagged URN rules. See `RULES.md` for complete specification.
117
+
118
+ ### Key Rules Summary:
119
+
120
+ 1. **Case Insensitive** - `cap:OP=Generate` == `cap:op=generate`
121
+ 2. **Order Independent** - `cap:a=1;b=2` == `cap:b=2;a=1`
122
+ 3. **Prefix Required** - Must start with `cap:`
123
+ 4. **Semicolon Separated** - Tags separated by `;`
124
+ 5. **Optional Trailing `;`** - `cap:a=1;` == `cap:a=1`
125
+ 6. **Canonical Form** - Lowercase, alphabetically sorted, no trailing `;`
126
+ 7. **Wildcard Values** - `*` allowed in values only, not keys
127
+ 8. **Extended Characters** - `/` and `:` allowed in tag components
128
+ 9. **No Duplicate Keys** - Fails hard on duplicates
129
+ 10. **No Numeric Keys** - Pure numeric keys forbidden
130
+
131
+ ## Testing
132
+
133
+ ```bash
134
+ npm test
135
+ ```
136
+
137
+ Runs comprehensive test suite covering all rules and edge cases.
138
+
139
+ ## Browser Support
140
+
141
+ Works in both Node.js and browsers:
142
+
143
+ ```html
144
+ <script src="tagged-urn.js"></script>
145
+ <script>
146
+ const urn = TaggedUrn.fromString('cap:op=generate');
147
+ console.log(urn.toString());
148
+ </script>
149
+ ```
150
+
151
+ ## Cross-Language Compatibility
152
+
153
+ This JavaScript implementation produces identical results to:
154
+ - [Rust implementation](../tagged-urn-rs/)
155
+ - [Go implementation](../tagged-urn-go/)
156
+ - [Objective-C implementation](../tagged-urn-objc/)
157
+
158
+ All implementations pass the same test cases and follow identical rules.
package/RULES.md ADDED
@@ -0,0 +1,92 @@
1
+ # Tagged URN Rules
2
+
3
+ ## Definitive specification for Tagged URN format and behavior
4
+
5
+ ### 1. Case Insensitivity
6
+ Tagged URNs are case insensitive. All input is normalized to lowercase for storage and comparison.
7
+
8
+ ### 2. Tag Order Independence
9
+ The order of tags in the URN string does not matter. Tags are always sorted alphabetically by key in canonical form.
10
+
11
+ ### 3. Mandatory Prefix
12
+ Tagged URNs must always be preceded by `cap:` which is the signifier of a tagged URN.
13
+
14
+ ### 4. Tag Separator
15
+ Tags are separated by semicolons (`;`).
16
+
17
+ ### 5. Trailing Semicolon Optional
18
+ Presence or absence of the final trailing semicolon does not matter. Both `cap:key=value` and `cap:key=value;` are equivalent.
19
+
20
+ ### 6. Character Restrictions
21
+ - No spaces in tagged URNs
22
+ - Allowed characters in tag keys and values: alphanumeric, dashes (`-`), underscores (`_`), slashes (`/`), colons (`:`), dots (`.`), and asterisk (`*` in values only)
23
+ - Quote marks (`"`, `'`), hash (`#`), and other special characters are not accepted
24
+
25
+ ### 7. Tag Structure
26
+ - Tag separator within a tag: `=` separates tag key from tag value
27
+ - Tag separator between tags: `;` separates key-value pairs
28
+ - After the initial `cap:` prefix, colons (`:`) are treated as normal characters, not separators
29
+
30
+ ### 8. No Special Tags
31
+ No reserved tag names - anything goes for tag keys.
32
+
33
+ ### 9. Canonical Form
34
+ The canonical form of Tagged URNs is all lowercase, with tags sorted by keys alphabetically, and no final trailing semicolon.
35
+
36
+ ### 10. Wildcard Support
37
+ - Wildcard `*` is accepted only as tag value, not as tag key
38
+ - When used as a tag value, `*` matches any value for that tag key
39
+
40
+ ### 11. Value-less Tags (Existence Assertion)
41
+ - Tags may be specified without a value: `cap:key1=value1;optimize;key2=value2`
42
+ - A value-less tag like `optimize` is equivalent to `optimize=*` (wildcard)
43
+ - This asserts that the tag exists but matches any value
44
+ - Value-less tags are useful as flags or for checking tag existence
45
+ - **Parsing:** `tag` (no `=`) is parsed as `tag=*`
46
+ - **Serialization:** `tag=*` is serialized as just `tag` (no `=*`)
47
+ - **Note:** `tag=` (explicit `=` with no value) is still an error - this is different from a value-less tag
48
+
49
+ ### 12. Matching Specificity
50
+ As more tags are specified, URNs become more specific:
51
+ - `cap:` matches any URN
52
+ - `cap:prop=*` matches any URN that has a `prop` tag with any value
53
+ - `cap:prop=1` matches only URNs that have `prop=1`, regardless of other tags
54
+
55
+ ### 13. Exact Tag Matching
56
+ `cap:prop=1` matches only URNs that have `prop=1` irrespective of other tags present.
57
+
58
+ ### 14. Subset Matching
59
+ Only the tags specified in the criteria affect matching. URNs with extra tags not mentioned in the criteria still match if they satisfy all specified criteria.
60
+
61
+ ### 15. Duplicate Keys
62
+ Duplicate keys in the same URN result in an error - last occurrence does not win.
63
+
64
+ ### 16. UTF-8 Support
65
+ Full UTF-8 character support within the allowed character set restrictions.
66
+
67
+ ### 17. Numeric Values
68
+ - Tag keys cannot be pure numeric
69
+ - Tag values can be pure numeric
70
+
71
+ ### 18. Empty Tagged URN
72
+ `cap:` with no tags is valid and means "matches all URNs" (universal matcher).
73
+
74
+ ### 19. Length Restrictions
75
+ No explicit length restrictions, though practical limits exist based on URL and system constraints (typically ~2000 characters).
76
+
77
+ ### 20. Wildcard Restrictions
78
+ Asterisk (`*`) in tag keys is not valid. Asterisk is only valid in tag values to signify wildcard matching.
79
+
80
+ ### 21. Colon Treatment
81
+ Forward slashes (`/`) and colons (`:`) are valid anywhere in tag components and treated as normal characters, except for the mandatory `cap:` prefix which is not part of the tag structure.
82
+
83
+ ## Implementation Notes
84
+
85
+ - All implementations must normalize input to lowercase
86
+ - All implementations must sort tags alphabetically in canonical output
87
+ - All implementations must handle trailing semicolons consistently
88
+ - All implementations must validate character restrictions identically
89
+ - All implementations must implement matching logic identically
90
+ - All implementations must reject duplicate keys with appropriate error messages
91
+ - All implementations must parse value-less tags as wildcard (`tag` → `tag=*`)
92
+ - All implementations must serialize wildcard tags as value-less (`tag=*` → `tag`)
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "tagged-urn",
3
+ "version": "1.0.0",
4
+ "description": "JavaScript implementation of Tagged URN with strict validation and matching",
5
+ "main": "tagged-urn.js",
6
+ "scripts": {
7
+ "test": "node tagged-urn.test.js"
8
+ },
9
+ "keywords": [
10
+ "tagged-urn",
11
+ "urn",
12
+ "namespace",
13
+ "validation",
14
+ "matching"
15
+ ],
16
+ "author": "Bahram Joharshamshiri",
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/jowharshamshiri/tagged-urn.git",
21
+ "directory": "tagged-urn-js"
22
+ },
23
+ "engines": {
24
+ "node": ">=14.0.0"
25
+ },
26
+ "files": [
27
+ "tagged-urn.js",
28
+ "tagged-urn.test.js",
29
+ "RULES.md",
30
+ "README.md"
31
+ ]
32
+ }