@fraczak/k 6.10.4 → 6.10.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/Examples/arithmetics.k +222 -110
- package/Examples/poly.k +14 -5
- package/browser/repl-browser.mjs +40 -22
- package/browser/shims/fs.mjs +2 -0
- package/browser/vfs-data.mjs +8 -11
- package/core.k +6 -78
- package/harness/suites/poly.mjs +1 -1
- package/package.json +1 -1
- package/repl-codecs.mjs +6 -3
- package/scripts/build-repl-html.mjs +6 -6
- package/scripts/perf-poly.mjs +1 -1
- package/tests/test-arithmetics.mjs +96 -0
- package/tests/test-repl-html.mjs +92 -1
package/core.k
CHANGED
|
@@ -4,15 +4,12 @@
|
|
|
4
4
|
-- 1. $bits — the canonical binary number / bit-path type
|
|
5
5
|
-- 2. $string — unicode strings (used for labels)
|
|
6
6
|
-- 3. $pattern — the type of pattern graphs
|
|
7
|
-
-- 4. Basic $bits arithmetic: succ, plus, times
|
|
8
7
|
|
|
9
8
|
---------------------------------------------------------------
|
|
10
9
|
-- §1. Bits
|
|
11
10
|
---------------------------------------------------------------
|
|
12
11
|
|
|
13
12
|
$ bit = < {} 0, {} 1 >;
|
|
14
|
-
b0 = {}|0 $bit;
|
|
15
|
-
b1 = {}|1 $bit;
|
|
16
13
|
|
|
17
14
|
$ bits = < {} _, bits 0, bits 1 >;
|
|
18
15
|
|
|
@@ -21,78 +18,8 @@ _ = {}|_;
|
|
|
21
18
|
0 = _ |0;
|
|
22
19
|
1 = _ |1;
|
|
23
20
|
|
|
24
|
-
inv = {() x, _ r} _inv;
|
|
25
|
-
_inv = ${bits x, bits r} < {.x /0 x, .r |0 r} _inv, {.x /1 x, .r |1 r} _inv, .r >;
|
|
26
|
-
|
|
27
|
-
concat = ${bits x, bits y} {.x inv x, .y y} concat_;
|
|
28
|
-
concat_ = ${bits x, bits y}
|
|
29
|
-
<
|
|
30
|
-
{.x /0 x, .y |0 y} concat_,
|
|
31
|
-
{.x /1 x, .y |1 y} concat_,
|
|
32
|
-
.y
|
|
33
|
-
>;
|
|
34
|
-
|
|
35
|
-
---------------------------------------------------------------
|
|
36
|
-
-- §2. Bits arithmetic
|
|
37
|
-
---------------------------------------------------------------
|
|
38
|
-
|
|
39
|
-
-- successor (increment by 1)
|
|
40
|
-
succ = {inv x, _ y} _inc;
|
|
41
|
-
|
|
42
|
-
_inc = ?{X x, X y}
|
|
43
|
-
<
|
|
44
|
-
{.x </0,/_|_> x, .y |1 y} concat_,
|
|
45
|
-
{.x /1 x, .y |0 y} _inc
|
|
46
|
-
> ?X;
|
|
47
|
-
|
|
48
|
-
-- constants
|
|
49
|
-
2 = 1 succ;
|
|
50
|
-
3 = 2 succ;
|
|
51
|
-
4 = 3 succ;
|
|
52
|
-
5 = 4 succ;
|
|
53
|
-
6 = 5 succ;
|
|
54
|
-
7 = 6 succ;
|
|
55
|
-
8 = 7 succ;
|
|
56
|
-
9 = 8 succ;
|
|
57
|
-
10 = 9 succ;
|
|
58
|
-
|
|
59
|
-
-- addition on $bits (unsigned, LSB-first with carry)
|
|
60
|
-
plus = ${bits x, bits y} {.x inv x, .y inv y, b0 c} _plus;
|
|
61
|
-
_plus = ${bits x, bits y, bit c}
|
|
62
|
-
<
|
|
63
|
-
{.x /_ if, .y /_ and, <.c/0 0, 1> then} .then,
|
|
64
|
-
{.x /_ if, .c/0 and, .y then} .then,
|
|
65
|
-
{.x y, .y /_|_ x, .c c} _plus,
|
|
66
|
-
{<.c/0 0, 1> x, .y y, .x /_ b0 c} _plus,
|
|
67
|
-
{.x /0 x, .y /0 y, .c/0 b0 c} _plus |0,
|
|
68
|
-
{.x /0 x, .y /0 y, .c/1 b0 c} _plus |1,
|
|
69
|
-
{.x /0 x, .y /1 y, .c/0 b0 c} _plus |1,
|
|
70
|
-
{.x /0 x, .y /1 y, .c/1 b1 c} _plus |0,
|
|
71
|
-
{.x /1 x, .y /0 y, .c/0 b0 c} _plus |1,
|
|
72
|
-
{.x /1 x, .y /0 y, .c/1 b1 c} _plus |0,
|
|
73
|
-
{.x /1 x, .y /1 y, .c/0 b1 c} _plus |0,
|
|
74
|
-
{.x /1 x, .y /1 y, .c/1 b1 c} _plus |1
|
|
75
|
-
>;
|
|
76
|
-
|
|
77
|
-
-- multiplication on $bits (unsigned, shift-and-add)
|
|
78
|
-
times = ${bits x, bits y} {0 result, .x inv _x, .y y} _times;
|
|
79
|
-
_times = ${bits result, bits _x, bits y}
|
|
80
|
-
<
|
|
81
|
-
{ .y /_ if, .result then } .then,
|
|
82
|
-
{
|
|
83
|
-
.result |0 result,
|
|
84
|
-
._x _x,
|
|
85
|
-
.y /0 y
|
|
86
|
-
} _times,
|
|
87
|
-
{
|
|
88
|
-
{._x x, .result |0 y, b0 c} _plus result,
|
|
89
|
-
._x _x,
|
|
90
|
-
.y /1 y
|
|
91
|
-
} _times
|
|
92
|
-
>;
|
|
93
|
-
|
|
94
21
|
---------------------------------------------------------------
|
|
95
|
-
-- §
|
|
22
|
+
-- §2. Unicode and String
|
|
96
23
|
---------------------------------------------------------------
|
|
97
24
|
|
|
98
25
|
-- 8-bit octet used as a building block for UTF-8 payload pieces.
|
|
@@ -108,8 +35,9 @@ $ ascii = { bit 6, bit 5, bit 4, bit 3, bit 2, bit 1, bit 0 };
|
|
|
108
35
|
-- Plane 0 is the Basic Multilingual Plane (BMP): U+0000..U+FFFF.
|
|
109
36
|
$ u0080_07FF = { bit 10, bit 9, bit 8, bit 7, bit 6, bit 5, bit 4, bit 3, bit 2, bit 1, bit 0 };
|
|
110
37
|
|
|
111
|
-
-- High-byte classes for common BMP ranges, excluding surrogate
|
|
112
|
-
--
|
|
38
|
+
-- High-byte classes for common BMP ranges, excluding surrogate and
|
|
39
|
+
-- private-use partitions.
|
|
40
|
+
-- BMP (Basic Multilingual Plane) is Plane 0: first 65,536 code points.
|
|
113
41
|
$ bmp_hi_common = <
|
|
114
42
|
{ bit 2, bit 1, bit 0 } h08_0F,
|
|
115
43
|
{ bit 6, bit 5, bit 4, bit 3, bit 2, bit 1, bit 0 } h10_7F,
|
|
@@ -158,12 +86,12 @@ $ unicode = <
|
|
|
158
86
|
$ string = < {} nil, { unicode car, string cdr } cons >;
|
|
159
87
|
|
|
160
88
|
---------------------------------------------------------------
|
|
161
|
-
-- §
|
|
89
|
+
-- §3. Pattern
|
|
162
90
|
---------------------------------------------------------------
|
|
163
91
|
|
|
164
92
|
-- Edge: a labeled edge in a pattern graph.
|
|
165
93
|
-- 'label' is the field name or tag name.
|
|
166
|
-
-- 'target' is
|
|
94
|
+
-- 'target' is index (as $bits) of the target node in the pattern list.
|
|
167
95
|
|
|
168
96
|
$ edge = { string label, bits target };
|
|
169
97
|
|
package/harness/suites/poly.mjs
CHANGED
|
@@ -48,7 +48,7 @@ export async function createPolySuite({ listLength = 40, cacheDir = null } = {})
|
|
|
48
48
|
return lines.join("\n") + "\n";
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const neededExports = ["0", "int", "inc", "dec", "nat", "zero_int?"
|
|
51
|
+
const neededExports = ["0", "int", "inc", "dec", "nat", "zero_int?"];
|
|
52
52
|
const exportPreamble = buildExportPreamble(neededExports, [arithmeticsLib]);
|
|
53
53
|
const polyLib = loadLibrary(compileLibrary(exportPreamble + polySource, {
|
|
54
54
|
source: polyPath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraczak/k",
|
|
3
|
-
"version": "6.10.
|
|
3
|
+
"version": "6.10.6",
|
|
4
4
|
"description": "k — a language for composing first-order partial functions over algebraic data types (products and tagged unions); includes runtime, type derivation, REPL, and codec toolchain",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"exports": {
|
package/repl-codecs.mjs
CHANGED
|
@@ -107,8 +107,10 @@ function registerCodec(state, rawCodec, filePath) {
|
|
|
107
107
|
|
|
108
108
|
function unregisterCodec(state, codecName) {
|
|
109
109
|
const store = codecStore(state);
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
const baseName = path.basename(codecName);
|
|
111
|
+
const key = store[codecName] ? codecName : (store[baseName] ? baseName : null);
|
|
112
|
+
if (!key) return 0;
|
|
113
|
+
delete store[key];
|
|
112
114
|
return 1;
|
|
113
115
|
}
|
|
114
116
|
|
|
@@ -129,7 +131,8 @@ function resolveCodec(state, codecName, capability = null) {
|
|
|
129
131
|
throw new Error("Codec name is required");
|
|
130
132
|
}
|
|
131
133
|
const store = codecStore(state);
|
|
132
|
-
const
|
|
134
|
+
const baseName = path.basename(codecName);
|
|
135
|
+
const codec = store[codecName] || store[baseName];
|
|
133
136
|
if (!codec) {
|
|
134
137
|
throw new Error(`Codec '${codecName}' is not loaded`);
|
|
135
138
|
}
|
|
@@ -78,8 +78,8 @@ if (fs.existsSync(coreKPath)) {
|
|
|
78
78
|
vfsFiles["core.k"] = fs.readFileSync(coreKPath, "utf8");
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
// Add verified k-code files: arithmetics.k, ieee.k, poly.k
|
|
82
|
-
const verifiedExamples = ["arithmetics.k", "ieee.k", "poly.k"];
|
|
81
|
+
// Add verified k-code files: arithmetics.k, ieee.k, poly.k, karatsuba-mult.k
|
|
82
|
+
const verifiedExamples = ["arithmetics.k", "ieee.k", "poly.k", "karatsuba-mult.k"];
|
|
83
83
|
const examplesDir = path.join(projectRoot, "Examples");
|
|
84
84
|
for (const filename of verifiedExamples) {
|
|
85
85
|
const fullPath = path.join(examplesDir, filename);
|
|
@@ -131,7 +131,6 @@ for (const filename of verifiedCodecs) {
|
|
|
131
131
|
});
|
|
132
132
|
const bundledContent = buildRes.outputFiles[0].text;
|
|
133
133
|
vfsFiles[filename] = bundledContent;
|
|
134
|
-
vfsFiles[`codecs/${filename}`] = bundledContent;
|
|
135
134
|
}
|
|
136
135
|
}
|
|
137
136
|
|
|
@@ -1663,14 +1662,15 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1663
1662
|
<option value="arithmetics.k">arithmetics.k</option>
|
|
1664
1663
|
<option value="ieee.k">ieee.k</option>
|
|
1665
1664
|
<option value="poly.k">poly.k</option>
|
|
1665
|
+
<option value="karatsuba-mult.k">karatsuba-mult.k</option>
|
|
1666
1666
|
</optgroup>
|
|
1667
1667
|
<optgroup label="Quick Expressions">
|
|
1668
1668
|
<option value="expr:{} | ok">{} | ok</option>
|
|
1669
1669
|
<option value="expr:()">()</option>
|
|
1670
1670
|
<option value="expr:$ bool = < {} true, {} false >; not = $ bool < / true | false, {} | true > $ bool; {} | true not">not (boolean relation with fallback)</option>
|
|
1671
|
-
<option value="expr:{10 x, 5 y} plus">{10 x, 5 y} plus (bits)</option>
|
|
1672
1671
|
<option value="expr:1 succ">1 succ</option>
|
|
1673
1672
|
<option value="expr:{10 int x, 5 int y} plus">{10 int x, 5 int y} plus (int)</option>
|
|
1673
|
+
<option value="expr:{10 int x, 5 int y} karatsuba">{10 int x, 5 int y} karatsuba</option>
|
|
1674
1674
|
<option value="expr::types">:types (list types)</option>
|
|
1675
1675
|
<option value="expr::rels">:rels (list relations)</option>
|
|
1676
1676
|
<option value="expr::engine">:engine (show engine)</option>
|
|
@@ -1693,8 +1693,8 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1693
1693
|
</div>
|
|
1694
1694
|
|
|
1695
1695
|
<!-- File Upload -->
|
|
1696
|
-
<label class="btn btn-secondary btn-file" title="Upload local
|
|
1697
|
-
<input type="file" id="file-upload" accept=".k,.klib,.wat,.ko" style="display:none;">
|
|
1696
|
+
<label class="btn btn-secondary btn-file" title="Upload local file into VFS">
|
|
1697
|
+
<input type="file" id="file-upload" accept=".k,.klib,.wat,.ko,.mjs,.js,.json" style="display:none;">
|
|
1698
1698
|
<span>📁 Upload</span>
|
|
1699
1699
|
</label>
|
|
1700
1700
|
|
package/scripts/perf-poly.mjs
CHANGED
|
@@ -193,7 +193,7 @@ function buildExportPreamble(exports, libraries) {
|
|
|
193
193
|
return lines.join("\n") + "\n";
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
const neededExports = ["0", "int", "inc", "dec", "nat", "zero_int?"
|
|
196
|
+
const neededExports = ["0", "int", "inc", "dec", "nat", "zero_int?"];
|
|
197
197
|
const exportPreamble = buildExportPreamble(neededExports, [arithmeticsLib]);
|
|
198
198
|
const polyLib = loadLibrary(compileLibrary(exportPreamble + polySource, {
|
|
199
199
|
source: polyPath,
|
|
@@ -100,10 +100,13 @@ try {
|
|
|
100
100
|
assertOk(resDec, "2 int dec dec zero_int? should succeed");
|
|
101
101
|
|
|
102
102
|
const divTestCases = [
|
|
103
|
+
{ x: "0", yAlias: "7", yVal: 7n },
|
|
104
|
+
{ x: "3", yAlias: "7", yVal: 7n },
|
|
103
105
|
{ x: "4", yAlias: "1", yVal: 1n },
|
|
104
106
|
{ x: "10", yAlias: "2", yVal: 2n },
|
|
105
107
|
{ x: "12", yAlias: "3", yVal: 3n },
|
|
106
108
|
{ x: "11", yAlias: "3", yVal: 3n },
|
|
109
|
+
{ x: "12", yAlias: "12", yVal: 12n },
|
|
107
110
|
{ x: "100", yAlias: "7", yVal: 7n },
|
|
108
111
|
{ x: "-100", yAlias: "7", yVal: 7n },
|
|
109
112
|
{ x: "1000", yAlias: "10", yVal: 10n }
|
|
@@ -143,6 +146,99 @@ try {
|
|
|
143
146
|
assert.equal(remOut, expectedRem, `div(${x}, ${yAlias}) remainder should match BigInt oracle`);
|
|
144
147
|
}
|
|
145
148
|
|
|
149
|
+
// Regression tests for EQ
|
|
150
|
+
const dummyWire = parseIntWire("0");
|
|
151
|
+
const resEqTrue = run(node, [
|
|
152
|
+
"k.mjs",
|
|
153
|
+
"--lib", libPath,
|
|
154
|
+
"--export", "EQ:EQ",
|
|
155
|
+
"--export", "int:int",
|
|
156
|
+
"--export", "2:two",
|
|
157
|
+
"{two x, two y} EQ .x int"
|
|
158
|
+
], { input: dummyWire });
|
|
159
|
+
assertOk(resEqTrue, "{2, 2} EQ should succeed");
|
|
160
|
+
|
|
161
|
+
const resEqFalse = run(node, [
|
|
162
|
+
"k.mjs",
|
|
163
|
+
"--lib", libPath,
|
|
164
|
+
"--export", "EQ:EQ",
|
|
165
|
+
"--export", "int:int",
|
|
166
|
+
"--export", "2:two",
|
|
167
|
+
"--export", "3:three",
|
|
168
|
+
"{two x, three y} EQ .x int"
|
|
169
|
+
], { input: dummyWire });
|
|
170
|
+
assert.notEqual(resEqFalse.status, 0, "{2, 3} EQ should fail");
|
|
171
|
+
|
|
172
|
+
// Regression tests for pos?
|
|
173
|
+
const resPosTrue = run(node, [
|
|
174
|
+
"k.mjs",
|
|
175
|
+
"--lib", libPath,
|
|
176
|
+
"--export", "pos?:pos?",
|
|
177
|
+
"--export", "int:int",
|
|
178
|
+
"--export", "2:two",
|
|
179
|
+
"two int pos?"
|
|
180
|
+
], { input: dummyWire });
|
|
181
|
+
assertOk(resPosTrue, "2 int pos? should succeed");
|
|
182
|
+
|
|
183
|
+
const resPosZero = run(node, [
|
|
184
|
+
"k.mjs",
|
|
185
|
+
"--lib", libPath,
|
|
186
|
+
"--export", "pos?:pos?",
|
|
187
|
+
"--export", "int:int",
|
|
188
|
+
"--export", "0:zero",
|
|
189
|
+
"zero int pos?"
|
|
190
|
+
], { input: dummyWire });
|
|
191
|
+
assert.notEqual(resPosZero.status, 0, "0 int pos? should fail");
|
|
192
|
+
|
|
193
|
+
// Regression tests for mod, gcd, lcm
|
|
194
|
+
const resMod = run(node, [
|
|
195
|
+
"k.mjs",
|
|
196
|
+
"--lib", libPath,
|
|
197
|
+
"--export", "mod:mod",
|
|
198
|
+
"--export", "int:int",
|
|
199
|
+
"--export", "10:ten",
|
|
200
|
+
"--export", "3:three",
|
|
201
|
+
"{ten x, three y} mod int"
|
|
202
|
+
], { input: dummyWire });
|
|
203
|
+
assertOk(resMod, "10 % 3 mod");
|
|
204
|
+
assert.equal(printIntWire(resMod.stdout, "mod"), "1");
|
|
205
|
+
|
|
206
|
+
const resGcd = run(node, [
|
|
207
|
+
"k.mjs",
|
|
208
|
+
"--lib", libPath,
|
|
209
|
+
"--export", "gcd:gcd",
|
|
210
|
+
"--export", "int:int",
|
|
211
|
+
"--export", "12:twelve",
|
|
212
|
+
"--export", "8:eight",
|
|
213
|
+
"{twelve x, eight y} gcd int"
|
|
214
|
+
], { input: dummyWire });
|
|
215
|
+
assertOk(resGcd, "gcd(12, 8)");
|
|
216
|
+
assert.equal(printIntWire(resGcd.stdout, "gcd"), "4");
|
|
217
|
+
|
|
218
|
+
const resGcdZero = run(node, [
|
|
219
|
+
"k.mjs",
|
|
220
|
+
"--lib", libPath,
|
|
221
|
+
"--export", "gcd:gcd",
|
|
222
|
+
"--export", "int:int",
|
|
223
|
+
"--export", "4:four",
|
|
224
|
+
"--export", "0b:zero_b",
|
|
225
|
+
"{four x, zero_b y} gcd int"
|
|
226
|
+
], { input: dummyWire });
|
|
227
|
+
assertOk(resGcdZero, "gcd(4, 0b)");
|
|
228
|
+
assert.equal(printIntWire(resGcdZero.stdout, "gcd 0b"), "4");
|
|
229
|
+
|
|
230
|
+
const resLcm = run(node, [
|
|
231
|
+
"k.mjs",
|
|
232
|
+
"--lib", libPath,
|
|
233
|
+
"--export", "lcm:lcm",
|
|
234
|
+
"--export", "int:int",
|
|
235
|
+
"--export", "4:four",
|
|
236
|
+
"--export", "6:six",
|
|
237
|
+
"{four x, six y} lcm int"
|
|
238
|
+
], { input: dummyWire });
|
|
239
|
+
assertOk(resLcm, "lcm(4, 6)");
|
|
240
|
+
assert.equal(printIntWire(resLcm.stdout, "lcm"), "12");
|
|
241
|
+
|
|
146
242
|
console.log("OK");
|
|
147
243
|
} finally {
|
|
148
244
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
package/tests/test-repl-html.mjs
CHANGED
|
@@ -144,6 +144,7 @@ if (chromiumBin) {
|
|
|
144
144
|
"ieee.mjs",
|
|
145
145
|
"int.mjs",
|
|
146
146
|
"json.mjs",
|
|
147
|
+
"karatsuba-mult.k",
|
|
147
148
|
"poly.k",
|
|
148
149
|
"unit.mjs",
|
|
149
150
|
"utf8.mjs"
|
|
@@ -158,6 +159,22 @@ if (chromiumBin) {
|
|
|
158
159
|
})()`);
|
|
159
160
|
assert(loadOut.includes("loaded arithmetics.k"), `Expected loaded arithmetics.k, got: ${loadOut}`);
|
|
160
161
|
|
|
162
|
+
// Test :load karatsuba-mult.k
|
|
163
|
+
const loadKaratsubaOut = await evaluateAsync(`(async () => {
|
|
164
|
+
await window.kRepl.executeCommand(":load karatsuba-mult.k");
|
|
165
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
166
|
+
return lines[lines.length - 1];
|
|
167
|
+
})()`);
|
|
168
|
+
assert(loadKaratsubaOut.includes("loaded karatsuba-mult.k"), `Expected loaded karatsuba-mult.k, got: ${loadKaratsubaOut}`);
|
|
169
|
+
|
|
170
|
+
// Test evaluating {1 int x, 1 int y} karatsuba
|
|
171
|
+
const karatsubaVal = await evaluateAsync(`(async () => {
|
|
172
|
+
await window.kRepl.executeCommand("{1 int x, 1 int y} karatsuba");
|
|
173
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
174
|
+
return lines[lines.length - 1];
|
|
175
|
+
})()`);
|
|
176
|
+
assert(karatsubaVal.includes("{}|_|1|+"), `Expected {}|_|1|+ in output, got: ${karatsubaVal}`);
|
|
177
|
+
|
|
161
178
|
// Test evaluating 10
|
|
162
179
|
const valOut = await evaluateAsync(`(async () => {
|
|
163
180
|
await window.kRepl.executeCommand("10");
|
|
@@ -618,7 +635,81 @@ if (chromiumBin) {
|
|
|
618
635
|
assert.strictEqual(engineCheck.jsEngine, "js");
|
|
619
636
|
assert.strictEqual(engineCheck.badgeJsText, "js-in-process");
|
|
620
637
|
assert.strictEqual(engineCheck.wasmEngine, "wasm");
|
|
621
|
-
|
|
638
|
+
// 7. Test uploading a file (adds to VFS without auto-loading)
|
|
639
|
+
const uploadCheck = await evaluateAsync(`(async () => {
|
|
640
|
+
const codecCode = \`export default {
|
|
641
|
+
name: "custom.mjs",
|
|
642
|
+
target: "(...)",
|
|
643
|
+
format: "custom",
|
|
644
|
+
parse: (raw) => raw,
|
|
645
|
+
print: (v) => "CUSTOM_VAL"
|
|
646
|
+
};\`;
|
|
647
|
+
const file = new File(
|
|
648
|
+
[codecCode],
|
|
649
|
+
"custom.mjs",
|
|
650
|
+
{ type: "text/javascript" }
|
|
651
|
+
);
|
|
652
|
+
await window.kRepl.handleFileUpload(file);
|
|
653
|
+
|
|
654
|
+
const vfsContent = window.kRepl.getVfsFile("custom.mjs");
|
|
655
|
+
const inVfsList = window.kRepl.getAllVfsFiles().includes("custom.mjs");
|
|
656
|
+
|
|
657
|
+
// Verify system message showed upload confirmation
|
|
658
|
+
const systemMessages = Array.from(
|
|
659
|
+
document.querySelectorAll(".system-msg")
|
|
660
|
+
).map(el => el.textContent);
|
|
661
|
+
const hasUploadedMsg = systemMessages.some(
|
|
662
|
+
m => m.includes("Uploaded custom.mjs into VFS")
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
// Verify it was NOT auto-loaded (:codecs does not list it)
|
|
666
|
+
await window.kRepl.executeCommand(":codecs");
|
|
667
|
+
const linesBefore = Array.from(
|
|
668
|
+
document.querySelectorAll(".entry-line")
|
|
669
|
+
).map(el => el.textContent);
|
|
670
|
+
const codecsBefore = linesBefore[linesBefore.length - 1];
|
|
671
|
+
|
|
672
|
+
// Explicitly load it to prove it can be loaded as a codec
|
|
673
|
+
await window.kRepl.executeCommand(":codec load custom.mjs");
|
|
674
|
+
const linesAfter = Array.from(
|
|
675
|
+
document.querySelectorAll(".entry-line")
|
|
676
|
+
).map(el => el.textContent);
|
|
677
|
+
const codecsAfter = linesAfter[linesAfter.length - 1];
|
|
678
|
+
|
|
679
|
+
return {
|
|
680
|
+
hasVfsContent: typeof vfsContent === "string" &&
|
|
681
|
+
vfsContent.includes("custom.mjs"),
|
|
682
|
+
inVfsList,
|
|
683
|
+
hasUploadedMsg,
|
|
684
|
+
codecsBefore,
|
|
685
|
+
codecsAfter
|
|
686
|
+
};
|
|
687
|
+
})()`);
|
|
688
|
+
assert.strictEqual(
|
|
689
|
+
uploadCheck.hasVfsContent,
|
|
690
|
+
true,
|
|
691
|
+
"Uploaded file must be present in VFS"
|
|
692
|
+
);
|
|
693
|
+
assert.strictEqual(
|
|
694
|
+
uploadCheck.inVfsList,
|
|
695
|
+
true,
|
|
696
|
+
"Uploaded file must be in getAllVfsFiles()"
|
|
697
|
+
);
|
|
698
|
+
assert.strictEqual(
|
|
699
|
+
uploadCheck.hasUploadedMsg,
|
|
700
|
+
true,
|
|
701
|
+
"Upload confirmation message must be displayed"
|
|
702
|
+
);
|
|
703
|
+
assert.strictEqual(
|
|
704
|
+
uploadCheck.codecsBefore.includes("custom.mjs"),
|
|
705
|
+
false,
|
|
706
|
+
"Upload must NOT auto-load the file"
|
|
707
|
+
);
|
|
708
|
+
assert.strictEqual(
|
|
709
|
+
uploadCheck.codecsAfter.includes("custom.mjs"),
|
|
710
|
+
true,
|
|
711
|
+
"Explicit :codec load of uploaded file must succeed"
|
|
712
|
+
);
|
|
622
713
|
|
|
623
714
|
ws.close();
|
|
624
715
|
console.log(" Browser execution verified successfully!");
|