@k67/kaitai-struct-ts 0.3.0 โ 0.5.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 +34 -17
- package/dist/index.d.mts +47 -5
- package/dist/index.d.ts +47 -5
- package/dist/index.js +167 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +167 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
@@ -18,13 +18,22 @@ Parse any binary data format by providing a `.ksy` (Kaitai Struct YAML) definiti
|
|
18
18
|
|
19
19
|
## Features
|
20
20
|
|
21
|
+
### Core Features
|
21
22
|
- ๐ **Runtime interpretation** - No code generation needed
|
22
23
|
- ๐ฆ **Zero dependencies** (runtime) - Only YAML parser for development
|
23
24
|
- ๐ฏ **TypeScript native** - Full type safety and IntelliSense support
|
24
25
|
- ๐ **Universal** - Works in Node.js and browsers
|
25
|
-
- ๐งช **Well tested** -
|
26
|
+
- ๐งช **Well tested** - 98 comprehensive tests
|
26
27
|
- ๐ **Well documented** - Clear API and examples
|
27
28
|
|
29
|
+
### Advanced Features
|
30
|
+
- โก **Expression evaluation** - Full support for Kaitai expressions
|
31
|
+
- ๐ **Switch/case types** - Dynamic type selection based on data
|
32
|
+
- ๐ **Instances** - Lazy-evaluated fields with caching
|
33
|
+
- ๐จ **Enums** - Named constants with expression access
|
34
|
+
- ๐ **Conditional parsing** - if, repeat-expr, repeat-until
|
35
|
+
- ๐ **Positioned reads** - Absolute positioning with pos attribute
|
36
|
+
|
28
37
|
## Installation
|
29
38
|
|
30
39
|
```bash
|
@@ -158,22 +167,30 @@ pnpm format
|
|
158
167
|
|
159
168
|
## Roadmap
|
160
169
|
|
161
|
-
### Phase 1: Foundation (MVP) -
|
162
|
-
- Basic parsing capability
|
163
|
-
- Fixed-size structures
|
164
|
-
- Primitive types
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
-
|
169
|
-
-
|
170
|
-
-
|
171
|
-
|
172
|
-
|
173
|
-
-
|
174
|
-
|
175
|
-
-
|
176
|
-
-
|
170
|
+
### Phase 1: Foundation (MVP) - โ
Complete
|
171
|
+
- โ
Basic parsing capability
|
172
|
+
- โ
Fixed-size structures
|
173
|
+
- โ
Primitive types (u1-u8, s1-s8, f4, f8)
|
174
|
+
- โ
String encoding support
|
175
|
+
- โ
Byte arrays and positioning
|
176
|
+
|
177
|
+
### Phase 2: Core Features - โ
Complete
|
178
|
+
- โ
Expression evaluator (full Kaitai expression language)
|
179
|
+
- โ
Conditionals (if attribute)
|
180
|
+
- โ
Enums with expression access
|
181
|
+
- โ
Repetitions (repeat-expr, repeat-until, repeat-eos)
|
182
|
+
- โ
Calculated sizes and positions
|
183
|
+
|
184
|
+
### Phase 3: Advanced Features - ๐ In Progress (30% Complete)
|
185
|
+
- โ
Switch/case type selection
|
186
|
+
- โ
Instances (lazy-evaluated fields)
|
187
|
+
- โณ Substreams and processing
|
188
|
+
- โณ Parametric types
|
189
|
+
- โณ Bit-sized integers
|
190
|
+
- โณ Type imports
|
191
|
+
- โณ Performance optimizations
|
192
|
+
|
193
|
+
**Current Status:** ~85% complete toward v1.0.0
|
177
194
|
|
178
195
|
## Contributing
|
179
196
|
|
package/dist/index.d.mts
CHANGED
@@ -299,6 +299,8 @@ interface AttributeSpec {
|
|
299
299
|
id?: string;
|
300
300
|
/** Data type to read */
|
301
301
|
type?: string | SwitchType;
|
302
|
+
/** Arguments for parametric types */
|
303
|
+
'type-args'?: Array<string | number | boolean>;
|
302
304
|
/** Size of the field (in bytes or expression) */
|
303
305
|
size?: number | string;
|
304
306
|
/** Size until specific byte value */
|
@@ -756,9 +758,30 @@ declare class TypeInterpreter {
|
|
756
758
|
*
|
757
759
|
* @param stream - Binary stream to parse
|
758
760
|
* @param parent - Parent object (for nested types)
|
761
|
+
* @param typeArgs - Arguments for parametric types
|
759
762
|
* @returns Parsed object
|
760
763
|
*/
|
761
|
-
parse(stream: KaitaiStream, parent?: unknown): Record<string, unknown>;
|
764
|
+
parse(stream: KaitaiStream, parent?: unknown, typeArgs?: Array<string | number | boolean>): Record<string, unknown>;
|
765
|
+
/**
|
766
|
+
* Set up lazy-evaluated instance getters.
|
767
|
+
* Instances are computed on first access and cached.
|
768
|
+
*
|
769
|
+
* @param result - Result object to add getters to
|
770
|
+
* @param stream - Stream for parsing
|
771
|
+
* @param context - Execution context
|
772
|
+
* @private
|
773
|
+
*/
|
774
|
+
private setupInstances;
|
775
|
+
/**
|
776
|
+
* Parse an instance (lazy-evaluated field).
|
777
|
+
*
|
778
|
+
* @param instance - Instance specification
|
779
|
+
* @param stream - Stream to read from
|
780
|
+
* @param context - Execution context
|
781
|
+
* @returns Parsed or calculated value
|
782
|
+
* @private
|
783
|
+
*/
|
784
|
+
private parseInstance;
|
762
785
|
/**
|
763
786
|
* Parse a single attribute according to its specification.
|
764
787
|
*
|
@@ -801,10 +824,21 @@ declare class TypeInterpreter {
|
|
801
824
|
* @param type - Type name or switch specification
|
802
825
|
* @param stream - Stream to read from
|
803
826
|
* @param context - Execution context
|
827
|
+
* @param typeArgs - Arguments for parametric types
|
804
828
|
* @returns Parsed value
|
805
829
|
* @private
|
806
830
|
*/
|
807
831
|
private parseType;
|
832
|
+
/**
|
833
|
+
* Parse a switch type (type selection based on expression).
|
834
|
+
*
|
835
|
+
* @param switchType - Switch type specification
|
836
|
+
* @param stream - Stream to read from
|
837
|
+
* @param context - Execution context
|
838
|
+
* @returns Parsed value
|
839
|
+
* @private
|
840
|
+
*/
|
841
|
+
private parseSwitchType;
|
808
842
|
/**
|
809
843
|
* Parse a built-in type.
|
810
844
|
*
|
@@ -836,11 +870,19 @@ declare class TypeInterpreter {
|
|
836
870
|
*/
|
837
871
|
private readFloat;
|
838
872
|
/**
|
839
|
-
*
|
840
|
-
*
|
841
|
-
*
|
873
|
+
* Apply processing transformation to data.
|
874
|
+
* Supports basic transformations like zlib decompression.
|
875
|
+
*
|
876
|
+
* @param data - Data to process
|
877
|
+
* @param process - Processing specification
|
878
|
+
* @returns Processed data
|
879
|
+
* @private
|
880
|
+
*/
|
881
|
+
private applyProcessing;
|
882
|
+
/**
|
883
|
+
* Evaluate a value that can be an expression or literal.
|
842
884
|
*
|
843
|
-
* @param value -
|
885
|
+
* @param value - Value to evaluate (expression string, number, or boolean)
|
844
886
|
* @param context - Execution context
|
845
887
|
* @returns Evaluated result
|
846
888
|
* @private
|
package/dist/index.d.ts
CHANGED
@@ -299,6 +299,8 @@ interface AttributeSpec {
|
|
299
299
|
id?: string;
|
300
300
|
/** Data type to read */
|
301
301
|
type?: string | SwitchType;
|
302
|
+
/** Arguments for parametric types */
|
303
|
+
'type-args'?: Array<string | number | boolean>;
|
302
304
|
/** Size of the field (in bytes or expression) */
|
303
305
|
size?: number | string;
|
304
306
|
/** Size until specific byte value */
|
@@ -756,9 +758,30 @@ declare class TypeInterpreter {
|
|
756
758
|
*
|
757
759
|
* @param stream - Binary stream to parse
|
758
760
|
* @param parent - Parent object (for nested types)
|
761
|
+
* @param typeArgs - Arguments for parametric types
|
759
762
|
* @returns Parsed object
|
760
763
|
*/
|
761
|
-
parse(stream: KaitaiStream, parent?: unknown): Record<string, unknown>;
|
764
|
+
parse(stream: KaitaiStream, parent?: unknown, typeArgs?: Array<string | number | boolean>): Record<string, unknown>;
|
765
|
+
/**
|
766
|
+
* Set up lazy-evaluated instance getters.
|
767
|
+
* Instances are computed on first access and cached.
|
768
|
+
*
|
769
|
+
* @param result - Result object to add getters to
|
770
|
+
* @param stream - Stream for parsing
|
771
|
+
* @param context - Execution context
|
772
|
+
* @private
|
773
|
+
*/
|
774
|
+
private setupInstances;
|
775
|
+
/**
|
776
|
+
* Parse an instance (lazy-evaluated field).
|
777
|
+
*
|
778
|
+
* @param instance - Instance specification
|
779
|
+
* @param stream - Stream to read from
|
780
|
+
* @param context - Execution context
|
781
|
+
* @returns Parsed or calculated value
|
782
|
+
* @private
|
783
|
+
*/
|
784
|
+
private parseInstance;
|
762
785
|
/**
|
763
786
|
* Parse a single attribute according to its specification.
|
764
787
|
*
|
@@ -801,10 +824,21 @@ declare class TypeInterpreter {
|
|
801
824
|
* @param type - Type name or switch specification
|
802
825
|
* @param stream - Stream to read from
|
803
826
|
* @param context - Execution context
|
827
|
+
* @param typeArgs - Arguments for parametric types
|
804
828
|
* @returns Parsed value
|
805
829
|
* @private
|
806
830
|
*/
|
807
831
|
private parseType;
|
832
|
+
/**
|
833
|
+
* Parse a switch type (type selection based on expression).
|
834
|
+
*
|
835
|
+
* @param switchType - Switch type specification
|
836
|
+
* @param stream - Stream to read from
|
837
|
+
* @param context - Execution context
|
838
|
+
* @returns Parsed value
|
839
|
+
* @private
|
840
|
+
*/
|
841
|
+
private parseSwitchType;
|
808
842
|
/**
|
809
843
|
* Parse a built-in type.
|
810
844
|
*
|
@@ -836,11 +870,19 @@ declare class TypeInterpreter {
|
|
836
870
|
*/
|
837
871
|
private readFloat;
|
838
872
|
/**
|
839
|
-
*
|
840
|
-
*
|
841
|
-
*
|
873
|
+
* Apply processing transformation to data.
|
874
|
+
* Supports basic transformations like zlib decompression.
|
875
|
+
*
|
876
|
+
* @param data - Data to process
|
877
|
+
* @param process - Processing specification
|
878
|
+
* @returns Processed data
|
879
|
+
* @private
|
880
|
+
*/
|
881
|
+
private applyProcessing;
|
882
|
+
/**
|
883
|
+
* Evaluate a value that can be an expression or literal.
|
842
884
|
*
|
843
|
-
* @param value -
|
885
|
+
* @param value - Value to evaluate (expression string, number, or boolean)
|
844
886
|
* @param context - Execution context
|
845
887
|
* @returns Evaluated result
|
846
888
|
* @private
|
package/dist/index.js
CHANGED
@@ -1857,9 +1857,7 @@ var Evaluator = class {
|
|
1857
1857
|
evaluateEnumAccess(enumName, valueName, context) {
|
1858
1858
|
const value = context.getEnumValue(enumName, valueName);
|
1859
1859
|
if (value === void 0) {
|
1860
|
-
throw new ParseError(
|
1861
|
-
`Enum value "${enumName}::${valueName}" not found`
|
1862
|
-
);
|
1860
|
+
throw new ParseError(`Enum value "${enumName}::${valueName}" not found`);
|
1863
1861
|
}
|
1864
1862
|
return value;
|
1865
1863
|
}
|
@@ -1968,12 +1966,21 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
1968
1966
|
*
|
1969
1967
|
* @param stream - Binary stream to parse
|
1970
1968
|
* @param parent - Parent object (for nested types)
|
1969
|
+
* @param typeArgs - Arguments for parametric types
|
1971
1970
|
* @returns Parsed object
|
1972
1971
|
*/
|
1973
|
-
parse(stream, parent) {
|
1972
|
+
parse(stream, parent, typeArgs) {
|
1974
1973
|
const result = {};
|
1975
1974
|
const context = new Context(stream, result, parent, this.schema.enums);
|
1976
1975
|
context.current = result;
|
1976
|
+
if (typeArgs && this.schema.params) {
|
1977
|
+
for (let i = 0; i < this.schema.params.length && i < typeArgs.length; i++) {
|
1978
|
+
const param = this.schema.params[i];
|
1979
|
+
const argValue = typeArgs[i];
|
1980
|
+
const evaluatedArg = typeof argValue === "string" ? this.evaluateValue(argValue, context) : argValue;
|
1981
|
+
context.set(param.id, evaluatedArg);
|
1982
|
+
}
|
1983
|
+
}
|
1977
1984
|
if (this.schema.seq) {
|
1978
1985
|
for (const attr of this.schema.seq) {
|
1979
1986
|
const value = this.parseAttribute(attr, context);
|
@@ -1982,8 +1989,83 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
1982
1989
|
}
|
1983
1990
|
}
|
1984
1991
|
}
|
1992
|
+
if (this.schema.instances) {
|
1993
|
+
this.setupInstances(result, stream, context);
|
1994
|
+
}
|
1985
1995
|
return result;
|
1986
1996
|
}
|
1997
|
+
/**
|
1998
|
+
* Set up lazy-evaluated instance getters.
|
1999
|
+
* Instances are computed on first access and cached.
|
2000
|
+
*
|
2001
|
+
* @param result - Result object to add getters to
|
2002
|
+
* @param stream - Stream for parsing
|
2003
|
+
* @param context - Execution context
|
2004
|
+
* @private
|
2005
|
+
*/
|
2006
|
+
setupInstances(result, stream, context) {
|
2007
|
+
if (!this.schema.instances) return;
|
2008
|
+
for (const [name, instance] of Object.entries(this.schema.instances)) {
|
2009
|
+
let cached = void 0;
|
2010
|
+
let evaluated = false;
|
2011
|
+
Object.defineProperty(result, name, {
|
2012
|
+
get: () => {
|
2013
|
+
if (!evaluated) {
|
2014
|
+
cached = this.parseInstance(
|
2015
|
+
instance,
|
2016
|
+
stream,
|
2017
|
+
context
|
2018
|
+
);
|
2019
|
+
evaluated = true;
|
2020
|
+
}
|
2021
|
+
return cached;
|
2022
|
+
},
|
2023
|
+
enumerable: true,
|
2024
|
+
configurable: true
|
2025
|
+
});
|
2026
|
+
}
|
2027
|
+
}
|
2028
|
+
/**
|
2029
|
+
* Parse an instance (lazy-evaluated field).
|
2030
|
+
*
|
2031
|
+
* @param instance - Instance specification
|
2032
|
+
* @param stream - Stream to read from
|
2033
|
+
* @param context - Execution context
|
2034
|
+
* @returns Parsed or calculated value
|
2035
|
+
* @private
|
2036
|
+
*/
|
2037
|
+
parseInstance(instance, stream, context) {
|
2038
|
+
if ("value" in instance) {
|
2039
|
+
return this.evaluateValue(
|
2040
|
+
instance.value,
|
2041
|
+
context
|
2042
|
+
);
|
2043
|
+
}
|
2044
|
+
const savedPos = stream.pos;
|
2045
|
+
try {
|
2046
|
+
if (instance.pos !== void 0) {
|
2047
|
+
const pos = this.evaluateValue(
|
2048
|
+
instance.pos,
|
2049
|
+
context
|
2050
|
+
);
|
2051
|
+
if (typeof pos === "number") {
|
2052
|
+
stream.seek(pos);
|
2053
|
+
} else if (typeof pos === "bigint") {
|
2054
|
+
stream.seek(Number(pos));
|
2055
|
+
} else {
|
2056
|
+
throw new ParseError(
|
2057
|
+
`pos must evaluate to a number, got ${typeof pos}`
|
2058
|
+
);
|
2059
|
+
}
|
2060
|
+
}
|
2061
|
+
const value = this.parseAttribute(instance, context);
|
2062
|
+
return value;
|
2063
|
+
} finally {
|
2064
|
+
if (instance.pos !== void 0) {
|
2065
|
+
stream.seek(savedPos);
|
2066
|
+
}
|
2067
|
+
}
|
2068
|
+
}
|
1987
2069
|
/**
|
1988
2070
|
* Parse a single attribute according to its specification.
|
1989
2071
|
*
|
@@ -2139,14 +2221,27 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2139
2221
|
}
|
2140
2222
|
if (type === "str" || !type) {
|
2141
2223
|
const encoding = attr.encoding || this.schema.meta.encoding || "UTF-8";
|
2224
|
+
let data;
|
2142
2225
|
if (type === "str") {
|
2143
|
-
|
2226
|
+
data = stream.readBytes(size);
|
2227
|
+
if (attr.process) {
|
2228
|
+
data = this.applyProcessing(data, attr.process);
|
2229
|
+
}
|
2230
|
+
return new TextDecoder(encoding).decode(data);
|
2144
2231
|
} else {
|
2145
|
-
|
2232
|
+
data = stream.readBytes(size);
|
2233
|
+
if (attr.process) {
|
2234
|
+
data = this.applyProcessing(data, attr.process);
|
2235
|
+
}
|
2236
|
+
return data;
|
2146
2237
|
}
|
2147
2238
|
} else {
|
2148
|
-
|
2149
|
-
|
2239
|
+
let data = stream.readBytes(size);
|
2240
|
+
if (attr.process) {
|
2241
|
+
data = this.applyProcessing(data, attr.process);
|
2242
|
+
}
|
2243
|
+
const substream = new KaitaiStream(data);
|
2244
|
+
return this.parseType(type, substream, context, attr["type-args"]);
|
2150
2245
|
}
|
2151
2246
|
}
|
2152
2247
|
if (attr["size-eos"]) {
|
@@ -2161,7 +2256,7 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2161
2256
|
if (!type) {
|
2162
2257
|
throw new ParseError("Attribute must have either type, size, or contents");
|
2163
2258
|
}
|
2164
|
-
return this.parseType(type, stream, context);
|
2259
|
+
return this.parseType(type, stream, context, attr["type-args"]);
|
2165
2260
|
}
|
2166
2261
|
/**
|
2167
2262
|
* Parse a value of a specific type.
|
@@ -2169,12 +2264,17 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2169
2264
|
* @param type - Type name or switch specification
|
2170
2265
|
* @param stream - Stream to read from
|
2171
2266
|
* @param context - Execution context
|
2267
|
+
* @param typeArgs - Arguments for parametric types
|
2172
2268
|
* @returns Parsed value
|
2173
2269
|
* @private
|
2174
2270
|
*/
|
2175
|
-
parseType(type, stream, context) {
|
2271
|
+
parseType(type, stream, context, typeArgs) {
|
2176
2272
|
if (typeof type === "object") {
|
2177
|
-
|
2273
|
+
return this.parseSwitchType(
|
2274
|
+
type,
|
2275
|
+
stream,
|
2276
|
+
context
|
2277
|
+
);
|
2178
2278
|
}
|
2179
2279
|
if (isBuiltinType(type)) {
|
2180
2280
|
return this.parseBuiltinType(type, stream, context);
|
@@ -2185,11 +2285,46 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2185
2285
|
if (this.schema.enums && !typeSchema.enums) {
|
2186
2286
|
typeSchema.enums = this.schema.enums;
|
2187
2287
|
}
|
2288
|
+
if (this.schema.types && !typeSchema.types) {
|
2289
|
+
typeSchema.types = this.schema.types;
|
2290
|
+
}
|
2188
2291
|
const interpreter = new _TypeInterpreter(typeSchema, meta);
|
2189
|
-
return interpreter.parse(stream, context.current);
|
2292
|
+
return interpreter.parse(stream, context.current, typeArgs);
|
2190
2293
|
}
|
2191
2294
|
throw new ParseError(`Unknown type: ${type}`);
|
2192
2295
|
}
|
2296
|
+
/**
|
2297
|
+
* Parse a switch type (type selection based on expression).
|
2298
|
+
*
|
2299
|
+
* @param switchType - Switch type specification
|
2300
|
+
* @param stream - Stream to read from
|
2301
|
+
* @param context - Execution context
|
2302
|
+
* @returns Parsed value
|
2303
|
+
* @private
|
2304
|
+
*/
|
2305
|
+
parseSwitchType(switchType, stream, context) {
|
2306
|
+
const switchOn = switchType["switch-on"];
|
2307
|
+
const cases = switchType["cases"];
|
2308
|
+
const defaultType = switchType["default"];
|
2309
|
+
if (!switchOn || typeof switchOn !== "string") {
|
2310
|
+
throw new ParseError("switch-on expression is required for switch types");
|
2311
|
+
}
|
2312
|
+
if (!cases) {
|
2313
|
+
throw new ParseError("cases are required for switch types");
|
2314
|
+
}
|
2315
|
+
const switchValue = this.evaluateValue(switchOn, context);
|
2316
|
+
const switchKey = String(switchValue);
|
2317
|
+
let selectedType = cases[switchKey];
|
2318
|
+
if (selectedType === void 0 && defaultType) {
|
2319
|
+
selectedType = defaultType;
|
2320
|
+
}
|
2321
|
+
if (selectedType === void 0) {
|
2322
|
+
throw new ParseError(
|
2323
|
+
`No matching case for switch value "${switchKey}" and no default type specified`
|
2324
|
+
);
|
2325
|
+
}
|
2326
|
+
return this.parseType(selectedType, stream, context);
|
2327
|
+
}
|
2193
2328
|
/**
|
2194
2329
|
* Parse a built-in type.
|
2195
2330
|
*
|
@@ -2267,11 +2402,27 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2267
2402
|
}
|
2268
2403
|
}
|
2269
2404
|
/**
|
2270
|
-
*
|
2271
|
-
*
|
2272
|
-
*
|
2405
|
+
* Apply processing transformation to data.
|
2406
|
+
* Supports basic transformations like zlib decompression.
|
2407
|
+
*
|
2408
|
+
* @param data - Data to process
|
2409
|
+
* @param process - Processing specification
|
2410
|
+
* @returns Processed data
|
2411
|
+
* @private
|
2412
|
+
*/
|
2413
|
+
applyProcessing(data, process) {
|
2414
|
+
const processType = typeof process === "string" ? process : process.algorithm;
|
2415
|
+
if (processType) {
|
2416
|
+
throw new NotImplementedError(
|
2417
|
+
`Processing type "${processType}" is not yet implemented. Supported in future versions with zlib, encryption, etc.`
|
2418
|
+
);
|
2419
|
+
}
|
2420
|
+
return data;
|
2421
|
+
}
|
2422
|
+
/**
|
2423
|
+
* Evaluate a value that can be an expression or literal.
|
2273
2424
|
*
|
2274
|
-
* @param value -
|
2425
|
+
* @param value - Value to evaluate (expression string, number, or boolean)
|
2275
2426
|
* @param context - Execution context
|
2276
2427
|
* @returns Evaluated result
|
2277
2428
|
* @private
|