xbintsc 0.5.37 → 0.5.41

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 (166) hide show
  1. package/dist/src/cli/help.d.ts +1 -1
  2. package/dist/src/codegen/generator/module.js +23 -7
  3. package/dist/src/codegen/generator/module.js.map +1 -1
  4. package/dist/src/codegen/generator/state.js +1 -0
  5. package/dist/src/codegen/generator/state.js.map +1 -1
  6. package/dist/src/codegen/generator/tables.js +1 -0
  7. package/dist/src/codegen/generator/tables.js.map +1 -1
  8. package/dist/src/driver/bundler/deferred.d.ts +63 -0
  9. package/dist/src/driver/bundler/deferred.js +279 -0
  10. package/dist/src/driver/bundler/deferred.js.map +1 -0
  11. package/dist/src/driver/bundler/merge.js +214 -2
  12. package/dist/src/driver/bundler/merge.js.map +1 -1
  13. package/dist/src/driver/runtime-objects.d.ts +1 -1
  14. package/dist/src/driver/runtime-objects.js +1 -0
  15. package/dist/src/driver/runtime-objects.js.map +1 -1
  16. package/dist/src/driver/version.d.ts +1 -1
  17. package/dist/src/driver/version.js +1 -1
  18. package/dist/src/extensions/gui/html.js +98 -15
  19. package/dist/src/extensions/gui/html.js.map +1 -1
  20. package/dist/src/extensions/gui/index.js +1 -0
  21. package/dist/src/extensions/gui/index.js.map +1 -1
  22. package/dist/src/extensions/gui/three.d.ts +4 -3
  23. package/dist/src/extensions/gui/three.js +4 -3
  24. package/dist/src/extensions/gui/three.js.map +1 -1
  25. package/dist/tests/codegen/closures.test.js +7 -0
  26. package/dist/tests/codegen/closures.test.js.map +1 -1
  27. package/dist/tests/e2e/compile-objects-classes.test.js +31 -0
  28. package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
  29. package/dist/tests/e2e/gui-canvas-state.test.d.ts +9 -0
  30. package/dist/tests/e2e/gui-canvas-state.test.js +297 -0
  31. package/dist/tests/e2e/gui-canvas-state.test.js.map +1 -0
  32. package/dist/tests/e2e/gui-draw-perf.test.d.ts +22 -0
  33. package/dist/tests/e2e/gui-draw-perf.test.js +405 -0
  34. package/dist/tests/e2e/gui-draw-perf.test.js.map +1 -0
  35. package/dist/tests/e2e/gui-perf.test.d.ts +23 -0
  36. package/dist/tests/e2e/gui-perf.test.js +283 -0
  37. package/dist/tests/e2e/gui-perf.test.js.map +1 -0
  38. package/dist/tests/e2e/gui-script-import-order.test.d.ts +10 -0
  39. package/dist/tests/e2e/gui-script-import-order.test.js +169 -0
  40. package/dist/tests/e2e/gui-script-import-order.test.js.map +1 -0
  41. package/dist/tests/e2e/gui-webgl.test.js +4 -4
  42. package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
  43. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.d.ts +8 -0
  44. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js +336 -0
  45. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js.map +1 -0
  46. package/dist/tests/e2e/gui-webgl2-shadow.test.d.ts +10 -0
  47. package/dist/tests/e2e/gui-webgl2-shadow.test.js +108 -0
  48. package/dist/tests/e2e/gui-webgl2-shadow.test.js.map +1 -0
  49. package/dist/tests/e2e/url.test.d.ts +6 -0
  50. package/dist/tests/e2e/url.test.js +114 -0
  51. package/dist/tests/e2e/url.test.js.map +1 -0
  52. package/dist/tests/extensions/gui.test.js +45 -10
  53. package/dist/tests/extensions/gui.test.js.map +1 -1
  54. package/doc/gui/api.md +28 -2
  55. package/doc/gui/canvas.md +110 -27
  56. package/doc/gui/known-issues.md +32 -10
  57. package/doc/gui/rendering.md +77 -0
  58. package/doc/gui/runtime.md +18 -3
  59. package/doc/gui/webgl.md +58 -29
  60. package/doc/gui-scripts.md +19 -19
  61. package/doc/gui.md +43 -9
  62. package/doc/implemented/fetch.md +5 -0
  63. package/doc/implemented/modules.md +13 -0
  64. package/doc/implemented/stdlib.md +17 -0
  65. package/doc/unimplemented/deviations.md +1 -0
  66. package/doc/zh-CN/gui/api.md +25 -2
  67. package/doc/zh-CN/gui/canvas.md +95 -26
  68. package/doc/zh-CN/gui/known-issues.md +21 -4
  69. package/doc/zh-CN/gui/rendering.md +59 -0
  70. package/doc/zh-CN/gui/runtime.md +13 -3
  71. package/doc/zh-CN/gui/webgl.md +30 -8
  72. package/doc/zh-CN/gui-scripts.md +14 -13
  73. package/doc/zh-CN/gui.md +32 -8
  74. package/doc/zh-CN/implemented/fetch.md +4 -0
  75. package/doc/zh-CN/implemented/modules.md +10 -0
  76. package/doc/zh-CN/implemented/stdlib.md +15 -0
  77. package/doc/zh-CN/unimplemented/deviations.md +1 -0
  78. package/package.json +7 -7
  79. package/runtime/ext_gui/browser.cpp +3 -1
  80. package/runtime/ext_gui/canvas.cpp +135 -89
  81. package/runtime/ext_gui/canvas.h +116 -0
  82. package/runtime/ext_gui/canvas_clip.cpp +143 -0
  83. package/runtime/ext_gui/canvas_composite.cpp +241 -0
  84. package/runtime/ext_gui/canvas_coverage.cpp +241 -0
  85. package/runtime/ext_gui/canvas_gradient.cpp +41 -4
  86. package/runtime/ext_gui/canvas_image_data.cpp +4 -0
  87. package/runtime/ext_gui/canvas_internal.h +34 -9
  88. package/runtime/ext_gui/canvas_raster.cpp +207 -178
  89. package/runtime/ext_gui/canvas_shadow.cpp +161 -0
  90. package/runtime/ext_gui/canvas_stroke.cpp +37 -9
  91. package/runtime/ext_gui/canvas_style.cpp +59 -0
  92. package/runtime/ext_gui/canvas_text.cpp +140 -0
  93. package/runtime/ext_gui/css.h +7 -0
  94. package/runtime/ext_gui/css_sheet.cpp +22 -0
  95. package/runtime/ext_gui/document.cpp +25 -1
  96. package/runtime/ext_gui/document.h +8 -0
  97. package/runtime/ext_gui/dom_api_canvas.cpp +16 -1
  98. package/runtime/ext_gui/dom_api_canvas_image.cpp +9 -0
  99. package/runtime/ext_gui/dom_api_canvas_state.cpp +217 -0
  100. package/runtime/ext_gui/dom_api_internal.h +36 -37
  101. package/runtime/ext_gui/dom_api_offscreen.cpp +213 -0
  102. package/runtime/ext_gui/dom_api_proto.cpp +18 -0
  103. package/runtime/ext_gui/dom_api_webgl.cpp +29 -9
  104. package/runtime/ext_gui/dom_api_webgl2.cpp +33 -1
  105. package/runtime/ext_gui/dom_api_webgl_internal.h +58 -0
  106. package/runtime/ext_gui/dom_api_webgl_source.cpp +18 -0
  107. package/runtime/ext_gui/gradient_values.cpp +12 -1
  108. package/runtime/ext_gui/gui.cpp +5 -0
  109. package/runtime/ext_gui/gui_canvas.cpp +4 -0
  110. package/runtime/ext_gui/gui_engine.h +32 -0
  111. package/runtime/ext_gui/gui_events.cpp +182 -58
  112. package/runtime/ext_gui/gui_media.cpp +3 -0
  113. package/runtime/ext_gui/image.cpp +69 -0
  114. package/runtime/ext_gui/image_bitmap_browser.cpp +11 -0
  115. package/runtime/ext_gui/layout.cpp +4 -0
  116. package/runtime/ext_gui/paint.cpp +19 -0
  117. package/runtime/ext_gui/paint_background.cpp +18 -1
  118. package/runtime/ext_gui/perf.cpp +171 -0
  119. package/runtime/ext_gui/perf.h +127 -0
  120. package/runtime/ext_gui/renderer.cpp +109 -41
  121. package/runtime/ext_gui/renderer.h +33 -9
  122. package/runtime/ext_gui/renderer_image.cpp +135 -65
  123. package/runtime/ext_gui/renderer_internal.h +15 -3
  124. package/runtime/ext_gui/renderer_text.cpp +24 -12
  125. package/runtime/ext_gui/script.cpp +31 -0
  126. package/runtime/ext_gui/style.cpp +101 -30
  127. package/runtime/ext_gui/style.h +6 -3
  128. package/runtime/ext_gui/style_print.cpp +7 -1
  129. package/runtime/ext_gui/text.cpp +23 -4
  130. package/runtime/ext_gui/text.h +29 -0
  131. package/runtime/ext_gui/text_cache.cpp +118 -0
  132. package/runtime/ext_gui/text_glyph_cache.cpp +100 -0
  133. package/runtime/ext_gui/webgl.h +48 -146
  134. package/runtime/ext_gui/webgl_blit.cpp +12 -1
  135. package/runtime/ext_gui/webgl_blit_depth.cpp +270 -0
  136. package/runtime/ext_gui/webgl_buffer.cpp +59 -1
  137. package/runtime/ext_gui/webgl_buffer_objects.cpp +106 -9
  138. package/runtime/ext_gui/webgl_clear.cpp +3 -0
  139. package/runtime/ext_gui/webgl_context.cpp +29 -3
  140. package/runtime/ext_gui/webgl_draw.cpp +75 -270
  141. package/runtime/ext_gui/webgl_draw_buffers.cpp +1 -0
  142. package/runtime/ext_gui/webgl_draw_setup.cpp +321 -0
  143. package/runtime/ext_gui/webgl_draw_state.h +130 -0
  144. package/runtime/ext_gui/webgl_glsl.h +3 -0
  145. package/runtime/ext_gui/webgl_glsl_scan.cpp +3 -8
  146. package/runtime/ext_gui/webgl_glsl_unroll.cpp +283 -0
  147. package/runtime/ext_gui/webgl_msl.cpp +4 -0
  148. package/runtime/ext_gui/webgl_objects.h +148 -0
  149. package/runtime/ext_gui/webgl_pipeline.cpp +8 -8
  150. package/runtime/ext_gui/webgl_program_internal.h +35 -0
  151. package/runtime/ext_gui/webgl_shader.cpp +34 -36
  152. package/runtime/ext_gui/webgl_webgl2.cpp +36 -0
  153. package/runtime/ext_gui/window.cpp +56 -1
  154. package/runtime/rt.h +3 -0
  155. package/runtime/xt_fetch/async.inc +60 -0
  156. package/runtime/xt_url_object.c +463 -0
  157. package/src/codegen/generator/module.ts +22 -7
  158. package/src/codegen/generator/state.ts +1 -0
  159. package/src/codegen/generator/tables.ts +1 -0
  160. package/src/driver/bundler/deferred.ts +351 -0
  161. package/src/driver/bundler/merge.ts +219 -2
  162. package/src/driver/runtime-objects.ts +1 -0
  163. package/src/driver/version.ts +1 -1
  164. package/src/extensions/gui/html.ts +100 -17
  165. package/src/extensions/gui/index.ts +1 -0
  166. package/src/extensions/gui/three.ts +4 -3
@@ -0,0 +1,336 @@
1
+ /**
2
+ * GUI e2e: comparative shadow samplers and `gl_FragDepth`.
3
+ *
4
+ * `sampler2DShadow` must reach SDL_GPU as a comparison sampler, and a fragment
5
+ * shader that writes `gl_FragDepth` must actually update the depth attachment.
6
+ * Both are checked by rendering and reading pixels back.
7
+ */
8
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
9
+ import { createGuiHarness, guiAvailable } from "./gui-helpers.js";
10
+ const harness = createGuiHarness();
11
+ const PAGE = `
12
+ <html><head><title>webgl2-shadow-sampler</title></head>
13
+ <body style="margin:0">
14
+ <canvas id="c" width="64" height="64"></canvas>
15
+ </body></html>`;
16
+ describe.skipIf(!guiAvailable)("gui extension — shadow samplers and gl_FragDepth", () => {
17
+ beforeAll(() => harness.setup());
18
+ afterAll(() => harness.cleanup());
19
+ it("samples a depth texture through sampler2DShadow", () => {
20
+ const { value } = harness.compileAndRun("webgl2-shadow-sampler", `
21
+ import { createWindow } from "gui";
22
+ import page from "./page.html";
23
+
24
+ const win = createWindow({ title: "shadow-sampler", width: 320, height: 240 });
25
+ const label = (name: string, value: any): void => console.log(name + "=" + value);
26
+
27
+ win.on("ready", () => {
28
+ const canvas: any = document.getElementById("c");
29
+ const gl: any = canvas.getContext("webgl2");
30
+ if (gl == null) {
31
+ console.log("webgl2=false");
32
+ win.close();
33
+ return;
34
+ }
35
+ const px = new Uint8Array(4);
36
+ const probe = (): string => {
37
+ gl.readPixels(32, 32, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, px);
38
+ return px[0] + "," + px[1] + "," + px[2] + "," + px[3];
39
+ };
40
+
41
+ const vs: any = gl.createShader(gl.VERTEX_SHADER);
42
+ gl.shaderSource(vs, "#version 300 es\\n" +
43
+ "in vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0.0, 1.0); }");
44
+ gl.compileShader(vs);
45
+
46
+ const fs: any = gl.createShader(gl.FRAGMENT_SHADER);
47
+ gl.shaderSource(fs, "#version 300 es\\n" +
48
+ "precision highp float;\\n" +
49
+ "uniform highp sampler2DShadow u_depth;\\n" +
50
+ "uniform highp float u_ref;\\n" +
51
+ "out highp vec4 fragColor;\\n" +
52
+ "void main() {\\n" +
53
+ " float v = texture(u_depth, vec3(0.5, 0.5, u_ref));\\n" +
54
+ " fragColor = vec4(v, v, v, 1.0);\\n" +
55
+ "}");
56
+ gl.compileShader(fs);
57
+ const fs_ok = gl.getShaderParameter(fs, gl.COMPILE_STATUS);
58
+ label("fs", fs_ok === true ? "true" : "false:" + gl.getShaderInfoLog(fs));
59
+
60
+ const program: any = gl.createProgram();
61
+ gl.attachShader(program, vs);
62
+ gl.attachShader(program, fs);
63
+ gl.linkProgram(program);
64
+ label("link", gl.getProgramParameter(program, gl.LINK_STATUS) === true);
65
+
66
+ const quad = gl.createBuffer();
67
+ gl.bindBuffer(gl.ARRAY_BUFFER, quad);
68
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
69
+ gl.useProgram(program);
70
+ const pos: any = gl.getAttribLocation(program, "a_pos");
71
+ gl.enableVertexAttribArray(pos);
72
+ gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 8, 0);
73
+
74
+ // A depth texture cleared to 0.5, attached to a 16x16 framebuffer.
75
+ const depthTex: any = gl.createTexture();
76
+ gl.bindTexture(gl.TEXTURE_2D, depthTex);
77
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT24, 16, 16, 0,
78
+ gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
79
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
80
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
81
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE);
82
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_FUNC, gl.LEQUAL);
83
+ label("compare-mode", gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_MODE));
84
+ label("compare-func", gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_FUNC));
85
+
86
+ const colorTex: any = gl.createTexture();
87
+ gl.bindTexture(gl.TEXTURE_2D, colorTex);
88
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
89
+
90
+ const fb: any = gl.createFramebuffer();
91
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
92
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTex, 0);
93
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0);
94
+ label("framebuffer", gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE);
95
+ gl.viewport(0, 0, 16, 16);
96
+ gl.clearColor(0, 0, 0, 1);
97
+ gl.clearDepth(0.5);
98
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
99
+
100
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
101
+ gl.viewport(0, 0, 64, 64);
102
+ gl.activeTexture(gl.TEXTURE0);
103
+ gl.bindTexture(gl.TEXTURE_2D, depthTex);
104
+ gl.uniform1i(gl.getUniformLocation(program, "u_depth"), 0);
105
+ const ref: any = gl.getUniformLocation(program, "u_ref");
106
+
107
+ gl.clearColor(0, 0, 0, 1);
108
+ gl.clear(gl.COLOR_BUFFER_BIT);
109
+ gl.uniform1f(ref, 0.25);
110
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
111
+ label("passes", probe());
112
+
113
+ gl.clear(gl.COLOR_BUFFER_BIT);
114
+ gl.uniform1f(ref, 0.75);
115
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
116
+ label("fails", probe());
117
+ label("error", gl.getError());
118
+ win.close();
119
+ });
120
+ win.loadHTML(page);
121
+ `, { files: { "page.html": PAGE } });
122
+ expect(value("fs")).toBe("true");
123
+ expect(value("link")).toBe("true");
124
+ expect(value("compare-mode")).toBe("34894"); // COMPARE_REF_TO_TEXTURE, 0x884e
125
+ expect(value("compare-func")).toBe("515"); // LEQUAL, 0x0203
126
+ expect(value("framebuffer")).toBe("true");
127
+ // ref 0.25 <= stored 0.5 -> pass (white); ref 0.75 > 0.5 -> fail (black).
128
+ expect(value("passes")).toBe("255,255,255,255");
129
+ expect(value("fails")).toBe("0,0,0,255");
130
+ expect(value("error")).toBe("0");
131
+ });
132
+ it("writes gl_FragDepth into the depth attachment", () => {
133
+ const { value } = harness.compileAndRun("webgl2-frag-depth", `
134
+ import { createWindow } from "gui";
135
+ import page from "./page.html";
136
+
137
+ const win = createWindow({ title: "frag-depth", width: 320, height: 240 });
138
+ const label = (name: string, value: any): void => console.log(name + "=" + value);
139
+
140
+ win.on("ready", () => {
141
+ const canvas: any = document.getElementById("c");
142
+ const gl: any = canvas.getContext("webgl2");
143
+ if (gl == null) {
144
+ console.log("webgl2=false");
145
+ win.close();
146
+ return;
147
+ }
148
+ const px = new Uint8Array(4);
149
+ const probe = (): string => {
150
+ gl.readPixels(32, 32, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, px);
151
+ return px[0] + "," + px[1] + "," + px[2] + "," + px[3];
152
+ };
153
+
154
+ const vs: any = gl.createShader(gl.VERTEX_SHADER);
155
+ gl.shaderSource(vs, "#version 300 es\\n" +
156
+ "in vec2 a_pos; uniform float u_z;" +
157
+ "void main() { gl_Position = vec4(a_pos, u_z, 1.0); }");
158
+ gl.compileShader(vs);
159
+
160
+ const depth_fs: any = gl.createShader(gl.FRAGMENT_SHADER);
161
+ gl.shaderSource(depth_fs, "#version 300 es\\n" +
162
+ "precision highp float; out vec4 fragColor;\\n" +
163
+ "void main() { gl_FragDepth = 0.25; fragColor = vec4(0.0, 0.0, 0.0, 1.0); }");
164
+ gl.compileShader(depth_fs);
165
+ label("depth-fs", gl.getShaderParameter(depth_fs, gl.COMPILE_STATUS) === true);
166
+
167
+ const red_fs: any = gl.createShader(gl.FRAGMENT_SHADER);
168
+ gl.shaderSource(red_fs, "#version 300 es\\n" +
169
+ "precision highp float; out vec4 fragColor;\\n" +
170
+ "void main() { fragColor = vec4(1.0, 0.0, 0.0, 1.0); }");
171
+ gl.compileShader(red_fs);
172
+
173
+ const depth_program: any = gl.createProgram();
174
+ gl.attachShader(depth_program, vs);
175
+ gl.attachShader(depth_program, depth_fs);
176
+ gl.linkProgram(depth_program);
177
+ label("depth-link", gl.getProgramParameter(depth_program, gl.LINK_STATUS) === true);
178
+
179
+ const red_program: any = gl.createProgram();
180
+ gl.attachShader(red_program, vs);
181
+ gl.attachShader(red_program, red_fs);
182
+ gl.linkProgram(red_program);
183
+ label("red-link", gl.getProgramParameter(red_program, gl.LINK_STATUS) === true);
184
+
185
+ const quad = gl.createBuffer();
186
+ gl.bindBuffer(gl.ARRAY_BUFFER, quad);
187
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
188
+
189
+ gl.viewport(0, 0, 64, 64);
190
+ gl.clearColor(0, 0, 0, 1);
191
+ gl.clearDepth(1.0);
192
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
193
+
194
+ gl.enable(gl.DEPTH_TEST);
195
+ gl.depthFunc(gl.ALWAYS);
196
+ gl.depthMask(true);
197
+ gl.useProgram(depth_program);
198
+ let pos: any = gl.getAttribLocation(depth_program, "a_pos");
199
+ gl.enableVertexAttribArray(pos);
200
+ gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 8, 0);
201
+ gl.uniform1f(gl.getUniformLocation(depth_program, "u_z"), 0.0);
202
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
203
+
204
+ // z = 0.5 is farther than the 0.25 the first pass wrote, so LESS fails.
205
+ gl.depthFunc(gl.LESS);
206
+ gl.depthMask(false);
207
+ gl.useProgram(red_program);
208
+ pos = gl.getAttribLocation(red_program, "a_pos");
209
+ gl.enableVertexAttribArray(pos);
210
+ gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 8, 0);
211
+ gl.uniform1f(gl.getUniformLocation(red_program, "u_z"), 0.5);
212
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
213
+ label("occluded", probe());
214
+
215
+ gl.depthMask(true);
216
+ gl.disable(gl.DEPTH_TEST);
217
+ label("error", gl.getError());
218
+ win.close();
219
+ });
220
+ win.loadHTML(page);
221
+ `, { files: { "page.html": PAGE } });
222
+ expect(value("depth-fs")).toBe("true");
223
+ expect(value("depth-link")).toBe("true");
224
+ expect(value("red-link")).toBe("true");
225
+ // If gl_FragDepth were ignored the depth stayed 1.0 and the red quad would
226
+ // have painted over the black first pass.
227
+ expect(value("occluded")).toBe("0,0,0,255");
228
+ expect(value("error")).toBe("0");
229
+ });
230
+ it("copies a depth attachment with blitFramebuffer", () => {
231
+ const { value } = harness.compileAndRun("webgl2-depth-blit", `
232
+ import { createWindow } from "gui";
233
+ import page from "./page.html";
234
+
235
+ const win = createWindow({ title: "depth-blit", width: 320, height: 240 });
236
+ const label = (name: string, value: any): void => console.log(name + "=" + value);
237
+
238
+ win.on("ready", () => {
239
+ const canvas: any = document.getElementById("c");
240
+ const gl: any = canvas.getContext("webgl2");
241
+ if (gl == null) {
242
+ console.log("webgl2=false");
243
+ win.close();
244
+ return;
245
+ }
246
+ const px = new Uint8Array(4);
247
+ const probe = (x: number, y: number): string => {
248
+ gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, px);
249
+ return px[0] + "," + px[1] + "," + px[2] + "," + px[3];
250
+ };
251
+ const make_program = (vs_source: string, fs_source: string): any => {
252
+ const vs: any = gl.createShader(gl.VERTEX_SHADER);
253
+ gl.shaderSource(vs, vs_source);
254
+ gl.compileShader(vs);
255
+ const fs: any = gl.createShader(gl.FRAGMENT_SHADER);
256
+ gl.shaderSource(fs, fs_source);
257
+ gl.compileShader(fs);
258
+ const program: any = gl.createProgram();
259
+ gl.attachShader(program, vs);
260
+ gl.attachShader(program, fs);
261
+ gl.linkProgram(program);
262
+ return program;
263
+ };
264
+ const show = make_program(
265
+ "#version 300 es\\nin vec2 a_pos; out vec2 v_uv;" +
266
+ "void main() { v_uv = a_pos * 0.5 + 0.5;" +
267
+ " gl_Position = vec4(a_pos, 0.0, 1.0); }",
268
+ "#version 300 es\\nprecision highp float; in vec2 v_uv;" +
269
+ "uniform sampler2D u_tex; out vec4 fragColor;" +
270
+ "void main() { fragColor = vec4(vec3(texture(u_tex, v_uv).r), 1.0); }");
271
+ label("show-link", gl.getProgramParameter(show, gl.LINK_STATUS) === true);
272
+
273
+ const make_target = (depth_value: number): any[] => {
274
+ const depth: any = gl.createTexture();
275
+ gl.bindTexture(gl.TEXTURE_2D, depth);
276
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT24, 16, 16, 0,
277
+ gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
278
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
279
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
280
+ const color: any = gl.createTexture();
281
+ gl.bindTexture(gl.TEXTURE_2D, color);
282
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
283
+ const fb: any = gl.createFramebuffer();
284
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
285
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, color, 0);
286
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depth, 0);
287
+ gl.viewport(0, 0, 16, 16);
288
+ gl.clearColor(0, 0, 0, 1);
289
+ gl.clearDepth(depth_value);
290
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
291
+ return [fb, color, depth];
292
+ };
293
+ const source = make_target(0.25);
294
+ const destination = make_target(1.0);
295
+ label("complete",
296
+ gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE);
297
+
298
+ // Copy only the left half of the source into the right half of the
299
+ // destination; the destination's left half must keep depth 1.0.
300
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, source[0]);
301
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, destination[0]);
302
+ gl.blitFramebuffer(0, 0, 8, 16, 8, 0, 16, 16, gl.DEPTH_BUFFER_BIT, gl.NEAREST);
303
+ label("blit-error", gl.getError());
304
+
305
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
306
+ gl.viewport(0, 0, 64, 64);
307
+ gl.useProgram(show);
308
+ gl.uniform1i(gl.getUniformLocation(show, "u_tex"), 0);
309
+ gl.activeTexture(gl.TEXTURE0);
310
+ gl.bindTexture(gl.TEXTURE_2D, destination[2]);
311
+ gl.clearColor(0, 0, 0, 1);
312
+ gl.clear(gl.COLOR_BUFFER_BIT);
313
+ const quad = gl.createBuffer();
314
+ gl.bindBuffer(gl.ARRAY_BUFFER, quad);
315
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
316
+ const pos: any = gl.getAttribLocation(show, "a_pos");
317
+ gl.enableVertexAttribArray(pos);
318
+ gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 8, 0);
319
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
320
+ label("untouched", probe(16, 32));
321
+ label("copied", probe(48, 32));
322
+ win.close();
323
+ });
324
+ win.loadHTML(page);
325
+ `, { files: { "page.html": PAGE } });
326
+ expect(value("show-link")).toBe("true");
327
+ expect(value("complete")).toBe("true");
328
+ expect(value("blit-error")).toBe("0");
329
+ // 1.0 stays white; the copied 0.25 depth reads back near 64.
330
+ expect(value("untouched")).toBe("255,255,255,255");
331
+ const copied = value("copied").split(",").map(Number);
332
+ expect(copied[0] ?? 0).toBeGreaterThan(55);
333
+ expect(copied[0] ?? 0).toBeLessThan(75);
334
+ });
335
+ });
336
+ //# sourceMappingURL=gui-webgl2-shadow-sampler.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gui-webgl2-shadow-sampler.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-webgl2-shadow-sampler.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;AAEnC,MAAM,IAAI,GAAG;;;;eAIE,CAAC;AAEhB,QAAQ,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,kDAAkD,EAAE,GAAG,EAAE;IACtF,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAElC,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,uBAAuB,EACvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqGC,EACD,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,CACjC,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,iCAAiC;QAC/E,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAI,iBAAiB;QAC/D,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,0EAA0E;QAC1E,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,mBAAmB,EACnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwFC,EACD,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,CACjC,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvC,2EAA2E;QAC3E,0CAA0C;QAC1C,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,mBAAmB,EACnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8FC,EACD,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,CACjC,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,6DAA6D;QAC7D,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * GUI e2e: WebGL 2 identity and three.js-style sampler arrays.
3
+ *
4
+ * A browser reports a WebGL 2 context as `WebGL2RenderingContext` (and *not* as
5
+ * `WebGLRenderingContext`), which is how three.js decides whether to use its
6
+ * WebGL 2 path. Its shadow-map chunks also index sampler arrays with a loop
7
+ * variable; Vulkan GLSL only accepts a constant index, so the rewriter has to
8
+ * expand the pragma-marked loop before compiling.
9
+ */
10
+ export {};
@@ -0,0 +1,108 @@
1
+ /**
2
+ * GUI e2e: WebGL 2 identity and three.js-style sampler arrays.
3
+ *
4
+ * A browser reports a WebGL 2 context as `WebGL2RenderingContext` (and *not* as
5
+ * `WebGLRenderingContext`), which is how three.js decides whether to use its
6
+ * WebGL 2 path. Its shadow-map chunks also index sampler arrays with a loop
7
+ * variable; Vulkan GLSL only accepts a constant index, so the rewriter has to
8
+ * expand the pragma-marked loop before compiling.
9
+ */
10
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
11
+ import { createGuiHarness, guiAvailable } from "./gui-helpers.js";
12
+ const harness = createGuiHarness();
13
+ const PAGE = `
14
+ <html><head><title>webgl2-shadow</title></head>
15
+ <body style="margin:0">
16
+ <canvas id="c" width="64" height="64"></canvas>
17
+ <canvas id="c1" width="32" height="32"></canvas>
18
+ </body></html>`;
19
+ describe.skipIf(!guiAvailable)("gui extension — WebGL 2 identity and sampler arrays", () => {
20
+ beforeAll(() => harness.setup());
21
+ afterAll(() => harness.cleanup());
22
+ it("reports the WebGL 2 constructor/prototype and unrolls shadow loops", () => {
23
+ const { value } = harness.compileAndRun("webgl2-shadow", `
24
+ import { createWindow } from "gui";
25
+ import page from "./page.html";
26
+
27
+ const win = createWindow({ title: "webgl2-shadow", width: 320, height: 240 });
28
+ const label = (name: string, value: any): void => console.log(name + "=" + value);
29
+
30
+ win.on("ready", () => {
31
+ const canvas: any = document.getElementById("c");
32
+ const gl: any = canvas.getContext("webgl2");
33
+ label("webgl2", gl != null);
34
+ if (gl == null) {
35
+ win.close();
36
+ return;
37
+ }
38
+ label("constructor", gl.constructor.name);
39
+ label("instanceof-webgl2", gl instanceof WebGL2RenderingContext);
40
+ label("instanceof-webgl1", gl instanceof WebGLRenderingContext);
41
+ label("constructor-after-global", gl.constructor.name);
42
+ label("canvas", gl.canvas === canvas);
43
+ label("drawing-buffer", gl.drawingBufferWidth + "x" + gl.drawingBufferHeight);
44
+ label("shared-prototype",
45
+ gl.constructor.prototype === WebGLRenderingContext.prototype);
46
+
47
+ const canvas1: any = document.getElementById("c1");
48
+ const gl1: any = canvas1.getContext("webgl");
49
+ label("webgl1", gl1 != null);
50
+ if (gl1 != null) {
51
+ label("constructor1", gl1.constructor.name);
52
+ label("gl1-instanceof-webgl2", gl1 instanceof WebGL2RenderingContext);
53
+ label("gl1-instanceof-webgl1", gl1 instanceof WebGLRenderingContext);
54
+ }
55
+
56
+ const vs: any = gl.createShader(gl.VERTEX_SHADER);
57
+ gl.shaderSource(vs, "#version 300 es\\n" +
58
+ "in vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0.0, 1.0); }");
59
+ gl.compileShader(vs);
60
+ label("vs", gl.getShaderParameter(vs, gl.COMPILE_STATUS));
61
+
62
+ const fs: any = gl.createShader(gl.FRAGMENT_SHADER);
63
+ gl.shaderSource(fs, "#version 300 es\\n" +
64
+ "precision highp float;\\n" +
65
+ "uniform sampler2D u_shadowMap[2];\\n" +
66
+ "out highp vec4 fragColor;\\n" +
67
+ "void main() {\\n" +
68
+ " float shadow = 0.0;\\n" +
69
+ " #pragma unroll_loop_start\\n" +
70
+ " for ( int i = 0; i < 2; i ++ ) {\\n" +
71
+ " #if ( UNROLLED_LOOP_INDEX < 2 )\\n" +
72
+ " shadow += texture( u_shadowMap[ i ], vec2( 0.5 ) ).r;\\n" +
73
+ " #endif\\n" +
74
+ " }\\n" +
75
+ " #pragma unroll_loop_end\\n" +
76
+ " fragColor = vec4( shadow );\\n" +
77
+ "}");
78
+ gl.compileShader(fs);
79
+ const ok = gl.getShaderParameter(fs, gl.COMPILE_STATUS);
80
+ label("fs", ok === true ? "true" : "false:" + gl.getShaderInfoLog(fs));
81
+
82
+ const program: any = gl.createProgram();
83
+ gl.attachShader(program, vs);
84
+ gl.attachShader(program, fs);
85
+ gl.linkProgram(program);
86
+ label("link", gl.getProgramParameter(program, gl.LINK_STATUS));
87
+ win.close();
88
+ });
89
+ win.loadHTML(page);
90
+ `, { files: { "page.html": PAGE } });
91
+ expect(value("webgl2")).toBe("true");
92
+ expect(value("constructor")).toBe("WebGL2RenderingContext");
93
+ expect(value("instanceof-webgl2")).toBe("true");
94
+ expect(value("instanceof-webgl1")).toBe("false");
95
+ expect(value("constructor-after-global")).toBe("WebGL2RenderingContext");
96
+ expect(value("shared-prototype")).toBe("false");
97
+ expect(value("canvas")).toBe("true");
98
+ expect(value("drawing-buffer")).toBe("64x64");
99
+ expect(value("webgl1")).toBe("true");
100
+ expect(value("constructor1")).toBe("WebGLRenderingContext");
101
+ expect(value("gl1-instanceof-webgl2")).toBe("false");
102
+ expect(value("gl1-instanceof-webgl1")).toBe("true");
103
+ expect(value("vs")).toBe("true");
104
+ expect(value("fs")).toBe("true");
105
+ expect(value("link")).toBe("true");
106
+ });
107
+ });
108
+ //# sourceMappingURL=gui-webgl2-shadow.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gui-webgl2-shadow.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-webgl2-shadow.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;AAEnC,MAAM,IAAI,GAAG;;;;;eAKE,CAAC;AAEhB,QAAQ,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,qDAAqD,EAAE,GAAG,EAAE;IACzF,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAElC,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,eAAe,EACf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmEC,EACD,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,CACjC,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QAC5D,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACjD,MAAM,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QACzE,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAC5D,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * URL: relative-reference resolution and the components a compiled program
3
+ * reads. A reference with no scheme is a filesystem path and defaults to the
4
+ * `file:` protocol (resolved against the working directory).
5
+ */
6
+ export {};
@@ -0,0 +1,114 @@
1
+ /**
2
+ * URL: relative-reference resolution and the components a compiled program
3
+ * reads. A reference with no scheme is a filesystem path and defaults to the
4
+ * `file:` protocol (resolved against the working directory).
5
+ */
6
+ import { join } from "node:path";
7
+ import { tmpdir } from "node:os";
8
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
9
+ import { afterAll, expect, it } from "vitest";
10
+ import { describeE2E } from "./harness.js";
11
+ const workdir = mkdtempSync(join(tmpdir(), "xbintsc-url-"));
12
+ afterAll(() => rmSync(workdir, { recursive: true, force: true }));
13
+ describeE2E("URL", (harness) => {
14
+ it("resolves relative references and exposes the components", () => {
15
+ const out = harness.runProgram(`
16
+ const base = new URL("https://example.com/a/b/index.html?x=1#h");
17
+ console.log(base.href);
18
+ console.log(new URL("../img/pic.png", base).href);
19
+ console.log(new URL("/root", base).href);
20
+ console.log(new URL("//cdn.example.com/x", base).href);
21
+ console.log(new URL("./x", "https://example.com/a/b/").href);
22
+ console.log(base.protocol, base.host, base.hostname, base.port);
23
+ console.log(base.pathname, base.search, base.hash);
24
+ console.log(base.toString());
25
+
26
+ const file = new URL("file:///D:/proj/build/app.exe");
27
+ console.log(file.origin, file.protocol, new URL("../../audio/clip.mp3", file).href);
28
+ `);
29
+ expect(out).toBe([
30
+ "https://example.com/a/b/index.html?x=1#h",
31
+ "https://example.com/a/img/pic.png",
32
+ "https://example.com/root",
33
+ "https://cdn.example.com/x",
34
+ "https://example.com/a/b/x",
35
+ "https: example.com example.com ",
36
+ "/a/b/index.html ?x=1 #h",
37
+ "https://example.com/a/b/index.html?x=1#h",
38
+ "null file: file:///D:/audio/clip.mp3",
39
+ ].join("\n"));
40
+ });
41
+ it("defaults a scheme-less reference to file://", () => {
42
+ const out = harness.runProgram(`
43
+ const url = new URL("./assets/model.glb?rev=2#top");
44
+ console.log(url.protocol);
45
+ console.log(url.origin);
46
+ console.log(url.href.startsWith("file:///"));
47
+ console.log(url.pathname.endsWith("/assets/model.glb"));
48
+ console.log(url.search, url.searchParams.get("rev"));
49
+ console.log(url.hash);
50
+ console.log(new URL("/tmp/shared.txt").href.startsWith("file:///"));
51
+ console.log(new URL("nested/file.txt").pathname.endsWith("/nested/file.txt"));
52
+ `);
53
+ expect(out).toBe([
54
+ "file:",
55
+ "null",
56
+ "true",
57
+ "true",
58
+ "?rev=2 2",
59
+ "#top",
60
+ "true",
61
+ "true",
62
+ ].join("\n"));
63
+ });
64
+ it("canonicalizes the Windows spellings of a file: URL", () => {
65
+ const windowsForm = JSON.stringify("file://C:\\proj\\build\\app.exe");
66
+ const backslashForm = JSON.stringify("file://D:\\assets\\model.glb?rev=2#top");
67
+ const out = harness.runProgram(`
68
+ const drive = new URL(${windowsForm});
69
+ console.log(drive.href);
70
+ console.log(drive.pathname, JSON.stringify(drive.host), drive.origin);
71
+ const backslash = new URL(${backslashForm});
72
+ console.log(backslash.href);
73
+ console.log(backslash.pathname, backslash.search, backslash.hash);
74
+ `);
75
+ expect(out).toBe([
76
+ "file:///C:/proj/build/app.exe",
77
+ "/C:/proj/build/app.exe \"\" null",
78
+ "file:///D:/assets/model.glb?rev=2#top",
79
+ "/D:/assets/model.glb ?rev=2 #top",
80
+ ].join("\n"));
81
+ });
82
+ it("reads relative and file: URLs with fetch", async () => {
83
+ const target = join(workdir, "payload.json");
84
+ writeFileSync(target, '{"value":"ok"}');
85
+ const out = harness.runProgram(`
86
+ async function main(): Promise<void> {
87
+ const response = await fetch(${JSON.stringify(target)});
88
+ console.log("status=" + response.status + " ok=" + response.ok);
89
+ const data: any = await response.json();
90
+ console.log("value=" + data.value);
91
+ console.log("file=" + (await fetch(${JSON.stringify("file://" + target)})).status);
92
+ const missing = await fetch(${JSON.stringify(join(workdir, "missing.txt"))}).then(
93
+ () => "resolved",
94
+ (error: any) => "rejected:" + (error instanceof TypeError),
95
+ );
96
+ console.log("missing=" + missing);
97
+ const empty = await fetch("").then(
98
+ () => "resolved",
99
+ (error: any) => "rejected:" + (error instanceof TypeError),
100
+ );
101
+ console.log("empty=" + empty);
102
+ }
103
+ main();
104
+ `);
105
+ expect(out).toBe([
106
+ "status=200 ok=true",
107
+ "value=ok",
108
+ "file=200",
109
+ "missing=rejected:true",
110
+ "empty=rejected:true",
111
+ ].join("\n"));
112
+ });
113
+ });
114
+ //# sourceMappingURL=url.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.test.js","sourceRoot":"","sources":["../../../tests/e2e/url.test.ts"],"names":[],"mappings":"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"}