@marcuspuchalla/nachos 0.1.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/CHANGELOG.md +64 -0
- package/LICENSE +674 -0
- package/README.md +345 -0
- package/dist/chunk-2FUTHZQQ.cjs +755 -0
- package/dist/chunk-2FUTHZQQ.cjs.map +1 -0
- package/dist/chunk-2HBCILJS.cjs +2034 -0
- package/dist/chunk-2HBCILJS.cjs.map +1 -0
- package/dist/chunk-7CFYWHS6.js +742 -0
- package/dist/chunk-7CFYWHS6.js.map +1 -0
- package/dist/chunk-PD72MVTX.cjs +160 -0
- package/dist/chunk-PD72MVTX.cjs.map +1 -0
- package/dist/chunk-ZDZ2B5PE.js +149 -0
- package/dist/chunk-ZDZ2B5PE.js.map +1 -0
- package/dist/chunk-ZRPJUEIZ.js +2020 -0
- package/dist/chunk-ZRPJUEIZ.js.map +1 -0
- package/dist/encoder/index.cjs +57 -0
- package/dist/encoder/index.cjs.map +1 -0
- package/dist/encoder/index.d.cts +72 -0
- package/dist/encoder/index.d.ts +72 -0
- package/dist/encoder/index.js +4 -0
- package/dist/encoder/index.js.map +1 -0
- package/dist/index.cjs +606 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +494 -0
- package/dist/index.d.ts +494 -0
- package/dist/index.js +523 -0
- package/dist/index.js.map +1 -0
- package/dist/metafile-cjs.json +1 -0
- package/dist/metafile-esm.json +1 -0
- package/dist/parser/index.cjs +85 -0
- package/dist/parser/index.cjs.map +1 -0
- package/dist/parser/index.d.cts +72 -0
- package/dist/parser/index.d.ts +72 -0
- package/dist/parser/index.js +4 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/types-DvNlfbKB.d.cts +301 -0
- package/dist/types-DvNlfbKB.d.ts +301 -0
- package/dist/useCborSimpleEncoder-ButVU988.d.cts +268 -0
- package/dist/useCborSimpleEncoder-TVxzNJ_9.d.ts +268 -0
- package/dist/useCborTag-B_iaShG6.d.ts +142 -0
- package/dist/useCborTag-BfTIV8HM.d.cts +142 -0
- package/package.json +102 -0
- package/src/__tests__/public-api.test.ts +326 -0
- package/src/encoder/__tests__/cbor-collection-encoder.test.ts +331 -0
- package/src/encoder/__tests__/cbor-integer-encoder.test.ts +283 -0
- package/src/encoder/__tests__/cbor-simple-encoder.test.ts +224 -0
- package/src/encoder/__tests__/cbor-string-encoder.test.ts +345 -0
- package/src/encoder/__tests__/cbor-tag-encoder.test.ts +565 -0
- package/src/encoder/composables/#useCborTagEncoder.ts# +158 -0
- package/src/encoder/composables/useCborCollectionEncoder.ts +424 -0
- package/src/encoder/composables/useCborEncoder.ts +203 -0
- package/src/encoder/composables/useCborIntegerEncoder.ts +188 -0
- package/src/encoder/composables/useCborSimpleEncoder.ts +266 -0
- package/src/encoder/composables/useCborStringEncoder.ts +266 -0
- package/src/encoder/composables/useCborTagEncoder.ts +158 -0
- package/src/encoder/index.ts +35 -0
- package/src/encoder/types.ts +88 -0
- package/src/encoder/utils.ts +80 -0
- package/src/index.ts +434 -0
- package/src/parser/__tests__/ast-tree-structure.test.ts +311 -0
- package/src/parser/__tests__/cbor-collection-errors.test.ts +296 -0
- package/src/parser/__tests__/cbor-collection.test.ts +369 -0
- package/src/parser/__tests__/cbor-deterministic-encoding.test.ts +432 -0
- package/src/parser/__tests__/cbor-diagnostic.test.ts +333 -0
- package/src/parser/__tests__/cbor-duplicate-keys.test.ts +235 -0
- package/src/parser/__tests__/cbor-float-errors.test.ts +222 -0
- package/src/parser/__tests__/cbor-float.test.ts +502 -0
- package/src/parser/__tests__/cbor-integer-errors.test.ts +139 -0
- package/src/parser/__tests__/cbor-integer.test.ts +200 -0
- package/src/parser/__tests__/cbor-map-duplicate-keys.test.ts +403 -0
- package/src/parser/__tests__/cbor-parser-errors.test.ts +126 -0
- package/src/parser/__tests__/cbor-security-dos-protection.test.ts +503 -0
- package/src/parser/__tests__/cbor-sequences.test.ts +150 -0
- package/src/parser/__tests__/cbor-source-map.test.ts +321 -0
- package/src/parser/__tests__/cbor-standard-tags.test.ts +340 -0
- package/src/parser/__tests__/cbor-string-errors.test.ts +227 -0
- package/src/parser/__tests__/cbor-string.test.ts +224 -0
- package/src/parser/__tests__/cbor-tag-advanced.test.ts +500 -0
- package/src/parser/__tests__/cbor-tag-errors.test.ts +447 -0
- package/src/parser/__tests__/cbor-tag-source-map.test.ts +360 -0
- package/src/parser/__tests__/cbor-tag.test.ts +684 -0
- package/src/parser/__tests__/extreme-edge-cases.test.ts +146 -0
- package/src/parser/__tests__/pathBuilder.test.ts +256 -0
- package/src/parser/__tests__/rfc-test-vectors.test.ts +607 -0
- package/src/parser/__tests__/security-limits.test.ts +248 -0
- package/src/parser/__tests__/utils-errors.test.ts +127 -0
- package/src/parser/composables/useCborCollection.ts +509 -0
- package/src/parser/composables/useCborDiagnostic.ts +381 -0
- package/src/parser/composables/useCborFloat.ts +256 -0
- package/src/parser/composables/useCborInteger.ts +114 -0
- package/src/parser/composables/useCborParser.ts +951 -0
- package/src/parser/composables/useCborString.ts +330 -0
- package/src/parser/composables/useCborStringTypes.ts +129 -0
- package/src/parser/composables/useCborTag.ts +739 -0
- package/src/parser/index.ts +56 -0
- package/src/parser/types.ts +371 -0
- package/src/parser/utils/pathBuilder.ts +259 -0
- package/src/parser/utils.ts +398 -0
- package/src/utils/__tests__/logger.test.ts +186 -0
- package/src/utils/logger.ts +96 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NACHOS Parser (Decoder) Module
|
|
3
|
+
*
|
|
4
|
+
* @module @marcuspuchalla/nachos/parser
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Export parser composables
|
|
8
|
+
export { useCborParser } from './composables/useCborParser'
|
|
9
|
+
export { useCborInteger } from './composables/useCborInteger'
|
|
10
|
+
export { useCborString } from './composables/useCborString'
|
|
11
|
+
export { useCborCollection } from './composables/useCborCollection'
|
|
12
|
+
export { useCborFloat } from './composables/useCborFloat'
|
|
13
|
+
export { useCborTag } from './composables/useCborTag'
|
|
14
|
+
|
|
15
|
+
// Export types
|
|
16
|
+
export type {
|
|
17
|
+
ParseResult,
|
|
18
|
+
ParseResultWithMap,
|
|
19
|
+
ParseOptions,
|
|
20
|
+
ParseError,
|
|
21
|
+
ParserLimits,
|
|
22
|
+
CborContext,
|
|
23
|
+
CborValue,
|
|
24
|
+
CborMap,
|
|
25
|
+
TaggedValue,
|
|
26
|
+
PlutusData,
|
|
27
|
+
PlutusConstr,
|
|
28
|
+
PlutusMap,
|
|
29
|
+
PlutusList,
|
|
30
|
+
PlutusInt,
|
|
31
|
+
PlutusBytes,
|
|
32
|
+
SourceMapEntry,
|
|
33
|
+
Result,
|
|
34
|
+
DupMapKeyMode
|
|
35
|
+
} from './types'
|
|
36
|
+
|
|
37
|
+
// Export constants and enums
|
|
38
|
+
export {
|
|
39
|
+
DEFAULT_OPTIONS,
|
|
40
|
+
DEFAULT_LIMITS,
|
|
41
|
+
CborMajorType,
|
|
42
|
+
CborAdditionalInfo,
|
|
43
|
+
CborSimpleValue,
|
|
44
|
+
CborTag
|
|
45
|
+
} from './types'
|
|
46
|
+
|
|
47
|
+
// Export utility functions
|
|
48
|
+
export {
|
|
49
|
+
hexToBytes,
|
|
50
|
+
bytesToHex,
|
|
51
|
+
readByte,
|
|
52
|
+
readUint,
|
|
53
|
+
readBigUint,
|
|
54
|
+
extractCborHeader,
|
|
55
|
+
validateUtf8Strict
|
|
56
|
+
} from './utils'
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CBOR Parser Type Definitions
|
|
3
|
+
* Following RFC 8949 specification
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parser resource limits for DoS protection
|
|
8
|
+
*/
|
|
9
|
+
export interface ParserLimits {
|
|
10
|
+
/** Maximum input size in bytes (default: 10 MB) */
|
|
11
|
+
maxInputSize?: number
|
|
12
|
+
/** Maximum output size in bytes (default: 100 MB) */
|
|
13
|
+
maxOutputSize?: number
|
|
14
|
+
/** Maximum string length in bytes (default: 1 MB) */
|
|
15
|
+
maxStringLength?: number
|
|
16
|
+
/** Maximum array length (default: 10,000) */
|
|
17
|
+
maxArrayLength?: number
|
|
18
|
+
/** Maximum map size (default: 10,000) */
|
|
19
|
+
maxMapSize?: number
|
|
20
|
+
/** Maximum nesting depth for arrays/maps (default: 100) */
|
|
21
|
+
maxDepth?: number
|
|
22
|
+
/** Maximum tag nesting depth (default: 100) - Prevents RUSTSEC-2019-0025 */
|
|
23
|
+
maxTagDepth?: number
|
|
24
|
+
/** Maximum bignum size in bytes for tags 2/3 (default: 1024 bytes = 8192 bits) - Prevents CVE-2020-28491 */
|
|
25
|
+
maxBignumBytes?: number
|
|
26
|
+
/** Maximum parse time in milliseconds (default: 1000) */
|
|
27
|
+
maxParseTime?: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Duplicate map key handling modes
|
|
32
|
+
* Following RFC 8949 Section 5.6 guidance
|
|
33
|
+
*/
|
|
34
|
+
export type DupMapKeyMode = 'allow' | 'warn' | 'reject'
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Parser options for controlling behavior
|
|
38
|
+
*/
|
|
39
|
+
export interface ParseOptions {
|
|
40
|
+
/** Enable strict Cardano mode (all validations) */
|
|
41
|
+
strict?: boolean
|
|
42
|
+
/** Validate canonical encoding (shortest form, sorted maps) */
|
|
43
|
+
validateCanonical?: boolean
|
|
44
|
+
/** Allow indefinite-length encoding (false in strict mode) */
|
|
45
|
+
allowIndefinite?: boolean
|
|
46
|
+
/**
|
|
47
|
+
* Duplicate map key handling (RFC 8949 Section 5.6)
|
|
48
|
+
* - 'allow': Allow duplicates (default, permissive)
|
|
49
|
+
* - 'warn': Warn on duplicates but continue parsing
|
|
50
|
+
* - 'reject': Throw error on duplicate keys (strict mode)
|
|
51
|
+
*/
|
|
52
|
+
dupMapKeyMode?: DupMapKeyMode
|
|
53
|
+
/** Validate UTF-8 strictly (reject overlongs) */
|
|
54
|
+
validateUtf8Strict?: boolean
|
|
55
|
+
/** Validate set uniqueness (Tag 258 - reject duplicates) */
|
|
56
|
+
validateSetUniqueness?: boolean
|
|
57
|
+
/** Validate semantic tag constraints (Tag 4, Tag 5 array structure) */
|
|
58
|
+
validateTagSemantics?: boolean
|
|
59
|
+
/** Validate Plutus constructor semantics (Tags 102, 121-127, 1280-1400) */
|
|
60
|
+
validatePlutusSemantics?: boolean
|
|
61
|
+
/** Resource limits */
|
|
62
|
+
limits?: ParserLimits
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Default resource limits for parser
|
|
67
|
+
*/
|
|
68
|
+
export const DEFAULT_LIMITS: Required<ParserLimits> = {
|
|
69
|
+
maxInputSize: 10 * 1024 * 1024, // 10 MB
|
|
70
|
+
maxOutputSize: 100 * 1024 * 1024, // 100 MB
|
|
71
|
+
maxStringLength: 1024 * 1024, // 1 MB
|
|
72
|
+
maxArrayLength: 10000,
|
|
73
|
+
maxMapSize: 10000,
|
|
74
|
+
maxDepth: 100, // Collection nesting depth (increased for compatibility)
|
|
75
|
+
maxTagDepth: 100, // Tag nesting depth (RUSTSEC-2019-0025 mitigation)
|
|
76
|
+
maxBignumBytes: 1024, // 1 KB = 8192 bits (CVE-2020-28491 mitigation)
|
|
77
|
+
maxParseTime: 1000 // 1 second
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Default parse options
|
|
82
|
+
*/
|
|
83
|
+
export const DEFAULT_OPTIONS: Required<ParseOptions> = {
|
|
84
|
+
strict: false,
|
|
85
|
+
validateCanonical: false,
|
|
86
|
+
allowIndefinite: true,
|
|
87
|
+
dupMapKeyMode: 'allow',
|
|
88
|
+
validateUtf8Strict: false,
|
|
89
|
+
validateSetUniqueness: false,
|
|
90
|
+
validateTagSemantics: false,
|
|
91
|
+
validatePlutusSemantics: false,
|
|
92
|
+
limits: DEFAULT_LIMITS
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Parsing context that tracks state during CBOR decoding
|
|
97
|
+
*/
|
|
98
|
+
export interface CborContext {
|
|
99
|
+
/** Raw byte buffer */
|
|
100
|
+
buffer: Uint8Array
|
|
101
|
+
/** Current byte offset */
|
|
102
|
+
offset: number
|
|
103
|
+
/** Source map entries for visualization */
|
|
104
|
+
sourceMap: SourceMapEntry[]
|
|
105
|
+
/** Current nesting depth for arrays/maps (for limit checking) */
|
|
106
|
+
currentDepth?: number
|
|
107
|
+
/** Current tag nesting depth (for tag limit checking) */
|
|
108
|
+
currentTagDepth?: number
|
|
109
|
+
/** Parse start time (for timeout checking) */
|
|
110
|
+
startTime?: number
|
|
111
|
+
/** Bytes allocated (for output size tracking) */
|
|
112
|
+
bytesAllocated?: number
|
|
113
|
+
/** Parser options */
|
|
114
|
+
options?: ParseOptions
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Result of parsing a CBOR value
|
|
119
|
+
*/
|
|
120
|
+
export interface ParseResult {
|
|
121
|
+
/** Decoded CBOR value */
|
|
122
|
+
value: CborValue
|
|
123
|
+
/** Number of bytes consumed */
|
|
124
|
+
bytesRead: number
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Parse result with source mapping information
|
|
129
|
+
*/
|
|
130
|
+
export interface ParseResultWithMap extends ParseResult {
|
|
131
|
+
/** Source map for hex/JSON linking */
|
|
132
|
+
sourceMap: SourceMapEntry[]
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Source map entry for bi-directional visualization
|
|
137
|
+
*/
|
|
138
|
+
export interface SourceMapEntry {
|
|
139
|
+
/** JSON Pointer path (RFC 6901) */
|
|
140
|
+
path: string
|
|
141
|
+
/** Starting byte offset */
|
|
142
|
+
start: number
|
|
143
|
+
/** Ending byte offset (exclusive) */
|
|
144
|
+
end: number
|
|
145
|
+
/** CBOR major type (0-7) */
|
|
146
|
+
majorType: number
|
|
147
|
+
/** Human-readable type description */
|
|
148
|
+
type: string
|
|
149
|
+
/** Parent entry path (for nested structures like tags) */
|
|
150
|
+
parent?: string
|
|
151
|
+
/** Child entry paths (for container types) */
|
|
152
|
+
children?: string[]
|
|
153
|
+
/** Whether this entry represents a header (initial byte + length info) */
|
|
154
|
+
isHeader?: boolean
|
|
155
|
+
/** Whether this entry represents content (payload data) */
|
|
156
|
+
isContent?: boolean
|
|
157
|
+
/** Byte offset where header ends and content begins */
|
|
158
|
+
headerEnd?: number
|
|
159
|
+
/** JSON Pointer path to the content portion (for split header/content entries) */
|
|
160
|
+
contentPath?: string
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* CBOR byte string (can be definite or indefinite length)
|
|
165
|
+
*/
|
|
166
|
+
export interface CborByteString {
|
|
167
|
+
readonly type: 'cbor-byte-string'
|
|
168
|
+
readonly bytes: Uint8Array
|
|
169
|
+
readonly chunks?: Uint8Array[] // Original chunks for indefinite byte strings
|
|
170
|
+
[INDEFINITE_SYMBOL]?: boolean
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* CBOR text string (can be definite or indefinite length)
|
|
175
|
+
*/
|
|
176
|
+
export interface CborTextString {
|
|
177
|
+
readonly type: 'cbor-text-string'
|
|
178
|
+
readonly text: string
|
|
179
|
+
readonly chunks?: string[] // Original chunks for indefinite text strings
|
|
180
|
+
[INDEFINITE_SYMBOL]?: boolean
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* All possible CBOR values
|
|
185
|
+
*/
|
|
186
|
+
export type CborValue =
|
|
187
|
+
| number
|
|
188
|
+
| bigint
|
|
189
|
+
| string
|
|
190
|
+
| boolean
|
|
191
|
+
| null
|
|
192
|
+
| undefined
|
|
193
|
+
| Uint8Array
|
|
194
|
+
| CborByteString
|
|
195
|
+
| CborTextString
|
|
196
|
+
| CborValue[]
|
|
197
|
+
| CborMap
|
|
198
|
+
| TaggedValue
|
|
199
|
+
| SimpleValue
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Symbol to mark arrays/maps as indefinite-length encoded
|
|
203
|
+
* This allows round-trip preservation of encoding style
|
|
204
|
+
*/
|
|
205
|
+
export const INDEFINITE_SYMBOL = Symbol('cbor.indefinite')
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Symbol used to store all map entries including duplicates
|
|
209
|
+
* This allows byte-perfect round-trip when CBOR maps have duplicate keys
|
|
210
|
+
* Stores: Array<[key: CborValue, value: CborValue]>
|
|
211
|
+
*/
|
|
212
|
+
export const ALL_ENTRIES_SYMBOL = Symbol('cbor.allEntries')
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* CBOR map type (Map with any CBOR value as keys)
|
|
216
|
+
*
|
|
217
|
+
* Uses JavaScript Map to preserve key types (integers, Uint8Arrays, etc.)
|
|
218
|
+
* This is essential for:
|
|
219
|
+
* - Cardano transactions (use integer keys 0-18)
|
|
220
|
+
* - Round-trip encoding (must preserve exact key types)
|
|
221
|
+
* - CBOR specification compliance (maps can have any type as keys)
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```typescript
|
|
225
|
+
* // Cardano transaction body
|
|
226
|
+
* new Map([
|
|
227
|
+
* [0, inputs], // Integer key
|
|
228
|
+
* [1, outputs], // Integer key
|
|
229
|
+
* [2, 1000000] // Integer key (fee)
|
|
230
|
+
* ])
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
export type CborMap = Map<CborValue, CborValue> & { [INDEFINITE_SYMBOL]?: boolean }
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* CBOR array type with optional indefinite-length marker
|
|
237
|
+
*/
|
|
238
|
+
export type CborArray = CborValue[] & { [INDEFINITE_SYMBOL]?: boolean }
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Tagged CBOR value (Major Type 6)
|
|
242
|
+
*/
|
|
243
|
+
export interface TaggedValue {
|
|
244
|
+
tag: number
|
|
245
|
+
value: CborValue
|
|
246
|
+
plutus?: PlutusConstr // Decoded Plutus constructor (for tags 102, 121-127, 1280-1400)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Simple CBOR value (Major Type 7, unassigned simple values)
|
|
251
|
+
*/
|
|
252
|
+
export interface SimpleValue {
|
|
253
|
+
simpleValue: number
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Plutus Data types for Cardano smart contracts
|
|
258
|
+
*/
|
|
259
|
+
export type PlutusData =
|
|
260
|
+
| PlutusConstr
|
|
261
|
+
| PlutusMap
|
|
262
|
+
| PlutusList
|
|
263
|
+
| PlutusInt
|
|
264
|
+
| PlutusBytes
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Plutus Constructor (algebraic data type)
|
|
268
|
+
*/
|
|
269
|
+
export interface PlutusConstr {
|
|
270
|
+
constructor: number
|
|
271
|
+
fields: PlutusData[]
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Plutus Map (key-value pairs)
|
|
276
|
+
*/
|
|
277
|
+
export interface PlutusMap {
|
|
278
|
+
entries: Array<[PlutusData, PlutusData]>
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Plutus List (sequential collection)
|
|
283
|
+
*/
|
|
284
|
+
export type PlutusList = PlutusData[]
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Plutus Integer (arbitrary precision)
|
|
288
|
+
*/
|
|
289
|
+
export type PlutusInt = number | bigint
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Plutus Bytes (bounded byte string, max 64 bytes)
|
|
293
|
+
*/
|
|
294
|
+
export type PlutusBytes = Uint8Array
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* CBOR Major Types (0-7)
|
|
298
|
+
*/
|
|
299
|
+
export enum CborMajorType {
|
|
300
|
+
UNSIGNED_INT = 0,
|
|
301
|
+
NEGATIVE_INT = 1,
|
|
302
|
+
BYTE_STRING = 2,
|
|
303
|
+
TEXT_STRING = 3,
|
|
304
|
+
ARRAY = 4,
|
|
305
|
+
MAP = 5,
|
|
306
|
+
TAG = 6,
|
|
307
|
+
SIMPLE = 7
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Additional Information values
|
|
312
|
+
*/
|
|
313
|
+
export enum CborAdditionalInfo {
|
|
314
|
+
DIRECT = 23, // Values 0-23
|
|
315
|
+
ONE_BYTE = 24, // 1 byte follows
|
|
316
|
+
TWO_BYTES = 25, // 2 bytes follow
|
|
317
|
+
FOUR_BYTES = 26, // 4 bytes follow
|
|
318
|
+
EIGHT_BYTES = 27, // 8 bytes follow
|
|
319
|
+
INDEFINITE = 31 // Indefinite length
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Simple values (Major Type 7)
|
|
324
|
+
*/
|
|
325
|
+
export enum CborSimpleValue {
|
|
326
|
+
FALSE = 20,
|
|
327
|
+
TRUE = 21,
|
|
328
|
+
NULL = 22,
|
|
329
|
+
UNDEFINED = 23,
|
|
330
|
+
FLOAT16 = 25,
|
|
331
|
+
FLOAT32 = 26,
|
|
332
|
+
FLOAT64 = 27,
|
|
333
|
+
BREAK = 31
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Common semantic tags
|
|
338
|
+
*/
|
|
339
|
+
export enum CborTag {
|
|
340
|
+
DATE_TIME_STRING = 0,
|
|
341
|
+
EPOCH_DATE_TIME = 1,
|
|
342
|
+
POSITIVE_BIGNUM = 2,
|
|
343
|
+
NEGATIVE_BIGNUM = 3,
|
|
344
|
+
DECIMAL_FRACTION = 4,
|
|
345
|
+
BIGFLOAT = 5,
|
|
346
|
+
BASE64URL = 21,
|
|
347
|
+
BASE64 = 22,
|
|
348
|
+
BASE16 = 23,
|
|
349
|
+
CBOR_ENCODED = 24,
|
|
350
|
+
URI = 32,
|
|
351
|
+
BASE64URL_NO_PAD = 33,
|
|
352
|
+
BASE64_NO_PAD = 34,
|
|
353
|
+
REGEXP = 35,
|
|
354
|
+
MIME_MESSAGE = 36
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Parse error types
|
|
359
|
+
*/
|
|
360
|
+
export interface ParseError {
|
|
361
|
+
type: 'INVALID_HEX' | 'UNEXPECTED_EOF' | 'INVALID_CBOR' | 'UNSUPPORTED_TYPE'
|
|
362
|
+
message: string
|
|
363
|
+
offset?: number
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Result type for operations that can fail
|
|
368
|
+
*/
|
|
369
|
+
export type Result<T, E> =
|
|
370
|
+
| { success: true; value: T }
|
|
371
|
+
| { success: false; error: E }
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PathBuilder - Utilities for creating and manipulating source map paths
|
|
3
|
+
*
|
|
4
|
+
* Path format follows JSON Pointer (RFC 6901) with CBOR-specific extensions:
|
|
5
|
+
* - Array indices: [0], [1], [2]
|
|
6
|
+
* - Object/Map keys: .key, .nested.key
|
|
7
|
+
* - Special markers: [#header], [#content], [#value]
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { PathBuilder } from './pathBuilder'
|
|
12
|
+
*
|
|
13
|
+
* const path = PathBuilder.arrayIndex('', 0) // "[0]"
|
|
14
|
+
* const nested = PathBuilder.arrayIndex(path, 1) // "[0][1]"
|
|
15
|
+
* const key = PathBuilder.mapKey(nested, 'amount') // "[0][1].amount"
|
|
16
|
+
* const header = PathBuilder.header(key) // "[0][1].amount[#header]"
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* PathBuilder utility object with methods for creating and manipulating paths
|
|
22
|
+
*/
|
|
23
|
+
export const PathBuilder = {
|
|
24
|
+
/**
|
|
25
|
+
* Create root path (empty string)
|
|
26
|
+
*/
|
|
27
|
+
root: (): string => '',
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create path for array index
|
|
31
|
+
* @param parent - Parent path
|
|
32
|
+
* @param index - Array index (0-based)
|
|
33
|
+
* @returns Path string like "[0]" or "[0][1]"
|
|
34
|
+
*/
|
|
35
|
+
arrayIndex: (parent: string, index: number): string => `${parent}[${index}]`,
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create path for map/object key
|
|
39
|
+
* @param parent - Parent path
|
|
40
|
+
* @param key - Key name (string or number)
|
|
41
|
+
* @returns Path string like ".key" or "[0].key"
|
|
42
|
+
*/
|
|
43
|
+
mapKey: (parent: string, key: string | number): string => {
|
|
44
|
+
const keyStr = String(key)
|
|
45
|
+
// Escape special characters in key names
|
|
46
|
+
const escapedKey = keyStr.replace(/[.\[\]\\]/g, '\\$&')
|
|
47
|
+
return parent ? `${parent}.${escapedKey}` : `.${escapedKey}`
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create path for map key by index (for non-string keys)
|
|
52
|
+
* @param parent - Parent path
|
|
53
|
+
* @param index - Key index in map
|
|
54
|
+
* @returns Path string like "[#key:0]"
|
|
55
|
+
*/
|
|
56
|
+
mapKeyIndex: (parent: string, index: number): string =>
|
|
57
|
+
`${parent}[#key:${index}]`,
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create path for header portion of a value
|
|
61
|
+
* Used for byte strings, text strings, arrays, maps, tags
|
|
62
|
+
* @param parent - Parent path
|
|
63
|
+
* @returns Path string like "[0][#header]"
|
|
64
|
+
*/
|
|
65
|
+
header: (parent: string): string => `${parent}[#header]`,
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Create path for content portion of a value
|
|
69
|
+
* Used for byte strings and text strings
|
|
70
|
+
* @param parent - Parent path
|
|
71
|
+
* @returns Path string like "[0][#content]"
|
|
72
|
+
*/
|
|
73
|
+
content: (parent: string): string => `${parent}[#content]`,
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Create path for tagged value
|
|
77
|
+
* @param parent - Parent path
|
|
78
|
+
* @returns Path string like "[0][#value]"
|
|
79
|
+
*/
|
|
80
|
+
tagValue: (parent: string): string => `${parent}[#value]`,
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Normalize a path by removing special markers (#header, #content, #value)
|
|
84
|
+
* Used for matching paths across different representations
|
|
85
|
+
* @param path - Path to normalize
|
|
86
|
+
* @returns Normalized path without special markers
|
|
87
|
+
*/
|
|
88
|
+
normalize: (path: string): string => {
|
|
89
|
+
return path.replace(/\[#(header|content|value)\]$/, '')
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Check if path is a header path
|
|
94
|
+
* @param path - Path to check
|
|
95
|
+
* @returns True if path ends with [#header]
|
|
96
|
+
*/
|
|
97
|
+
isHeader: (path: string): boolean => path.endsWith('[#header]'),
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Check if path is a content path
|
|
101
|
+
* @param path - Path to check
|
|
102
|
+
* @returns True if path ends with [#content]
|
|
103
|
+
*/
|
|
104
|
+
isContent: (path: string): boolean => path.endsWith('[#content]'),
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Check if path is a tag value path
|
|
108
|
+
* @param path - Path to check
|
|
109
|
+
* @returns True if path ends with [#value]
|
|
110
|
+
*/
|
|
111
|
+
isTagValue: (path: string): boolean => path.endsWith('[#value]'),
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check if path has any special marker
|
|
115
|
+
* @param path - Path to check
|
|
116
|
+
* @returns True if path ends with any special marker
|
|
117
|
+
*/
|
|
118
|
+
hasMarker: (path: string): boolean =>
|
|
119
|
+
/\[#(header|content|value)\]$/.test(path),
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get parent path (one level up)
|
|
123
|
+
* @param path - Path to get parent of
|
|
124
|
+
* @returns Parent path or null if already at root
|
|
125
|
+
*/
|
|
126
|
+
getParent: (path: string): string | null => {
|
|
127
|
+
// First normalize to remove markers
|
|
128
|
+
const normalized = PathBuilder.normalize(path)
|
|
129
|
+
if (normalized === '') return null
|
|
130
|
+
|
|
131
|
+
// Match array index or object key at end
|
|
132
|
+
const arrayMatch = normalized.match(/^(.+)\[\d+\]$/)
|
|
133
|
+
if (arrayMatch && arrayMatch[1] !== undefined) return arrayMatch[1]
|
|
134
|
+
|
|
135
|
+
const keyMatch = normalized.match(/^(.+)\.[^.]+$/)
|
|
136
|
+
if (keyMatch && keyMatch[1] !== undefined) return keyMatch[1]
|
|
137
|
+
|
|
138
|
+
// Root level item
|
|
139
|
+
if (normalized.startsWith('[') || normalized.startsWith('.')) {
|
|
140
|
+
return ''
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return null
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get the depth (nesting level) of a path
|
|
148
|
+
* @param path - Path to measure
|
|
149
|
+
* @returns Number indicating nesting depth (0 for root)
|
|
150
|
+
*/
|
|
151
|
+
getDepth: (path: string): number => {
|
|
152
|
+
const normalized = PathBuilder.normalize(path)
|
|
153
|
+
if (normalized === '') return 0
|
|
154
|
+
|
|
155
|
+
// Count array indices and object keys
|
|
156
|
+
const arrayDepth = (normalized.match(/\[\d+\]/g) || []).length
|
|
157
|
+
const keyDepth = (normalized.match(/\./g) || []).length
|
|
158
|
+
|
|
159
|
+
return arrayDepth + keyDepth
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Parse a path into its components
|
|
164
|
+
* @param path - Path to parse
|
|
165
|
+
* @returns Array of path segments
|
|
166
|
+
*/
|
|
167
|
+
parse: (path: string): Array<{ type: 'index' | 'key' | 'marker'; value: string | number }> => {
|
|
168
|
+
const normalized = PathBuilder.normalize(path)
|
|
169
|
+
const segments: Array<{ type: 'index' | 'key' | 'marker'; value: string | number }> = []
|
|
170
|
+
|
|
171
|
+
// Check for marker
|
|
172
|
+
const markerMatch = path.match(/\[#(header|content|value)\]$/)
|
|
173
|
+
|
|
174
|
+
// Parse the normalized path
|
|
175
|
+
let remaining = normalized
|
|
176
|
+
while (remaining) {
|
|
177
|
+
// Array index
|
|
178
|
+
const indexMatch = remaining.match(/^\[(\d+)\]/)
|
|
179
|
+
if (indexMatch && indexMatch[1] !== undefined) {
|
|
180
|
+
segments.push({ type: 'index', value: parseInt(indexMatch[1], 10) })
|
|
181
|
+
remaining = remaining.slice(indexMatch[0].length)
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Object key
|
|
186
|
+
const keyMatch = remaining.match(/^\.([^.\[\]]+)/)
|
|
187
|
+
if (keyMatch && keyMatch[1] !== undefined) {
|
|
188
|
+
// Unescape special characters
|
|
189
|
+
const key = keyMatch[1].replace(/\\(.)/g, '$1')
|
|
190
|
+
segments.push({ type: 'key', value: key })
|
|
191
|
+
remaining = remaining.slice(keyMatch[0].length)
|
|
192
|
+
continue
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Unknown format, break to avoid infinite loop
|
|
196
|
+
break
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Add marker if present
|
|
200
|
+
if (markerMatch && markerMatch[1] !== undefined) {
|
|
201
|
+
segments.push({ type: 'marker', value: markerMatch[1] })
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return segments
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Build a path from segments
|
|
209
|
+
* @param segments - Array of path segments
|
|
210
|
+
* @returns Path string
|
|
211
|
+
*/
|
|
212
|
+
build: (segments: Array<{ type: 'index' | 'key' | 'marker'; value: string | number }>): string => {
|
|
213
|
+
let path = ''
|
|
214
|
+
|
|
215
|
+
for (const segment of segments) {
|
|
216
|
+
if (segment.type === 'index') {
|
|
217
|
+
path += `[${segment.value}]`
|
|
218
|
+
} else if (segment.type === 'key') {
|
|
219
|
+
const escapedKey = String(segment.value).replace(/[.\[\]\\]/g, '\\$&')
|
|
220
|
+
path += `.${escapedKey}`
|
|
221
|
+
} else if (segment.type === 'marker') {
|
|
222
|
+
path += `[#${segment.value}]`
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return path
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Join multiple path segments
|
|
231
|
+
* @param paths - Path segments to join
|
|
232
|
+
* @returns Combined path
|
|
233
|
+
*/
|
|
234
|
+
join: (...paths: string[]): string => {
|
|
235
|
+
return paths.filter(p => p !== '').join('')
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Check if a path is a descendant of another path
|
|
240
|
+
* @param path - Path to check
|
|
241
|
+
* @param ancestor - Potential ancestor path
|
|
242
|
+
* @returns True if path is a descendant of ancestor
|
|
243
|
+
*/
|
|
244
|
+
isDescendantOf: (path: string, ancestor: string): boolean => {
|
|
245
|
+
const normalizedPath = PathBuilder.normalize(path)
|
|
246
|
+
const normalizedAncestor = PathBuilder.normalize(ancestor)
|
|
247
|
+
|
|
248
|
+
if (normalizedAncestor === '') {
|
|
249
|
+
return normalizedPath !== ''
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return normalizedPath.startsWith(normalizedAncestor) &&
|
|
253
|
+
normalizedPath.length > normalizedAncestor.length &&
|
|
254
|
+
(normalizedPath[normalizedAncestor.length] === '[' ||
|
|
255
|
+
normalizedPath[normalizedAncestor.length] === '.')
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export default PathBuilder
|