@sfmc-bds/bds-tools 0.2.0-beta.17 → 0.2.0-beta.18

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.
Files changed (46) hide show
  1. package/dist/bds-manager.d.ts.map +1 -1
  2. package/dist/bds-manager.js +8 -1
  3. package/dist/bds-manager.js.map +2 -2
  4. package/dist/changelog.d.ts +11 -7
  5. package/dist/changelog.d.ts.map +1 -1
  6. package/dist/changelog.js +60 -34
  7. package/dist/changelog.js.map +2 -2
  8. package/dist/check-update.d.ts.map +1 -1
  9. package/dist/check-update.js +50 -13
  10. package/dist/check-update.js.map +2 -2
  11. package/dist/qq-events-notify.d.ts +1 -1
  12. package/dist/qq-events-notify.d.ts.map +1 -1
  13. package/dist/qq-events-notify.js.map +1 -1
  14. package/dist/qqutil.d.ts.map +1 -1
  15. package/dist/qqutil.js +14 -12
  16. package/dist/qqutil.js.map +2 -2
  17. package/dist/server-properties-i18n.d.ts.map +1 -1
  18. package/dist/server-properties-i18n.js +4 -0
  19. package/dist/server-properties-i18n.js.map +2 -2
  20. package/dist/server-properties.d.ts +3 -0
  21. package/dist/server-properties.d.ts.map +1 -1
  22. package/dist/server-properties.js +15 -0
  23. package/dist/server-properties.js.map +2 -2
  24. package/dist/types.d.ts +1 -0
  25. package/dist/types.d.ts.map +1 -1
  26. package/package.json +4 -4
  27. package/dist/dependency-negotiator.test.js +0 -120
  28. package/dist/dependency-negotiator.test.js.map +0 -7
  29. package/dist/fsx.test.js +0 -60
  30. package/dist/fsx.test.js.map +0 -7
  31. package/dist/level-dat.test.js +0 -172
  32. package/dist/level-dat.test.js.map +0 -7
  33. package/dist/pack-manager-lifecycle.test.js +0 -252
  34. package/dist/pack-manager-lifecycle.test.js.map +0 -7
  35. package/dist/pack-update/config.test.js +0 -75
  36. package/dist/pack-update/config.test.js.map +0 -7
  37. package/dist/pack-update/version-policy.test.js +0 -137
  38. package/dist/pack-update/version-policy.test.js.map +0 -7
  39. package/dist/process-probe.test.js +0 -76
  40. package/dist/process-probe.test.js.map +0 -7
  41. package/dist/server-properties.test.js +0 -127
  42. package/dist/server-properties.test.js.map +0 -7
  43. package/dist/world-packs.test.js +0 -698
  44. package/dist/world-packs.test.js.map +0 -7
  45. package/dist/zipx.test.js +0 -36
  46. package/dist/zipx.test.js.map +0 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sfmc-bds/bds-tools",
3
- "version": "0.2.0-beta.17",
3
+ "version": "0.2.0-beta.18",
4
4
  "description": "SFMC BDS auto-updater, lifecycle manager, and behavior-pack assembler",
5
5
  "author": "Shiroha7z",
6
6
  "license": "AGPL-3.0-only",
@@ -33,6 +33,7 @@
33
33
  "./pack-manager": "./dist/cli-pack-manager.js",
34
34
  "./pack-manager-lib": "./dist/pack-manager.js",
35
35
  "./process-probe": "./dist/process-probe.js",
36
+ "./qq-events-notify": "./dist/qq-events-notify.js",
36
37
  "./package.json": "./package.json",
37
38
  "./taskbar": "./dist/taskbar.js",
38
39
  "./types": {
@@ -53,10 +54,10 @@
53
54
  "dependencies": {
54
55
  "jszip": "^3.10.1",
55
56
  "node-html-parser": "^9.0.0",
56
- "@sfmc-bds/sdk": "0.2.0-beta.17"
57
+ "@sfmc-bds/sdk": "0.2.0-beta.18"
57
58
  },
58
59
  "devDependencies": {
59
- "@sfmc-bds/tools": "0.2.0-beta.18"
60
+ "@sfmc-bds/tools": "0.2.0-beta.19"
60
61
  },
61
62
  "publishConfig": {
62
63
  "access": "public",
@@ -70,7 +71,6 @@
70
71
  "start": "node dist/bds-manager.js start",
71
72
  "status": "node dist/bds-manager.js status",
72
73
  "stop": "node dist/bds-manager.js stop",
73
- "test": "pnpm run build && node --test \"dist/**/*.test.js\"",
74
74
  "typecheck": "tsc7 --noEmit",
75
75
  "update": "node dist/check-update.js",
76
76
  "update:check": "node dist/check-update.js --check-only",
@@ -1,120 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import {
4
- BASELINE_BEDROCK_DEPENDENCIES,
5
- cleanBedrockVersion,
6
- compareBedrockVersion,
7
- isGametestBetaRequired,
8
- negotiateBedrockDependencies
9
- } from "./dependency-negotiator.js";
10
- test("cleanBedrockVersion: \u6E05\u7406 npm range \u7B26\u53F7", () => {
11
- assert.equal(cleanBedrockVersion("^2.0.0-beta"), "2.0.0-beta");
12
- assert.equal(cleanBedrockVersion(">=2.0.0-beta <3.0.0"), "2.0.0-beta");
13
- assert.equal(cleanBedrockVersion("~1.3.0"), "1.3.0");
14
- assert.equal(cleanBedrockVersion("v1.18.0"), "1.18.0");
15
- assert.equal(cleanBedrockVersion(" 1.2.0 "), "1.2.0");
16
- });
17
- test("compareBedrockVersion: \u5927\u7248\u672C\u4E0E\u6B21\u7248\u672C\u6BD4\u8F83", () => {
18
- assert.ok(compareBedrockVersion("1.18.0", "1.3.0") > 0);
19
- assert.ok(compareBedrockVersion("1.3.0", "1.18.0") < 0);
20
- assert.equal(compareBedrockVersion("1.3.0", "1.3.0"), 0);
21
- });
22
- test("compareBedrockVersion: Beta \u4E0E\u6B63\u5F0F\u7248\u6BD4\u8F83\uFF08\u8DE8\u5927\u7248\u672C\u63D0\u5347\uFF09", () => {
23
- assert.ok(compareBedrockVersion("2.0.0-beta", "1.3.0") > 0);
24
- assert.ok(compareBedrockVersion("2.0.0", "2.0.0-beta") > 0);
25
- assert.ok(compareBedrockVersion("2.2.0-beta", "2.0.0-beta") > 0);
26
- });
27
- test("isGametestBetaRequired: \u51C6\u786E\u8BC6\u522B\u6D4B\u8BD5\u7248\u73A9\u6CD5\u9700\u6C42", () => {
28
- assert.equal(
29
- isGametestBetaRequired({ module_name: "@minecraft/server-ui", version: "2.0.0-beta" }),
30
- true
31
- );
32
- assert.equal(
33
- isGametestBetaRequired({ module_name: "@minecraft/server", version: "2.10.0-beta" }),
34
- true
35
- );
36
- assert.equal(
37
- isGametestBetaRequired({ module_name: "@minecraft/server-gametest", version: "1.0.0" }),
38
- true
39
- );
40
- assert.equal(
41
- isGametestBetaRequired({ module_name: "@minecraft/server-ui", version: "1.3.0" }),
42
- false
43
- );
44
- assert.equal(
45
- isGametestBetaRequired({ module_name: "@minecraft/server", version: "1.18.0" }),
46
- false
47
- );
48
- assert.equal(
49
- isGametestBetaRequired({ module_name: "@minecraft/server-net", version: "1.0.0-beta" }),
50
- false
51
- );
52
- assert.equal(
53
- isGametestBetaRequired({ module_name: "@minecraft/server-admin", version: "1.0.0-beta" }),
54
- false
55
- );
56
- });
57
- test("negotiateBedrockDependencies: \u65E0\u6A21\u7EC4\u9700\u6C42\u65F6\u4FDD\u6301\u57FA\u51C6\u4F9D\u8D56", () => {
58
- const result = negotiateBedrockDependencies(BASELINE_BEDROCK_DEPENDENCIES, []);
59
- assert.equal(result.dependencies.length, BASELINE_BEDROCK_DEPENDENCIES.length);
60
- assert.equal(result.promotions.length, 0);
61
- assert.equal(result.hasBetaApis, false);
62
- const uiDep = result.dependencies.find((d) => d.module_name === "@minecraft/server-ui");
63
- assert.equal(uiDep?.version, "1.3.0");
64
- });
65
- test("negotiateBedrockDependencies: \u5355\u6A21\u5757\u63D0\u5347 @minecraft/server-ui \u81F3 2.0.0-beta", () => {
66
- const result = negotiateBedrockDependencies(BASELINE_BEDROCK_DEPENDENCIES, [
67
- {
68
- folderId: "custom-menu",
69
- dependencies: [
70
- { module_name: "@minecraft/server-ui", version: "2.0.0-beta" }
71
- ]
72
- }
73
- ]);
74
- const uiDep = result.dependencies.find((d) => d.module_name === "@minecraft/server-ui");
75
- assert.equal(uiDep?.version, "2.0.0-beta");
76
- assert.equal(result.hasBetaApis, true);
77
- assert.equal(result.promotions.length, 1);
78
- assert.equal(result.promotions[0].moduleName, "@minecraft/server-ui");
79
- assert.equal(result.promotions[0].from, "1.3.0");
80
- assert.equal(result.promotions[0].to, "2.0.0-beta");
81
- assert.deepEqual(result.promotions[0].requestedBy, ["custom-menu"]);
82
- });
83
- test("negotiateBedrockDependencies: \u591A\u6A21\u5757\u4E0D\u540C\u7248\u672C\u9700\u6C42\u53D6\u6700\u9AD8\u517C\u5BB9", () => {
84
- const result = negotiateBedrockDependencies(BASELINE_BEDROCK_DEPENDENCIES, [
85
- {
86
- folderId: "mod-a",
87
- dependencies: [
88
- { module_name: "@minecraft/server-ui", version: "2.0.0-beta" }
89
- ]
90
- },
91
- {
92
- folderId: "mod-b",
93
- dependencies: [
94
- { module_name: "@minecraft/server-ui", version: "2.2.0-beta" }
95
- ]
96
- },
97
- {
98
- folderId: "mod-c",
99
- dependencies: [
100
- { module_name: "@minecraft/server-ui", version: "1.3.0" }
101
- ]
102
- }
103
- ]);
104
- const uiDep = result.dependencies.find((d) => d.module_name === "@minecraft/server-ui");
105
- assert.equal(uiDep?.version, "2.2.0-beta");
106
- assert.equal(result.hasBetaApis, true);
107
- });
108
- test("negotiateBedrockDependencies: \u652F\u6301\u5F15\u5165\u57FA\u51C6\u5916\u65B0\u539F\u751F\u6A21\u5757", () => {
109
- const result = negotiateBedrockDependencies(BASELINE_BEDROCK_DEPENDENCIES, [
110
- {
111
- folderId: "mod-gametest",
112
- dependencies: [
113
- { module_name: "@minecraft/server-gametest", version: "1.0.0-beta" }
114
- ]
115
- }
116
- ]);
117
- assert.ok(result.dependencies.some((d) => d.module_name === "@minecraft/server-gametest"));
118
- assert.equal(result.hasBetaApis, true);
119
- });
120
- //# sourceMappingURL=dependency-negotiator.test.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/dependency-negotiator.test.ts"],
4
- "sourcesContent": ["import assert from \"node:assert/strict\";\nimport test from \"node:test\";\nimport {\n BASELINE_BEDROCK_DEPENDENCIES,\n cleanBedrockVersion,\n compareBedrockVersion,\n isGametestBetaRequired,\n negotiateBedrockDependencies,\n} from \"./dependency-negotiator.js\";\n\ntest(\"cleanBedrockVersion: \u6E05\u7406 npm range \u7B26\u53F7\", () => {\n assert.equal(cleanBedrockVersion(\"^2.0.0-beta\"), \"2.0.0-beta\");\n assert.equal(cleanBedrockVersion(\">=2.0.0-beta <3.0.0\"), \"2.0.0-beta\");\n assert.equal(cleanBedrockVersion(\"~1.3.0\"), \"1.3.0\");\n assert.equal(cleanBedrockVersion(\"v1.18.0\"), \"1.18.0\");\n assert.equal(cleanBedrockVersion(\" 1.2.0 \"), \"1.2.0\");\n});\n\ntest(\"compareBedrockVersion: \u5927\u7248\u672C\u4E0E\u6B21\u7248\u672C\u6BD4\u8F83\", () => {\n assert.ok(compareBedrockVersion(\"1.18.0\", \"1.3.0\") > 0);\n assert.ok(compareBedrockVersion(\"1.3.0\", \"1.18.0\") < 0);\n assert.equal(compareBedrockVersion(\"1.3.0\", \"1.3.0\"), 0);\n});\n\ntest(\"compareBedrockVersion: Beta \u4E0E\u6B63\u5F0F\u7248\u6BD4\u8F83\uFF08\u8DE8\u5927\u7248\u672C\u63D0\u5347\uFF09\", () => {\n // 2.0.0-beta \u5E94\u9AD8\u4E8E 1.3.0\n assert.ok(compareBedrockVersion(\"2.0.0-beta\", \"1.3.0\") > 0);\n // \u540C\u7248\u672C\u4E0B\u6B63\u5F0F\u7248\u9AD8\u4E8E\u6D4B\u8BD5\u7248\n assert.ok(compareBedrockVersion(\"2.0.0\", \"2.0.0-beta\") > 0);\n // \u4E24\u4E2A beta \u4E4B\u95F4\u6B21\u7248\u672C\u5BF9\u6BD4\n assert.ok(compareBedrockVersion(\"2.2.0-beta\", \"2.0.0-beta\") > 0);\n});\n\ntest(\"isGametestBetaRequired: \u51C6\u786E\u8BC6\u522B\u6D4B\u8BD5\u7248\u73A9\u6CD5\u9700\u6C42\", () => {\n // \u9700\u8981\u6D4B\u8BD5\u73A9\u6CD5\u7684\u9879\n assert.equal(\n isGametestBetaRequired({ module_name: \"@minecraft/server-ui\", version: \"2.0.0-beta\" }),\n true\n );\n assert.equal(\n isGametestBetaRequired({ module_name: \"@minecraft/server\", version: \"2.10.0-beta\" }),\n true\n );\n assert.equal(\n isGametestBetaRequired({ module_name: \"@minecraft/server-gametest\", version: \"1.0.0\" }),\n true\n );\n\n // \u4E0D\u9700\u8981\u6D4B\u8BD5\u73A9\u6CD5\u7684\u7A33\u5B9A\u7248\u6216\u7EAF\u6743\u9650\u6A21\u5757\n assert.equal(\n isGametestBetaRequired({ module_name: \"@minecraft/server-ui\", version: \"1.3.0\" }),\n false\n );\n assert.equal(\n isGametestBetaRequired({ module_name: \"@minecraft/server\", version: \"1.18.0\" }),\n false\n );\n assert.equal(\n isGametestBetaRequired({ module_name: \"@minecraft/server-net\", version: \"1.0.0-beta\" }),\n false\n );\n assert.equal(\n isGametestBetaRequired({ module_name: \"@minecraft/server-admin\", version: \"1.0.0-beta\" }),\n false\n );\n});\n\ntest(\"negotiateBedrockDependencies: \u65E0\u6A21\u7EC4\u9700\u6C42\u65F6\u4FDD\u6301\u57FA\u51C6\u4F9D\u8D56\", () => {\n const result = negotiateBedrockDependencies(BASELINE_BEDROCK_DEPENDENCIES, []);\n assert.equal(result.dependencies.length, BASELINE_BEDROCK_DEPENDENCIES.length);\n assert.equal(result.promotions.length, 0);\n assert.equal(result.hasBetaApis, false);\n\n const uiDep = result.dependencies.find((d) => d.module_name === \"@minecraft/server-ui\");\n assert.equal(uiDep?.version, \"1.3.0\");\n});\n\ntest(\"negotiateBedrockDependencies: \u5355\u6A21\u5757\u63D0\u5347 @minecraft/server-ui \u81F3 2.0.0-beta\", () => {\n const result = negotiateBedrockDependencies(BASELINE_BEDROCK_DEPENDENCIES, [\n {\n folderId: \"custom-menu\",\n dependencies: [\n { module_name: \"@minecraft/server-ui\", version: \"2.0.0-beta\" },\n ],\n },\n ]);\n\n const uiDep = result.dependencies.find((d) => d.module_name === \"@minecraft/server-ui\");\n assert.equal(uiDep?.version, \"2.0.0-beta\");\n assert.equal(result.hasBetaApis, true);\n assert.equal(result.promotions.length, 1);\n assert.equal(result.promotions[0].moduleName, \"@minecraft/server-ui\");\n assert.equal(result.promotions[0].from, \"1.3.0\");\n assert.equal(result.promotions[0].to, \"2.0.0-beta\");\n assert.deepEqual(result.promotions[0].requestedBy, [\"custom-menu\"]);\n});\n\ntest(\"negotiateBedrockDependencies: \u591A\u6A21\u5757\u4E0D\u540C\u7248\u672C\u9700\u6C42\u53D6\u6700\u9AD8\u517C\u5BB9\", () => {\n const result = negotiateBedrockDependencies(BASELINE_BEDROCK_DEPENDENCIES, [\n {\n folderId: \"mod-a\",\n dependencies: [\n { module_name: \"@minecraft/server-ui\", version: \"2.0.0-beta\" },\n ],\n },\n {\n folderId: \"mod-b\",\n dependencies: [\n { module_name: \"@minecraft/server-ui\", version: \"2.2.0-beta\" },\n ],\n },\n {\n folderId: \"mod-c\",\n dependencies: [\n { module_name: \"@minecraft/server-ui\", version: \"1.3.0\" },\n ],\n },\n ]);\n\n const uiDep = result.dependencies.find((d) => d.module_name === \"@minecraft/server-ui\");\n assert.equal(uiDep?.version, \"2.2.0-beta\");\n assert.equal(result.hasBetaApis, true);\n});\n\ntest(\"negotiateBedrockDependencies: \u652F\u6301\u5F15\u5165\u57FA\u51C6\u5916\u65B0\u539F\u751F\u6A21\u5757\", () => {\n const result = negotiateBedrockDependencies(BASELINE_BEDROCK_DEPENDENCIES, [\n {\n folderId: \"mod-gametest\",\n dependencies: [\n { module_name: \"@minecraft/server-gametest\", version: \"1.0.0-beta\" },\n ],\n },\n ]);\n\n assert.ok(result.dependencies.some((d) => d.module_name === \"@minecraft/server-gametest\"));\n assert.equal(result.hasBetaApis, true);\n});\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
package/dist/fsx.test.js DELETED
@@ -1,60 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import fs from "node:fs";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import { describe, it } from "node:test";
6
- import { copyDirSync, copyFileSyncSafe, getDirSize, rmSafe } from "./fsx.js";
7
- describe("fsx: copyDirSync & copyFileSyncSafe", () => {
8
- it("copyDirSync \u81EA\u52A8\u5FFD\u7565 .git / node_modules \u7B49\u5143\u6570\u636E", () => {
9
- const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-fsx-test-"));
10
- try {
11
- const src = path.join(tmp, "src");
12
- const dest = path.join(tmp, "dest");
13
- fs.mkdirSync(path.join(src, ".git", "objects"), { recursive: true });
14
- fs.mkdirSync(path.join(src, "scripts"), { recursive: true });
15
- fs.writeFileSync(path.join(src, ".git", "HEAD"), "ref: refs/heads/main\n");
16
- fs.writeFileSync(path.join(src, "scripts", "main.js"), "console.log('hello');\n");
17
- fs.writeFileSync(path.join(src, ".DS_Store"), "junk");
18
- copyDirSync(src, dest);
19
- assert.ok(fs.existsSync(path.join(dest, "scripts", "main.js")));
20
- assert.ok(!fs.existsSync(path.join(dest, ".git")));
21
- assert.ok(!fs.existsSync(path.join(dest, ".DS_Store")));
22
- } finally {
23
- rmSafe(tmp);
24
- }
25
- });
26
- it("copyFileSyncSafe \u80FD\u591F\u5B89\u5168\u8986\u5199 Windows \u5E73\u53F0\u4E0B\u7684\u53EA\u8BFB\u53EA\u8BFB\u5C5E\u6027\u6587\u4EF6", () => {
27
- const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-fsx-ro-"));
28
- try {
29
- const src = path.join(tmp, "src.txt");
30
- const dest = path.join(tmp, "dest.txt");
31
- fs.writeFileSync(src, "version 2");
32
- fs.writeFileSync(dest, "version 1");
33
- fs.chmodSync(dest, 292);
34
- copyFileSyncSafe(src, dest);
35
- assert.equal(fs.readFileSync(dest, "utf8"), "version 2");
36
- fs.writeFileSync(src, "version 3");
37
- copyFileSyncSafe(src, dest);
38
- assert.equal(fs.readFileSync(dest, "utf8"), "version 3");
39
- } finally {
40
- try {
41
- fs.chmodSync(path.join(tmp, "dest.txt"), 438);
42
- } catch {
43
- }
44
- rmSafe(tmp);
45
- }
46
- });
47
- it("getDirSize \u5FFD\u7565 .git \u76EE\u5F55\u5927\u5C0F", () => {
48
- const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-fsx-size-"));
49
- try {
50
- fs.mkdirSync(path.join(tmp, ".git"), { recursive: true });
51
- fs.writeFileSync(path.join(tmp, ".git", "pack"), "x".repeat(1e4));
52
- fs.writeFileSync(path.join(tmp, "data.txt"), "hello");
53
- const size = getDirSize(tmp);
54
- assert.equal(size, 5);
55
- } finally {
56
- rmSafe(tmp);
57
- }
58
- });
59
- });
60
- //# sourceMappingURL=fsx.test.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/fsx.test.ts"],
4
- "sourcesContent": ["import assert from \"node:assert/strict\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { describe, it } from \"node:test\";\nimport { copyDirSync, copyFileSyncSafe, getDirSize, rmSafe } from \"./fsx.js\";\n\ndescribe(\"fsx: copyDirSync & copyFileSyncSafe\", () => {\n it(\"copyDirSync \u81EA\u52A8\u5FFD\u7565 .git / node_modules \u7B49\u5143\u6570\u636E\", () => {\n const tmp = fs.mkdtempSync(path.join(os.tmpdir(), \"sfmc-fsx-test-\"));\n try {\n const src = path.join(tmp, \"src\");\n const dest = path.join(tmp, \"dest\");\n fs.mkdirSync(path.join(src, \".git\", \"objects\"), { recursive: true });\n fs.mkdirSync(path.join(src, \"scripts\"), { recursive: true });\n fs.writeFileSync(path.join(src, \".git\", \"HEAD\"), \"ref: refs/heads/main\\n\");\n fs.writeFileSync(path.join(src, \"scripts\", \"main.js\"), \"console.log('hello');\\n\");\n fs.writeFileSync(path.join(src, \".DS_Store\"), \"junk\");\n\n copyDirSync(src, dest);\n\n assert.ok(fs.existsSync(path.join(dest, \"scripts\", \"main.js\")));\n assert.ok(!fs.existsSync(path.join(dest, \".git\")));\n assert.ok(!fs.existsSync(path.join(dest, \".DS_Store\")));\n } finally {\n rmSafe(tmp);\n }\n });\n\n it(\"copyFileSyncSafe \u80FD\u591F\u5B89\u5168\u8986\u5199 Windows \u5E73\u53F0\u4E0B\u7684\u53EA\u8BFB\u53EA\u8BFB\u5C5E\u6027\u6587\u4EF6\", () => {\n const tmp = fs.mkdtempSync(path.join(os.tmpdir(), \"sfmc-fsx-ro-\"));\n try {\n const src = path.join(tmp, \"src.txt\");\n const dest = path.join(tmp, \"dest.txt\");\n fs.writeFileSync(src, \"version 2\");\n fs.writeFileSync(dest, \"version 1\");\n // \u8BBE\u7F6E\u76EE\u6807\u6587\u4EF6\u53EA\u8BFB (Windows 0444)\n fs.chmodSync(dest, 0o444);\n\n // copyFileSyncSafe \u5E94\u5F53\u6355\u83B7 EPERM \u5E76\u8986\u5199\u6210\u529F\uFF0C\u800C\u4E0D\u662F\u5D29\u6E83\n copyFileSyncSafe(src, dest);\n\n assert.equal(fs.readFileSync(dest, \"utf8\"), \"version 2\");\n\n // \u518D\u6B21\u8986\u5199\uFF0C\u786E\u4FDD\u4E0D\u4F1A\u9057\u7559\u53EA\u8BFB\u963B\u65AD\u4E0B\u4E00\u6B21\u5907\u4EFD\n fs.writeFileSync(src, \"version 3\");\n copyFileSyncSafe(src, dest);\n assert.equal(fs.readFileSync(dest, \"utf8\"), \"version 3\");\n } finally {\n try {\n fs.chmodSync(path.join(tmp, \"dest.txt\"), 0o666);\n } catch {}\n rmSafe(tmp);\n }\n });\n\n it(\"getDirSize \u5FFD\u7565 .git \u76EE\u5F55\u5927\u5C0F\", () => {\n const tmp = fs.mkdtempSync(path.join(os.tmpdir(), \"sfmc-fsx-size-\"));\n try {\n fs.mkdirSync(path.join(tmp, \".git\"), { recursive: true });\n fs.writeFileSync(path.join(tmp, \".git\", \"pack\"), \"x\".repeat(10000));\n fs.writeFileSync(path.join(tmp, \"data.txt\"), \"hello\");\n\n const size = getDirSize(tmp);\n assert.equal(size, 5);\n } finally {\n rmSafe(tmp);\n }\n });\n});\n"],
5
- "mappings": "AAAA,OAAO,YAAY;AACnB,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,UAAU,UAAU;AAC7B,SAAS,aAAa,kBAAkB,YAAY,cAAc;AAElE,SAAS,uCAAuC,MAAM;AACpD,KAAG,qFAA6C,MAAM;AACpD,UAAM,MAAM,GAAG,YAAY,KAAK,KAAK,GAAG,OAAO,GAAG,gBAAgB,CAAC;AACnE,QAAI;AACF,YAAM,MAAM,KAAK,KAAK,KAAK,KAAK;AAChC,YAAM,OAAO,KAAK,KAAK,KAAK,MAAM;AAClC,SAAG,UAAU,KAAK,KAAK,KAAK,QAAQ,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AACnE,SAAG,UAAU,KAAK,KAAK,KAAK,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3D,SAAG,cAAc,KAAK,KAAK,KAAK,QAAQ,MAAM,GAAG,wBAAwB;AACzE,SAAG,cAAc,KAAK,KAAK,KAAK,WAAW,SAAS,GAAG,yBAAyB;AAChF,SAAG,cAAc,KAAK,KAAK,KAAK,WAAW,GAAG,MAAM;AAEpD,kBAAY,KAAK,IAAI;AAErB,aAAO,GAAG,GAAG,WAAW,KAAK,KAAK,MAAM,WAAW,SAAS,CAAC,CAAC;AAC9D,aAAO,GAAG,CAAC,GAAG,WAAW,KAAK,KAAK,MAAM,MAAM,CAAC,CAAC;AACjD,aAAO,GAAG,CAAC,GAAG,WAAW,KAAK,KAAK,MAAM,WAAW,CAAC,CAAC;AAAA,IACxD,UAAE;AACA,aAAO,GAAG;AAAA,IACZ;AAAA,EACF,CAAC;AAED,KAAG,0IAAgD,MAAM;AACvD,UAAM,MAAM,GAAG,YAAY,KAAK,KAAK,GAAG,OAAO,GAAG,cAAc,CAAC;AACjE,QAAI;AACF,YAAM,MAAM,KAAK,KAAK,KAAK,SAAS;AACpC,YAAM,OAAO,KAAK,KAAK,KAAK,UAAU;AACtC,SAAG,cAAc,KAAK,WAAW;AACjC,SAAG,cAAc,MAAM,WAAW;AAElC,SAAG,UAAU,MAAM,GAAK;AAGxB,uBAAiB,KAAK,IAAI;AAE1B,aAAO,MAAM,GAAG,aAAa,MAAM,MAAM,GAAG,WAAW;AAGvD,SAAG,cAAc,KAAK,WAAW;AACjC,uBAAiB,KAAK,IAAI;AAC1B,aAAO,MAAM,GAAG,aAAa,MAAM,MAAM,GAAG,WAAW;AAAA,IACzD,UAAE;AACA,UAAI;AACF,WAAG,UAAU,KAAK,KAAK,KAAK,UAAU,GAAG,GAAK;AAAA,MAChD,QAAQ;AAAA,MAAC;AACT,aAAO,GAAG;AAAA,IACZ;AAAA,EACF,CAAC;AAED,KAAG,yDAA2B,MAAM;AAClC,UAAM,MAAM,GAAG,YAAY,KAAK,KAAK,GAAG,OAAO,GAAG,gBAAgB,CAAC;AACnE,QAAI;AACF,SAAG,UAAU,KAAK,KAAK,KAAK,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AACxD,SAAG,cAAc,KAAK,KAAK,KAAK,QAAQ,MAAM,GAAG,IAAI,OAAO,GAAK,CAAC;AAClE,SAAG,cAAc,KAAK,KAAK,KAAK,UAAU,GAAG,OAAO;AAEpD,YAAM,OAAO,WAAW,GAAG;AAC3B,aAAO,MAAM,MAAM,CAAC;AAAA,IACtB,UAAE;AACA,aAAO,GAAG;AAAA,IACZ;AAAA,EACF,CAAC;AACH,CAAC;",
6
- "names": []
7
- }
@@ -1,172 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import fs from "node:fs";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import test from "node:test";
6
- import {
7
- enableBetaApisInLevelDat,
8
- enableExperimentsInLevelDat,
9
- KNOWN_EXPERIMENTS,
10
- parseBedrockLevelDat,
11
- readLevelDatExperiments,
12
- resolveExperimentDefinition,
13
- TAG_BYTE,
14
- TAG_COMPOUND,
15
- TAG_END
16
- } from "./level-dat.js";
17
- function buildMockLevelDat(includeExperiments, gametestVal) {
18
- const rootName = "";
19
- const rootNameBuf = Buffer.from(rootName, "utf8");
20
- const childBuffers = [];
21
- const sampleName = Buffer.from("LevelName", "utf8");
22
- const sampleVal = Buffer.from("TestWorld", "utf8");
23
- const strTag = Buffer.alloc(1 + 2 + sampleName.length + 2 + sampleVal.length);
24
- strTag.writeUInt8(8, 0);
25
- strTag.writeUInt16LE(sampleName.length, 1);
26
- sampleName.copy(strTag, 3);
27
- strTag.writeUInt16LE(sampleVal.length, 3 + sampleName.length);
28
- sampleVal.copy(strTag, 5 + sampleName.length);
29
- childBuffers.push(strTag);
30
- if (includeExperiments) {
31
- const expName = Buffer.from("experiments", "utf8");
32
- const expChildren = [];
33
- const everUsedName = Buffer.from("experiments_ever_used", "utf8");
34
- const b1 = Buffer.alloc(1 + 2 + everUsedName.length + 1);
35
- b1.writeUInt8(TAG_BYTE, 0);
36
- b1.writeUInt16LE(everUsedName.length, 1);
37
- everUsedName.copy(b1, 3);
38
- b1.writeInt8(0, 3 + everUsedName.length);
39
- expChildren.push(b1);
40
- if (gametestVal !== void 0) {
41
- const gtName = Buffer.from("gametest", "utf8");
42
- const b2 = Buffer.alloc(1 + 2 + gtName.length + 1);
43
- b2.writeUInt8(TAG_BYTE, 0);
44
- b2.writeUInt16LE(gtName.length, 1);
45
- gtName.copy(b2, 3);
46
- b2.writeInt8(gametestVal, 3 + gtName.length);
47
- expChildren.push(b2);
48
- }
49
- expChildren.push(Buffer.from([TAG_END]));
50
- const expHeader = Buffer.alloc(1 + 2 + expName.length);
51
- expHeader.writeUInt8(TAG_COMPOUND, 0);
52
- expHeader.writeUInt16LE(expName.length, 1);
53
- expName.copy(expHeader, 3);
54
- childBuffers.push(Buffer.concat([expHeader, ...expChildren]));
55
- }
56
- childBuffers.push(Buffer.from([TAG_END]));
57
- const rootHeader = Buffer.alloc(1 + 2 + rootNameBuf.length);
58
- rootHeader.writeUInt8(TAG_COMPOUND, 0);
59
- rootHeader.writeUInt16LE(rootNameBuf.length, 1);
60
- rootNameBuf.copy(rootHeader, 3);
61
- const nbtBody = Buffer.concat([rootHeader, ...childBuffers]);
62
- const fileHeader = Buffer.alloc(8);
63
- fileHeader.writeUInt32LE(10, 0);
64
- fileHeader.writeUInt32LE(nbtBody.length, 4);
65
- return Buffer.concat([fileHeader, nbtBody]);
66
- }
67
- test("readLevelDatExperiments: correctly reads experiments status", () => {
68
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-lvl-test-"));
69
- try {
70
- const file = path.join(tmpDir, "level.dat");
71
- fs.writeFileSync(file, buildMockLevelDat(true));
72
- const info1 = readLevelDatExperiments(file);
73
- assert.ok(info1);
74
- assert.equal(info1.hasBetaApis, false);
75
- assert.equal(info1.experimentsEverUsed, false);
76
- assert.equal(info1.knownExperiments.gametest, false);
77
- assert.equal(info1.knownExperiments.upcoming_creator_features, false);
78
- fs.writeFileSync(file, buildMockLevelDat(true, 1));
79
- const info2 = readLevelDatExperiments(file);
80
- assert.ok(info2);
81
- assert.equal(info2.hasBetaApis, true);
82
- assert.equal(info2.knownExperiments.gametest, true);
83
- } finally {
84
- fs.rmSync(tmpDir, { recursive: true, force: true });
85
- }
86
- });
87
- test("resolveExperimentDefinition: resolves aliases and canonical ids", () => {
88
- assert.equal(resolveExperimentDefinition("upcoming")?.id, "upcoming_creator_features");
89
- assert.equal(resolveExperimentDefinition("cameras")?.id, "experimental_creator_cameras");
90
- assert.equal(resolveExperimentDefinition("voxel")?.id, "voxel_shapes");
91
- assert.equal(resolveExperimentDefinition("trades")?.id, "villager_trades_rebalance");
92
- assert.equal(resolveExperimentDefinition("drop3")?.id, "drop_3_2026");
93
- assert.equal(resolveExperimentDefinition("wilderness_bound")?.id, "drop_3_2026");
94
- assert.equal(resolveExperimentDefinition("edu")?.id, "education");
95
- assert.equal(resolveExperimentDefinition("unknown_feature"), null);
96
- });
97
- test("enableBetaApisInLevelDat: enables gametest and updates header length", async () => {
98
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-lvl-test-"));
99
- try {
100
- const file = path.join(tmpDir, "level.dat");
101
- fs.writeFileSync(file, buildMockLevelDat(true));
102
- const res1 = await enableBetaApisInLevelDat(file);
103
- assert.equal(res1.success, true);
104
- assert.equal(res1.changed, true);
105
- assert.ok(res1.backupPath && fs.existsSync(res1.backupPath));
106
- const info = readLevelDatExperiments(file);
107
- assert.ok(info);
108
- assert.equal(info.hasBetaApis, true);
109
- assert.equal(info.experimentsEverUsed, true);
110
- assert.equal(info.savedWithToggledExperiments, true);
111
- const finalBuf = fs.readFileSync(file);
112
- const parsed = parseBedrockLevelDat(finalBuf);
113
- assert.equal(parsed.dataLength, finalBuf.length - 8);
114
- const res2 = await enableBetaApisInLevelDat(file);
115
- assert.equal(res2.success, true);
116
- assert.equal(res2.changed, false);
117
- } finally {
118
- fs.rmSync(tmpDir, { recursive: true, force: true });
119
- }
120
- });
121
- test("enableExperimentsInLevelDat: enables all known experiments and handles education root tag", async () => {
122
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-lvl-test-"));
123
- try {
124
- const file = path.join(tmpDir, "level.dat");
125
- fs.writeFileSync(file, buildMockLevelDat(true));
126
- const res = await enableExperimentsInLevelDat(file, "all");
127
- assert.equal(res.success, true);
128
- assert.equal(res.changed, true);
129
- const info = readLevelDatExperiments(file);
130
- assert.ok(info);
131
- assert.equal(info.hasBetaApis, true);
132
- assert.equal(info.experimentsEverUsed, true);
133
- assert.equal(info.savedWithToggledExperiments, true);
134
- for (const def of KNOWN_EXPERIMENTS) {
135
- assert.equal(info.knownExperiments[def.id], true, `Experiment ${def.id} should be enabled`);
136
- }
137
- const finalBuf = fs.readFileSync(file);
138
- const parsed = parseBedrockLevelDat(finalBuf);
139
- assert.equal(parsed.dataLength, finalBuf.length - 8);
140
- const eduRoot = parsed.rootChildren.get("educationFeaturesEnabled");
141
- assert.ok(eduRoot);
142
- assert.equal(eduRoot.value, 1);
143
- const res2 = await enableExperimentsInLevelDat(file, "all");
144
- assert.equal(res2.success, true);
145
- assert.equal(res2.changed, false);
146
- } finally {
147
- fs.rmSync(tmpDir, { recursive: true, force: true });
148
- }
149
- });
150
- test("enableExperimentsInLevelDat: enables specific subset from scratch (no experiments compound)", async () => {
151
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-lvl-test-"));
152
- try {
153
- const file = path.join(tmpDir, "level.dat");
154
- fs.writeFileSync(file, buildMockLevelDat(false));
155
- const res = await enableExperimentsInLevelDat(file, ["upcoming", "voxel", "edu"]);
156
- assert.equal(res.success, true);
157
- assert.equal(res.changed, true);
158
- const info = readLevelDatExperiments(file);
159
- assert.ok(info);
160
- assert.equal(info.knownExperiments.upcoming_creator_features, true);
161
- assert.equal(info.knownExperiments.voxel_shapes, true);
162
- assert.equal(info.knownExperiments.education, true);
163
- assert.equal(info.knownExperiments.gametest, false);
164
- assert.equal(info.knownExperiments.villager_trades_rebalance, false);
165
- const finalBuf = fs.readFileSync(file);
166
- const parsed = parseBedrockLevelDat(finalBuf);
167
- assert.equal(parsed.dataLength, finalBuf.length - 8);
168
- } finally {
169
- fs.rmSync(tmpDir, { recursive: true, force: true });
170
- }
171
- });
172
- //# sourceMappingURL=level-dat.test.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/level-dat.test.ts"],
4
- "sourcesContent": ["import assert from \"node:assert/strict\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport test from \"node:test\";\nimport {\n enableBetaApisInLevelDat,\n enableExperimentsInLevelDat,\n KNOWN_EXPERIMENTS,\n parseBedrockLevelDat,\n readLevelDatExperiments,\n resolveExperimentDefinition,\n TAG_BYTE,\n TAG_COMPOUND,\n TAG_END,\n} from \"./level-dat.js\";\n\nfunction buildMockLevelDat(includeExperiments: boolean, gametestVal?: number): Buffer {\n const rootName = \"\";\n const rootNameBuf = Buffer.from(rootName, \"utf8\");\n\n const childBuffers: Buffer[] = [];\n\n // Random sample tag\n const sampleName = Buffer.from(\"LevelName\", \"utf8\");\n const sampleVal = Buffer.from(\"TestWorld\", \"utf8\");\n const strTag = Buffer.alloc(1 + 2 + sampleName.length + 2 + sampleVal.length);\n strTag.writeUInt8(8, 0); // TAG_String\n strTag.writeUInt16LE(sampleName.length, 1);\n sampleName.copy(strTag, 3);\n strTag.writeUInt16LE(sampleVal.length, 3 + sampleName.length);\n sampleVal.copy(strTag, 5 + sampleName.length);\n childBuffers.push(strTag);\n\n if (includeExperiments) {\n const expName = Buffer.from(\"experiments\", \"utf8\");\n const expChildren: Buffer[] = [];\n\n // experiments_ever_used = 0\n const everUsedName = Buffer.from(\"experiments_ever_used\", \"utf8\");\n const b1 = Buffer.alloc(1 + 2 + everUsedName.length + 1);\n b1.writeUInt8(TAG_BYTE, 0);\n b1.writeUInt16LE(everUsedName.length, 1);\n everUsedName.copy(b1, 3);\n b1.writeInt8(0, 3 + everUsedName.length);\n expChildren.push(b1);\n\n if (gametestVal !== undefined) {\n const gtName = Buffer.from(\"gametest\", \"utf8\");\n const b2 = Buffer.alloc(1 + 2 + gtName.length + 1);\n b2.writeUInt8(TAG_BYTE, 0);\n b2.writeUInt16LE(gtName.length, 1);\n gtName.copy(b2, 3);\n b2.writeInt8(gametestVal, 3 + gtName.length);\n expChildren.push(b2);\n }\n\n expChildren.push(Buffer.from([TAG_END]));\n\n const expHeader = Buffer.alloc(1 + 2 + expName.length);\n expHeader.writeUInt8(TAG_COMPOUND, 0);\n expHeader.writeUInt16LE(expName.length, 1);\n expName.copy(expHeader, 3);\n\n childBuffers.push(Buffer.concat([expHeader, ...expChildren]));\n }\n\n childBuffers.push(Buffer.from([TAG_END]));\n\n const rootHeader = Buffer.alloc(1 + 2 + rootNameBuf.length);\n rootHeader.writeUInt8(TAG_COMPOUND, 0);\n rootHeader.writeUInt16LE(rootNameBuf.length, 1);\n rootNameBuf.copy(rootHeader, 3);\n\n const nbtBody = Buffer.concat([rootHeader, ...childBuffers]);\n\n const fileHeader = Buffer.alloc(8);\n fileHeader.writeUInt32LE(10, 0); // version 10\n fileHeader.writeUInt32LE(nbtBody.length, 4);\n\n return Buffer.concat([fileHeader, nbtBody]);\n}\n\ntest(\"readLevelDatExperiments: correctly reads experiments status\", () => {\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), \"sfmc-lvl-test-\"));\n try {\n const file = path.join(tmpDir, \"level.dat\");\n\n // 1. Without gametest\n fs.writeFileSync(file, buildMockLevelDat(true));\n const info1 = readLevelDatExperiments(file);\n assert.ok(info1);\n assert.equal(info1.hasBetaApis, false);\n assert.equal(info1.experimentsEverUsed, false);\n assert.equal(info1.knownExperiments.gametest, false);\n assert.equal(info1.knownExperiments.upcoming_creator_features, false);\n\n // 2. With gametest = 1\n fs.writeFileSync(file, buildMockLevelDat(true, 1));\n const info2 = readLevelDatExperiments(file);\n assert.ok(info2);\n assert.equal(info2.hasBetaApis, true);\n assert.equal(info2.knownExperiments.gametest, true);\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n }\n});\n\ntest(\"resolveExperimentDefinition: resolves aliases and canonical ids\", () => {\n assert.equal(resolveExperimentDefinition(\"upcoming\")?.id, \"upcoming_creator_features\");\n assert.equal(resolveExperimentDefinition(\"cameras\")?.id, \"experimental_creator_cameras\");\n assert.equal(resolveExperimentDefinition(\"voxel\")?.id, \"voxel_shapes\");\n assert.equal(resolveExperimentDefinition(\"trades\")?.id, \"villager_trades_rebalance\");\n assert.equal(resolveExperimentDefinition(\"drop3\")?.id, \"drop_3_2026\");\n assert.equal(resolveExperimentDefinition(\"wilderness_bound\")?.id, \"drop_3_2026\");\n assert.equal(resolveExperimentDefinition(\"edu\")?.id, \"education\");\n assert.equal(resolveExperimentDefinition(\"unknown_feature\"), null);\n});\n\ntest(\"enableBetaApisInLevelDat: enables gametest and updates header length\", async () => {\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), \"sfmc-lvl-test-\"));\n try {\n const file = path.join(tmpDir, \"level.dat\");\n fs.writeFileSync(file, buildMockLevelDat(true));\n\n const res1 = await enableBetaApisInLevelDat(file);\n assert.equal(res1.success, true);\n assert.equal(res1.changed, true);\n assert.ok(res1.backupPath && fs.existsSync(res1.backupPath));\n\n // Check after change\n const info = readLevelDatExperiments(file);\n assert.ok(info);\n assert.equal(info.hasBetaApis, true);\n assert.equal(info.experimentsEverUsed, true);\n assert.equal(info.savedWithToggledExperiments, true);\n\n // Verify header length matches buffer length\n const finalBuf = fs.readFileSync(file);\n const parsed = parseBedrockLevelDat(finalBuf);\n assert.equal(parsed.dataLength, finalBuf.length - 8);\n\n // Idempotency test: second run shouldn't change\n const res2 = await enableBetaApisInLevelDat(file);\n assert.equal(res2.success, true);\n assert.equal(res2.changed, false);\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n }\n});\n\ntest(\"enableExperimentsInLevelDat: enables all known experiments and handles education root tag\", async () => {\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), \"sfmc-lvl-test-\"));\n try {\n const file = path.join(tmpDir, \"level.dat\");\n fs.writeFileSync(file, buildMockLevelDat(true));\n\n const res = await enableExperimentsInLevelDat(file, \"all\");\n assert.equal(res.success, true);\n assert.equal(res.changed, true);\n\n const info = readLevelDatExperiments(file);\n assert.ok(info);\n assert.equal(info.hasBetaApis, true);\n assert.equal(info.experimentsEverUsed, true);\n assert.equal(info.savedWithToggledExperiments, true);\n\n // All known experiments must be true\n for (const def of KNOWN_EXPERIMENTS) {\n assert.equal(info.knownExperiments[def.id], true, `Experiment ${def.id} should be enabled`);\n }\n\n // Verify root educationFeaturesEnabled is 1\n const finalBuf = fs.readFileSync(file);\n const parsed = parseBedrockLevelDat(finalBuf);\n assert.equal(parsed.dataLength, finalBuf.length - 8);\n\n const eduRoot = parsed.rootChildren.get(\"educationFeaturesEnabled\");\n assert.ok(eduRoot);\n assert.equal(eduRoot.value, 1);\n\n // Second run should be idempotent\n const res2 = await enableExperimentsInLevelDat(file, \"all\");\n assert.equal(res2.success, true);\n assert.equal(res2.changed, false);\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n }\n});\n\ntest(\"enableExperimentsInLevelDat: enables specific subset from scratch (no experiments compound)\", async () => {\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), \"sfmc-lvl-test-\"));\n try {\n const file = path.join(tmpDir, \"level.dat\");\n fs.writeFileSync(file, buildMockLevelDat(false));\n\n const res = await enableExperimentsInLevelDat(file, [\"upcoming\", \"voxel\", \"edu\"]);\n assert.equal(res.success, true);\n assert.equal(res.changed, true);\n\n const info = readLevelDatExperiments(file);\n assert.ok(info);\n assert.equal(info.knownExperiments.upcoming_creator_features, true);\n assert.equal(info.knownExperiments.voxel_shapes, true);\n assert.equal(info.knownExperiments.education, true);\n assert.equal(info.knownExperiments.gametest, false);\n assert.equal(info.knownExperiments.villager_trades_rebalance, false);\n\n const finalBuf = fs.readFileSync(file);\n const parsed = parseBedrockLevelDat(finalBuf);\n assert.equal(parsed.dataLength, finalBuf.length - 8);\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n }\n});\n"],
5
- "mappings": "AAAA,OAAO,YAAY;AACnB,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,UAAU;AACjB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,kBAAkB,oBAA6B,aAA8B;AACpF,QAAM,WAAW;AACjB,QAAM,cAAc,OAAO,KAAK,UAAU,MAAM;AAEhD,QAAM,eAAyB,CAAC;AAGhC,QAAM,aAAa,OAAO,KAAK,aAAa,MAAM;AAClD,QAAM,YAAY,OAAO,KAAK,aAAa,MAAM;AACjD,QAAM,SAAS,OAAO,MAAM,IAAI,IAAI,WAAW,SAAS,IAAI,UAAU,MAAM;AAC5E,SAAO,WAAW,GAAG,CAAC;AACtB,SAAO,cAAc,WAAW,QAAQ,CAAC;AACzC,aAAW,KAAK,QAAQ,CAAC;AACzB,SAAO,cAAc,UAAU,QAAQ,IAAI,WAAW,MAAM;AAC5D,YAAU,KAAK,QAAQ,IAAI,WAAW,MAAM;AAC5C,eAAa,KAAK,MAAM;AAExB,MAAI,oBAAoB;AACtB,UAAM,UAAU,OAAO,KAAK,eAAe,MAAM;AACjD,UAAM,cAAwB,CAAC;AAG/B,UAAM,eAAe,OAAO,KAAK,yBAAyB,MAAM;AAChE,UAAM,KAAK,OAAO,MAAM,IAAI,IAAI,aAAa,SAAS,CAAC;AACvD,OAAG,WAAW,UAAU,CAAC;AACzB,OAAG,cAAc,aAAa,QAAQ,CAAC;AACvC,iBAAa,KAAK,IAAI,CAAC;AACvB,OAAG,UAAU,GAAG,IAAI,aAAa,MAAM;AACvC,gBAAY,KAAK,EAAE;AAEnB,QAAI,gBAAgB,QAAW;AAC7B,YAAM,SAAS,OAAO,KAAK,YAAY,MAAM;AAC7C,YAAM,KAAK,OAAO,MAAM,IAAI,IAAI,OAAO,SAAS,CAAC;AACjD,SAAG,WAAW,UAAU,CAAC;AACzB,SAAG,cAAc,OAAO,QAAQ,CAAC;AACjC,aAAO,KAAK,IAAI,CAAC;AACjB,SAAG,UAAU,aAAa,IAAI,OAAO,MAAM;AAC3C,kBAAY,KAAK,EAAE;AAAA,IACrB;AAEA,gBAAY,KAAK,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC;AAEvC,UAAM,YAAY,OAAO,MAAM,IAAI,IAAI,QAAQ,MAAM;AACrD,cAAU,WAAW,cAAc,CAAC;AACpC,cAAU,cAAc,QAAQ,QAAQ,CAAC;AACzC,YAAQ,KAAK,WAAW,CAAC;AAEzB,iBAAa,KAAK,OAAO,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC,CAAC;AAAA,EAC9D;AAEA,eAAa,KAAK,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC;AAExC,QAAM,aAAa,OAAO,MAAM,IAAI,IAAI,YAAY,MAAM;AAC1D,aAAW,WAAW,cAAc,CAAC;AACrC,aAAW,cAAc,YAAY,QAAQ,CAAC;AAC9C,cAAY,KAAK,YAAY,CAAC;AAE9B,QAAM,UAAU,OAAO,OAAO,CAAC,YAAY,GAAG,YAAY,CAAC;AAE3D,QAAM,aAAa,OAAO,MAAM,CAAC;AACjC,aAAW,cAAc,IAAI,CAAC;AAC9B,aAAW,cAAc,QAAQ,QAAQ,CAAC;AAE1C,SAAO,OAAO,OAAO,CAAC,YAAY,OAAO,CAAC;AAC5C;AAEA,KAAK,+DAA+D,MAAM;AACxE,QAAM,SAAS,GAAG,YAAY,KAAK,KAAK,GAAG,OAAO,GAAG,gBAAgB,CAAC;AACtE,MAAI;AACF,UAAM,OAAO,KAAK,KAAK,QAAQ,WAAW;AAG1C,OAAG,cAAc,MAAM,kBAAkB,IAAI,CAAC;AAC9C,UAAM,QAAQ,wBAAwB,IAAI;AAC1C,WAAO,GAAG,KAAK;AACf,WAAO,MAAM,MAAM,aAAa,KAAK;AACrC,WAAO,MAAM,MAAM,qBAAqB,KAAK;AAC7C,WAAO,MAAM,MAAM,iBAAiB,UAAU,KAAK;AACnD,WAAO,MAAM,MAAM,iBAAiB,2BAA2B,KAAK;AAGpE,OAAG,cAAc,MAAM,kBAAkB,MAAM,CAAC,CAAC;AACjD,UAAM,QAAQ,wBAAwB,IAAI;AAC1C,WAAO,GAAG,KAAK;AACf,WAAO,MAAM,MAAM,aAAa,IAAI;AACpC,WAAO,MAAM,MAAM,iBAAiB,UAAU,IAAI;AAAA,EACpD,UAAE;AACA,OAAG,OAAO,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EACpD;AACF,CAAC;AAED,KAAK,mEAAmE,MAAM;AAC5E,SAAO,MAAM,4BAA4B,UAAU,GAAG,IAAI,2BAA2B;AACrF,SAAO,MAAM,4BAA4B,SAAS,GAAG,IAAI,8BAA8B;AACvF,SAAO,MAAM,4BAA4B,OAAO,GAAG,IAAI,cAAc;AACrE,SAAO,MAAM,4BAA4B,QAAQ,GAAG,IAAI,2BAA2B;AACnF,SAAO,MAAM,4BAA4B,OAAO,GAAG,IAAI,aAAa;AACpE,SAAO,MAAM,4BAA4B,kBAAkB,GAAG,IAAI,aAAa;AAC/E,SAAO,MAAM,4BAA4B,KAAK,GAAG,IAAI,WAAW;AAChE,SAAO,MAAM,4BAA4B,iBAAiB,GAAG,IAAI;AACnE,CAAC;AAED,KAAK,wEAAwE,YAAY;AACvF,QAAM,SAAS,GAAG,YAAY,KAAK,KAAK,GAAG,OAAO,GAAG,gBAAgB,CAAC;AACtE,MAAI;AACF,UAAM,OAAO,KAAK,KAAK,QAAQ,WAAW;AAC1C,OAAG,cAAc,MAAM,kBAAkB,IAAI,CAAC;AAE9C,UAAM,OAAO,MAAM,yBAAyB,IAAI;AAChD,WAAO,MAAM,KAAK,SAAS,IAAI;AAC/B,WAAO,MAAM,KAAK,SAAS,IAAI;AAC/B,WAAO,GAAG,KAAK,cAAc,GAAG,WAAW,KAAK,UAAU,CAAC;AAG3D,UAAM,OAAO,wBAAwB,IAAI;AACzC,WAAO,GAAG,IAAI;AACd,WAAO,MAAM,KAAK,aAAa,IAAI;AACnC,WAAO,MAAM,KAAK,qBAAqB,IAAI;AAC3C,WAAO,MAAM,KAAK,6BAA6B,IAAI;AAGnD,UAAM,WAAW,GAAG,aAAa,IAAI;AACrC,UAAM,SAAS,qBAAqB,QAAQ;AAC5C,WAAO,MAAM,OAAO,YAAY,SAAS,SAAS,CAAC;AAGnD,UAAM,OAAO,MAAM,yBAAyB,IAAI;AAChD,WAAO,MAAM,KAAK,SAAS,IAAI;AAC/B,WAAO,MAAM,KAAK,SAAS,KAAK;AAAA,EAClC,UAAE;AACA,OAAG,OAAO,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EACpD;AACF,CAAC;AAED,KAAK,6FAA6F,YAAY;AAC5G,QAAM,SAAS,GAAG,YAAY,KAAK,KAAK,GAAG,OAAO,GAAG,gBAAgB,CAAC;AACtE,MAAI;AACF,UAAM,OAAO,KAAK,KAAK,QAAQ,WAAW;AAC1C,OAAG,cAAc,MAAM,kBAAkB,IAAI,CAAC;AAE9C,UAAM,MAAM,MAAM,4BAA4B,MAAM,KAAK;AACzD,WAAO,MAAM,IAAI,SAAS,IAAI;AAC9B,WAAO,MAAM,IAAI,SAAS,IAAI;AAE9B,UAAM,OAAO,wBAAwB,IAAI;AACzC,WAAO,GAAG,IAAI;AACd,WAAO,MAAM,KAAK,aAAa,IAAI;AACnC,WAAO,MAAM,KAAK,qBAAqB,IAAI;AAC3C,WAAO,MAAM,KAAK,6BAA6B,IAAI;AAGnD,eAAW,OAAO,mBAAmB;AACnC,aAAO,MAAM,KAAK,iBAAiB,IAAI,EAAE,GAAG,MAAM,cAAc,IAAI,EAAE,oBAAoB;AAAA,IAC5F;AAGA,UAAM,WAAW,GAAG,aAAa,IAAI;AACrC,UAAM,SAAS,qBAAqB,QAAQ;AAC5C,WAAO,MAAM,OAAO,YAAY,SAAS,SAAS,CAAC;AAEnD,UAAM,UAAU,OAAO,aAAa,IAAI,0BAA0B;AAClE,WAAO,GAAG,OAAO;AACjB,WAAO,MAAM,QAAQ,OAAO,CAAC;AAG7B,UAAM,OAAO,MAAM,4BAA4B,MAAM,KAAK;AAC1D,WAAO,MAAM,KAAK,SAAS,IAAI;AAC/B,WAAO,MAAM,KAAK,SAAS,KAAK;AAAA,EAClC,UAAE;AACA,OAAG,OAAO,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EACpD;AACF,CAAC;AAED,KAAK,+FAA+F,YAAY;AAC9G,QAAM,SAAS,GAAG,YAAY,KAAK,KAAK,GAAG,OAAO,GAAG,gBAAgB,CAAC;AACtE,MAAI;AACF,UAAM,OAAO,KAAK,KAAK,QAAQ,WAAW;AAC1C,OAAG,cAAc,MAAM,kBAAkB,KAAK,CAAC;AAE/C,UAAM,MAAM,MAAM,4BAA4B,MAAM,CAAC,YAAY,SAAS,KAAK,CAAC;AAChF,WAAO,MAAM,IAAI,SAAS,IAAI;AAC9B,WAAO,MAAM,IAAI,SAAS,IAAI;AAE9B,UAAM,OAAO,wBAAwB,IAAI;AACzC,WAAO,GAAG,IAAI;AACd,WAAO,MAAM,KAAK,iBAAiB,2BAA2B,IAAI;AAClE,WAAO,MAAM,KAAK,iBAAiB,cAAc,IAAI;AACrD,WAAO,MAAM,KAAK,iBAAiB,WAAW,IAAI;AAClD,WAAO,MAAM,KAAK,iBAAiB,UAAU,KAAK;AAClD,WAAO,MAAM,KAAK,iBAAiB,2BAA2B,KAAK;AAEnE,UAAM,WAAW,GAAG,aAAa,IAAI;AACrC,UAAM,SAAS,qBAAqB,QAAQ;AAC5C,WAAO,MAAM,OAAO,YAAY,SAAS,SAAS,CAAC;AAAA,EACrD,UAAE;AACA,OAAG,OAAO,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EACpD;AACF,CAAC;",
6
- "names": []
7
- }