quickjs 0.11.1 → 0.12.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb155f6499b100170fefe4f9e8d3a24eb221bf5cd3363adaae7c1dda6d157f12
4
- data.tar.gz: 7b51767a21b911a62eab5f561aaebc14461fa9078874491db8163fd92bdced5a
3
+ metadata.gz: 64bdf2de37fc8ac24b7acec74dd724226f732a4acbef401d579b9b46e12c86f1
4
+ data.tar.gz: 4d76617466a380a038de3684495dc38eacb29be11002b3d6b1536c2c6caf0d80
5
5
  SHA512:
6
- metadata.gz: f45ea57fcb653f3f886443fef85ebd9138cdfd0ddff0322ac628a25d18033f6eb23fc81f578951d04af23dff604fe7196efca663cf46d74d1820ef3f346bd7ce
7
- data.tar.gz: 4e721e1621759230776b78194657de9199e9fc7af61d6b091e31a379b010cd2f7a76ce825d6e693595a2cb12bc7b12d76d9fa9ef683b1b726ef1a8e03851ec06
6
+ metadata.gz: 0fcf3bde7047e39038983326a6d867430e4e5fc63ac65bc6823c44df4222904a8bef385abd12bcadcca5213c9610198a08cdf4f12bc949dc2f7613509a8e46fc
7
+ data.tar.gz: af03f93832d2400cbb1418c764fbdaca5b1a51fd232462731b404a897851ff60cb3437c745ab2c03e36a7a5219c3618751075649b1cdc2e9935689b4e218ccf7
data/CLAUDE.md ADDED
@@ -0,0 +1,62 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ quickjs.rb is a Ruby gem wrapping QuickJS (a lightweight JavaScript interpreter) via a C extension. It lets Ruby programs evaluate JavaScript code without a full Node.js runtime.
8
+
9
+ ## Build & Test Commands
10
+
11
+ ```bash
12
+ bundle exec rake # Full cycle: clobber → compile → test
13
+ bundle exec rake compile # Compile C extension only
14
+ bundle exec rake test # Run all tests
15
+ bundle exec ruby -Itest:lib test/quickjs_test.rb # Run single test file
16
+ bundle exec ruby -Itest:lib test/quickjs_test.rb -n test_name # Run single test
17
+ rake polyfills:build # Rebuild Intl polyfill bundle (requires npm)
18
+ ```
19
+
20
+ QuickJS source lives as a git submodule under `ext/quickjsrb/quickjs/` — clone with `--recurse-submodules`.
21
+
22
+ ## Design Principles
23
+
24
+ - **Never modify QuickJS core** — the engine is a git submodule; all customization is implemented externally in our C extension or Ruby layer
25
+ - **Prefer Ruby over C** — don't rush to write C code; use Ruby where it provides better extendability and maintainability
26
+ - **Security-conscious C layer** — avoid adding flexible C-level features that introduce security risks; expose QuickJS's own options as-is rather than inventing new attack surface
27
+ - **Keep the default footprint small** — don't increase bundle size or add JavaScript overhead (e.g. polyfills) by default; additional capabilities should be opt-in via feature flags
28
+
29
+ ## Architecture
30
+
31
+ **C Extension** (`ext/quickjsrb/`):
32
+ - `quickjsrb.c` / `quickjsrb.h` — Core extension: creates QuickJS runtime/context, handles Ruby↔JS value conversion, timeout interrupts, and Ruby function bridging into JS
33
+ - Value conversion uses JSON serialization for complex types (objects/arrays); direct conversion for primitives
34
+ - `VMData` struct holds the JS context, defined Ruby functions, timeout state, console logs, and error references
35
+ - Ruby GC integration via `vm_mark`, `vm_free`, `vm_compact` callbacks
36
+
37
+ **Ruby layer** (`lib/quickjs/`):
38
+ - `Quickjs::VM` — Persistent JS runtime with `eval_code`, `import`, `define_function`
39
+ - `Quickjs.eval_code` — Convenience method for one-shot evaluation
40
+ - Exception hierarchy maps JS error types (SyntaxError, TypeError, etc.) to Ruby classes under `Quickjs::`
41
+ - Special values: `Quickjs::Value::UNDEFINED` and `Quickjs::Value::NAN` represent JS undefined/NaN
42
+
43
+ **Feature flags** (passed to `VM.new` via `features:` array):
44
+ - `:feature_std`, `:feature_os` — QuickJS std/os modules
45
+ - `:feature_timeout` — setTimeout/setInterval via CRuby threads
46
+ - `:feature_polyfill_intl` — Intl API polyfill (DateTimeFormat, NumberFormat, PluralRules, Locale)
47
+
48
+ **Polyfills** (`polyfills/`):
49
+ - Built from FormatJS packages via rolldown, output minified JS embedded as C source
50
+ - Polyfill version must match gem version (enforced during `rake release`)
51
+
52
+ ## Testing
53
+
54
+ Tests use minitest with `describe`/`it` blocks. Key test files:
55
+ - `test/quickjs_test.rb` — Main test suite (value conversion, errors, VM features, ESM imports, function definitions)
56
+ - `test/quickjs_polyfill_test.rb` — Intl polyfill tests
57
+
58
+ ## Build Notes
59
+
60
+ - `extconf.rb` compiles with `-DNDEBUG` to avoid conflicts with Ruby 4.0 GC assertions
61
+ - Symbol visibility is hidden by default (`-fvisibility=hidden`)
62
+ - CI matrix: Ruby 3.2/3.3/3.4/4.0 × Ubuntu/macOS
data/README.md CHANGED
@@ -63,6 +63,9 @@ vm = Quickjs.eval_code(features: [::Quickjs::FEATURE_TIMEOUT])
63
63
 
64
64
  # Inject the polyfill of Intl
65
65
  vm = Quickjs.eval_code(features: [::Quickjs::POLYFILL_INTL])
66
+
67
+ # Inject the polyfill of Blob and File (W3C File API)
68
+ vm = Quickjs.eval_code(features: [::Quickjs::POLYFILL_FILE])
66
69
  ```
67
70
 
68
71
  </details>
@@ -103,6 +106,9 @@ vm = Quickjs::VM.new(features: [::Quickjs::FEATURE_TIMEOUT])
103
106
 
104
107
  # Inject the polyfill of Intl
105
108
  vm = Quickjs::VM.new(features: [::Quickjs::POLYFILL_INTL])
109
+
110
+ # Inject the polyfill of Blob and File (W3C File API)
111
+ vm = Quickjs::VM.new(features: [::Quickjs::POLYFILL_FILE])
106
112
  ```
107
113
 
108
114
  #### VM timeout
@@ -163,7 +169,7 @@ vm.logs.last.raw #=> ['log me', nil]
163
169
 
164
170
  - `ext/quickjsrb/quickjs`
165
171
  - [MIT License Copyright (c) 2017-2021 by Fabrice Bellard and Charlie Gordon](https://github.com/bellard/quickjs/blob/6e2e68fd0896957f92eb6c242a2e048c1ef3cae0/LICENSE).
166
- - `ext/quickjsrb/vendor/polyfill-intl-en.min.js` (bundled and minified)
172
+ - `ext/quickjsrb/vendor/polyfill-intl-en.min.js` ([bundled and minified from `polyfills/`](https://github.com/hmsk/quickjs.rb/tree/main/polyfills))
167
173
  - MIT License Copyright (c) 2023 FormatJS
168
174
  - [@formatjs/intl-getcanonicallocales](https://github.com/formatjs/formatjs/blob/main/packages/intl-getcanonicallocales/LICENSE.md)
169
175
  - [@formatjs/intl-locale](https://github.com/formatjs/formatjs/blob/main/packages/intl-locale/LICENSE.md)
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ end
11
11
 
12
12
  require 'rake/extensiontask'
13
13
 
14
- task build: :compile
14
+ task build: [:compile, 'polyfills:version:warn']
15
15
 
16
16
  GEMSPEC = Gem::Specification.load('quickjs.gemspec')
17
17
 
@@ -19,4 +19,56 @@ Rake::ExtensionTask.new('quickjsrb', GEMSPEC) do |ext|
19
19
  ext.lib_dir = 'lib/quickjs'
20
20
  end
21
21
 
22
+ def check_polyfill_version!
23
+ require 'json'
24
+ require_relative 'lib/quickjs/version'
25
+
26
+ package = JSON.parse(File.read(File.expand_path('polyfills/package.json', __dir__)))
27
+ return if package['version'] == Quickjs::VERSION
28
+
29
+ yield package['version'], Quickjs::VERSION
30
+ end
31
+
32
+ namespace :polyfills do
33
+ desc 'Build polyfill bundles with rolldown and recompile'
34
+ task build: :clobber do
35
+ require 'json'
36
+ require_relative 'lib/quickjs/version'
37
+
38
+ polyfills_dir = File.expand_path('polyfills', __dir__)
39
+ package_json_path = File.join(polyfills_dir, 'package.json')
40
+ package = JSON.parse(File.read(package_json_path))
41
+ old_version = package['version']
42
+ package['version'] = Quickjs::VERSION
43
+ File.write(package_json_path, "#{JSON.pretty_generate(package)}\n")
44
+ if old_version != Quickjs::VERSION
45
+ warn "\n⚠️ polyfills/package.json version was #{old_version}, updated to #{Quickjs::VERSION}\n\n"
46
+ end
47
+
48
+ Dir.chdir(polyfills_dir) do
49
+ sh 'npm install' unless File.exist?('node_modules/.package-lock.json') &&
50
+ File.mtime('node_modules/.package-lock.json') >= File.mtime('package.json')
51
+ sh 'npx rolldown -c rolldown.config.mjs'
52
+ end
53
+
54
+ Rake::Task[:compile].invoke
55
+ end
56
+
57
+ namespace :version do
58
+ task :check do
59
+ check_polyfill_version! do |pkg_v, gem_v|
60
+ abort "polyfills/package.json version (#{pkg_v}) does not match gem version (#{gem_v}). Run `rake polyfills:build` first."
61
+ end
62
+ end
63
+
64
+ task :warn do
65
+ check_polyfill_version! do |pkg_v, gem_v|
66
+ warn "⚠️ polyfills/package.json version (#{pkg_v}) does not match gem version (#{gem_v}). Run `rake polyfills:build` to sync."
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ task 'release:guard_clean' => 'polyfills:version:check'
73
+
22
74
  task default: %i[clobber compile test]
@@ -12,6 +12,7 @@ $srcs = [
12
12
  'quickjs.c',
13
13
  'quickjs-libc.c',
14
14
  'polyfill-intl-en.min.c',
15
+ 'polyfill-file.min.c',
15
16
  'quickjsrb.c',
16
17
  ]
17
18
 
@@ -37,7 +38,8 @@ else
37
38
  end
38
39
 
39
40
  append_cflags('-fwrapv')
40
- $CFLAGS << ' ' << '-D_GNU_SOURCE -DCONFIG_VERSION=\"2024-02-14\"'
41
+ # NDEBUG: suppress QuickJS debug assertions that conflict with Ruby 4.0 GC
42
+ $CFLAGS << ' ' << '-D_GNU_SOURCE -DCONFIG_VERSION=\"2024-02-14\" -DNDEBUG'
41
43
 
42
44
  abort('could not find quickjs.h') unless find_header('quickjs.h')
43
45
  abort('could not find cutils.h') unless find_header('cutils.h')
@@ -60,6 +62,10 @@ polyfill-intl-en.min.js:
60
62
  $(COPY) $(srcdir)/vendor/$@ $@
61
63
  polyfill-intl-en.min.c: ./qjsc polyfill-intl-en.min.js
62
64
  ./qjsc $(POLYFILL_OPTS) -c -M polyfill/intl-en.so,intlen -m -o $@ polyfill-intl-en.min.js
65
+ polyfill-file.min.js:
66
+ $(COPY) $(srcdir)/vendor/$@ $@
67
+ polyfill-file.min.c: ./qjsc polyfill-file.min.js
68
+ ./qjsc -fno-string-normalize -fno-eval -fno-proxy -fno-module-loader -c -M polyfill/file.so,file -m -o $@ polyfill-file.min.js
63
69
  COMPILE_POLYFILL
64
70
  conf
65
71
  end
@@ -8,6 +8,10 @@ JSValue j_error_from_ruby_error(JSContext *ctx, VALUE r_error)
8
8
  int objectId = NUM2INT(r_object_id);
9
9
  JS_SetPropertyStr(ctx, j_error, "rb_object_id", JS_NewInt32(ctx, objectId));
10
10
 
11
+ // Keep the error alive in VMData to prevent GC before find_ruby_error retrieves it
12
+ VMData *data = JS_GetContextOpaque(ctx);
13
+ rb_hash_aset(data->alive_errors, r_object_id, r_error);
14
+
11
15
  VALUE r_exception_message = rb_funcall(r_error, rb_intern("message"), 0);
12
16
  const char *errorMessage = StringValueCStr(r_exception_message);
13
17
  JS_SetPropertyStr(ctx, j_error, "message", JS_NewString(ctx, errorMessage));
@@ -89,8 +93,11 @@ VALUE find_ruby_error(JSContext *ctx, JSValue j_error)
89
93
  JS_FreeValue(ctx, j_errorOriginalRubyObjectId);
90
94
  if (errorOriginalRubyObjectId > 0)
91
95
  {
92
- // may be nice if cover the case of object is missing
93
- return rb_funcall(rb_const_get(rb_cClass, rb_intern("ObjectSpace")), rb_intern("_id2ref"), 1, INT2NUM(errorOriginalRubyObjectId));
96
+ VMData *data = JS_GetContextOpaque(ctx);
97
+ VALUE r_key = INT2NUM(errorOriginalRubyObjectId);
98
+ VALUE r_error = rb_hash_aref(data->alive_errors, r_key);
99
+ rb_hash_delete(data->alive_errors, r_key);
100
+ return r_error;
94
101
  }
95
102
  }
96
103
  else
@@ -329,7 +336,7 @@ static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int
329
336
  JS_FreeValue(ctx, j_v);
330
337
  }
331
338
  rb_ary_push(r_call_args, r_argv);
332
- rb_ary_push(r_call_args, ULONG2NUM(data->eval_time->limit * 1000 / CLOCKS_PER_SEC));
339
+ rb_ary_push(r_call_args, ULONG2NUM(data->eval_time->limit_ms));
333
340
 
334
341
  int sadnessHappened;
335
342
 
@@ -507,7 +514,7 @@ static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE r_self)
507
514
  VMData *data;
508
515
  TypedData_Get_Struct(r_self, VMData, &vm_type, data);
509
516
 
510
- data->eval_time->limit = (clock_t)(CLOCKS_PER_SEC * NUM2UINT(r_timeout_msec) / 1000);
517
+ data->eval_time->limit_ms = (int64_t)NUM2UINT(r_timeout_msec);
511
518
  JS_SetContextOpaque(data->context, data);
512
519
  JSRuntime *runtime = JS_GetRuntime(data->context);
513
520
 
@@ -554,6 +561,13 @@ static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE r_self)
554
561
  JS_FreeValue(data->context, j_polyfillIntlResult);
555
562
  }
556
563
 
564
+ if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, QUICKJSRB_SYM(featurePolyfillFileId))))
565
+ {
566
+ JSValue j_polyfillFileObject = JS_ReadObject(data->context, &qjsc_polyfill_file_min, qjsc_polyfill_file_min_size, JS_READ_OBJ_BYTECODE);
567
+ JSValue j_polyfillFileResult = JS_EvalFunction(data->context, j_polyfillFileObject);
568
+ JS_FreeValue(data->context, j_polyfillFileResult);
569
+ }
570
+
557
571
  JSValue j_console = JS_NewObject(data->context);
558
572
  JS_SetPropertyStr(
559
573
  data->context, j_console, "log",
@@ -580,7 +594,11 @@ static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE r_self)
580
594
  static int interrupt_handler(JSRuntime *runtime, void *opaque)
581
595
  {
582
596
  EvalTime *eval_time = opaque;
583
- return clock() >= eval_time->started_at + eval_time->limit ? 1 : 0;
597
+ struct timespec now;
598
+ clock_gettime(CLOCK_MONOTONIC, &now);
599
+ int64_t elapsed_ms = (int64_t)(now.tv_sec - eval_time->started_at.tv_sec) * 1000
600
+ + (now.tv_nsec - eval_time->started_at.tv_nsec) / 1000000;
601
+ return elapsed_ms >= eval_time->limit_ms ? 1 : 0;
584
602
  }
585
603
 
586
604
  static VALUE vm_m_evalCode(VALUE r_self, VALUE r_code)
@@ -594,7 +612,7 @@ static VALUE vm_m_evalCode(VALUE r_self, VALUE r_code)
594
612
  rb_raise(rb_eTypeError, "JavaScript code must be a String, got %s", StringValueCStr(r_code_class));
595
613
  }
596
614
 
597
- data->eval_time->started_at = clock();
615
+ clock_gettime(CLOCK_MONOTONIC, &data->eval_time->started_at);
598
616
  JS_SetInterruptHandler(JS_GetRuntime(data->context), interrupt_handler, data->eval_time);
599
617
 
600
618
  char *code = StringValueCStr(r_code);
@@ -612,9 +630,12 @@ static VALUE vm_m_evalCode(VALUE r_self, VALUE r_code)
612
630
  }
613
631
  else
614
632
  {
633
+ // Free j_awaitedResult before to_rb_value because to_rb_value may
634
+ // raise (longjmp) for JS exceptions, which would skip any cleanup
635
+ // after it and leak JS GC objects.
636
+ JS_FreeValue(data->context, j_awaitedResult);
615
637
  VALUE result = to_rb_value(data->context, j_returnedValue);
616
638
  JS_FreeValue(data->context, j_returnedValue);
617
- JS_FreeValue(data->context, j_awaitedResult);
618
639
  return result;
619
640
  }
620
641
  }
@@ -14,11 +14,14 @@
14
14
 
15
15
  extern const uint32_t qjsc_polyfill_intl_en_min_size;
16
16
  extern const uint8_t qjsc_polyfill_intl_en_min;
17
+ extern const uint32_t qjsc_polyfill_file_min_size;
18
+ extern const uint8_t qjsc_polyfill_file_min;
17
19
 
18
20
  const char *featureStdId = "feature_std";
19
21
  const char *featureOsId = "feature_os";
20
22
  const char *featureTimeoutId = "feature_timeout";
21
23
  const char *featurePolyfillIntlId = "feature_polyfill_intl";
24
+ const char *featurePolyfillFileId = "feature_polyfill_file";
22
25
 
23
26
  const char *undefinedId = "undefined";
24
27
  const char *nanId = "NaN";
@@ -39,8 +42,8 @@ const char *native_errors[] = {
39
42
 
40
43
  typedef struct EvalTime
41
44
  {
42
- clock_t limit;
43
- clock_t started_at;
45
+ int64_t limit_ms;
46
+ struct timespec started_at;
44
47
  } EvalTime;
45
48
 
46
49
  typedef struct VMData
@@ -49,6 +52,7 @@ typedef struct VMData
49
52
  VALUE defined_functions;
50
53
  struct EvalTime *eval_time;
51
54
  VALUE logs;
55
+ VALUE alive_errors;
52
56
  } VMData;
53
57
 
54
58
  static void vm_free(void *ptr)
@@ -75,6 +79,15 @@ static void vm_mark(void *ptr)
75
79
  VMData *data = (VMData *)ptr;
76
80
  rb_gc_mark_movable(data->defined_functions);
77
81
  rb_gc_mark_movable(data->logs);
82
+ rb_gc_mark_movable(data->alive_errors);
83
+ }
84
+
85
+ static void vm_compact(void *ptr)
86
+ {
87
+ VMData *data = (VMData *)ptr;
88
+ data->defined_functions = rb_gc_location(data->defined_functions);
89
+ data->logs = rb_gc_location(data->logs);
90
+ data->alive_errors = rb_gc_location(data->alive_errors);
78
91
  }
79
92
 
80
93
  static const rb_data_type_t vm_type = {
@@ -83,6 +96,7 @@ static const rb_data_type_t vm_type = {
83
96
  .dmark = vm_mark,
84
97
  .dfree = vm_free,
85
98
  .dsize = vm_size,
99
+ .dcompact = vm_compact,
86
100
  },
87
101
  .flags = RUBY_TYPED_FREE_IMMEDIATELY,
88
102
  };
@@ -93,6 +107,7 @@ static VALUE vm_alloc(VALUE r_self)
93
107
  VALUE obj = TypedData_Make_Struct(r_self, VMData, &vm_type, data);
94
108
  data->defined_functions = rb_hash_new();
95
109
  data->logs = rb_ary_new();
110
+ data->alive_errors = rb_hash_new();
96
111
 
97
112
  EvalTime *eval_time = malloc(sizeof(EvalTime));
98
113
  data->eval_time = eval_time;
@@ -138,6 +153,7 @@ static void r_define_constants(VALUE r_parent_class)
138
153
  rb_define_const(r_parent_class, "MODULE_OS", QUICKJSRB_SYM(featureOsId));
139
154
  rb_define_const(r_parent_class, "FEATURE_TIMEOUT", QUICKJSRB_SYM(featureTimeoutId));
140
155
  rb_define_const(r_parent_class, "POLYFILL_INTL", QUICKJSRB_SYM(featurePolyfillIntlId));
156
+ rb_define_const(r_parent_class, "POLYFILL_FILE", QUICKJSRB_SYM(featurePolyfillFileId));
141
157
 
142
158
  VALUE rb_cQuickjsValue = rb_define_class_under(r_parent_class, "Value", rb_cObject);
143
159
  rb_define_const(rb_cQuickjsValue, "UNDEFINED", QUICKJSRB_SYM(undefinedId));
@@ -0,0 +1 @@
1
+ (function(){var e=class e{#e;#t;constructor(n,i){if(this.#t=t(i?.type??``),!n){this.#e=new Uint8Array;return}if(!Array.isArray(n))throw TypeError(`Failed to construct 'Blob': The provided value cannot be converted to a sequence.`);let a=[],o=0;for(let t of n){let n;n=typeof t==`string`?r(t):t instanceof ArrayBuffer?new Uint8Array(t):ArrayBuffer.isView(t)?new Uint8Array(t.buffer,t.byteOffset,t.byteLength):t instanceof e?t.#e:r(String(t)),a.push(n),o+=n.byteLength}let s=new Uint8Array(o),c=0;for(let e of a)s.set(e,c),c+=e.byteLength;this.#e=s}get size(){return this.#e.byteLength}get type(){return this.#t}slice(r,i,a){let o=this.size,s=r===void 0?0:n(r,o),c=i===void 0?o:n(i,o),l=Math.max(c-s,0),u=new e;return u.#e=this.#e.slice(s,s+l),u.#t=t(a??``),u}text(){return Promise.resolve(i(this.#e))}arrayBuffer(){return Promise.resolve(this.#e.buffer.slice(this.#e.byteOffset,this.#e.byteOffset+this.#e.byteLength))}toString(){return`[object Blob]`}get[Symbol.toStringTag](){return`Blob`}};function t(e){return/[^\x20-\x7E]/.test(e)?``:e.toLowerCase()}function n(e,t){return e<0?Math.max(t+e,0):Math.min(e,t)}function r(e){let t=[];for(let n=0;n<e.length;n++){let r=e.charCodeAt(n);if(r>=55296&&r<=56319&&n+1<e.length){let t=e.charCodeAt(n+1);t>=56320&&t<=57343&&(r=(r-55296)*1024+(t-56320)+65536,n++)}r<=127?t.push(r):r<=2047?t.push(192|r>>6,128|r&63):r<=65535?t.push(224|r>>12,128|r>>6&63,128|r&63):t.push(240|r>>18,128|r>>12&63,128|r>>6&63,128|r&63)}return new Uint8Array(t)}function i(e){let t=``,n=0;for(;n<e.length;){let r,i=e[n];i<=127?(r=i,n++):(i&224)==192?(r=(i&31)<<6|e[n+1]&63,n+=2):(i&240)==224?(r=(i&15)<<12|(e[n+1]&63)<<6|e[n+2]&63,n+=3):(r=(i&7)<<18|(e[n+1]&63)<<12|(e[n+2]&63)<<6|e[n+3]&63,n+=4),r<=65535?t+=String.fromCharCode(r):(r-=65536,t+=String.fromCharCode(55296+(r>>10),56320+(r&1023)))}return t}var a=class extends e{#e;#t;constructor(e,t,n){if(arguments.length<2)throw TypeError(`Failed to construct 'File': 2 arguments required, but only `+arguments.length+` present.`);super(e,n),this.#e=String(t),this.#t=n?.lastModified===void 0?Date.now():Number(n.lastModified)}get name(){return this.#e}get lastModified(){return this.#t}toString(){return`[object File]`}get[Symbol.toStringTag](){return`File`}};globalThis.Blob=e,globalThis.File=a})();