samengine 1.9.1 → 1.10.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/LICENSE +201 -0
- package/README.md +168 -0
- package/dist/config/buildconfig.d.ts +146 -0
- package/dist/config/buildconfig.js +115 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.js +1 -0
- package/dist/core.d.ts +17 -0
- package/dist/core.js +24 -0
- package/dist/html.d.ts +29 -0
- package/dist/html.js +20 -0
- package/dist/input.d.ts +51 -0
- package/dist/input.js +44 -3
- package/dist/keys.d.ts +6 -0
- package/dist/keys.js +6 -2
- package/dist/logger.d.ts +8 -0
- package/dist/logger.js +8 -1
- package/dist/nonbrowser/getversion.d.ts +13 -0
- package/dist/nonbrowser/getversion.js +35 -0
- package/dist/nonbrowser/ghresolver.d.ts +1 -0
- package/dist/nonbrowser/ghresolver.js +7 -0
- package/dist/nonbrowser/index.d.ts +9 -0
- package/dist/nonbrowser/index.js +9 -0
- package/dist/nonbrowser/internal/buildhelper.d.ts +42 -0
- package/dist/nonbrowser/internal/buildhelper.js +144 -0
- package/dist/nonbrowser/internal/cli/argparser.d.ts +18 -0
- package/dist/nonbrowser/internal/cli/argparser.js +36 -0
- package/dist/nonbrowser/internal/cli/main.d.ts +13 -0
- package/dist/nonbrowser/internal/cli/main.js +265 -0
- package/dist/nonbrowser/internal/config.d.ts +9 -0
- package/dist/nonbrowser/internal/config.js +40 -0
- package/dist/nonbrowser/internal/exporthtml.d.ts +37 -0
- package/dist/nonbrowser/internal/exporthtml.js +622 -0
- package/dist/nonbrowser/utils.d.ts +8 -0
- package/dist/nonbrowser/utils.js +18 -0
- package/dist/physics/collision.d.ts +33 -0
- package/dist/physics/collision.js +27 -0
- package/dist/physics/physicsEngine.d.ts +18 -0
- package/dist/physics/physicsEngine.js +18 -0
- package/dist/physics/physicsObject.d.ts +20 -0
- package/dist/physics/physicsObject.js +20 -0
- package/dist/renderer.d.ts +78 -0
- package/dist/renderer.js +72 -9
- package/dist/samegui/index.d.ts +29 -0
- package/dist/samegui/index.js +26 -0
- package/dist/save.d.ts +12 -0
- package/dist/save.js +10 -0
- package/dist/sound/audioplayer.d.ts +39 -0
- package/dist/sound/audioplayer.js +39 -5
- package/dist/storage/index.d.ts +40 -2
- package/dist/storage/index.js +34 -3
- package/dist/text/index.d.ts +14 -0
- package/dist/text/index.js +58 -0
- package/dist/texture.d.ts +100 -0
- package/dist/texture.js +75 -41
- package/dist/types/button.d.ts +25 -0
- package/dist/types/button.js +22 -0
- package/dist/types/circle.d.ts +26 -0
- package/dist/types/circle.js +21 -7
- package/dist/types/color.d.ts +17 -0
- package/dist/types/color.js +11 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/rectangle.d.ts +29 -0
- package/dist/types/rectangle.js +23 -7
- package/dist/types/triangle.d.ts +23 -0
- package/dist/types/triangle.js +20 -6
- package/dist/types/vector2d.d.ts +42 -0
- package/dist/types/vector2d.js +39 -11
- package/dist/types/vector3d.d.ts +38 -0
- package/dist/types/vector3d.js +35 -11
- package/dist/utils/index.d.ts +11 -4
- package/dist/utils/index.js +11 -4
- package/dist/utils/logger/index.d.ts +24 -0
- package/dist/utils/logger/index.js +44 -0
- package/dist/utils/math.d.ts +18 -0
- package/dist/utils/math.js +18 -4
- package/package.json +29 -11
- package/dist/utils/csv/index.d.ts +0 -3
- package/dist/utils/csv/index.js +0 -2
- package/dist/utils/csv/parser.d.ts +0 -25
- package/dist/utils/csv/parser.js +0 -212
- package/dist/utils/csv/stringifier.d.ts +0 -30
- package/dist/utils/csv/stringifier.js +0 -130
- package/dist/utils/csv/types.d.ts +0 -63
- package/dist/utils/csv/types.js +0 -1
- package/dist/utils/jsonc-parser.d.ts +0 -4
- package/dist/utils/jsonc-parser.js +0 -166
- package/dist/utils/markdown.d.ts +0 -41
- package/dist/utils/markdown.js +0 -699
package/dist/types/vector3d.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// 3d Vector
|
|
2
2
|
import { clamp, lerp, map } from "../utils/math.js";
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Creates a new 3D vector.
|
|
5
|
+
*/
|
|
4
6
|
export function makeVector3d(x, y, z) {
|
|
5
7
|
return {
|
|
6
8
|
x: x,
|
|
@@ -8,7 +10,9 @@ export function makeVector3d(x, y, z) {
|
|
|
8
10
|
z: z
|
|
9
11
|
};
|
|
10
12
|
}
|
|
11
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Adds two vectors and returns a new vector.
|
|
15
|
+
*/
|
|
12
16
|
export function add3d(vector1, vector2) {
|
|
13
17
|
return {
|
|
14
18
|
x: vector1.x + vector2.x,
|
|
@@ -16,7 +20,9 @@ export function add3d(vector1, vector2) {
|
|
|
16
20
|
z: vector1.z + vector2.z,
|
|
17
21
|
};
|
|
18
22
|
}
|
|
19
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Subtracts `vector2` from `vector1` and returns a new vector.
|
|
25
|
+
*/
|
|
20
26
|
export function subtract3d(vector1, vector2) {
|
|
21
27
|
return {
|
|
22
28
|
x: vector1.x - vector2.x,
|
|
@@ -24,13 +30,19 @@ export function subtract3d(vector1, vector2) {
|
|
|
24
30
|
z: vector1.z - vector2.z,
|
|
25
31
|
};
|
|
26
32
|
}
|
|
27
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Returns the Euclidean length/magnitude of a 3D vector.
|
|
35
|
+
*/
|
|
28
36
|
export function length3d(vector) {
|
|
29
37
|
let produkt = vector.x * vector.x + vector.y * vector.y + vector.z * vector.z;
|
|
30
38
|
let root = Math.sqrt(produkt);
|
|
31
39
|
return root;
|
|
32
40
|
}
|
|
33
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Normalizes a vector to length `1`.
|
|
43
|
+
*
|
|
44
|
+
* Important: this function mutates and returns the original vector object.
|
|
45
|
+
*/
|
|
34
46
|
export function normalize3d(vector) {
|
|
35
47
|
// Check if the Vector is zero because then you dont need to
|
|
36
48
|
// calculate sth
|
|
@@ -43,11 +55,15 @@ export function normalize3d(vector) {
|
|
|
43
55
|
vector.z = vector.z / root;
|
|
44
56
|
return vector;
|
|
45
57
|
}
|
|
46
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Returns the dot product of two vectors.
|
|
60
|
+
*/
|
|
47
61
|
export function dot3d(v1, v2) {
|
|
48
62
|
return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);
|
|
49
63
|
}
|
|
50
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Returns the cross product of two 3D vectors.
|
|
66
|
+
*/
|
|
51
67
|
export function crossprodukt3d(v1, v2) {
|
|
52
68
|
return {
|
|
53
69
|
x: (v1.y * v2.z - v1.z * v2.y),
|
|
@@ -55,12 +71,16 @@ export function crossprodukt3d(v1, v2) {
|
|
|
55
71
|
z: (v1.x * v2.y - v1.y * v2.x),
|
|
56
72
|
};
|
|
57
73
|
}
|
|
58
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Returns the distance between two 3D vector positions.
|
|
76
|
+
*/
|
|
59
77
|
export function distance3d(v1, v2) {
|
|
60
78
|
let tmp = subtract3d(v1, v2);
|
|
61
79
|
return length3d(tmp);
|
|
62
80
|
}
|
|
63
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Clamps vector components between the matching `min` and `max` components.
|
|
83
|
+
*/
|
|
64
84
|
export function clamp3d(vector, min, max) {
|
|
65
85
|
return {
|
|
66
86
|
x: clamp(vector.x, min.x, max.x),
|
|
@@ -68,7 +88,9 @@ export function clamp3d(vector, min, max) {
|
|
|
68
88
|
z: clamp(vector.y, min.y, max.y),
|
|
69
89
|
};
|
|
70
90
|
}
|
|
71
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Linearly interpolates each component from `start` to `end`.
|
|
93
|
+
*/
|
|
72
94
|
export function lerp3d(start, end, t) {
|
|
73
95
|
return {
|
|
74
96
|
x: lerp(start.x, end.x, t.x),
|
|
@@ -76,7 +98,9 @@ export function lerp3d(start, end, t) {
|
|
|
76
98
|
z: lerp(start.y, end.y, t.y),
|
|
77
99
|
};
|
|
78
100
|
}
|
|
79
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Maps each vector component from one numeric range into another range.
|
|
103
|
+
*/
|
|
80
104
|
export function map3d(value, inMin, inMax, outMin, outMax) {
|
|
81
105
|
return {
|
|
82
106
|
x: map(value.x, inMin.x, inMax.x, outMin.x, outMax.x),
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
export { clamp, lerp, map, scale } from "./math.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Creates a small deterministic 32-bit hash from a string.
|
|
4
|
+
*
|
|
5
|
+
* Used internally by the immediate-mode HTML UI to turn labels into stable
|
|
6
|
+
* numeric ids. This is not a cryptographic hash.
|
|
7
|
+
*/
|
|
6
8
|
export declare function hash(str: string): number;
|
|
9
|
+
/**
|
|
10
|
+
* Shuffles an array in place using the Fisher-Yates algorithm.
|
|
11
|
+
*
|
|
12
|
+
* The same array instance is returned for convenient chaining.
|
|
13
|
+
*/
|
|
7
14
|
export declare function shuffle<T>(array: T[]): T[];
|
package/dist/utils/index.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// Utils Package
|
|
2
2
|
// Math Utilities
|
|
3
3
|
export { clamp, lerp, map, scale } from "./math.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Creates a small deterministic 32-bit hash from a string.
|
|
6
|
+
*
|
|
7
|
+
* Used internally by the immediate-mode HTML UI to turn labels into stable
|
|
8
|
+
* numeric ids. This is not a cryptographic hash.
|
|
9
|
+
*/
|
|
7
10
|
export function hash(str) {
|
|
8
11
|
let h = 0;
|
|
9
12
|
for (let i = 0; i < str.length; i++) {
|
|
@@ -12,7 +15,11 @@ export function hash(str) {
|
|
|
12
15
|
}
|
|
13
16
|
return h;
|
|
14
17
|
}
|
|
15
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Shuffles an array in place using the Fisher-Yates algorithm.
|
|
20
|
+
*
|
|
21
|
+
* The same array instance is returned for convenient chaining.
|
|
22
|
+
*/
|
|
16
23
|
export function shuffle(array) {
|
|
17
24
|
for (let i = array.length - 1; i > 0; i--) {
|
|
18
25
|
const j = Math.floor(Math.random() * (i + 1));
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Function to get the Date for the Logger
|
|
3
|
+
* @returns String for the current Date
|
|
4
|
+
*/
|
|
5
|
+
export declare function getDate(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Logger Class for samengine
|
|
8
|
+
*/
|
|
9
|
+
export declare class Logger {
|
|
10
|
+
constructor();
|
|
11
|
+
log(input: string): void;
|
|
12
|
+
warn(input: string): void;
|
|
13
|
+
error(input: string): void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Logger Class with the same functions but they are doing nothing
|
|
17
|
+
* to remove the console logs in the export
|
|
18
|
+
*/
|
|
19
|
+
export declare class ExportLogger {
|
|
20
|
+
constructor();
|
|
21
|
+
log(input: string): void;
|
|
22
|
+
warn(input: string): void;
|
|
23
|
+
error(input: string): void;
|
|
24
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Logger
|
|
2
|
+
/**
|
|
3
|
+
* Function to get the Date for the Logger
|
|
4
|
+
* @returns String for the current Date
|
|
5
|
+
*/
|
|
6
|
+
export function getDate() {
|
|
7
|
+
const now = new Date();
|
|
8
|
+
const time = `[${now.getHours().toString().padStart(2, "0")}:` +
|
|
9
|
+
`${now.getMinutes().toString().padStart(2, "0")}:` +
|
|
10
|
+
`${now.getSeconds().toString().padStart(2, "0")}.` +
|
|
11
|
+
`${now.getMilliseconds().toString().padStart(3, "0")}]`;
|
|
12
|
+
return time;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Logger Class for samengine
|
|
16
|
+
*/
|
|
17
|
+
export class Logger {
|
|
18
|
+
constructor() { }
|
|
19
|
+
log(input) {
|
|
20
|
+
console.log(`${getDate()} ${input}`);
|
|
21
|
+
}
|
|
22
|
+
warn(input) {
|
|
23
|
+
console.warn(`${getDate()} ${input}`);
|
|
24
|
+
}
|
|
25
|
+
error(input) {
|
|
26
|
+
console.error(`${getDate()} ${input}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Logger Class with the same functions but they are doing nothing
|
|
31
|
+
* to remove the console logs in the export
|
|
32
|
+
*/
|
|
33
|
+
export class ExportLogger {
|
|
34
|
+
constructor() { }
|
|
35
|
+
log(input) {
|
|
36
|
+
// Empty
|
|
37
|
+
}
|
|
38
|
+
warn(input) {
|
|
39
|
+
// Empty
|
|
40
|
+
}
|
|
41
|
+
error(input) {
|
|
42
|
+
// Empty
|
|
43
|
+
}
|
|
44
|
+
}
|
package/dist/utils/math.d.ts
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Restricts a number to the inclusive range between `min` and `max`.
|
|
3
|
+
*/
|
|
1
4
|
export declare function clamp(input: number, min: number, max: number): number;
|
|
5
|
+
/**
|
|
6
|
+
* Linear interpolation between `start` and `end`.
|
|
7
|
+
*
|
|
8
|
+
* `t = 0` returns `start`, `t = 1` returns `end`, and values between them blend.
|
|
9
|
+
*/
|
|
2
10
|
export declare function lerp(start: number, end: number, t: number): number;
|
|
11
|
+
/**
|
|
12
|
+
* Maps a number from one range into another range.
|
|
13
|
+
*
|
|
14
|
+
* Example: `map(5, 0, 10, 0, 100)` returns `50`.
|
|
15
|
+
*/
|
|
3
16
|
export declare function map(value: number, inMin: number, inMax: number, outMin: number, outMax: number): number;
|
|
17
|
+
/**
|
|
18
|
+
* Multiplies two numbers.
|
|
19
|
+
*
|
|
20
|
+
* Kept as a tiny utility so vector helpers can use the same naming style.
|
|
21
|
+
*/
|
|
4
22
|
export declare function scale(n1: number, n2: number): number;
|
package/dist/utils/math.js
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Restricts a number to the inclusive range between `min` and `max`.
|
|
3
|
+
*/
|
|
2
4
|
export function clamp(input, min, max) {
|
|
3
5
|
return Math.min(Math.max(input, min), max);
|
|
4
6
|
}
|
|
5
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Linear interpolation between `start` and `end`.
|
|
9
|
+
*
|
|
10
|
+
* `t = 0` returns `start`, `t = 1` returns `end`, and values between them blend.
|
|
11
|
+
*/
|
|
6
12
|
export function lerp(start, end, t) {
|
|
7
13
|
return (start + (end - start) * t);
|
|
8
14
|
}
|
|
9
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Maps a number from one range into another range.
|
|
17
|
+
*
|
|
18
|
+
* Example: `map(5, 0, 10, 0, 100)` returns `50`.
|
|
19
|
+
*/
|
|
10
20
|
export function map(value, inMin, inMax, outMin, outMax) {
|
|
11
21
|
return (outMax - outMin) * ((value - inMin) / (inMax - inMin)) + outMin;
|
|
12
22
|
}
|
|
13
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Multiplies two numbers.
|
|
25
|
+
*
|
|
26
|
+
* Kept as a tiny utility so vector helpers can use the same naming style.
|
|
27
|
+
*/
|
|
14
28
|
export function scale(n1, n2) {
|
|
15
29
|
return n1 * n2;
|
|
16
30
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "samengine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.1",
|
|
4
4
|
"description": "A TypeScript game library to make HTML Games",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"files": [
|
|
@@ -12,35 +12,53 @@
|
|
|
12
12
|
"pack": "node ../../scripts/clean.js && npm run build && npm pack && git push --follow-tags && npm run up",
|
|
13
13
|
"test": "bun test"
|
|
14
14
|
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"samengine": "dist/nonbrowser/internal/cli/main.js",
|
|
17
|
+
"samengine-proj": "dist/nonbrowser/internal/projcreator/main.js"
|
|
18
|
+
},
|
|
15
19
|
"exports": {
|
|
16
20
|
".": "./dist/index.js",
|
|
17
21
|
"./types": "./dist/types/index.js",
|
|
18
22
|
"./sound": "./dist/sound/index.js",
|
|
19
23
|
"./utils": "./dist/utils/index.js",
|
|
20
|
-
"./utils/
|
|
24
|
+
"./utils/logger": "./dist/utils/logger/index.js",
|
|
21
25
|
"./build": "./dist/build/index.js",
|
|
22
26
|
"./physics": "./dist/physics/index.js",
|
|
23
27
|
"./samegui": "./dist/samegui/index.js",
|
|
24
|
-
"./storage": "./dist/storage/index.js"
|
|
28
|
+
"./storage": "./dist/storage/index.js",
|
|
29
|
+
"./text": "./dist/text/index.js",
|
|
30
|
+
"./config": "./dist/config/index.js",
|
|
31
|
+
"./nonbrowser": "./dist/nonbrowser/index.js"
|
|
25
32
|
},
|
|
26
33
|
"keywords": [
|
|
27
34
|
"game",
|
|
28
|
-
"typescript"
|
|
35
|
+
"typescript",
|
|
36
|
+
"gamelib"
|
|
29
37
|
],
|
|
30
38
|
"author": "Shadowdara",
|
|
31
|
-
"license": "
|
|
39
|
+
"license": "Apache-2.0",
|
|
32
40
|
"devDependencies": {
|
|
33
|
-
"
|
|
34
|
-
"@types/node": "^25.
|
|
35
|
-
"
|
|
36
|
-
"
|
|
41
|
+
"@types/html-minifier-terser": "^7.0.2",
|
|
42
|
+
"@types/node": "^25.9.4",
|
|
43
|
+
"@types/ws": "^8.18.1",
|
|
44
|
+
"ts-node": "^10.9.2",
|
|
45
|
+
"typescript": "^6.0.3"
|
|
37
46
|
},
|
|
38
47
|
"repository": {
|
|
39
48
|
"type": "git",
|
|
40
|
-
"url": "https://github.com/shadowdara/samengine"
|
|
49
|
+
"url": "https://github.com/shadowdara/samengine",
|
|
50
|
+
"directory": "packages/samengine"
|
|
41
51
|
},
|
|
42
52
|
"bugs": {
|
|
43
53
|
"url": "https://github.com/shadowdara/samengine/issues"
|
|
44
54
|
},
|
|
45
|
-
"type": "module"
|
|
55
|
+
"type": "module",
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"esbuild": "^0.28.0",
|
|
58
|
+
"html-minifier-terser": "^7.2.0",
|
|
59
|
+
"mime": "^4.1.0",
|
|
60
|
+
"ws": "^8.20.0",
|
|
61
|
+
"chalk": "^5.6.2",
|
|
62
|
+
"samengine-cli": "2.0.4"
|
|
63
|
+
}
|
|
46
64
|
}
|
package/dist/utils/csv/index.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { CSVParserOptions, ParseResult } from "./types";
|
|
2
|
-
/**
|
|
3
|
-
* RFC 4180-konformer CSV-Parser mit erweitertem Escaping.
|
|
4
|
-
*
|
|
5
|
-
* Unterstützt:
|
|
6
|
-
* - Gequotete Felder mit eingebetteten Zeilenumbrüchen, Trennzeichen und Anführungszeichen
|
|
7
|
-
* - Doppelt-Anführungszeichen-Escaping: "" → "
|
|
8
|
-
* - Optionales Backslash-Escaping: \" → "
|
|
9
|
-
* - Konfigurierbare Trennzeichen, Anführungszeichen, Kommentar-Zeilen
|
|
10
|
-
* - Warnung bei ungleicher Feldanzahl
|
|
11
|
-
*/
|
|
12
|
-
export declare class CSVParser {
|
|
13
|
-
private readonly opts;
|
|
14
|
-
constructor(options?: CSVParserOptions);
|
|
15
|
-
/**
|
|
16
|
-
* Parst einen CSV-String und gibt strukturierte Daten zurück.
|
|
17
|
-
*/
|
|
18
|
-
parse<T = Record<string, string>>(text: string): ParseResult<T>;
|
|
19
|
-
/**
|
|
20
|
-
* Parst einen CSV-String und gibt ausschließlich die Records zurück.
|
|
21
|
-
* Kurzform für einfache Anwendungsfälle.
|
|
22
|
-
*/
|
|
23
|
-
parseRecords<T = Record<string, string>>(text: string): T[];
|
|
24
|
-
private tokenize;
|
|
25
|
-
}
|
package/dist/utils/csv/parser.js
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
const DEFAULT_OPTIONS = {
|
|
2
|
-
delimiter: ",",
|
|
3
|
-
quoteChar: '"',
|
|
4
|
-
escapeChar: "",
|
|
5
|
-
hasHeader: true,
|
|
6
|
-
trimFields: false,
|
|
7
|
-
skipEmptyLines: true,
|
|
8
|
-
commentChar: "",
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* RFC 4180-konformer CSV-Parser mit erweitertem Escaping.
|
|
12
|
-
*
|
|
13
|
-
* Unterstützt:
|
|
14
|
-
* - Gequotete Felder mit eingebetteten Zeilenumbrüchen, Trennzeichen und Anführungszeichen
|
|
15
|
-
* - Doppelt-Anführungszeichen-Escaping: "" → "
|
|
16
|
-
* - Optionales Backslash-Escaping: \" → "
|
|
17
|
-
* - Konfigurierbare Trennzeichen, Anführungszeichen, Kommentar-Zeilen
|
|
18
|
-
* - Warnung bei ungleicher Feldanzahl
|
|
19
|
-
*/
|
|
20
|
-
export class CSVParser {
|
|
21
|
-
constructor(options = {}) {
|
|
22
|
-
this.opts = { ...DEFAULT_OPTIONS, ...options };
|
|
23
|
-
if (this.opts.delimiter.length !== 1) {
|
|
24
|
-
throw new Error("delimiter muss genau ein Zeichen lang sein.");
|
|
25
|
-
}
|
|
26
|
-
if (this.opts.quoteChar.length !== 1) {
|
|
27
|
-
throw new Error("quoteChar muss genau ein Zeichen lang sein.");
|
|
28
|
-
}
|
|
29
|
-
if (this.opts.escapeChar && this.opts.escapeChar.length !== 1) {
|
|
30
|
-
throw new Error("escapeChar muss genau ein Zeichen lang sein.");
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Parst einen CSV-String und gibt strukturierte Daten zurück.
|
|
35
|
-
*/
|
|
36
|
-
parse(text) {
|
|
37
|
-
const rawRows = this.tokenize(text);
|
|
38
|
-
const warnings = [];
|
|
39
|
-
if (rawRows.length === 0) {
|
|
40
|
-
return { records: [], headers: [], rawRows: [], warnings };
|
|
41
|
-
}
|
|
42
|
-
let headers = [];
|
|
43
|
-
let dataRows;
|
|
44
|
-
if (this.opts.hasHeader) {
|
|
45
|
-
headers = rawRows[0].map((h) => this.opts.trimFields ? h.trim() : h);
|
|
46
|
-
dataRows = rawRows.slice(1);
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
// Numerische Header generieren: "0", "1", "2", ...
|
|
50
|
-
const width = rawRows[0].length;
|
|
51
|
-
headers = Array.from({ length: width }, (_, i) => String(i));
|
|
52
|
-
dataRows = rawRows;
|
|
53
|
-
}
|
|
54
|
-
const records = [];
|
|
55
|
-
for (let i = 0; i < dataRows.length; i++) {
|
|
56
|
-
const row = dataRows[i];
|
|
57
|
-
if (row.length !== headers.length) {
|
|
58
|
-
warnings.push({
|
|
59
|
-
row: i + (this.opts.hasHeader ? 2 : 1),
|
|
60
|
-
message: `Zeile hat ${row.length} Felder, erwartet ${headers.length}.`,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
const record = {};
|
|
64
|
-
for (let j = 0; j < headers.length; j++) {
|
|
65
|
-
let value = row[j] ?? "";
|
|
66
|
-
if (this.opts.trimFields)
|
|
67
|
-
value = value.trim();
|
|
68
|
-
record[headers[j]] = value;
|
|
69
|
-
}
|
|
70
|
-
records.push(record);
|
|
71
|
-
}
|
|
72
|
-
return { records, headers, rawRows, warnings };
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Parst einen CSV-String und gibt ausschließlich die Records zurück.
|
|
76
|
-
* Kurzform für einfache Anwendungsfälle.
|
|
77
|
-
*/
|
|
78
|
-
parseRecords(text) {
|
|
79
|
-
return this.parse(text).records;
|
|
80
|
-
}
|
|
81
|
-
// ─────────────────────────────────────────────
|
|
82
|
-
// Privater Tokenizer (Zustandsmaschine)
|
|
83
|
-
// ─────────────────────────────────────────────
|
|
84
|
-
tokenize(text) {
|
|
85
|
-
const { delimiter, quoteChar, escapeChar, skipEmptyLines, commentChar } = this.opts;
|
|
86
|
-
const rows = [];
|
|
87
|
-
let row = [];
|
|
88
|
-
let field = "";
|
|
89
|
-
let inQuotes = false;
|
|
90
|
-
let i = 0;
|
|
91
|
-
const len = text.length;
|
|
92
|
-
const pushRow = () => {
|
|
93
|
-
const isEmpty = row.length === 1 && row[0] === "" && field === "";
|
|
94
|
-
if (skipEmptyLines && isEmpty)
|
|
95
|
-
return;
|
|
96
|
-
if (commentChar && row.length === 0 && field.startsWith(commentChar))
|
|
97
|
-
return;
|
|
98
|
-
row.push(field);
|
|
99
|
-
rows.push(row);
|
|
100
|
-
row = [];
|
|
101
|
-
field = "";
|
|
102
|
-
};
|
|
103
|
-
while (i < len) {
|
|
104
|
-
const ch = text[i];
|
|
105
|
-
const next = i + 1 < len ? text[i + 1] : "";
|
|
106
|
-
if (inQuotes) {
|
|
107
|
-
// Backslash-Escape innerhalb von Quotes
|
|
108
|
-
if (escapeChar && ch === escapeChar && next === quoteChar) {
|
|
109
|
-
field += quoteChar;
|
|
110
|
-
i += 2;
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
// Doppeltes Anführungszeichen → ein literales Anführungszeichen
|
|
114
|
-
if (ch === quoteChar && next === quoteChar) {
|
|
115
|
-
field += quoteChar;
|
|
116
|
-
i += 2;
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
// Schließendes Anführungszeichen
|
|
120
|
-
if (ch === quoteChar) {
|
|
121
|
-
inQuotes = false;
|
|
122
|
-
i++;
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
field += ch;
|
|
126
|
-
i++;
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
// Außerhalb von Quotes:
|
|
130
|
-
// Kommentarzeile (nur am Zeilenanfang)
|
|
131
|
-
if (commentChar && ch === commentChar && row.length === 0 && field === "") {
|
|
132
|
-
// Rest der Zeile überspringen
|
|
133
|
-
while (i < len && text[i] !== "\n" && text[i] !== "\r")
|
|
134
|
-
i++;
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
// Öffnendes Anführungszeichen
|
|
138
|
-
if (ch === quoteChar) {
|
|
139
|
-
inQuotes = true;
|
|
140
|
-
i++;
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
// Backslash-Escape außerhalb von Quotes (optionales Feature)
|
|
144
|
-
if (escapeChar && ch === escapeChar) {
|
|
145
|
-
if (next === "n") {
|
|
146
|
-
field += "\n";
|
|
147
|
-
i += 2;
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
if (next === "r") {
|
|
151
|
-
field += "\r";
|
|
152
|
-
i += 2;
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
if (next === "t") {
|
|
156
|
-
field += "\t";
|
|
157
|
-
i += 2;
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
if (next === "0") {
|
|
161
|
-
field += "\0";
|
|
162
|
-
i += 2;
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
if (next === escapeChar) {
|
|
166
|
-
field += escapeChar;
|
|
167
|
-
i += 2;
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
if (next === delimiter) {
|
|
171
|
-
field += delimiter;
|
|
172
|
-
i += 2;
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
// Unbekannte Escape-Sequenz: Backslash literal übernehmen
|
|
176
|
-
field += ch;
|
|
177
|
-
i++;
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
// Trennzeichen
|
|
181
|
-
if (ch === delimiter) {
|
|
182
|
-
row.push(field);
|
|
183
|
-
field = "";
|
|
184
|
-
i++;
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
// Zeilenumbruch \r\n
|
|
188
|
-
if (ch === "\r" && next === "\n") {
|
|
189
|
-
pushRow();
|
|
190
|
-
i += 2;
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
// Zeilenumbruch \r oder \n
|
|
194
|
-
if (ch === "\n" || ch === "\r") {
|
|
195
|
-
pushRow();
|
|
196
|
-
i++;
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
field += ch;
|
|
200
|
-
i++;
|
|
201
|
-
}
|
|
202
|
-
// Letzte Zeile (kein abschließendes Newline)
|
|
203
|
-
if (row.length > 0 || field !== "") {
|
|
204
|
-
row.push(field);
|
|
205
|
-
const isEmpty = row.length === 1 && row[0] === "";
|
|
206
|
-
if (!(skipEmptyLines && isEmpty)) {
|
|
207
|
-
rows.push(row);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return rows;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { CSVStringifierOptions } from "./types";
|
|
2
|
-
/**
|
|
3
|
-
* Serialisiert JavaScript-Objekte zu einem validen CSV-String.
|
|
4
|
-
*
|
|
5
|
-
* Unterstützt:
|
|
6
|
-
* - RFC 4180 Doppelquote-Escaping: " → ""
|
|
7
|
-
* - Optionales Backslash-Escaping: \ → \\
|
|
8
|
-
* - Optionales Null-Byte-Escaping: \0 → \\0
|
|
9
|
-
* - Automatisches Quoting bei Sonderzeichen
|
|
10
|
-
* - Konfigurierbare Spaltenreihenfolge
|
|
11
|
-
* - Wahlweise immer quoten (alwaysQuote)
|
|
12
|
-
*/
|
|
13
|
-
export declare class CSVStringifier {
|
|
14
|
-
private readonly opts;
|
|
15
|
-
private readonly escapeRules;
|
|
16
|
-
constructor(options?: CSVStringifierOptions);
|
|
17
|
-
/**
|
|
18
|
-
* Serialisiert ein Array von Objekten zu einem CSV-String.
|
|
19
|
-
*/
|
|
20
|
-
stringify(records: Record<string, unknown>[]): string;
|
|
21
|
-
/**
|
|
22
|
-
* Serialisiert ein einzelnes Feld mit korrektem Escaping.
|
|
23
|
-
* Öffentlich, damit einzelne Werte unabhängig escaped werden können.
|
|
24
|
-
*/
|
|
25
|
-
escapeField(raw: string): string;
|
|
26
|
-
/**
|
|
27
|
-
* Streamt Records zeilenweise als Generator (speicherschonend für große Dateien).
|
|
28
|
-
*/
|
|
29
|
-
stream(records: Iterable<Record<string, unknown>>, columns: string[]): Generator<string>;
|
|
30
|
-
}
|