zuzu-js 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -98,12 +98,6 @@ The npm package includes JavaScript-backed runtime modules under
98
98
  `stdlib/modules/`. Pure ZuzuScript modules are loaded, parsed, and
99
99
  evaluated through normal ZuzuScript runtime semantics.
100
100
 
101
- ## Status
102
-
103
- This is an early npm release of the JavaScript runtime. It is useful for
104
- running and compiling ZuzuScript programs, but language and standard
105
- library compatibility are still under active development.
106
-
107
101
  ## Licence
108
102
 
109
103
  `zuzu-js` is free software; you may redistribute it and/or modify it
@@ -7894,6 +7894,8 @@ globalThis[${JSON.stringify(node.id.name)}] = ${name2};` : declaration;
7894
7894
  return ["__zuzu_call_args", signature.headName, signature.restName, signature.namedName, "__i", "__a"];
7895
7895
  case "scalar_pairlist":
7896
7896
  return ["__zuzu_call_args", signature.headName, signature.namedName, "__i", "__a"];
7897
+ case "scalar_pairlist_array":
7898
+ return ["__zuzu_call_args", signature.headName, signature.namedName, signature.restName, "__i", "__a"];
7897
7899
  case "variadic":
7898
7900
  return ["__zuzu_call_args", signature.headName, signature.restName, "__i", "__a"];
7899
7901
  default:
@@ -7944,6 +7946,14 @@ globalThis[${JSON.stringify(node.id.name)}] = ${name2};` : declaration;
7944
7946
  namedName: params[1].name
7945
7947
  };
7946
7948
  }
7949
+ if (params.length === 3 && params[0].type === "Parameter" && params[1].type === "SpecialParameter" && params[1].special === "rest_only" && params[1].containerType === "PairList" && params[2].type === "Parameter" && (params[2].typeName === "Array" || params[2].typeName == null)) {
7950
+ return {
7951
+ kind: "scalar_pairlist_array",
7952
+ headName: params[0].name,
7953
+ namedName: params[1].name,
7954
+ restName: params[2].name
7955
+ };
7956
+ }
7947
7957
  if (params.length === 1 && params[0].type === "SpecialParameter" && params[0].special === "lead_rest" && params[0].containerType === "Array") {
7948
7958
  return {
7949
7959
  kind: "variadic",
@@ -8075,6 +8085,19 @@ globalThis[${JSON.stringify(node.id.name)}] = ${name2};` : declaration;
8075
8085
  "}",
8076
8086
  `const ${signature.namedName} = __zuzu_named_args;`
8077
8087
  ].join("\n");
8088
+ case "scalar_pairlist_array":
8089
+ return [
8090
+ "const __zuzu_call_args = Array.prototype.slice.call( arguments );",
8091
+ `const ${signature.headName} = __zuzu_call_args[0];`,
8092
+ "let __zuzu_named_args = __zuzu_pairlist_literal( [] );",
8093
+ "const __zuzu_rest_args = [];",
8094
+ "for ( let __i = 1; __i < __zuzu_call_args.length; __i++ ) {",
8095
+ "const __a = __zuzu_call_args[__i];",
8096
+ "if ( __zuzu_is_pairlist( __a ) ) { __zuzu_named_args = __a; } else { __zuzu_rest_args.push( __a ); }",
8097
+ "}",
8098
+ `const ${signature.restName} = __zuzu_rest_args;`,
8099
+ `const ${signature.namedName} = __zuzu_named_args;`
8100
+ ].join("\n");
8078
8101
  case "variadic":
8079
8102
  return [
8080
8103
  "const __zuzu_call_args = Array.prototype.slice.call( arguments );",
@@ -9198,6 +9221,9 @@ ${cleanup}
9198
9221
  return weakStorage || node.isWeakWrite ? `${left} = __zuzu_assign_weak( ${left}, ${right} )` : `${left} = __zuzu_assign_strong( ${left}, ${right} )`;
9199
9222
  }
9200
9223
  if (node.left && node.left.type === "MemberExpression") {
9224
+ if (!node.left.computed) {
9225
+ throw new UnsupportedSyntaxError("Invalid assignment target");
9226
+ }
9201
9227
  const object = emitExpression(node.left.object);
9202
9228
  const objectTarget = emitAssignmentTarget(node.left.object);
9203
9229
  const key = node.left.computed ? node.left.property.type === "BraceIdentifier" ? `__zuzu_resolve_brace_key( ${object}, ${JSON.stringify(node.left.property.name)}, () => ${node.left.property.name} )` : emitExpression(node.left.property) : JSON.stringify(node.left.property.name);
@@ -9673,12 +9699,29 @@ ${cleanup}
9673
9699
  }
9674
9700
  return;
9675
9701
  case "UpdateExpression":
9702
+ requireValidAssignmentTarget(node.argument);
9676
9703
  if (node.argument && node.argument.type === "Identifier") {
9677
9704
  requireMutable(node.argument, scope);
9678
9705
  } else {
9679
9706
  validateExpression(node.argument, scope);
9680
9707
  }
9681
9708
  return;
9709
+ case "UnaryExpression":
9710
+ if (node.operator === "++" || node.operator === "--") {
9711
+ requireValidAssignmentTarget(node.argument);
9712
+ if (node.argument && node.argument.type === "Identifier") {
9713
+ requireMutable(node.argument, scope);
9714
+ } else {
9715
+ validateExpression(node.argument, scope);
9716
+ }
9717
+ return;
9718
+ }
9719
+ validateChildNodes(node, scope);
9720
+ return;
9721
+ case "RefExpression":
9722
+ requireValidAssignmentTarget(node.argument);
9723
+ validateExpression(node.argument, scope);
9724
+ return;
9682
9725
  case "FunctionExpression":
9683
9726
  validateFunctionBody(node, scope);
9684
9727
  return;
@@ -9695,7 +9738,10 @@ ${cleanup}
9695
9738
  if (!target) {
9696
9739
  throw syntaxError("Invalid assignment target", node);
9697
9740
  }
9698
- if (target.type === "Identifier" || target.type === "MemberExpression" || target.type === "SliceExpression") {
9741
+ if (target.type === "Identifier" || target.type === "SliceExpression") {
9742
+ return;
9743
+ }
9744
+ if (target.type === "MemberExpression" && target.computed) {
9699
9745
  return;
9700
9746
  }
9701
9747
  if (target.type === "BinaryExpression" && ["@", "@@", "@?"].includes(target.operator)) {
@@ -10695,6 +10741,79 @@ ${cleanup}
10695
10741
  }
10696
10742
  });
10697
10743
 
10744
+ // package.json
10745
+ var require_package = __commonJS({
10746
+ "package.json"(exports2, module2) {
10747
+ module2.exports = {
10748
+ name: "zuzu-js",
10749
+ version: "0.3.0",
10750
+ description: "JavaScript runtime, compiler, and browser bundle for ZuzuScript.",
10751
+ main: "lib/zuzu.js",
10752
+ bin: {
10753
+ zuzu: "bin/zuzu",
10754
+ "zuzu-js": "bin/zuzu-js",
10755
+ "zuzu-js-compile": "bin/zuzu-js-compile",
10756
+ "zuzu-js-electron": "bin/zuzu-js-electron"
10757
+ },
10758
+ files: [
10759
+ "bin/",
10760
+ "dist/zuzu-browser.js",
10761
+ "dist/zuzu-browser-worker.js",
10762
+ "lib/",
10763
+ "modules/",
10764
+ "stdlib/modules/"
10765
+ ],
10766
+ directories: {
10767
+ example: "examples",
10768
+ lib: "lib",
10769
+ test: "test"
10770
+ },
10771
+ scripts: {
10772
+ "electron:demo": "electron bin/zuzu-js-electron docs/examples/09_gui_dialogue_demo.zzs",
10773
+ test: "node test/cli.js && node test/argument-spread.js && node test/default-operator.js && node test/declaration-unpacking.js && node test/collection-copy.js && node test/std-marshal-cbor.js && node test/std-marshal-data-graph.js && node test/std-marshal-user-object.js && node test/std-marshal-code-table.js && node test/async-parity.js && node test/browser-stdlib-generator.js && node test/browser-gui.js && node test/electron-gui.js && node test/electron-renderer.js && node test/electron-packaging.js",
10774
+ "test:electron": "node test/electron-gui.js && node test/electron-renderer.js && node test/electron-packaging.js"
10775
+ },
10776
+ keywords: [
10777
+ "zuzu",
10778
+ "zuzuscript",
10779
+ "language",
10780
+ "runtime",
10781
+ "compiler"
10782
+ ],
10783
+ author: "",
10784
+ license: "Artistic-1.0 OR GPL-2.0-or-later",
10785
+ repository: {
10786
+ type: "git",
10787
+ url: "git+ssh://git@github.com/zuzuscript/zuzu-js.git"
10788
+ },
10789
+ bugs: {
10790
+ url: "https://github.com/zuzuscript/zuzu-js/issues"
10791
+ },
10792
+ homepage: "https://zuzulang.org/",
10793
+ engines: {
10794
+ node: ">=16"
10795
+ },
10796
+ dependencies: {
10797
+ "@xmldom/xmldom": "^0.9.10",
10798
+ "adm-zip": "^0.5.17",
10799
+ "better-sqlite3": "^11.10.0",
10800
+ "cbor-x": "^1.6.4",
10801
+ cborg: "^5.1.1",
10802
+ compressjs: "^1.0.3",
10803
+ "js-yaml": "^4.1.1",
10804
+ koffi: "^2.16.1",
10805
+ mysql2: "^3.22.3",
10806
+ "node-forge": "^1.4.0",
10807
+ pg: "^8.20.0",
10808
+ xpath: "^0.0.34"
10809
+ },
10810
+ devDependencies: {
10811
+ electron: "^41.3.0"
10812
+ }
10813
+ };
10814
+ }
10815
+ });
10816
+
10698
10817
  // modules/std/io.js
10699
10818
  var require_io = __commonJS({
10700
10819
  "modules/std/io.js"(exports2, module2) {
@@ -11215,6 +11334,26 @@ ${cleanup}
11215
11334
  var textEncoder = new TextEncoder();
11216
11335
  var utf8Decoder = new TextDecoder("utf-8", { fatal: true });
11217
11336
  var ZUZU_SKIP_BUILD = /* @__PURE__ */ Symbol.for("zuzu.skip_build");
11337
+ var runtimeVersion = (() => {
11338
+ if (typeof globalThis === "object" && globalThis !== null) {
11339
+ const globalVersion = globalThis.__ZUZU_RUNTIME_VERSION;
11340
+ if (typeof globalVersion === "string" && globalVersion.trim()) {
11341
+ return String(globalVersion);
11342
+ }
11343
+ }
11344
+ if (typeof process === "object" && process && process.versions) {
11345
+ try {
11346
+ const packageMetadata = require_package();
11347
+ const packageVersion = packageMetadata && packageMetadata.version;
11348
+ if (packageVersion) {
11349
+ return String(packageVersion);
11350
+ }
11351
+ } catch (err) {
11352
+ return "dev";
11353
+ }
11354
+ }
11355
+ return "dev";
11356
+ })();
11218
11357
  function defaultModuleSearchRoots(host, repoRoot, includePaths) {
11219
11358
  if (host.name === "browser") {
11220
11359
  return [
@@ -12330,10 +12469,15 @@ ${cleanup}
12330
12469
  zuzuStoreField(this, field, value);
12331
12470
  }
12332
12471
  let named = null;
12472
+ const lastArg = ctorArgs[ctorArgs.length - 1];
12333
12473
  if (ctorArgs.length === 1 && isPairListLike(ctorArgs[0])) {
12334
12474
  named = ctorArgs[0];
12335
12475
  } else if (ctorArgs.length === 1 && ctorArgs[0] && typeof ctorArgs[0] === "object" && !Array.isArray(ctorArgs[0])) {
12336
12476
  named = ctorArgs[0];
12477
+ } else if (ctorArgs.length > 1 && isPairListLike(lastArg)) {
12478
+ named = lastArg;
12479
+ } else if (ctorArgs.length > 1 && lastArg && typeof lastArg === "object" && !Array.isArray(lastArg)) {
12480
+ named = lastArg;
12337
12481
  }
12338
12482
  const releaseTemporaryNamed = !skipBuild && isPairListLike(named) && named.__zuzu_temporary_call_args === true;
12339
12483
  try {
@@ -12485,9 +12629,6 @@ ${cleanup}
12485
12629
  if (dictMethod) {
12486
12630
  return dictMethod(...args);
12487
12631
  }
12488
- if (args.length === 0) {
12489
- return value;
12490
- }
12491
12632
  throw new TypeError(`${String(property)} is not a function`);
12492
12633
  }
12493
12634
  function zuzuMaybeDemolish(value) {
@@ -13393,7 +13534,7 @@ ${cleanup}
13393
13534
  const systemGlobalSeed = {
13394
13535
  language_version: 0,
13395
13536
  runtime: "zuzu-js",
13396
- runtime_version: "dev",
13537
+ runtime_version: runtimeVersion,
13397
13538
  platform: this.host.name,
13398
13539
  inc: Object.freeze(moduleSearchRoots.slice()),
13399
13540
  deny_fs: capabilityFlags.fs ? false : true,
@@ -39487,9 +39628,9 @@ ${lines.join("\n")}
39487
39628
  }
39488
39629
  });
39489
39630
 
39490
- // ../../../../../tmp/zuzu-browser-build.WePoiO/browser-stdlib.generated.js
39631
+ // ../../../../../tmp/zuzu-browser-build.mbIbiI/browser-stdlib.generated.js
39491
39632
  var require_browser_stdlib_generated = __commonJS({
39492
- "../../../../../tmp/zuzu-browser-build.WePoiO/browser-stdlib.generated.js"(exports2, module2) {
39633
+ "../../../../../tmp/zuzu-browser-build.mbIbiI/browser-stdlib.generated.js"(exports2, module2) {
39493
39634
  "use strict";
39494
39635
  function createBrowserStdlib2() {
39495
39636
  const jsModules = /* @__PURE__ */ Object.create(null);
@@ -45347,10 +45488,9 @@ class ZZTemplate extends ZTemplate {
45347
45488
  }
45348
45489
  }
45349
45490
  `;
45350
- virtualFiles["/modules/test/more.zzm"] = '=encoding utf8\n\n=head1 NAME\n\ntest/more - Write unit tests and integration tests in ZuzuScript.\n\n=head1 SYNOPSIS\n\n from test/more import *;\n from my/project import frobnicate;\n\n is(frobnicate(21), 42, "frobinated 21 correctly");\n is(frobnicate(null), null, "frobinate on null input");\n\n done_testing();\n\n=cut\n\n\nlet Number _COUNT := 0;\nlet Number _PASSED := 0;\nlet Number _FAILED := 0;\nlet Number _LEVEL := 0;\nlet Number _PLAN := -1;\nlet Number _TODO_FAILED := 0;\nlet Number _TODO_PASSED := 0;\n\nlet TODO;\n\nfunction _directive () {\n if ( TODO ) {\n if ( TODO instanceof String ) {\n return ` # TODO ${TODO}`;\n }\n else {\n return " # TODO";\n }\n }\n return "";\n}\n\nfunction _indent () {\n let i := _LEVEL;\n let str := "";\n while ( i > 0 ) {\n str _= " ";\n i--;\n }\n\n return str;\n}\n\nclass BailOutException extends Exception;\nclass SkipAllException extends Exception;\n\nfunction _module_name_is_valid ( String module ) {\n return module ~ /^[A-Za-z_][A-Za-z0-9_]*(\\/[A-Za-z_][A-Za-z0-9_]*)*$/;\n}\n\nfunction _module_is_available ( String module ) {\n from std/eval import eval;\n\n try {\n eval( `do { from ${module} import *; }; true;` );\n return true;\n }\n catch {\n return false;\n }\n}\n\nfunction _capability_is_available ( String capability ) {\n if ( not( capability ~ /^[A-Za-z_][A-Za-z0-9_]*$/ ) ) {\n die `Invalid capability name: ${capability}`;\n }\n\n let deny_key := `deny_${capability}`;\n if ( deny_key in __system__ ) {\n return not __system__.get(deny_key);\n }\n\n return false;\n}\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nC<< test/more >> is a module for test-driven development. It can be used\nfor writing unit tests and integration tests. It generates output in TAP\nL<https://testanything.org/>.\n\nThis module should feel familiar to anybody who has used C<< Test::More >>\nfor Perl, though there are some minor differences.\n\n=head2 Functions\n\n=over\n\n=item C<< pass(String name?) >>\n\nIndicates the named test has passed.\n\n=cut\n\nfunction pass ( String name? ) {\n ++_PASSED;\n ++_COUNT;\n ++_TODO_PASSED if TODO;\n if ( name \u2261 null ) {\n say `${_indent()}ok ${_COUNT}${_directive()}`;\n }\n else {\n say `${_indent()}ok ${_COUNT} - ${name}${_directive()}`;\n }\n\n return true;\n}\n\n=item C<< fail(String name?) >>\n\nIndicates the named test has failed.\n\n=cut\n\nfunction fail ( String name ) {\n ++_FAILED;\n ++_COUNT;\n ++_TODO_FAILED if TODO;\n if ( name \u2261 null ) {\n say `${_indent()}not ok ${_COUNT}${_directive()}`;\n }\n else {\n say `${_indent()}not ok ${_COUNT} - ${name}${_directive()}`;\n }\n\n return false;\n}\n\n=item C<< ok(expr, String name?) >>\n\nPasses the named test only if C<expr> is truthy.\n\n=cut\n\nfunction ok ( expr, String name? ) {\n if (expr) {\n return pass(name);\n }\n else {\n return fail(name);\n }\n}\n\nfunction _coerced_equal ( got, expected ) {\n if ( got \u2261 expected ) {\n return true;\n }\n\n if ( got instanceof Boolean and expected instanceof Number ) {\n return ( got ? 1: 0 ) = expected;\n }\n if ( got instanceof Number and expected instanceof Boolean ) {\n return got = ( expected ? 1: 0 );\n }\n\n return false;\n}\n\n=item C<< is(got, expected, String name?) >>\n\nPasses the named test only if C<< got \u2261 expected >>.\n\n=cut\n\nfunction is ( got, expected, String name? ) {\n\n if ( not ok( _coerced_equal( got, expected ), name ) ) {\n from std/dump import Dumper;\n say `${_indent()}# expected: ${Dumper.dump(expected)}`;\n say `${_indent()}# got: ${Dumper.dump(got)}`;\n return false;\n }\n\n return true;\n}\n\n=item C<< isnt(got, unexpected, String name?) >>\n\nPasses the named test only if C<< got \u2262 expected >>.\n\n=cut\n\nfunction isnt ( got, unexpected, String name? ) {\n return ok( not _coerced_equal( got, unexpected ), name );\n}\n\n=item C<< like(got, expected_re, String name?) >>\n\nPasses the named test only if C<< got ~ expected >>.\n\n=cut\n\nfunction like ( got, Regexp expected_re, String name? ) {\n\n if ( not ok( got ~ expected_re, name ) ) {\n say `${_indent()}# expected (regexp): ${expected_re}`;\n say `${_indent()}# got: ${got}`;\n return false;\n }\n\n return true;\n}\n\n=item C<< unlike(got, unexpected_re, String name?) >>\n\nFails the named test only if C<< got ~ expected >>.\n\n=cut\n\nfunction unlike ( got, Regexp unexpected_re, String name? ) {\n\n if ( not ok( not( got ~ unexpected_re ), name ) ) {\n say `${_indent()}# unexpected (regexp): ${unexpected_re}`;\n say `${_indent()}# got: ${got}`;\n return false;\n }\n\n return true;\n}\n\n=item C<< diag(diagnostic) >>\n\nOutputs the diagnostic.\n\n=cut\n\nfunction diag (diagnostic) {\n say `${_indent()}# ${diagnostic}`;\n return true;\n}\n\n=item C<< explain(value) >>\n\nCombines diag with Dumper.dump();\n\n=cut\n\nfunction explain (value) {\n from std/dump import Dumper;\n return diag( Dumper.dump(value) );\n}\n\n=item C<< bail_out(String message?) >>\n\nBail out of running further tests.\n\n=cut\n\nfunction bail_out ( String message? ) {\n let e;\n if ( message \u2261 null ) {\n e := new BailOutException( message: "Bail out!" );\n }\n else {\n e := new BailOutException( message: `Bail out! ${message}` );\n }\n throw e;\n}\n\n=item C<< skip_all(String reason) >>\n\nSkips the whole test file by emitting a TAP skip-all plan and exiting\nsuccessfully when the runtime provides C<std/proc>.\n\nThis is intended for guards at the beginning of a test script, before\nany tests have run. Browser-like hosts without C<std/proc> use a\nspecial exception fallback that the browser ztest runner treats as a\nskip.\n\n=cut\n\nfunction skip_all ( String reason ) {\n die "Cannot skip all tests after tests have already run" if _COUNT > 0;\n die "Cannot skip all tests in a subtest" if _LEVEL > 0;\n _PLAN := 0;\n say `1..${_PLAN} # SKIP: ${reason}`;\n\n from std/proc try import Proc;\n Proc.exit(0) if Proc \u2262 null;\n throw new SkipAllException( message: reason );\n}\n\n=item C<< requires_module(String module) >>\n\nSkips the whole test file if the named module cannot be loaded.\n\n=cut\n\nfunction requires_module ( String module ) {\n if ( not _module_name_is_valid(module) ) {\n die `Invalid module name: ${module}`;\n }\n\n if ( not _module_is_available(module) ) {\n skip_all( `module ${module} is unavailable` );\n }\n\n return true;\n}\n\n=item C<< requires_capability(String capability) >>\n\nSkips the whole test file if the named runtime capability is not\navailable.\n\n=cut\n\nfunction requires_capability ( String capability ) {\n if ( not _capability_is_available(capability) ) {\n skip_all( `capability ${capability} is unavailable` );\n }\n\n return true;\n}\n\n=item C<< author_testing() >>\n\nSkips the whole test file unless the C<AUTHOR_TESTING> environment\nvariable is set to a true value.\n\n=cut\n\nfunction author_testing () {\n requires_capability( "proc" );\n from std/proc try import Env;\n if ( Env and Env.get( "AUTHOR_TESTING", false ) ) {\n return true;\n }\n skip_all( "requires AUTHOR_TESTING=1" );\n}\n\n=item C<< extended_testing() >>\n\nSkips the whole test file unless the C<EXTENDED_TESTING> environment\nvariable is set to a true value.\n\n=cut\n\nfunction extended_testing () {\n requires_capability( "proc" );\n from std/proc try import Env;\n if ( Env and Env.get( "EXTENDED_TESTING", false ) ) {\n return true;\n }\n skip_all( "requires EXTENDED_TESTING=1" );\n}\n\n=item C<< plan(Number n) >>\n\nIndicate the number of tests you intend on running, before running any\ntests.\n\ndone_testing() can later check the number.\n\n=cut\n\nfunction plan ( Number n ) {\n die "Must plan() before running any tests" if _COUNT > 0;\n die "Cannot plan() in a subtest" if _LEVEL > 0;\n die "Must plan() at least one test" if n < 1;\n _PLAN := n;\n say `1..${_PLAN}`;\n}\n\n=item C<< done_testing(Number n?) >>\n\nIndicates that testing is finished.\n\nYou may optionally provide the expected total number of tests, and the\nfunction will throw an exception if that doesn\'t match the number of\ntests which were actually run.\n\nWill also throw an error if you called plan() earlier and the actual\nnumber of tests run differs from your plan.\n\n=cut\n\nfunction done_testing ( Number n? ) {\n die "Unexpected done_testing() in subtest" if _LEVEL > 0;\n die `Expected ${n} tests, but ran ${_COUNT}` if n \u2262 null and n \u2260 _COUNT;\n\n function maybe_fail_count ( should_die ) {\n if ( _TODO_PASSED > 0 ) {\n warn `Passed ${_TODO_PASSED} TODO tests!`;\n }\n if ( _FAILED > 0 ) {\n let msg := `Failed ${_FAILED} tests`;\n msg _= ` (${_TODO_FAILED} todo, ${_FAILED - _TODO_FAILED} true fails)` if _TODO_FAILED > 0;\n die msg if should_die and ( _FAILED > _TODO_FAILED );\n warn msg;\n }\n }\n\n if ( _PLAN < 0 ) {\n say `1..${_COUNT}`;\n }\n else if ( _PLAN \u2260 _COUNT ) {\n maybe_fail_count(false);\n die `Planned ${_PLAN} tests, but ran ${_COUNT}`;\n }\n\n maybe_fail_count(true);\n return true;\n}\n\n=item C<< subtest(String name, Function f) >>\n\nCalls f() as a subtest.\n\nDon\'t use done_testing() within the function.\n\n=cut\n\nfunction subtest ( String name, Function f ) {\n let orig_context := {\n count: _COUNT,\n passed: _PASSED,\n failed: _FAILED,\n level: _LEVEL,\n todo_passed: _TODO_PASSED,\n todo_failed: _TODO_FAILED,\n };\n\n function restore_context () {\n _COUNT := orig_context{count};\n _PASSED := orig_context{passed};\n _FAILED := orig_context{failed};\n _LEVEL := orig_context{level};\n _TODO_PASSED := orig_context{todo_passed};\n _TODO_FAILED := orig_context{todo_failed};\n }\n\n _COUNT := 0;\n _PASSED := 0;\n _FAILED := 0;\n _TODO_PASSED := 0;\n _TODO_FAILED := 0;\n _LEVEL++;\n let is_ok := true;\n\n try {\n diag( `Subtest: ${name}` );\n f();\n say `${_indent()}1..${_COUNT}`;\n is_ok := false if _FAILED > _TODO_FAILED;\n restore_context();\n }\n catch ( BailOutException e ) {\n throw e;\n }\n catch ( Exception e ) {\n is_ok := false;\n restore_context();\n }\n\n return ok( is_ok, name );\n}\n\n=back\n\n=item C<< todo(Boolean c, String reason, Function f) >>\n\nRuns the tests in the given function, but if condition c is true then\nfirst sets TODO to true.\n\nYou can manually set and unset the TODO variable instead.\n\n=cut\n\nfunction todo ( Boolean c, String reason, Function f ) {\n let orig := TODO;\n TODO := reason if c;\n try {\n f();\n }\n catch {\n }\n TODO := orig;\n return not c;\n}\n\n=back\n\n=item C<< exception(Function f) >>\n\nCalls f() and returns any exception thrown.\n\nReturns null if no exception was thrown.\n\n=cut\n\nfunction exception ( Function f ) {\n try {\n f();\n return null;\n }\n catch ( BailOutException e ) {\n throw e;\n }\n catch ( Exception e ) {\n return e;\n }\n}\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< test/more >> is copyright Toby Inkster.\n\nIt is free software; you may redistribute it and/or modify it under\nthe terms of either the Artistic License 1.0 or the GNU General Public\nLicense version 2.\n\n=cut\n';
45351
45491
  return {
45352
45492
  repoRoot: "/",
45353
- includePaths: ["/__zuzu_browser_modules__/0"],
45493
+ includePaths: [],
45354
45494
  jsModules,
45355
45495
  virtualFiles
45356
45496
  };
@@ -45726,7 +45866,7 @@ class ZZTemplate extends ZTemplate {
45726
45866
  }
45727
45867
  });
45728
45868
 
45729
- // ../../../../../tmp/zuzu-browser-build.WePoiO/browser-worker-entry.generated.js
45869
+ // ../../../../../tmp/zuzu-browser-build.mbIbiI/browser-worker-entry.generated.js
45730
45870
  var { createBrowserStdlib } = require_browser_stdlib_generated();
45731
45871
  globalThis.__ZUZU_BROWSER_DEFAULT_RUNTIME_OPTIONS__ = createBrowserStdlib();
45732
45872
  var { installBrowserWorker } = require_browser_worker_entry();