@hellpig/anarchy-shared 1.5.1 → 1.6.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/CHANGELOG.md +1 -0
- package/NOTICE.md +1 -1
- package/README.md +1 -1
- package/ScriptUtils/CheckMinify.sh +74 -0
- package/ScriptUtils/CopyDir.js +24 -0
- package/ScriptUtils/CopyFiles.js +40 -0
- package/ScriptUtils/EnvUtils.js +55 -0
- package/ScriptUtils/GenerateVersionsFile.js +43 -0
- package/ScriptUtils/InjectMetadata.js +37 -0
- package/ScriptUtils/MakeIcoFromPng.js +139 -0
- package/ScriptUtils/ModeUtils.js +42 -0
- package/ScriptUtils/PostprocessScreenshots.js +37 -0
- package/ScriptUtils/RenameFile.js +23 -0
- package/ScriptUtils/SanitizeAssets.js +120 -0
- package/legal/DISCLAIMER.md +1 -1
- package/legal/EULA.md +2 -2
- package/legal/PRIVACY.md +5 -5
- package/legal/SECURITY.md +3 -3
- package/package.json +6 -7
package/CHANGELOG.md
CHANGED
package/NOTICE.md
CHANGED
|
@@ -9,4 +9,4 @@ Full attributions and license texts are provided **offline** at:
|
|
|
9
9
|
|
|
10
10
|
Nothing in this pointer modifies third-party licenses. If there is any conflict between this note and a third-party license, the third-party license controls.
|
|
11
11
|
|
|
12
|
-
Questions:
|
|
12
|
+
Questions: pnf036+anarchy@gmail.com
|
package/README.md
CHANGED
|
@@ -31,4 +31,4 @@ SOFTWARE.
|
|
|
31
31
|
See local files `LICENSE`, `CHANGELOG`, `NOTICE` (pointer), and files in `./legal`:
|
|
32
32
|
`DISCLAIMER`, `EULA`, `PRIVACY`, `SECURITY`, `NOTICE` (full), `THIRD_PARTY_LICENSES`.
|
|
33
33
|
|
|
34
|
-
Contacts — Privacy:
|
|
34
|
+
Contacts — Privacy: pnf036+anarchy@gmail.com, Security: pnf036+anarchy_security@gmail.com.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
JS_FILE="$1"
|
|
4
|
+
MAP_FILE="$2"
|
|
5
|
+
GZ_FILE="${JS_FILE}.gz"
|
|
6
|
+
BR_FILE="${JS_FILE}.br"
|
|
7
|
+
|
|
8
|
+
if [[ -z "$JS_FILE" || -z "$MAP_FILE" ]]; then
|
|
9
|
+
echo "❌ Usage: ./CheckMinify.sh path/to/file.js path/to/file.js.map"
|
|
10
|
+
exit 1
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
if [[ ! -f "$JS_FILE" ]]; then
|
|
14
|
+
echo "❌ JS file not found: $JS_FILE"
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
if [[ ! -f "$MAP_FILE" ]]; then
|
|
19
|
+
echo "❌ Map file not found: $MAP_FILE"
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
#LINES=$(wc -l < "$JS_FILE")
|
|
24
|
+
#if [[ "$LINES" -gt 50 ]]; then
|
|
25
|
+
# echo "❌ Too many lines in JS file ($LINES). Probably not minified."
|
|
26
|
+
# exit 1
|
|
27
|
+
#else
|
|
28
|
+
# echo "✅ JS file appears minified ($LINES lines)"
|
|
29
|
+
#fi
|
|
30
|
+
|
|
31
|
+
if [[ -f "$GZ_FILE" ]]; then
|
|
32
|
+
gunzip -c "$GZ_FILE" | cmp -s - "$JS_FILE"
|
|
33
|
+
if [[ $? -ne 0 ]]; then
|
|
34
|
+
echo "❌ Gzip file doesn't match JS file"
|
|
35
|
+
exit 1
|
|
36
|
+
else
|
|
37
|
+
echo "✅ Gzip file matches"
|
|
38
|
+
fi
|
|
39
|
+
else
|
|
40
|
+
echo "⚠️ Gzip file not found: $GZ_FILE"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
if [[ -f "$BR_FILE" ]]; then
|
|
44
|
+
brotli -d -c "$BR_FILE" | cmp -s - "$JS_FILE"
|
|
45
|
+
if [[ $? -ne 0 ]]; then
|
|
46
|
+
echo "❌ Brotli file doesn't match JS file"
|
|
47
|
+
exit 1
|
|
48
|
+
else
|
|
49
|
+
echo "✅ Brotli file matches"
|
|
50
|
+
fi
|
|
51
|
+
else
|
|
52
|
+
echo "⚠️ Brotli file not found: $BR_FILE"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
echo "🔍 Checking sourcemap integrity..."
|
|
56
|
+
|
|
57
|
+
TMP_JSON=$(mktemp -t sourcemapXXXXXX.json)
|
|
58
|
+
|
|
59
|
+
SOURCEMAP_ERR=$(npx --yes source-map-explorer "$JS_FILE" "$MAP_FILE" --json "$TMP_JSON" 2>&1)
|
|
60
|
+
echo "📄 Sourcemap analysis written to: $TMP_JSON"
|
|
61
|
+
|
|
62
|
+
if [[ $? -eq 0 && -s "$TMP_JSON" ]]; then
|
|
63
|
+
echo "✅ Sourcemap check passed: $JS_FILE"
|
|
64
|
+
rm "$TMP_JSON"
|
|
65
|
+
else
|
|
66
|
+
echo "❌ Failed to analyze sourcemap: $JS_FILE"
|
|
67
|
+
echo "🧾 stderr:"
|
|
68
|
+
echo "$SOURCEMAP_ERR"
|
|
69
|
+
[[ -f "$TMP_JSON" ]] && cat "$TMP_JSON"
|
|
70
|
+
rm -f "$TMP_JSON"
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
echo "🎉 All checks passed"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { copy } from 'fs-extra';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
|
|
4
|
+
const args = process.argv.slice(2);
|
|
5
|
+
const fromArg = args.find((arg) => arg.startsWith('from='));
|
|
6
|
+
const toArg = args.find((arg) => arg.startsWith('to='));
|
|
7
|
+
|
|
8
|
+
const fromPath = resolve(process.cwd(), fromArg.split('=')[1]);
|
|
9
|
+
const toPath = resolve(process.cwd(), toArg.split('=')[1]);
|
|
10
|
+
|
|
11
|
+
console.log(`Copying files...\nFrom: ${fromPath}\nTo: ${toPath}`);
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
await copy(fromPath, toPath, {
|
|
15
|
+
overwrite: true,
|
|
16
|
+
errorOnExist: false,
|
|
17
|
+
recursive: true
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
console.log('✅ Copy completed.');
|
|
21
|
+
} catch (err) {
|
|
22
|
+
console.error('Copy failed:', err);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { copyFile, mkdir } from 'fs/promises';
|
|
2
|
+
import { resolve, dirname } from 'path';
|
|
3
|
+
|
|
4
|
+
const args = process.argv.slice(2);
|
|
5
|
+
|
|
6
|
+
// last arg must be the destination (e.g. to=dist-app/draco)
|
|
7
|
+
const toArg = args.find((arg) => arg.startsWith('to='));
|
|
8
|
+
if (!toArg) {
|
|
9
|
+
console.error('❌ Missing argument: to=<destination folder>');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const toDir = resolve(process.cwd(), toArg.split('=')[1]);
|
|
14
|
+
|
|
15
|
+
// all other args treated as relative file paths to copy
|
|
16
|
+
const filePaths = args.filter((arg) => !arg.startsWith('to='));
|
|
17
|
+
if (filePaths.length === 0) {
|
|
18
|
+
console.error('❌ No files to copy provided.');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.log(`Copying files to: ${toDir}`);
|
|
23
|
+
console.log(filePaths.map((f) => ` - ${f}`).join('\n'));
|
|
24
|
+
|
|
25
|
+
(async () => {
|
|
26
|
+
try {
|
|
27
|
+
for (const relativePath of filePaths) {
|
|
28
|
+
const src = resolve(process.cwd(), relativePath);
|
|
29
|
+
const dest = resolve(toDir, relativePath.split('/').pop());
|
|
30
|
+
|
|
31
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
32
|
+
await copyFile(src, dest);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log('✅ Files copied successfully.');
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error('❌ Copy failed:', err);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
})();
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { config as dotenvConfig } from 'dotenv';
|
|
4
|
+
import { expand as dotenvExpand } from 'dotenv-expand';
|
|
5
|
+
|
|
6
|
+
export function buildEnvChain(mode) {
|
|
7
|
+
const chain = ['.env', '.env.local'];
|
|
8
|
+
const tokens = String(mode).split('.').filter(Boolean);
|
|
9
|
+
const first = tokens[0]?.toLowerCase();
|
|
10
|
+
const firstAlias = first === 'dev' ? 'development' : first === 'prod' ? 'production' : undefined;
|
|
11
|
+
|
|
12
|
+
const seqs = [tokens];
|
|
13
|
+
if (firstAlias && firstAlias !== first) seqs.push([firstAlias, ...tokens.slice(1)]);
|
|
14
|
+
|
|
15
|
+
for (const seq of seqs) {
|
|
16
|
+
const accum = [];
|
|
17
|
+
for (const p of seq) {
|
|
18
|
+
accum.push(p);
|
|
19
|
+
const key = accum.join('.');
|
|
20
|
+
chain.push(`.env.${key}`);
|
|
21
|
+
chain.push(`.env.${key}.local`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return Array.from(new Set(chain));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function loadModeEnv(mode, cwd = process.cwd()) {
|
|
29
|
+
const loadEnvFile = (file) => {
|
|
30
|
+
const full = path.resolve(cwd, file);
|
|
31
|
+
if (!fs.existsSync(full)) return false;
|
|
32
|
+
const result = dotenvConfig({ path: full, override: true });
|
|
33
|
+
if (result.parsed) dotenvExpand({ parsed: result.parsed });
|
|
34
|
+
return true;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const chain = buildEnvChain(mode);
|
|
38
|
+
for (const file of chain) loadEnvFile(file);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function parseListEnv(val) {
|
|
42
|
+
if (!val) return [];
|
|
43
|
+
return String(val)
|
|
44
|
+
.split(',')
|
|
45
|
+
.map((s) => s.trim())
|
|
46
|
+
.filter(Boolean);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function parseBoolEnv(val, defaultValue = false) {
|
|
50
|
+
if (val == null) return defaultValue;
|
|
51
|
+
const s = String(val).trim().toLowerCase();
|
|
52
|
+
if (s === '1' || s === 'true' || s === 'yes' || s === 'on') return true;
|
|
53
|
+
if (s === '0' || s === 'false' || s === 'no' || s === 'off') return false;
|
|
54
|
+
return defaultValue;
|
|
55
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { writeFileSync, existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { resolve, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const cwd = process.cwd();
|
|
6
|
+
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
const pkgArg = args.find((arg) => arg.startsWith('--pkg='));
|
|
9
|
+
const outArg = args.find((arg) => arg.startsWith('--out='));
|
|
10
|
+
|
|
11
|
+
const pkgPath = pkgArg ? resolve(cwd, pkgArg.split('=')[1]) : resolve(cwd, 'package.json');
|
|
12
|
+
|
|
13
|
+
const outPath = outArg ? resolve(cwd, outArg.split('=')[1]) : resolve(cwd, './versions.ts');
|
|
14
|
+
|
|
15
|
+
if (!existsSync(pkgPath)) {
|
|
16
|
+
console.error(`❌ package.json not found at: ${pkgPath}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const raw = readFileSync(pkgPath, 'utf8');
|
|
21
|
+
let version;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const pkg = JSON.parse(raw);
|
|
25
|
+
version = pkg.version;
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.error(`❌ Failed to parse package.json:`, err);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!version) {
|
|
32
|
+
console.error(`❌ "version" field not found in ${pkgPath}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const content = `// This file is auto-generated by GenerateVersionsFile.js
|
|
37
|
+
// Do not edit manually. Run the script to update.
|
|
38
|
+
|
|
39
|
+
export const packageJsonVersion = '${version}';\n`;
|
|
40
|
+
writeFileSync(outPath, content, 'utf8');
|
|
41
|
+
|
|
42
|
+
console.log(`✅ version.ts generated at: ${outPath}`);
|
|
43
|
+
console.log(`📦 version: ${version}`);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import encode from 'png-chunks-encode';
|
|
5
|
+
import extract from 'png-chunks-extract';
|
|
6
|
+
|
|
7
|
+
//THis script meant to be used for testing of cleaning metadata
|
|
8
|
+
|
|
9
|
+
const inputPath = path.resolve(process.cwd(), './test.png');
|
|
10
|
+
const outputPath = path.resolve(process.cwd(), './test.png');
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
13
|
+
export function createTextChunk(keyword, text) {
|
|
14
|
+
// eslint-disable-next-line spellcheck/spell-checker
|
|
15
|
+
const keywordBuffer = Buffer.from(keyword, 'latin1');
|
|
16
|
+
const nullByte = Buffer.from([0x00]);
|
|
17
|
+
// eslint-disable-next-line spellcheck/spell-checker
|
|
18
|
+
const textBuffer = Buffer.from(text, 'latin1');
|
|
19
|
+
const data = Buffer.concat([keywordBuffer, nullByte, textBuffer]);
|
|
20
|
+
return {
|
|
21
|
+
name: 'tEXt',
|
|
22
|
+
data
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const pngBuffer = fs.readFileSync(inputPath);
|
|
27
|
+
|
|
28
|
+
const chunks = extract(pngBuffer);
|
|
29
|
+
|
|
30
|
+
const myTextChunk = createTextChunk('Author', 'Someone (to remove)');
|
|
31
|
+
|
|
32
|
+
chunks.splice(1, 0, myTextChunk);
|
|
33
|
+
|
|
34
|
+
const newBuffer = Buffer.from(encode(chunks));
|
|
35
|
+
fs.writeFileSync(outputPath, newBuffer);
|
|
36
|
+
|
|
37
|
+
console.log('✅ PNG with metadata saved to with-meta.png');
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Creates .ico file from one or more PNGs, with optional resizing.
|
|
2
|
+
// Usage:
|
|
3
|
+
// node scripts/MakeIcoFromPng.js --in assets/icon-1024.png --out assets/icon.ico
|
|
4
|
+
// node scripts/MakeIcoFromPng.js --in assets/256.png,assets/128.png --out assets/app.ico
|
|
5
|
+
// node scripts/MakeIcoFromPng.js --in assets/pngs --out assets/icon.ico
|
|
6
|
+
// node scripts/MakeIcoFromPng.js --in assets/icon-1024.png --out assets/icon.ico --size 256
|
|
7
|
+
//
|
|
8
|
+
// Notes:
|
|
9
|
+
// - If --size is provided, all inputs are resized to NxN (requires dev-dep 'sharp').
|
|
10
|
+
// - If --size is omitted, inputs are used as-is (no resizing).
|
|
11
|
+
// - png-to-ico builds a valid ICO from given PNG buffers.
|
|
12
|
+
|
|
13
|
+
import { readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
14
|
+
import { extname, resolve } from 'node:path';
|
|
15
|
+
import process from 'node:process';
|
|
16
|
+
import pngToIco from 'png-to-ico';
|
|
17
|
+
import sharp from 'sharp';
|
|
18
|
+
|
|
19
|
+
function printHelp() {
|
|
20
|
+
console.log(
|
|
21
|
+
`mk-ico — build a .ico from PNG(s)
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
--in, -i Path to a PNG file. Can be repeated or comma-separated.
|
|
25
|
+
If a directory is passed, all *.png files in it are used (non-recursive).
|
|
26
|
+
--out, -o Output .ico path (required).
|
|
27
|
+
--size, -s Optional square size in px (e.g. 256). If omitted, images are not resized.
|
|
28
|
+
--help, -h Show this help.
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
node scripts/MakeIcoFromPng.js --in assets/icon-1024.png --out assets/icon.ico
|
|
32
|
+
node scripts/MakeIcoFromPng.js -i assets/256.png,assets/128.png -o assets/icon.ico
|
|
33
|
+
node scripts/MakeIcoFromPng.js -i assets/pngs -o assets/icon.ico
|
|
34
|
+
node scripts/MakeIcoFromPng.js -i assets/icon-1024.png -o assets/icon.ico -s 256
|
|
35
|
+
`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseArgs(argv) {
|
|
40
|
+
const args = { inputs: [], out: null, help: false, size: null };
|
|
41
|
+
for (let i = 0; i < argv.length; i++) {
|
|
42
|
+
const a = argv[i];
|
|
43
|
+
if (a === '--help' || a === '-h') {
|
|
44
|
+
args.help = true;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (a === '--out' || a === '-o') {
|
|
48
|
+
args.out = argv[++i];
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (a === '--in' || a === '-i') {
|
|
52
|
+
const val = argv[++i];
|
|
53
|
+
if (val) args.inputs.push(...val.split(','));
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (a === '--size' || a === '-s') {
|
|
57
|
+
const val = argv[++i];
|
|
58
|
+
if (val && /^\d+$/.test(val)) args.size = Number(val);
|
|
59
|
+
else throw new Error(`--size expects a positive integer, got: ${val ?? '(missing)'}`);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return args;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function listPngsFromDir(dirPath) {
|
|
67
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
68
|
+
return entries.filter((e) => e.isFile() && extname(e.name).toLowerCase() === '.png').map((e) => resolve(dirPath, e.name));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function resolveInputs(maybePaths) {
|
|
72
|
+
const result = [];
|
|
73
|
+
for (const p of maybePaths) {
|
|
74
|
+
const abs = resolve(p);
|
|
75
|
+
let st;
|
|
76
|
+
try {
|
|
77
|
+
st = await stat(abs);
|
|
78
|
+
} catch {
|
|
79
|
+
throw new Error(`Input path does not exist: ${abs}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (st.isDirectory()) {
|
|
83
|
+
const pngs = await listPngsFromDir(abs);
|
|
84
|
+
if (pngs.length === 0) throw new Error(`Directory has no PNGs: ${abs}`);
|
|
85
|
+
result.push(...pngs);
|
|
86
|
+
} else if (st.isFile()) {
|
|
87
|
+
if (extname(abs).toLowerCase() !== '.png') throw new Error(`Input is not a .png file: ${abs}`);
|
|
88
|
+
result.push(abs);
|
|
89
|
+
} else {
|
|
90
|
+
throw new Error(`Unsupported input type (not file/dir): ${abs}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// de-dup, keep order
|
|
94
|
+
return [...new Set(result)];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function maybeResize(buf, size) {
|
|
98
|
+
if (!size) return buf;
|
|
99
|
+
if (!(Number.isInteger(size) && size > 0 && size <= 4096)) {
|
|
100
|
+
throw new Error(`--size must be an integer in range 1..4096, got ${size}`);
|
|
101
|
+
}
|
|
102
|
+
return sharp(buf).resize(size, size, { fit: 'contain', withoutEnlargement: false }).png().toBuffer();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function main() {
|
|
106
|
+
const { inputs, out, help, size } = parseArgs(process.argv.slice(2));
|
|
107
|
+
if (help || inputs.length === 0 || !out) {
|
|
108
|
+
printHelp();
|
|
109
|
+
if (help) return;
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const inFiles = await resolveInputs(inputs);
|
|
114
|
+
if (inFiles.length === 0) throw new Error('No PNG inputs resolved.');
|
|
115
|
+
|
|
116
|
+
// Read PNGs, optional resize
|
|
117
|
+
const bufs = await Promise.all(
|
|
118
|
+
inFiles.map(async (p) => {
|
|
119
|
+
const b = await readFile(p);
|
|
120
|
+
return maybeResize(b, size);
|
|
121
|
+
})
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const icoBuf = await pngToIco(bufs);
|
|
126
|
+
const outPath = resolve(out);
|
|
127
|
+
await writeFile(outPath, icoBuf);
|
|
128
|
+
const sizeMsg = size ? ` (resized to ${size}x${size})` : '';
|
|
129
|
+
console.log(`✔ Wrote ICO: ${outPath} from ${inFiles.length} PNG file(s)${sizeMsg}.`);
|
|
130
|
+
} catch (e) {
|
|
131
|
+
console.error('✖ Failed to build ICO:', e?.message || e);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
main().catch((err) => {
|
|
137
|
+
console.error('✖ Error:', err?.message || err);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Small helpers to parse and normalize build/run modes across scripts.
|
|
2
|
+
|
|
3
|
+
// Extracts mode from argv supporting "--mode <val>" and "--mode=<val>"
|
|
4
|
+
export function parseModeArg(argv = []) {
|
|
5
|
+
const idx = argv.findIndex((a) => a === '--mode');
|
|
6
|
+
if (idx !== -1 && argv[idx + 1]) return argv[idx + 1];
|
|
7
|
+
const eq = argv.find((a) => a.startsWith('--mode='));
|
|
8
|
+
if (eq) return eq.slice('--mode='.length);
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Normalizes common synonyms to canonical values and classifies composite modes.
|
|
13
|
+
// dev -> development, prod -> production
|
|
14
|
+
// development.* -> development, production.* -> production
|
|
15
|
+
export function normalizeMode(input) {
|
|
16
|
+
if (!input) return 'production';
|
|
17
|
+
const s = String(input).trim().toLowerCase();
|
|
18
|
+
if (s === 'prod' || s === 'production' || s.startsWith('production.')) return 'production';
|
|
19
|
+
if (s === 'dev' || s === 'development' || s.startsWith('development.') || s.startsWith('dev.')) return 'development';
|
|
20
|
+
return s;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Resolves the effective mode from argv and environment WITHOUT normalizing,
|
|
24
|
+
// so Vite receives the full composite value (e.g., production.mac.arm64).
|
|
25
|
+
// Priority: argv -> MODE -> npm_config_mode -> NODE_ENV -> default
|
|
26
|
+
export function resolveMode(argv = [], env = process.env) {
|
|
27
|
+
const argMode = parseModeArg(argv);
|
|
28
|
+
const envMode = env.MODE;
|
|
29
|
+
const npmMode = env.npm_config_mode;
|
|
30
|
+
const nodeEnv = env.NODE_ENV;
|
|
31
|
+
return argMode || envMode || npmMode || nodeEnv || 'production';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Detects a dry-run flag in argv.
|
|
35
|
+
export function parseDryRunArg(argv = []) {
|
|
36
|
+
return argv.some((a) => a === '--dry-run' || a === '--dryrun' || a === '--dry');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Resolves whether to run in dry-run mode from argv and environment.
|
|
40
|
+
export function resolveDryRun(argv = [], env = process.env) {
|
|
41
|
+
return env.DRY_RUN === '1' || parseDryRunArg(argv);
|
|
42
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import sharp from 'sharp';
|
|
4
|
+
|
|
5
|
+
const SCREENSHOT_DIR = path.resolve(process.cwd(), './src');
|
|
6
|
+
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
8
|
+
async function processPng(filePath) {
|
|
9
|
+
const outputPath = filePath;
|
|
10
|
+
const buffer = await sharp(filePath)
|
|
11
|
+
.png({ force: true }) // 👈 без .withMetadata()
|
|
12
|
+
.toBuffer();
|
|
13
|
+
await fs.writeFile(outputPath, buffer);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
17
|
+
async function processAllPngs(dir) {
|
|
18
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
19
|
+
|
|
20
|
+
// eslint-disable-next-line functional/no-loop-statements
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
const fullPath = path.join(dir, entry.name);
|
|
23
|
+
if (entry.isDirectory()) {
|
|
24
|
+
await processAllPngs(fullPath);
|
|
25
|
+
} else if (entry.name.toLowerCase().endsWith('.png')) {
|
|
26
|
+
await processPng(fullPath);
|
|
27
|
+
console.log(`🧼 Cleaned metadata: ${fullPath}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
processAllPngs(SCREENSHOT_DIR)
|
|
33
|
+
.then(() => console.log('✅ Screenshot metadata cleanup complete'))
|
|
34
|
+
.catch((err) => {
|
|
35
|
+
console.error('❌ Failed to clean screenshots:', err);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
const [, , inputPath, outputPath] = process.argv;
|
|
7
|
+
|
|
8
|
+
if (!inputPath || !outputPath) {
|
|
9
|
+
console.error('❌ Usage: node rename-preload.js <input.js> <output.mjs>');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const resolvedInput = path.resolve(inputPath);
|
|
15
|
+
const resolvedOutput = path.resolve(outputPath);
|
|
16
|
+
|
|
17
|
+
await fs.rename(resolvedInput, resolvedOutput);
|
|
18
|
+
|
|
19
|
+
console.log(`✅ Renamed:\n ${resolvedInput} →\n ${resolvedOutput}`);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.error('❌ Rename failed:', err);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { NodeIO } from '@gltf-transform/core';
|
|
2
|
+
import { exec } from 'child_process';
|
|
3
|
+
import fg from 'fast-glob';
|
|
4
|
+
import { promises as fs } from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import sharp from 'sharp';
|
|
7
|
+
import { optimize as optimizeSvg } from 'svgo';
|
|
8
|
+
// eslint-disable-next-line spellcheck/spell-checker
|
|
9
|
+
import { promisify } from 'util';
|
|
10
|
+
|
|
11
|
+
// eslint-disable-next-line spellcheck/spell-checker
|
|
12
|
+
const execAsync = promisify(exec);
|
|
13
|
+
|
|
14
|
+
// Allow passing target directory as CLI arg; default to ./public
|
|
15
|
+
const cliArg = process.argv[2];
|
|
16
|
+
if (cliArg === '--help' || cliArg === '-h') {
|
|
17
|
+
console.log('Usage: node SanitizeAssets.js [targetDir]\nDefault targetDir is ./public');
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
const TARGET_DIR = path.resolve(process.cwd(), cliArg || './public');
|
|
21
|
+
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
23
|
+
async function cleanImage(filePath) {
|
|
24
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
25
|
+
const image = sharp(filePath);
|
|
26
|
+
const buffer = ext === '.png' ? await image.png({ force: true }).toBuffer() : await image.jpeg({ force: true }).toBuffer();
|
|
27
|
+
await fs.writeFile(filePath, buffer);
|
|
28
|
+
console.log(`🧼 Cleaned ${ext.toUpperCase()}: ${filePath}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
32
|
+
async function cleanSvg(filePath) {
|
|
33
|
+
const original = await fs.readFile(filePath, 'utf-8');
|
|
34
|
+
const result = optimizeSvg(original, {
|
|
35
|
+
multipass: true,
|
|
36
|
+
plugins: ['removeMetadata', 'removeTitle', 'removeDesc']
|
|
37
|
+
});
|
|
38
|
+
await fs.writeFile(filePath, result.data);
|
|
39
|
+
console.log(`🧼 Optimized SVG: ${filePath}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
43
|
+
async function cleanMp3(filePath) {
|
|
44
|
+
const tmp = filePath + '.cleaned.mp3';
|
|
45
|
+
await execAsync(`ffmpeg -i "${filePath}" -map_metadata -1 -y "${tmp}"`);
|
|
46
|
+
await fs.rename(tmp, filePath);
|
|
47
|
+
console.log(`🧼 Stripped metadata from MP3: ${filePath}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
51
|
+
function sanitizeDocument(doc) {
|
|
52
|
+
const root = doc.getRoot();
|
|
53
|
+
|
|
54
|
+
// eslint-disable-next-line functional/immutable-data
|
|
55
|
+
root.getAsset().generator = undefined;
|
|
56
|
+
|
|
57
|
+
[
|
|
58
|
+
root.listAccessors(),
|
|
59
|
+
root.listAnimations(),
|
|
60
|
+
root.listBuffers(),
|
|
61
|
+
// root.listBufferViews(),
|
|
62
|
+
root.listCameras(),
|
|
63
|
+
// root.listImages(),
|
|
64
|
+
root.listMaterials(),
|
|
65
|
+
root.listMeshes(),
|
|
66
|
+
root.listNodes(),
|
|
67
|
+
// root.listSamplers(),
|
|
68
|
+
root.listScenes(),
|
|
69
|
+
root.listSkins(),
|
|
70
|
+
root.listTextures()
|
|
71
|
+
]
|
|
72
|
+
.flat()
|
|
73
|
+
.forEach((item) => {
|
|
74
|
+
item.setExtras(undefined);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// eslint-disable-next-line functional/no-loop-statements
|
|
78
|
+
for (const extension of root.listExtensions()) {
|
|
79
|
+
// eslint-disable-next-line spellcheck/spell-checker
|
|
80
|
+
root.unregisterExtension(extension);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
85
|
+
async function cleanGlb(filePath) {
|
|
86
|
+
try {
|
|
87
|
+
const io = new NodeIO();
|
|
88
|
+
const doc = await io.read(filePath);
|
|
89
|
+
sanitizeDocument(doc);
|
|
90
|
+
await io.write(filePath, doc);
|
|
91
|
+
console.log(`🧼 Cleaned GLB metadata: ${filePath}`);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.warn(`⚠️ Failed to clean GLB: ${filePath}`, err.message);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
98
|
+
async function sanitizeAssets() {
|
|
99
|
+
const files = await fg(['**/*.{png,jpg,jpeg,svg,mp3,ogg,glb,gltf}'], {
|
|
100
|
+
cwd: TARGET_DIR,
|
|
101
|
+
absolute: true
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// eslint-disable-next-line functional/no-loop-statements
|
|
105
|
+
for (const file of files) {
|
|
106
|
+
const ext = path.extname(file).toLowerCase();
|
|
107
|
+
try {
|
|
108
|
+
if (['.png', '.jpg', '.jpeg'].includes(ext)) await cleanImage(file);
|
|
109
|
+
else if (ext === '.svg') await cleanSvg(file);
|
|
110
|
+
else if (['.mp3', '.ogg'].includes(ext)) await cleanMp3(file);
|
|
111
|
+
else if (['.glb'].includes(ext)) await cleanGlb(file);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
console.warn(`⚠️ Failed to clean ${file}:`, err.message);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log('✅ Done: all assets sanitized');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
sanitizeAssets();
|
package/legal/DISCLAIMER.md
CHANGED
|
@@ -32,7 +32,7 @@ The Project may include or interface with **third-party open-source components**
|
|
|
32
32
|
|
|
33
33
|
## Accessibility (EAA and similar)
|
|
34
34
|
|
|
35
|
-
Where required by applicable law (e.g., the **EU Accessibility Act**), we aim to address **accessibility feedback** for distributed binaries **within reasonable and proportionate limits**. Contact: **
|
|
35
|
+
Where required by applicable law (e.g., the **EU Accessibility Act**), we aim to address **accessibility feedback** for distributed binaries **within reasonable and proportionate limits**. Contact: **pnf036+anarchy@gmail.com**. This section **does not** create service levels or guarantees.
|
|
36
36
|
|
|
37
37
|
## Security and Support
|
|
38
38
|
|
package/legal/EULA.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# End User License Terms — @hellpig/anarchy-shared
|
|
2
2
|
|
|
3
|
-
**Effective date:**
|
|
3
|
+
**Effective date:** 11 January 2026
|
|
4
4
|
This repository is provided under **MIT** (see `LICENSE`).
|
|
5
5
|
|
|
6
6
|
1. **License & Ownership.** The code is licensed, not sold. Copyrights remain with the authors.
|
|
@@ -9,7 +9,7 @@ This repository is provided under **MIT** (see `LICENSE`).
|
|
|
9
9
|
4. **Third-Party Components (License Precedence).** Dependencies retain their own licenses and notices. **If this text ever conflicts with a third-party license for a specific component, that third-party license governs for that component.** See `NOTICE` / `THIRD_PARTY_LICENSES`.
|
|
10
10
|
5. **No Warranty / Liability.** Provided **“AS IS”**, **to the extent permitted by law**, without warranties; **statutory consumer rights (if any) are not affected**.
|
|
11
11
|
|
|
12
|
-
**Accessibility.** Where required by law, we aim to address **accessibility feedback** for officially published artifacts **within reasonable and proportionate limits**. Contact: **
|
|
12
|
+
**Accessibility.** Where required by law, we aim to address **accessibility feedback** for officially published artifacts **within reasonable and proportionate limits**. Contact: **pnf036+anarchy@gmail.com**. This section does **not** create service levels or guarantees.
|
|
13
13
|
|
|
14
14
|
## Governing Language
|
|
15
15
|
|
package/legal/PRIVACY.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Privacy Notice — @hellpig/anarchy-shared
|
|
2
2
|
|
|
3
|
-
**Effective date:**
|
|
3
|
+
**Effective date:** 11 January 2026
|
|
4
4
|
|
|
5
|
-
**Controller:** Sergei Panfilov — Contact:
|
|
5
|
+
**Controller:** Sergei Panfilov — Contact: pnf036+anarchy@gmail.com
|
|
6
6
|
|
|
7
7
|
**Scope / Identification.** This notice applies to the **open-source project** **@hellpig/anarchy-shared** (hosted on or mirrored to **public code hosting platform(s)**). We do **not** operate a consumer service through this repository.
|
|
8
8
|
|
|
@@ -19,7 +19,7 @@ Copies of public content may also appear in mirrors, forks, caches, archival cop
|
|
|
19
19
|
|
|
20
20
|
## 2. Communications
|
|
21
21
|
|
|
22
|
-
If you email **
|
|
22
|
+
If you email **pnf036+anarchy@gmail.com**, we process your **email address**, **message content**, and any information you provide to respond.
|
|
23
23
|
|
|
24
24
|
**Legal basis:** our **legitimate interests** in responding to inquiries (or your **consent**, where applicable).
|
|
25
25
|
|
|
@@ -31,7 +31,7 @@ We may use service providers (e.g., email or CI hosting) as **processors**, boun
|
|
|
31
31
|
|
|
32
32
|
## 4. Your Rights
|
|
33
33
|
|
|
34
|
-
Where applicable, you may request **access**, **rectification**, **erasure**, **restriction**, **objection**, **portability**, and **withdrawal of consent** (for processing based on consent) at **
|
|
34
|
+
Where applicable, you may request **access**, **rectification**, **erasure**, **restriction**, **objection**, **portability**, and **withdrawal of consent** (for processing based on consent) at **pnf036+anarchy@gmail.com**. You may also contact your local supervisory authority (e.g., the Dutch **Autoriteit Persoonsgegevens**, Brazil’s **ANPD**, Canada’s **OPC**, Australia’s **OAIC**). We may request reasonable information to **verify your identity** before acting on a request and will respond **within timelines required by applicable law**.
|
|
35
35
|
|
|
36
36
|
## 5. Security
|
|
37
37
|
|
|
@@ -49,4 +49,4 @@ We may update this notice by committing changes to the repository (the commit hi
|
|
|
49
49
|
|
|
50
50
|
This **notice** is drafted in English. Translations may be provided for convenience. In markets where local-language versions are required by law for clarity and fairness, the local-language version controls to the extent required by applicable law; otherwise, the English version controls.
|
|
51
51
|
|
|
52
|
-
**Contact:**
|
|
52
|
+
**Contact:** pnf036+anarchy@gmail.com
|
package/legal/SECURITY.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Security Policy — @hellpig/anarchy-shared
|
|
2
2
|
|
|
3
|
-
**Effective date:**
|
|
3
|
+
**Effective date:** 11 January 2026
|
|
4
4
|
|
|
5
|
-
**Security Contact:**
|
|
5
|
+
**Security Contact:** pnf036+anarchy_security@gmail.com (email preferred for sensitive reports)
|
|
6
6
|
|
|
7
7
|
## Scope
|
|
8
8
|
|
|
@@ -13,7 +13,7 @@ It covers the Project’s **source code** and our **officially published release
|
|
|
13
13
|
|
|
14
14
|
## Reporting (CVD)
|
|
15
15
|
|
|
16
|
-
- **Report:** email **
|
|
16
|
+
- **Report:** email **pnf036+anarchy_security@gmail.com** with steps to reproduce, affected commit/tag, and impact (if known). If possible, include a short patch or mitigation suggestion.
|
|
17
17
|
- **Public disclosure:** please **do not file public issues with exploit details**; coordinate timing with us to allow a fix or mitigation to be available where reasonably possible.
|
|
18
18
|
|
|
19
19
|
## Handling & Disclosure
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@hellpig/anarchy-shared",
|
|
3
3
|
"author": "S. Panfilov",
|
|
4
4
|
"private": false,
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.6.1",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
"publishConfig": {
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
|
-
"sideEffects":
|
|
19
|
+
"sideEffects": [
|
|
20
|
+
"**/*.scss"
|
|
21
|
+
],
|
|
20
22
|
"license": "MIT",
|
|
21
23
|
"description": "Anarchy-shared – a library with shared utilities and scripts for Anarchy Engine.",
|
|
22
24
|
"keywords": [
|
|
@@ -31,7 +33,8 @@
|
|
|
31
33
|
"NOTICE.md",
|
|
32
34
|
"LICENSE",
|
|
33
35
|
"README.md",
|
|
34
|
-
"dist/"
|
|
36
|
+
"dist/",
|
|
37
|
+
"ScriptUtils/"
|
|
35
38
|
],
|
|
36
39
|
"exports": {
|
|
37
40
|
"./Utils": {
|
|
@@ -50,10 +53,6 @@
|
|
|
50
53
|
"types": "./dist/Plugins/index.d.ts",
|
|
51
54
|
"import": "./dist/Plugins/index.es.js"
|
|
52
55
|
},
|
|
53
|
-
"./assets": {
|
|
54
|
-
"types": "./dist/assets/index.d.ts",
|
|
55
|
-
"import": "./dist/assets/index.es.js"
|
|
56
|
-
},
|
|
57
56
|
"./assets/*": "./src/assets/*",
|
|
58
57
|
"./ScriptUtils/*": "./ScriptUtils/*"
|
|
59
58
|
},
|