diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index fa2a322..54dfbab 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml
@@ -14,22 +14,22 @@ jobs: job_001: - name: "analyze_and_format; linux; Dart 2.12.0; PKG: pkgs/shelf_packages_handler; `dart analyze --fatal-infos .`" + name: "analyze_and_format; linux; Dart 2.14.0; PKGS: pkgs/shelf_packages_handler, pkgs/shelf_proxy, pkgs/shelf_router, pkgs/shelf_router_generator, pkgs/shelf_static, pkgs/shelf_test_handler, pkgs/shelf_web_socket; `dart analyze --fatal-infos .`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@v3 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:pkgs/shelf_packages_handler;commands:analyze" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0;packages:pkgs/shelf_packages_handler-pkgs/shelf_proxy-pkgs/shelf_router-pkgs/shelf_router_generator-pkgs/shelf_static-pkgs/shelf_test_handler-pkgs/shelf_web_socket;commands:analyze" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:pkgs/shelf_packages_handler - os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0 + os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0;packages:pkgs/shelf_packages_handler-pkgs/shelf_proxy-pkgs/shelf_router-pkgs/shelf_router_generator-pkgs/shelf_static-pkgs/shelf_test_handler-pkgs/shelf_web_socket + os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0 os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest - uses: dart-lang/setup-dart@v1.3 with: - sdk: "2.12.0" + sdk: "2.14.0" - id: checkout uses: actions/checkout@v3 - id: pkgs_shelf_packages_handler_pub_upgrade @@ -41,53 +41,6 @@ if: "always() && steps.pkgs_shelf_packages_handler_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_packages_handler run: dart analyze --fatal-infos . - job_002: - name: "analyze_and_format; linux; Dart 2.12.0; PKG: pkgs/shelf_static; `dart analyze --fatal-infos .`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@v3 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:pkgs/shelf_static;commands:analyze" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:pkgs/shelf_static - os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0 - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: "2.12.0" - - id: checkout - uses: actions/checkout@v3 - - id: pkgs_shelf_static_pub_upgrade - name: pkgs/shelf_static; dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: pkgs/shelf_static - run: dart pub upgrade - - name: "pkgs/shelf_static; dart analyze --fatal-infos ." - if: "always() && steps.pkgs_shelf_static_pub_upgrade.conclusion == 'success'" - working-directory: pkgs/shelf_static - run: dart analyze --fatal-infos . - job_003: - name: "analyze_and_format; linux; Dart 2.14.0; PKG: pkgs/shelf_proxy; `dart analyze --fatal-infos .`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@v3 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0;packages:pkgs/shelf_proxy;commands:analyze" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0;packages:pkgs/shelf_proxy - os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0 - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: "2.14.0" - - id: checkout - uses: actions/checkout@v3 - id: pkgs_shelf_proxy_pub_upgrade name: pkgs/shelf_proxy; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -97,7 +50,52 @@ if: "always() && steps.pkgs_shelf_proxy_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_proxy run: dart analyze --fatal-infos . - job_004: + - id: pkgs_shelf_router_pub_upgrade + name: pkgs/shelf_router; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_router + run: dart pub upgrade + - name: "pkgs/shelf_router; dart analyze --fatal-infos ." + if: "always() && steps.pkgs_shelf_router_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_router + run: dart analyze --fatal-infos . + - id: pkgs_shelf_router_generator_pub_upgrade + name: pkgs/shelf_router_generator; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_router_generator + run: dart pub upgrade + - name: "pkgs/shelf_router_generator; dart analyze --fatal-infos ." + if: "always() && steps.pkgs_shelf_router_generator_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_router_generator + run: dart analyze --fatal-infos . + - id: pkgs_shelf_static_pub_upgrade + name: pkgs/shelf_static; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_static + run: dart pub upgrade + - name: "pkgs/shelf_static; dart analyze --fatal-infos ." + if: "always() && steps.pkgs_shelf_static_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_static + run: dart analyze --fatal-infos . + - id: pkgs_shelf_test_handler_pub_upgrade + name: pkgs/shelf_test_handler; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: dart pub upgrade + - name: "pkgs/shelf_test_handler; dart analyze --fatal-infos ." + if: "always() && steps.pkgs_shelf_test_handler_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: dart analyze --fatal-infos . + - id: pkgs_shelf_web_socket_pub_upgrade + name: pkgs/shelf_web_socket; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: dart pub upgrade + - name: "pkgs/shelf_web_socket; dart analyze --fatal-infos ." + if: "always() && steps.pkgs_shelf_web_socket_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: dart analyze --fatal-infos . + job_002: name: "analyze_and_format; linux; Dart 2.16.0; PKG: pkgs/shelf; `dart analyze --fatal-infos .`" runs-on: ubuntu-latest steps: @@ -125,17 +123,17 @@ if: "always() && steps.pkgs_shelf_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf run: dart analyze --fatal-infos . - job_005: - name: "analyze_and_format; linux; Dart dev; PKG: pkgs/shelf; `dart analyze --fatal-infos .`" + job_003: + name: "analyze_and_format; linux; Dart dev; PKGS: pkgs/shelf, pkgs/shelf_packages_handler, pkgs/shelf_proxy, pkgs/shelf_router, pkgs/shelf_router_generator, pkgs/shelf_static, pkgs/shelf_test_handler, pkgs/shelf_web_socket; `dart analyze --fatal-infos .`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@v3 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf;commands:analyze" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf-pkgs/shelf_packages_handler-pkgs/shelf_proxy-pkgs/shelf_router-pkgs/shelf_router_generator-pkgs/shelf_static-pkgs/shelf_test_handler-pkgs/shelf_web_socket;commands:analyze" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf + os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf-pkgs/shelf_packages_handler-pkgs/shelf_proxy-pkgs/shelf_router-pkgs/shelf_router_generator-pkgs/shelf_static-pkgs/shelf_test_handler-pkgs/shelf_web_socket os:ubuntu-latest;pub-cache-hosted;sdk:dev os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest @@ -153,25 +151,6 @@ if: "always() && steps.pkgs_shelf_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf run: dart analyze --fatal-infos . - job_006: - name: "analyze_and_format; linux; Dart dev; PKG: pkgs/shelf_packages_handler; `dart analyze --fatal-infos .`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@v3 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_packages_handler;commands:analyze" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_packages_handler - os:ubuntu-latest;pub-cache-hosted;sdk:dev - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: dev - - id: checkout - uses: actions/checkout@v3 - id: pkgs_shelf_packages_handler_pub_upgrade name: pkgs/shelf_packages_handler; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -181,25 +160,6 @@ if: "always() && steps.pkgs_shelf_packages_handler_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_packages_handler run: dart analyze --fatal-infos . - job_007: - name: "analyze_and_format; linux; Dart dev; PKG: pkgs/shelf_proxy; `dart analyze --fatal-infos .`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@v3 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_proxy;commands:analyze" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_proxy - os:ubuntu-latest;pub-cache-hosted;sdk:dev - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: dev - - id: checkout - uses: actions/checkout@v3 - id: pkgs_shelf_proxy_pub_upgrade name: pkgs/shelf_proxy; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -209,25 +169,24 @@ if: "always() && steps.pkgs_shelf_proxy_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_proxy run: dart analyze --fatal-infos . - job_008: - name: "analyze_and_format; linux; Dart dev; PKG: pkgs/shelf_static; `dart analyze --fatal-infos .`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@v3 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_static;commands:analyze" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_static - os:ubuntu-latest;pub-cache-hosted;sdk:dev - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: dev - - id: checkout - uses: actions/checkout@v3 + - id: pkgs_shelf_router_pub_upgrade + name: pkgs/shelf_router; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_router + run: dart pub upgrade + - name: "pkgs/shelf_router; dart analyze --fatal-infos ." + if: "always() && steps.pkgs_shelf_router_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_router + run: dart analyze --fatal-infos . + - id: pkgs_shelf_router_generator_pub_upgrade + name: pkgs/shelf_router_generator; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_router_generator + run: dart pub upgrade + - name: "pkgs/shelf_router_generator; dart analyze --fatal-infos ." + if: "always() && steps.pkgs_shelf_router_generator_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_router_generator + run: dart analyze --fatal-infos . - id: pkgs_shelf_static_pub_upgrade name: pkgs/shelf_static; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -237,17 +196,35 @@ if: "always() && steps.pkgs_shelf_static_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_static run: dart analyze --fatal-infos . - job_009: - name: "analyze_and_format; linux; Dart dev; PKG: pkgs/shelf; `dart format --output=none --set-exit-if-changed .`" + - id: pkgs_shelf_test_handler_pub_upgrade + name: pkgs/shelf_test_handler; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: dart pub upgrade + - name: "pkgs/shelf_test_handler; dart analyze --fatal-infos ." + if: "always() && steps.pkgs_shelf_test_handler_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: dart analyze --fatal-infos . + - id: pkgs_shelf_web_socket_pub_upgrade + name: pkgs/shelf_web_socket; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: dart pub upgrade + - name: "pkgs/shelf_web_socket; dart analyze --fatal-infos ." + if: "always() && steps.pkgs_shelf_web_socket_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: dart analyze --fatal-infos . + job_004: + name: "analyze_and_format; linux; Dart dev; PKGS: pkgs/shelf, pkgs/shelf_packages_handler, pkgs/shelf_proxy, pkgs/shelf_router, pkgs/shelf_router_generator, pkgs/shelf_static, pkgs/shelf_test_handler, pkgs/shelf_web_socket; `dart format --output=none --set-exit-if-changed .`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@v3 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf;commands:format" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf-pkgs/shelf_packages_handler-pkgs/shelf_proxy-pkgs/shelf_router-pkgs/shelf_router_generator-pkgs/shelf_static-pkgs/shelf_test_handler-pkgs/shelf_web_socket;commands:format" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf + os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf-pkgs/shelf_packages_handler-pkgs/shelf_proxy-pkgs/shelf_router-pkgs/shelf_router_generator-pkgs/shelf_static-pkgs/shelf_test_handler-pkgs/shelf_web_socket os:ubuntu-latest;pub-cache-hosted;sdk:dev os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest @@ -265,25 +242,6 @@ if: "always() && steps.pkgs_shelf_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf run: "dart format --output=none --set-exit-if-changed ." - job_010: - name: "analyze_and_format; linux; Dart dev; PKG: pkgs/shelf_packages_handler; `dart format --output=none --set-exit-if-changed .`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@v3 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_packages_handler;commands:format" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_packages_handler - os:ubuntu-latest;pub-cache-hosted;sdk:dev - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: dev - - id: checkout - uses: actions/checkout@v3 - id: pkgs_shelf_packages_handler_pub_upgrade name: pkgs/shelf_packages_handler; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -293,25 +251,6 @@ if: "always() && steps.pkgs_shelf_packages_handler_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_packages_handler run: "dart format --output=none --set-exit-if-changed ." - job_011: - name: "analyze_and_format; linux; Dart dev; PKG: pkgs/shelf_proxy; `dart format --output=none --set-exit-if-changed .`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@v3 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_proxy;commands:format" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_proxy - os:ubuntu-latest;pub-cache-hosted;sdk:dev - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: dev - - id: checkout - uses: actions/checkout@v3 - id: pkgs_shelf_proxy_pub_upgrade name: pkgs/shelf_proxy; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -321,25 +260,24 @@ if: "always() && steps.pkgs_shelf_proxy_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_proxy run: "dart format --output=none --set-exit-if-changed ." - job_012: - name: "analyze_and_format; linux; Dart dev; PKG: pkgs/shelf_static; `dart format --output=none --set-exit-if-changed .`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@v3 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_static;commands:format" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_static - os:ubuntu-latest;pub-cache-hosted;sdk:dev - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: dev - - id: checkout - uses: actions/checkout@v3 + - id: pkgs_shelf_router_pub_upgrade + name: pkgs/shelf_router; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_router + run: dart pub upgrade + - name: "pkgs/shelf_router; dart format --output=none --set-exit-if-changed ." + if: "always() && steps.pkgs_shelf_router_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_router + run: "dart format --output=none --set-exit-if-changed ." + - id: pkgs_shelf_router_generator_pub_upgrade + name: pkgs/shelf_router_generator; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_router_generator + run: dart pub upgrade + - name: "pkgs/shelf_router_generator; dart format --output=none --set-exit-if-changed ." + if: "always() && steps.pkgs_shelf_router_generator_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_router_generator + run: "dart format --output=none --set-exit-if-changed ." - id: pkgs_shelf_static_pub_upgrade name: pkgs/shelf_static; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -349,23 +287,41 @@ if: "always() && steps.pkgs_shelf_static_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_static run: "dart format --output=none --set-exit-if-changed ." - job_013: - name: "unit_test; linux; Dart 2.12.0; PKG: pkgs/shelf_packages_handler; `dart test --test-randomize-ordering-seed=random`" + - id: pkgs_shelf_test_handler_pub_upgrade + name: pkgs/shelf_test_handler; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: dart pub upgrade + - name: "pkgs/shelf_test_handler; dart format --output=none --set-exit-if-changed ." + if: "always() && steps.pkgs_shelf_test_handler_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: "dart format --output=none --set-exit-if-changed ." + - id: pkgs_shelf_web_socket_pub_upgrade + name: pkgs/shelf_web_socket; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: dart pub upgrade + - name: "pkgs/shelf_web_socket; dart format --output=none --set-exit-if-changed ." + if: "always() && steps.pkgs_shelf_web_socket_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: "dart format --output=none --set-exit-if-changed ." + job_005: + name: "unit_test; linux; Dart 2.14.0; PKGS: pkgs/shelf_packages_handler, pkgs/shelf_proxy, pkgs/shelf_router, pkgs/shelf_router_generator, pkgs/shelf_static, pkgs/shelf_test_handler, pkgs/shelf_web_socket; `dart test --test-randomize-ordering-seed=random`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@v3 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:pkgs/shelf_packages_handler;commands:test_0" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0;packages:pkgs/shelf_packages_handler-pkgs/shelf_proxy-pkgs/shelf_router-pkgs/shelf_router_generator-pkgs/shelf_static-pkgs/shelf_test_handler-pkgs/shelf_web_socket;commands:test_0" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:pkgs/shelf_packages_handler - os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0 + os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0;packages:pkgs/shelf_packages_handler-pkgs/shelf_proxy-pkgs/shelf_router-pkgs/shelf_router_generator-pkgs/shelf_static-pkgs/shelf_test_handler-pkgs/shelf_web_socket + os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0 os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest - uses: dart-lang/setup-dart@v1.3 with: - sdk: "2.12.0" + sdk: "2.14.0" - id: checkout uses: actions/checkout@v3 - id: pkgs_shelf_packages_handler_pub_upgrade @@ -377,79 +333,6 @@ if: "always() && steps.pkgs_shelf_packages_handler_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_packages_handler run: "dart test --test-randomize-ordering-seed=random" - needs: - - job_001 - - job_002 - - job_003 - - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_014: - name: "unit_test; linux; Dart 2.12.0; PKG: pkgs/shelf_static; `dart test --test-randomize-ordering-seed=random`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@v3 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:pkgs/shelf_static;commands:test_0" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:pkgs/shelf_static - os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0 - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: "2.12.0" - - id: checkout - uses: actions/checkout@v3 - - id: pkgs_shelf_static_pub_upgrade - name: pkgs/shelf_static; dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: pkgs/shelf_static - run: dart pub upgrade - - name: "pkgs/shelf_static; dart test --test-randomize-ordering-seed=random" - if: "always() && steps.pkgs_shelf_static_pub_upgrade.conclusion == 'success'" - working-directory: pkgs/shelf_static - run: "dart test --test-randomize-ordering-seed=random" - needs: - - job_001 - - job_002 - - job_003 - - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_015: - name: "unit_test; linux; Dart 2.14.0; PKG: pkgs/shelf_proxy; `dart test --test-randomize-ordering-seed=random`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@v3 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0;packages:pkgs/shelf_proxy;commands:test_0" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0;packages:pkgs/shelf_proxy - os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0 - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: "2.14.0" - - id: checkout - uses: actions/checkout@v3 - id: pkgs_shelf_proxy_pub_upgrade name: pkgs/shelf_proxy; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -459,20 +342,90 @@ if: "always() && steps.pkgs_shelf_proxy_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_proxy run: "dart test --test-randomize-ordering-seed=random" + - id: pkgs_shelf_router_pub_upgrade + name: pkgs/shelf_router; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_router + run: dart pub upgrade + - name: "pkgs/shelf_router; dart test --test-randomize-ordering-seed=random" + if: "always() && steps.pkgs_shelf_router_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_router + run: "dart test --test-randomize-ordering-seed=random" + - id: pkgs_shelf_router_generator_pub_upgrade + name: pkgs/shelf_router_generator; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_router_generator + run: dart pub upgrade + - name: "pkgs/shelf_router_generator; dart test --test-randomize-ordering-seed=random" + if: "always() && steps.pkgs_shelf_router_generator_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_router_generator + run: "dart test --test-randomize-ordering-seed=random" + - id: pkgs_shelf_static_pub_upgrade + name: pkgs/shelf_static; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_static + run: dart pub upgrade + - name: "pkgs/shelf_static; dart test --test-randomize-ordering-seed=random" + if: "always() && steps.pkgs_shelf_static_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_static + run: "dart test --test-randomize-ordering-seed=random" + - id: pkgs_shelf_test_handler_pub_upgrade + name: pkgs/shelf_test_handler; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: dart pub upgrade + - name: "pkgs/shelf_test_handler; dart test --test-randomize-ordering-seed=random" + if: "always() && steps.pkgs_shelf_test_handler_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: "dart test --test-randomize-ordering-seed=random" + - id: pkgs_shelf_web_socket_pub_upgrade + name: pkgs/shelf_web_socket; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: dart pub upgrade + - name: "pkgs/shelf_web_socket; dart test --test-randomize-ordering-seed=random" + if: "always() && steps.pkgs_shelf_web_socket_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: "dart test --test-randomize-ordering-seed=random" needs: - job_001 - job_002 - job_003 - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_016: + job_006: + name: "unit_test; linux; Dart 2.14.0; PKG: pkgs/shelf_test_handler; `dart test --test-randomize-ordering-seed=random -p chrome`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@v3 + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0;packages:pkgs/shelf_test_handler;commands:test_1" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0;packages:pkgs/shelf_test_handler + os:ubuntu-latest;pub-cache-hosted;sdk:2.14.0 + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - uses: dart-lang/setup-dart@v1.3 + with: + sdk: "2.14.0" + - id: checkout + uses: actions/checkout@v3 + - id: pkgs_shelf_test_handler_pub_upgrade + name: pkgs/shelf_test_handler; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: dart pub upgrade + - name: "pkgs/shelf_test_handler; dart test --test-randomize-ordering-seed=random -p chrome" + if: "always() && steps.pkgs_shelf_test_handler_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: "dart test --test-randomize-ordering-seed=random -p chrome" + needs: + - job_001 + - job_002 + - job_003 + - job_004 + job_007: name: "unit_test; linux; Dart 2.16.0; PKG: pkgs/shelf; `dart test --test-randomize-ordering-seed=random -p chrome`" runs-on: ubuntu-latest steps: @@ -505,15 +458,7 @@ - job_002 - job_003 - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_017: + job_008: name: "unit_test; linux; Dart 2.16.0; PKG: pkgs/shelf; `dart test --test-randomize-ordering-seed=random`" runs-on: ubuntu-latest steps: @@ -546,25 +491,17 @@ - job_002 - job_003 - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_018: - name: "unit_test; linux; Dart dev; PKG: pkgs/shelf; `dart test --test-randomize-ordering-seed=random -p chrome`" + job_009: + name: "unit_test; linux; Dart dev; PKGS: pkgs/shelf, pkgs/shelf_test_handler; `dart test --test-randomize-ordering-seed=random -p chrome`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@v3 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf;commands:test_1" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf-pkgs/shelf_test_handler;commands:test_1" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf + os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf-pkgs/shelf_test_handler os:ubuntu-latest;pub-cache-hosted;sdk:dev os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest @@ -582,30 +519,31 @@ if: "always() && steps.pkgs_shelf_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf run: "dart test --test-randomize-ordering-seed=random -p chrome" + - id: pkgs_shelf_test_handler_pub_upgrade + name: pkgs/shelf_test_handler; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: dart pub upgrade + - name: "pkgs/shelf_test_handler; dart test --test-randomize-ordering-seed=random -p chrome" + if: "always() && steps.pkgs_shelf_test_handler_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: "dart test --test-randomize-ordering-seed=random -p chrome" needs: - job_001 - job_002 - job_003 - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_019: - name: "unit_test; linux; Dart dev; PKG: pkgs/shelf; `dart test --test-randomize-ordering-seed=random`" + job_010: + name: "unit_test; linux; Dart dev; PKGS: pkgs/shelf, pkgs/shelf_packages_handler, pkgs/shelf_proxy, pkgs/shelf_router, pkgs/shelf_router_generator, pkgs/shelf_static, pkgs/shelf_test_handler, pkgs/shelf_web_socket; `dart test --test-randomize-ordering-seed=random`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@v3 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf;commands:test_0" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf-pkgs/shelf_packages_handler-pkgs/shelf_proxy-pkgs/shelf_router-pkgs/shelf_router_generator-pkgs/shelf_static-pkgs/shelf_test_handler-pkgs/shelf_web_socket;commands:test_0" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf + os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf-pkgs/shelf_packages_handler-pkgs/shelf_proxy-pkgs/shelf_router-pkgs/shelf_router_generator-pkgs/shelf_static-pkgs/shelf_test_handler-pkgs/shelf_web_socket os:ubuntu-latest;pub-cache-hosted;sdk:dev os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest @@ -623,38 +561,6 @@ if: "always() && steps.pkgs_shelf_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf run: "dart test --test-randomize-ordering-seed=random" - needs: - - job_001 - - job_002 - - job_003 - - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_020: - name: "unit_test; linux; Dart dev; PKG: pkgs/shelf_packages_handler; `dart test --test-randomize-ordering-seed=random`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@v3 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_packages_handler;commands:test_0" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_packages_handler - os:ubuntu-latest;pub-cache-hosted;sdk:dev - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: dev - - id: checkout - uses: actions/checkout@v3 - id: pkgs_shelf_packages_handler_pub_upgrade name: pkgs/shelf_packages_handler; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -664,38 +570,6 @@ if: "always() && steps.pkgs_shelf_packages_handler_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_packages_handler run: "dart test --test-randomize-ordering-seed=random" - needs: - - job_001 - - job_002 - - job_003 - - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_021: - name: "unit_test; linux; Dart dev; PKG: pkgs/shelf_proxy; `dart test --test-randomize-ordering-seed=random`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@v3 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_proxy;commands:test_0" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_proxy - os:ubuntu-latest;pub-cache-hosted;sdk:dev - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: dev - - id: checkout - uses: actions/checkout@v3 - id: pkgs_shelf_proxy_pub_upgrade name: pkgs/shelf_proxy; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -705,38 +579,24 @@ if: "always() && steps.pkgs_shelf_proxy_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_proxy run: "dart test --test-randomize-ordering-seed=random" - needs: - - job_001 - - job_002 - - job_003 - - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_022: - name: "unit_test; linux; Dart dev; PKG: pkgs/shelf_static; `dart test --test-randomize-ordering-seed=random`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@v3 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_static;commands:test_0" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/shelf_static - os:ubuntu-latest;pub-cache-hosted;sdk:dev - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: dev - - id: checkout - uses: actions/checkout@v3 + - id: pkgs_shelf_router_pub_upgrade + name: pkgs/shelf_router; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_router + run: dart pub upgrade + - name: "pkgs/shelf_router; dart test --test-randomize-ordering-seed=random" + if: "always() && steps.pkgs_shelf_router_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_router + run: "dart test --test-randomize-ordering-seed=random" + - id: pkgs_shelf_router_generator_pub_upgrade + name: pkgs/shelf_router_generator; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_router_generator + run: dart pub upgrade + - name: "pkgs/shelf_router_generator; dart test --test-randomize-ordering-seed=random" + if: "always() && steps.pkgs_shelf_router_generator_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_router_generator + run: "dart test --test-randomize-ordering-seed=random" - id: pkgs_shelf_static_pub_upgrade name: pkgs/shelf_static; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -746,26 +606,36 @@ if: "always() && steps.pkgs_shelf_static_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_static run: "dart test --test-randomize-ordering-seed=random" + - id: pkgs_shelf_test_handler_pub_upgrade + name: pkgs/shelf_test_handler; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: dart pub upgrade + - name: "pkgs/shelf_test_handler; dart test --test-randomize-ordering-seed=random" + if: "always() && steps.pkgs_shelf_test_handler_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: "dart test --test-randomize-ordering-seed=random" + - id: pkgs_shelf_web_socket_pub_upgrade + name: pkgs/shelf_web_socket; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: dart pub upgrade + - name: "pkgs/shelf_web_socket; dart test --test-randomize-ordering-seed=random" + if: "always() && steps.pkgs_shelf_web_socket_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: "dart test --test-randomize-ordering-seed=random" needs: - job_001 - job_002 - job_003 - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_023: - name: "unit_test; windows; Dart 2.12.0; PKG: pkgs/shelf_packages_handler; `dart test --test-randomize-ordering-seed=random`" + job_011: + name: "unit_test; windows; Dart 2.14.0; PKGS: pkgs/shelf_packages_handler, pkgs/shelf_static, pkgs/shelf_test_handler, pkgs/shelf_web_socket; `dart test --test-randomize-ordering-seed=random`" runs-on: windows-latest steps: - uses: dart-lang/setup-dart@v1.3 with: - sdk: "2.12.0" + sdk: "2.14.0" - id: checkout uses: actions/checkout@v3 - id: pkgs_shelf_packages_handler_pub_upgrade @@ -777,28 +647,6 @@ if: "always() && steps.pkgs_shelf_packages_handler_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_packages_handler run: "dart test --test-randomize-ordering-seed=random" - needs: - - job_001 - - job_002 - - job_003 - - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_024: - name: "unit_test; windows; Dart 2.12.0; PKG: pkgs/shelf_static; `dart test --test-randomize-ordering-seed=random`" - runs-on: windows-latest - steps: - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: "2.12.0" - - id: checkout - uses: actions/checkout@v3 - id: pkgs_shelf_static_pub_upgrade name: pkgs/shelf_static; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -808,20 +656,53 @@ if: "always() && steps.pkgs_shelf_static_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_static run: "dart test --test-randomize-ordering-seed=random" + - id: pkgs_shelf_test_handler_pub_upgrade + name: pkgs/shelf_test_handler; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: dart pub upgrade + - name: "pkgs/shelf_test_handler; dart test --test-randomize-ordering-seed=random" + if: "always() && steps.pkgs_shelf_test_handler_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: "dart test --test-randomize-ordering-seed=random" + - id: pkgs_shelf_web_socket_pub_upgrade + name: pkgs/shelf_web_socket; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: dart pub upgrade + - name: "pkgs/shelf_web_socket; dart test --test-randomize-ordering-seed=random" + if: "always() && steps.pkgs_shelf_web_socket_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: "dart test --test-randomize-ordering-seed=random" needs: - job_001 - job_002 - job_003 - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_025: + job_012: + name: "unit_test; windows; Dart 2.14.0; PKG: pkgs/shelf_test_handler; `dart test --test-randomize-ordering-seed=random -p chrome`" + runs-on: windows-latest + steps: + - uses: dart-lang/setup-dart@v1.3 + with: + sdk: "2.14.0" + - id: checkout + uses: actions/checkout@v3 + - id: pkgs_shelf_test_handler_pub_upgrade + name: pkgs/shelf_test_handler; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: dart pub upgrade + - name: "pkgs/shelf_test_handler; dart test --test-randomize-ordering-seed=random -p chrome" + if: "always() && steps.pkgs_shelf_test_handler_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: "dart test --test-randomize-ordering-seed=random -p chrome" + needs: + - job_001 + - job_002 + - job_003 + - job_004 + job_013: name: "unit_test; windows; Dart 2.16.0; PKG: pkgs/shelf; `dart test --test-randomize-ordering-seed=random -p chrome`" runs-on: windows-latest steps: @@ -844,16 +725,8 @@ - job_002 - job_003 - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_026: - name: "unit_test; windows; Dart dev; PKG: pkgs/shelf; `dart test --test-randomize-ordering-seed=random -p chrome`" + job_014: + name: "unit_test; windows; Dart dev; PKGS: pkgs/shelf, pkgs/shelf_test_handler; `dart test --test-randomize-ordering-seed=random -p chrome`" runs-on: windows-latest steps: - uses: dart-lang/setup-dart@v1.3 @@ -870,21 +743,22 @@ if: "always() && steps.pkgs_shelf_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf run: "dart test --test-randomize-ordering-seed=random -p chrome" + - id: pkgs_shelf_test_handler_pub_upgrade + name: pkgs/shelf_test_handler; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: dart pub upgrade + - name: "pkgs/shelf_test_handler; dart test --test-randomize-ordering-seed=random -p chrome" + if: "always() && steps.pkgs_shelf_test_handler_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: "dart test --test-randomize-ordering-seed=random -p chrome" needs: - job_001 - job_002 - job_003 - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_027: - name: "unit_test; windows; Dart dev; PKG: pkgs/shelf_packages_handler; `dart test --test-randomize-ordering-seed=random`" + job_015: + name: "unit_test; windows; Dart dev; PKGS: pkgs/shelf_packages_handler, pkgs/shelf_static, pkgs/shelf_test_handler, pkgs/shelf_web_socket; `dart test --test-randomize-ordering-seed=random`" runs-on: windows-latest steps: - uses: dart-lang/setup-dart@v1.3 @@ -901,28 +775,6 @@ if: "always() && steps.pkgs_shelf_packages_handler_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_packages_handler run: "dart test --test-randomize-ordering-seed=random" - needs: - - job_001 - - job_002 - - job_003 - - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012 - job_028: - name: "unit_test; windows; Dart dev; PKG: pkgs/shelf_static; `dart test --test-randomize-ordering-seed=random`" - runs-on: windows-latest - steps: - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: dev - - id: checkout - uses: actions/checkout@v3 - id: pkgs_shelf_static_pub_upgrade name: pkgs/shelf_static; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -932,16 +784,26 @@ if: "always() && steps.pkgs_shelf_static_pub_upgrade.conclusion == 'success'" working-directory: pkgs/shelf_static run: "dart test --test-randomize-ordering-seed=random" + - id: pkgs_shelf_test_handler_pub_upgrade + name: pkgs/shelf_test_handler; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: dart pub upgrade + - name: "pkgs/shelf_test_handler; dart test --test-randomize-ordering-seed=random" + if: "always() && steps.pkgs_shelf_test_handler_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_test_handler + run: "dart test --test-randomize-ordering-seed=random" + - id: pkgs_shelf_web_socket_pub_upgrade + name: pkgs/shelf_web_socket; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: dart pub upgrade + - name: "pkgs/shelf_web_socket; dart test --test-randomize-ordering-seed=random" + if: "always() && steps.pkgs_shelf_web_socket_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/shelf_web_socket + run: "dart test --test-randomize-ordering-seed=random" needs: - job_001 - job_002 - job_003 - job_004 - - job_005 - - job_006 - - job_007 - - job_008 - - job_009 - - job_010 - - job_011 - - job_012
diff --git a/README.md b/README.md new file mode 100644 index 0000000..93ca1ee --- /dev/null +++ b/README.md
@@ -0,0 +1,41 @@ +[![Build Status](https://github.com/dart-lang/shelf/workflows/Dart%20CI/badge.svg)](https://github.com/dart-lang/shelf/actions?query=workflow%3A"Dart+CI"+branch%3Amaster) + +## shelf [![Pub Package](https://img.shields.io/pub/v/shelf.svg)](https://pub.dev/packages/shelf) + +- Package: <https://pub.dev/packages/shelf> +- [Source code](pkgs/shelf) + +## shelf_packages_handler [![Pub Package](https://img.shields.io/pub/v/shelf_packages_handler.svg)](https://pub.dev/packages/shelf_packages_handler) + +- Package: <https://pub.dev/packages/shelf_packages_handler> +- [Source code](pkgs/shelf_packages_handler) + +## shelf_proxy [![Pub Package](https://img.shields.io/pub/v/shelf_proxy.svg)](https://pub.dev/packages/shelf_proxy) + +- Package: <https://pub.dev/packages/shelf_proxy> +- [Source code](pkgs/shelf_proxy) + +## shelf_router [![Pub Package](https://img.shields.io/pub/v/shelf_router.svg)](https://pub.dev/packages/shelf_router) + +- Package: <https://pub.dev/packages/shelf_router> +- [Source code](pkgs/shelf_router) + +## shelf_router_generator [![Pub Package](https://img.shields.io/pub/v/shelf_router_generator.svg)](https://pub.dev/packages/shelf_router_generator) + +- Package: <https://pub.dev/packages/shelf_router_generator> +- [Source code](pkgs/shelf_router_generator) + +## shelf_static [![Pub Package](https://img.shields.io/pub/v/shelf_static.svg)](https://pub.dev/packages/shelf_static) + +- Package: <https://pub.dev/packages/shelf_static> +- [Source code](pkgs/shelf_static) + +## shelf_test_handler [![Pub Package](https://img.shields.io/pub/v/shelf_test_handler.svg)](https://pub.dev/packages/shelf_test_handler) + +- Package: <https://pub.dev/packages/shelf_test_handler> +- [Source code](pkgs/shelf_test_handler) + +## shelf_web_socket [![Pub Package](https://img.shields.io/pub/v/shelf_web_socket.svg)](https://pub.dev/packages/shelf_web_socket) + +- Package: <https://pub.dev/packages/shelf_web_socket> +- [Source code](pkgs/shelf_web_socket)
diff --git a/pkgs/shelf/analysis_options.yaml b/analysis_options.yaml similarity index 100% rename from pkgs/shelf/analysis_options.yaml rename to analysis_options.yaml
diff --git a/mono_repo.yaml b/mono_repo.yaml new file mode 100644 index 0000000..7dc1d9e --- /dev/null +++ b/mono_repo.yaml
@@ -0,0 +1,3 @@ +merge_stages: + - analyze_and_format + - unit_test
diff --git a/pkgs/shelf/test/shelf_io_test.dart b/pkgs/shelf/test/shelf_io_test.dart index 9ae3ad2..417f8cb 100644 --- a/pkgs/shelf/test/shelf_io_test.dart +++ b/pkgs/shelf/test/shelf_io_test.dart
@@ -521,13 +521,13 @@ var sslClient = HttpClient(context: securityContext); - Future<HttpClientRequest> _scheduleSecureGet() => + Future<HttpClientRequest> scheduleSecureGet() => sslClient.getUrl(Uri.https('localhost:${_server!.port}', '')); test('secure sync handler returns a value to the client', () async { await _scheduleServer(syncHandler, securityContext: securityContext); - var req = await _scheduleSecureGet(); + var req = await scheduleSecureGet(); var response = await req.close(); expect(response.statusCode, HttpStatus.ok); @@ -538,7 +538,7 @@ test('secure async handler returns a value to the client', () async { await _scheduleServer(asyncHandler, securityContext: securityContext); - var req = await _scheduleSecureGet(); + var req = await scheduleSecureGet(); var response = await req.close(); expect(response.statusCode, HttpStatus.ok); expect(await response.cast<List<int>>().transform(utf8.decoder).single,
diff --git a/pkgs/shelf_packages_handler/CHANGELOG.md b/pkgs/shelf_packages_handler/CHANGELOG.md index 4c7dfaf..6e72d91 100644 --- a/pkgs/shelf_packages_handler/CHANGELOG.md +++ b/pkgs/shelf_packages_handler/CHANGELOG.md
@@ -1,5 +1,7 @@ ## 3.0.1-dev +- Require Dart `2.14`. + ## 3.0.0 * Migrate to null safety. @@ -13,9 +15,9 @@ ### Breaking changes * Dropped the dependency on `package_resolver`. - * All `PackageResolver` apis now take a `Map<String, Uri>` of package name - to the base uri for resolving `package:` uris for that package. - * Named arguments have been renamed from `resolver` to `packageMap`. + * All `PackageResolver` apis now take a `Map<String, Uri>` of package name to the base uri for resolving `package:` + uris for that package. + * Named arguments have been renamed from `resolver` to `packageMap`. ## 1.0.4
diff --git a/pkgs/shelf_packages_handler/analysis_options.yaml b/pkgs/shelf_packages_handler/analysis_options.yaml deleted file mode 100644 index d183bf1..0000000 --- a/pkgs/shelf_packages_handler/analysis_options.yaml +++ /dev/null
@@ -1,80 +0,0 @@ -include: package:pedantic/analysis_options.yaml - -analyzer: - strong-mode: - implicit-casts: false - -linter: - rules: - - avoid_bool_literals_in_conditional_expressions - - avoid_catching_errors - - avoid_classes_with_only_static_members - - avoid_function_literals_in_foreach_calls - - avoid_private_typedef_functions - - avoid_redundant_argument_values - - avoid_renaming_method_parameters - - avoid_returning_null - - avoid_returning_null_for_future - - avoid_returning_null_for_void - - avoid_returning_this - - avoid_single_cascade_in_expression_statements - - avoid_unused_constructor_parameters - - avoid_void_async - - await_only_futures - - camel_case_types - - cancel_subscriptions - - cascade_invocations - - comment_references - - constant_identifier_names - - control_flow_in_finally - - directives_ordering - - empty_statements - - file_names - - hash_and_equals - - implementation_imports - - invariant_booleans - - iterable_contains_unrelated_type - - join_return_with_assignment - - lines_longer_than_80_chars - - list_remove_unrelated_type - - literal_only_boolean_expressions - - missing_whitespace_between_adjacent_strings - - no_adjacent_strings_in_list - - no_runtimeType_toString - - non_constant_identifier_names - - only_throw_errors - - overridden_fields - - package_api_docs - - package_names - - package_prefixed_library_names - - prefer_asserts_in_initializer_lists - - prefer_const_constructors - - prefer_const_declarations - - prefer_expression_function_bodies - - prefer_final_locals - - prefer_function_declarations_over_variables - - prefer_initializing_formals - - prefer_inlined_adds - - prefer_interpolation_to_compose_strings - - prefer_is_not_operator - - prefer_null_aware_operators - - prefer_relative_imports - - prefer_typing_uninitialized_variables - - prefer_void_to_null - - provide_deprecation_message - - sort_pub_dependencies - - test_types_in_equals - - throw_in_finally - - type_annotate_public_apis - - unnecessary_await_in_return - - unnecessary_brace_in_string_interps - - unnecessary_getters_setters - - unnecessary_lambdas - - unnecessary_null_aware_assignments - - unnecessary_overrides - - unnecessary_parenthesis - - unnecessary_statements - - unnecessary_string_interpolations - - use_is_even_rather_than_modulo - - use_string_buffers - - void_checks
diff --git a/pkgs/shelf_packages_handler/mono_pkg.yaml b/pkgs/shelf_packages_handler/mono_pkg.yaml index 75b1a04..80a16a3 100644 --- a/pkgs/shelf_packages_handler/mono_pkg.yaml +++ b/pkgs/shelf_packages_handler/mono_pkg.yaml
@@ -1,5 +1,5 @@ sdk: -- 2.12.0 +- 2.14.0 - dev stages:
diff --git a/pkgs/shelf_packages_handler/pubspec.yaml b/pkgs/shelf_packages_handler/pubspec.yaml index 652a99a..26836b1 100644 --- a/pkgs/shelf_packages_handler/pubspec.yaml +++ b/pkgs/shelf_packages_handler/pubspec.yaml
@@ -5,7 +5,7 @@ repository: https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_packages_handler environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.14.0 <3.0.0' dependencies: path: ^1.8.0 @@ -13,7 +13,7 @@ shelf_static: ^1.0.0 dev_dependencies: - pedantic: ^1.10.0 + lints: ^1.0.0 test: ^1.16.0 dependency_overrides:
diff --git a/pkgs/shelf_proxy/lib/shelf_proxy.dart b/pkgs/shelf_proxy/lib/shelf_proxy.dart index 1bd7dc3..7bd3c86 100644 --- a/pkgs/shelf_proxy/lib/shelf_proxy.dart +++ b/pkgs/shelf_proxy/lib/shelf_proxy.dart
@@ -22,7 +22,7 @@ /// /// [proxyName] is used in headers to identify this proxy. It should be a valid /// HTTP token or a hostname. It defaults to `shelf_proxy`. -Handler proxyHandler(url, {http.Client? client, String? proxyName}) { +Handler proxyHandler(Object url, {http.Client? client, String? proxyName}) { Uri uri; if (url is String) { uri = Uri.parse(url);
diff --git a/pkgs/shelf_proxy/test/shelf_proxy_test.dart b/pkgs/shelf_proxy/test/shelf_proxy_test.dart index b1ec3fd..822d262 100644 --- a/pkgs/shelf_proxy/test/shelf_proxy_test.dart +++ b/pkgs/shelf_proxy/test/shelf_proxy_test.dart
@@ -173,7 +173,7 @@ /// /// [targetPath] is the root-relative path on the target server to proxy to. It /// defaults to `/`. -Future createProxy(shelf.Handler handler, {String? targetPath}) async { +Future<void> createProxy(shelf.Handler handler, {String? targetPath}) async { handler = expectAsync1(handler, reason: 'target server handler'); final targetServer = await shelf_io.serve(handler, 'localhost', 0); targetUri = Uri.parse('http://localhost:${targetServer.port}');
diff --git a/pkgs/shelf_router/CHANGELOG.md b/pkgs/shelf_router/CHANGELOG.md new file mode 100644 index 0000000..a8c0eb0 --- /dev/null +++ b/pkgs/shelf_router/CHANGELOG.md
@@ -0,0 +1,66 @@ +## v1.1.2 + + * Remove trailing slash requirement when using `mount`. + +## v1.1.1 + + * Fix `Router.routeNotFound` to enable multiple `read()` calls on it. + +## v1.1.0 + * `params` is deprecated in favor of `Request.params` adding using an extension + on `Request`. + * The default `notFoundHandler` now returns a sentinel `routeNotFound` response + object which causes 404 with the message 'Route not found'. + * __Minor breaking__: Handlers and sub-routers that return the sentinel + `routeNotFound` response object will be ignored and pattern matching will + continue on additional routes/handlers. + +Changing the router to continue pattern matching additional routes if a matched +_handler_ or _nested router_ returns the sentinel `routeNotFound` response +object is technically a _breaking change_. However, it only affects scenarios +where the request matches a _mounted sub-router_, but does not match any route +on this sub-router. In this case, `shelf_router` version `1.0.0` would +immediately respond 404, without attempting to match further routes. With this +release, the behavior changes to matching additional routes until one returns +a custom 404 response object, or all routes have been matched. + +This behavior is more in line with how `shelf_router` version `0.7.x` worked, +and since many affected users consider the behavior from `1.0.0` a defect, +we decided to remedy the situation. + +## v1.0.0 + + * Migrate package to null-safety + * Since handlers are not allowed to return `null` in `shelf` 1.0.0, a router + will return a default 404 response instead. + This behavior can be overridden with the `notFoundHandler` constructor + parameter. + * __Breaking__: Remove deprecated `Router.handler` getter. + The router itself is a handler. + +## v0.7.4 + + * Update `Router.mount` parameter to accept a `Handler`. + * Make `Router` to be considered a `Handler`. + * Deprecate the `Router.handler` getter. + +## v0.7.3 + + * Added `@sealed` annotation to `Router` and `Route`. + +## v0.7.2 + + * Always register a `HEAD` handler whenever a `GET` handler is registered. + Defaulting to calling the `GET` handler and throwing away the body. + +## v0.7.1 + + * Use `Function` instead of `dynamic` in `RouterEntry` to improve typing. + +## v0.7.0+1 + + * Fixed description to fit size recommendations. + +## v0.7.0 + + * Initial release
diff --git a/pkgs/shelf_router/LICENSE b/pkgs/shelf_router/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/pkgs/shelf_router/LICENSE
@@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file
diff --git a/pkgs/shelf_router/README.md b/pkgs/shelf_router/README.md new file mode 100644 index 0000000..7558cdc --- /dev/null +++ b/pkgs/shelf_router/README.md
@@ -0,0 +1,45 @@ +# Web Request Router for Shelf + +[Shelf][shelf] makes it easy to build web +applications in Dart by composing request handlers. This package offers a +request router for Shelf, matching request to handlers using route patterns. + +Also see the [`shelf_router_generator`][shelf_router_generator] package +for how to automatically generate +a `Route` using the `Route` annotation in this package. + +## Example + +```dart +import 'package:shelf_router/shelf_router.dart'; +import 'package:shelf/shelf.dart'; +import 'package:shelf/shelf_io.dart' as io; + +var app = Router(); + +app.get('/hello', (Request request) { + return Response.ok('hello-world'); +}); + +app.get('/user/<user>', (Request request, String user) { + return Response.ok('hello $user'); +}); + +var server = await io.serve(app, 'localhost', 8080); +``` + +See reference documentation of `Router` class for more information. + +## See also + * Package [`shelf`][shelf] for which this package can create routers. + * Package [`shelf_router_generator`][shelf_router_generator] which can generate + a router using source code annotations. + * Third-party tutorial by [creativebracket.com]: + * Video: [Build RESTful Web APIs with shelf_router][1] + * Sample: [repository for tutorial][2] + +[shelf]: https://pub.dev/packages/shelf +[shelf_router_generator]: https://pub.dev/packages/shelf_router_generator +[creativebracket.com]: https://creativebracket.com/ +[1]: https://www.youtube.com/watch?v=v7FhaV9e3yY +[2]: https://github.com/graphicbeacon/shelf_router_api_tutorial
diff --git a/pkgs/shelf_router/analysis_options.yaml b/pkgs/shelf_router/analysis_options.yaml new file mode 100644 index 0000000..572dd23 --- /dev/null +++ b/pkgs/shelf_router/analysis_options.yaml
@@ -0,0 +1 @@ +include: package:lints/recommended.yaml
diff --git a/pkgs/shelf_router/example/main.dart b/pkgs/shelf_router/example/main.dart new file mode 100644 index 0000000..ebd7dfd --- /dev/null +++ b/pkgs/shelf_router/example/main.dart
@@ -0,0 +1,85 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:async' show Future; +import 'package:shelf_router/shelf_router.dart'; +import 'package:shelf/shelf.dart'; +import 'package:shelf/shelf_io.dart' as shelf_io; + +class Service { + // The [Router] can be used to create a handler, which can be used with + // [shelf_io.serve]. + Handler get handler { + final router = Router(); + + // Handlers can be added with `router.<verb>('<route>', handler)`, the + // '<route>' may embed URL-parameters, and these may be taken as parameters + // by the handler (but either all URL parameters or no URL parameters, must + // be taken parameters by the handler). + router.get('/say-hi/<name>', (Request request, String name) { + return Response.ok('hi $name'); + }); + + // Embedded URL parameters may also be associated with a regular-expression + // that the pattern must match. + router.get('/user/<userId|[0-9]+>', (Request request, String userId) { + return Response.ok('User has the user-number: $userId'); + }); + + // Handlers can be asynchronous (returning `FutureOr` is also allowed). + router.get('/wave', (Request request) async { + await Future.delayed(Duration(milliseconds: 100)); + return Response.ok('_o/'); + }); + + // Other routers can be mounted... + router.mount('/api/', Api().router); + + // You can catch all verbs and use a URL-parameter with a regular expression + // that matches everything to catch app. + router.all('/<ignored|.*>', (Request request) { + return Response.notFound('Page not found'); + }); + + return router; + } +} + +class Api { + Future<Response> _messages(Request request) async { + return Response.ok('[]'); + } + + // By exposing a [Router] for an object, it can be mounted in other routers. + Router get router { + final router = Router(); + + // A handler can have more that one route. + router.get('/messages', _messages); + router.get('/messages/', _messages); + + // This nested catch-all, will only catch /api/.* when mounted above. + // Notice that ordering if annotated handlers and mounts is significant. + router.all('/<ignored|.*>', (Request request) => Response.notFound('null')); + + return router; + } +} + +// Run shelf server and host a [Service] instance on port 8080. +void main() async { + final service = Service(); + final server = await shelf_io.serve(service.handler, 'localhost', 8080); + print('Server running on localhost:${server.port}'); +}
diff --git a/pkgs/shelf_router/lib/shelf_router.dart b/pkgs/shelf_router/lib/shelf_router.dart new file mode 100644 index 0000000..40b2e35 --- /dev/null +++ b/pkgs/shelf_router/lib/shelf_router.dart
@@ -0,0 +1,85 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// A request routing library for shelf. +/// +/// When writing a shelf web server it is often desirable to route requests to +/// different handlers based on HTTP method and path patterns. The following +/// example demonstrates how to do this using [Router]. +/// +/// **Example** +/// ```dart +/// import 'package:shelf_router/shelf_router.dart'; +/// import 'package:shelf/shelf.dart' show Request, Response; +/// import 'package:shelf/shelf_io.dart' as io; +/// +/// void main() async { +/// // Create a router +/// final router = Router(); +/// +/// // Handle GET requests with a path matching ^/say-hello/[^\]*$ +/// router.get('/say-hello/<name>', (Request request, String name) async { +/// return Response.ok('hello $name'); +/// }); +/// +/// // Listen for requests on port localhost:8080 +/// await io.serve(router, 'localhost', 8080); +/// } +/// ``` +/// +/// As it is often useful to organize request handlers in classes, methods can +/// be annotated with the [Route] annotation, allowing the +/// `shelf_router_generator` package to generated a method for creating a +/// [Router] wrapping the class. +/// +/// To automatically generate add the `shelf_router_generator` and +/// `build_runner` packages to `dev_dependencies`. The follow the example +/// below and generate code using `pub run build_runner build`. +/// +/// **Example**, assume file name is `hello.dart`. +/// ```dart +/// import 'package:shelf_router/shelf_router.dart'; +/// import 'package:shelf/shelf.dart' show Request, Response; +/// import 'package:shelf/shelf_io.dart' as io; +/// +/// // include the generated part, assumes current file is 'hello.dart'. +/// part 'hello.g.dart'; +/// +/// class HelloService { +/// // Annotate a handler with the `Route` annotation. +/// @Route.get('/say-hello/<name>') +/// Future<Response> _sayHello(Request request, String name) async { +/// return Response.ok('hello $name'); +/// } +/// +/// // Use the generated function `_$<ClassName>Router(<ClassName> instance)` +/// // to create a getter returning a `Router` for this instance of +/// // `HelloService` +/// Router get router => _$HelloServiceRouter(this); +/// } +/// +/// void main() async { +/// // Create a `HelloService` instance +/// final service = HelloService(); +/// +/// await io.serve(service.router.handler, 'localhost', 8080); +/// } +/// ``` +/// +library shelf_router; + +import 'package:shelf_router/src/router.dart'; +import 'package:shelf_router/src/route.dart'; +export 'package:shelf_router/src/router.dart'; +export 'package:shelf_router/src/route.dart';
diff --git a/pkgs/shelf_router/lib/src/route.dart b/pkgs/shelf_router/lib/src/route.dart new file mode 100644 index 0000000..62da18f --- /dev/null +++ b/pkgs/shelf_router/lib/src/route.dart
@@ -0,0 +1,92 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:meta/meta.dart' show sealed; +import 'package:shelf_router/src/router.dart'; + +/// Annotation for handler methods that requests should be routed when using +/// package `shelf_router_generator`. +/// +/// The `shelf_router_generator` packages makes it easy to generate a function +/// that wraps your class and returns a [Router] that forwards requests to +/// annotated methods. Simply add the `shelf_router_generator` and +/// `build_runner` packages to `dev_dependencies`, write as illustrated in the +/// following example and run `pub run build_runner build` to generate code. +/// +/// **Example** +/// ```dart +/// // Always import 'shelf_router' without 'show' or 'as'. +/// import 'package:shelf_router/shelf_router.dart'; +/// import 'package:shelf/shelf.dart' show Request, Response; +/// +/// // Include generated code, this assumes current file is 'my_service.dart'. +/// part 'my_service.g.dart'; +/// +/// class MyService { +/// @Route.get('/say-hello/<name>') +/// Future<Response> _sayHello(Request request, String name) async { +/// return Response.ok('hello $name'); +/// } +/// +/// /// Get a router for this service. +/// Router get router => _$MyServiceRouter(this); +/// } +/// ``` +/// +/// It is also permitted to annotate public members, the only requirement is +/// that the member has a signature accepted by [Router] as `handler`. +@sealed +class Route { + /// HTTP verb for requests routed to the annotated method. + final String verb; + + /// HTTP route for request routed to the annotated method. + final String route; + + /// Create an annotation that routes requests matching [verb] and [route] to + /// the annotated method. + const Route(this.verb, this.route); + + /// Route all requests matching [route] to annotated method. + const Route.all(this.route) : verb = r'$all'; + + /// Route `GET` requests matching [route] to annotated method. + const Route.get(this.route) : verb = 'GET'; + + /// Route `HEAD` requests matching [route] to annotated method. + const Route.head(this.route) : verb = 'HEAD'; + + /// Route `POST` requests matching [route] to annotated method. + const Route.post(this.route) : verb = 'POST'; + + /// Route `PUT` requests matching [route] to annotated method. + const Route.put(this.route) : verb = 'PUT'; + + /// Route `DELETE` requests matching [route] to annotated method. + const Route.delete(this.route) : verb = 'DELETE'; + + /// Route `CONNECT` requests matching [route] to annotated method. + const Route.connect(this.route) : verb = 'CONNECT'; + + /// Route `OPTIONS` requests matching [route] to annotated method. + const Route.options(this.route) : verb = 'OPTIONS'; + + /// Route `TRACE` requests matching [route] to annotated method. + const Route.trace(this.route) : verb = 'TRACE'; + + /// Route `MOUNT` requests matching [route] to annotated method. + const Route.mount(String prefix) + : verb = r'$mount', + route = prefix; +}
diff --git a/pkgs/shelf_router/lib/src/router.dart b/pkgs/shelf_router/lib/src/router.dart new file mode 100644 index 0000000..9460788 --- /dev/null +++ b/pkgs/shelf_router/lib/src/router.dart
@@ -0,0 +1,305 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:collection' show UnmodifiableMapView; +import 'dart:convert'; + +import 'package:http_methods/http_methods.dart'; +import 'package:meta/meta.dart' show sealed; +import 'package:shelf/shelf.dart'; +import 'package:shelf_router/src/router_entry.dart' show RouterEntry; + +/// Get a URL parameter captured by the [Router]. +@Deprecated('Use Request.params instead') +String params(Request request, String name) { + final value = request.params[name]; + if (value == null) { + throw Exception('no such parameter $name'); + } + return value; +} + +final _emptyParams = UnmodifiableMapView(<String, String>{}); + +extension RouterParams on Request { + /// Get URL parameters captured by the [Router]. + /// + /// **Example** + /// ```dart + /// final app = Router(); + /// + /// app.get('/hello/<name>', (Request request) { + /// final name = request.params['name']; + /// return Response.ok('Hello $name'); + /// }); + /// ``` + /// + /// If no parameters are captured this returns an empty map. + /// + /// The returned map is unmodifiable. + Map<String, String> get params { + final p = context['shelf_router/params']; + if (p is Map<String, String>) { + return UnmodifiableMapView(p); + } + return _emptyParams; + } +} + +/// Middleware to remove body from request. +final _removeBody = createMiddleware(responseHandler: (r) { + if (r.headers.containsKey('content-length')) { + r = r.change(headers: {'content-length': '0'}); + } + return r.change(body: <int>[]); +}); + +/// A shelf [Router] routes requests to handlers based on HTTP verb and route +/// pattern. +/// +/// ```dart +/// import 'package:shelf_router/shelf_router.dart'; +/// import 'package:shelf/shelf.dart'; +/// import 'package:shelf/shelf_io.dart' as io; +/// +/// var app = Router(); +/// +/// // Route pattern parameters can be specified <paramName> +/// app.get('/users/<userName>/whoami', (Request request) async { +/// // The matched values can be read with params(request, param) +/// var userName = request.params['userName']; +/// return Response.ok('You are ${userName}'); +/// }); +/// +/// // The matched value can also be taken as parameter, if the handler given +/// // doesn't implement Handler, it's assumed to take all parameters in the +/// // order they appear in the route pattern. +/// app.get('/users/<userName>/say-hello', (Request request, String userName) async { +/// assert(userName == request.params['userName']); +/// return Response.ok('Hello ${userName}'); +/// }); +/// +/// // It is possible to have multiple parameters, and if desired a custom +/// // regular expression can be specified with <paramName|REGEXP>, where +/// // REGEXP is a regular expression (leaving out ^ and $). +/// // If no regular expression is specified `[^/]+` will be used. +/// app.get('/users/<userName>/messages/<msgId|\d+>', (Request request) async { +/// var msgId = int.parse(request.params['msgId']!); +/// return Response.ok(message.getById(msgId)); +/// }); +/// +/// var server = await io.serve(app, 'localhost', 8080); +/// ``` +/// +/// If multiple routes match the same request, the handler for the first +/// route is called. +/// If no route matches a request, a [Response.notFound] will be returned +/// instead. The default matcher can be overridden with the `notFoundHandler` +/// constructor parameter. +@sealed +class Router { + final List<RouterEntry> _routes = []; + final Handler _notFoundHandler; + + /// Creates a new [Router] routing requests to handlers. + /// + /// The [notFoundHandler] will be invoked for requests where no matching route + /// was found. By default, a simple [Response.notFound] will be used instead. + Router({Handler notFoundHandler = _defaultNotFound}) + : _notFoundHandler = notFoundHandler; + + /// Add [handler] for [verb] requests to [route]. + /// + /// If [verb] is `GET` the [handler] will also be called for `HEAD` requests + /// matching [route]. This is because handling `GET` requests without handling + /// `HEAD` is always wrong. To explicitely implement a `HEAD` handler it must + /// be registered before the `GET` handler. + void add(String verb, String route, Function handler) { + if (!isHttpMethod(verb)) { + throw ArgumentError.value(verb, 'verb', 'expected a valid HTTP method'); + } + verb = verb.toUpperCase(); + + if (verb == 'GET') { + // Handling in a 'GET' request without handling a 'HEAD' request is always + // wrong, thus, we add a default implementation that discards the body. + _routes.add(RouterEntry('HEAD', route, handler, middleware: _removeBody)); + } + _routes.add(RouterEntry(verb, route, handler)); + } + + /// Handle all request to [route] using [handler]. + void all(String route, Function handler) { + _routes.add(RouterEntry('ALL', route, handler)); + } + + /// Mount a handler below a prefix. + /// + /// In this case prefix may not contain any parameters, nor + void mount(String prefix, Handler handler) { + if (!prefix.startsWith('/')) { + throw ArgumentError.value(prefix, 'prefix', 'must start with a slash'); + } + + // first slash is always in request.handlerPath + final path = prefix.substring(1); + if (prefix.endsWith('/')) { + all(prefix + '<path|[^]*>', (Request request) { + return handler(request.change(path: path)); + }); + } else { + all(prefix, (Request request) { + return handler(request.change(path: path)); + }); + all(prefix + '/<path|[^]*>', (Request request) { + return handler(request.change(path: path + '/')); + }); + } + } + + /// Route incoming requests to registered handlers. + /// + /// This method allows a Router instance to be a [Handler]. + Future<Response> call(Request request) async { + // Note: this is a great place to optimize the implementation by building + // a trie for faster matching... left as an exercise for the reader :) + for (var route in _routes) { + if (route.verb != request.method.toUpperCase() && route.verb != 'ALL') { + continue; + } + var params = route.match('/' + request.url.path); + if (params != null) { + final response = await route.invoke(request, params); + if (response != routeNotFound) { + return response; + } + } + } + return _notFoundHandler(request); + } + + // Handlers for all methods + + /// Handle `GET` request to [route] using [handler]. + /// + /// If no matching handler for `HEAD` requests is registered, such requests + /// will also be routed to the [handler] registered here. + void get(String route, Function handler) => add('GET', route, handler); + + /// Handle `HEAD` request to [route] using [handler]. + void head(String route, Function handler) => add('HEAD', route, handler); + + /// Handle `POST` request to [route] using [handler]. + void post(String route, Function handler) => add('POST', route, handler); + + /// Handle `PUT` request to [route] using [handler]. + void put(String route, Function handler) => add('PUT', route, handler); + + /// Handle `DELETE` request to [route] using [handler]. + void delete(String route, Function handler) => add('DELETE', route, handler); + + /// Handle `CONNECT` request to [route] using [handler]. + void connect(String route, Function handler) => + add('CONNECT', route, handler); + + /// Handle `OPTIONS` request to [route] using [handler]. + void options(String route, Function handler) => + add('OPTIONS', route, handler); + + /// Handle `TRACE` request to [route] using [handler]. + void trace(String route, Function handler) => add('TRACE', route, handler); + + /// Handle `PATCH` request to [route] using [handler]. + void patch(String route, Function handler) => add('PATCH', route, handler); + + static Response _defaultNotFound(Request request) => routeNotFound; + + /// Sentinel [Response] object indicating that no matching route was found. + /// + /// This is the default response value from a [Router] created without a + /// `notFoundHandler`, when no routes matches the incoming request. + /// + /// If the [routeNotFound] object is returned from a [Handler] the [Router] + /// will consider the route _not matched_, and attempt to match other routes. + /// This is useful when mounting nested routers, or when matching a route + /// is conditioned on properties beyond the path of the URL. + /// + /// **Example** + /// ```dart + /// final app = Router(); + /// + /// // The pattern for this route will match '/search' and '/search?q=...', + /// // but if request does not have `?q=...', then the handler will return + /// // [Router.routeNotFound] causing the router to attempt further routes. + /// app.get('/search', (Request request) async { + /// if (!request.uri.queryParameters.containsKey('q')) { + /// return Router.routeNotFound; + /// } + /// return Response.ok('TODO: make search results'); + /// }); + /// + /// // Same pattern as above + /// app.get('/search', (Request request) async { + /// return Response.ok('TODO: return search form'); + /// }); + /// + /// // Create a single nested router we can mount for handling API requests. + /// final api = Router(); + /// + /// api.get('/version', (Request request) => Response.ok('1')); + /// + /// // Mounting router under '/api' + /// app.mount('/api', api); + /// + /// // If a request matches `/api/...` then the routes in the [api] router + /// // will be attempted. However, for a request like `/api/hello` there is + /// // no matching route in the [api] router. Thus, the router will return + /// // [Router.routeNotFound], which will cause matching to continue. + /// // Hence, the catch-all route below will be matched, causing a custom 404 + /// // response with message 'nothing found'. + /// + /// // In the pattern below `<anything|.*>` is on the form `<name|regex>`, + /// // thus, this simply creates a URL parameter called `anything` which + /// // matches anything. + /// app.all('/<anything|.*>', (Request request) { + /// return Response.notFound('nothing found'); + /// }); + /// ``` + static final Response routeNotFound = _RouteNotFoundResponse(); +} + +/// Extends [Response] to allow it to be used multiple times in the +/// actual content being served. +class _RouteNotFoundResponse extends Response { + static const _message = 'Route not found'; + static final _messageBytes = utf8.encode(_message); + + _RouteNotFoundResponse() : super.notFound(_message); + + @override + Stream<List<int>> read() => Stream<List<int>>.value(_messageBytes); + + @override + Response change({ + Map<String, /* String | List<String> */ Object?>? headers, + Map<String, Object?>? context, + body, + }) { + return super.change( + headers: headers, + context: context, + body: body ?? _message, + ); + } +}
diff --git a/pkgs/shelf_router/lib/src/router_entry.dart b/pkgs/shelf_router/lib/src/router_entry.dart new file mode 100644 index 0000000..4860ef3 --- /dev/null +++ b/pkgs/shelf_router/lib/src/router_entry.dart
@@ -0,0 +1,113 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:async'; +import 'package:shelf/shelf.dart'; + +/// Check if the [regexp] is non-capturing. +bool _isNoCapture(String regexp) { + // Construct a new regular expression matching anything containing regexp, + // then match with empty-string and count number of groups. + return RegExp('^(?:$regexp)|.*\$').firstMatch('')!.groupCount == 0; +} + +/// Entry in the router. +/// +/// This class implements the logic for matching the path pattern. +class RouterEntry { + /// Pattern for parsing the route pattern + static final RegExp _parser = RegExp(r'([^<]*)(?:<([^>|]+)(?:\|([^>]*))?>)?'); + + final String verb, route; + final Function _handler; + final Middleware _middleware; + + /// Expression that the request path must match. + /// + /// This also captures any parameters in the route pattern. + final RegExp _routePattern; + + /// Names for the parameters in the route pattern. + final List<String> _params; + + /// List of parameter names in the route pattern. + List<String> get params => _params.toList(); // exposed for using generator. + + RouterEntry._(this.verb, this.route, this._handler, this._middleware, + this._routePattern, this._params); + + factory RouterEntry( + String verb, + String route, + Function handler, { + Middleware? middleware, + }) { + middleware = middleware ?? ((Handler fn) => fn); + + if (!route.startsWith('/')) { + throw ArgumentError.value( + route, 'route', 'expected route to start with a slash'); + } + + final params = <String>[]; + var pattern = ''; + for (var m in _parser.allMatches(route)) { + pattern += RegExp.escape(m[1]!); + if (m[2] != null) { + params.add(m[2]!); + if (m[3] != null && !_isNoCapture(m[3]!)) { + throw ArgumentError.value( + route, 'route', 'expression for "${m[2]}" is capturing'); + } + pattern += '(${m[3] ?? r'[^/]+'})'; + } + } + final routePattern = RegExp('^$pattern\$'); + + return RouterEntry._( + verb, route, handler, middleware, routePattern, params); + } + + /// Returns a map from parameter name to value, if the path matches the + /// route pattern. Otherwise returns null. + Map<String, String>? match(String path) { + // Check if path matches the route pattern + var m = _routePattern.firstMatch(path); + if (m == null) { + return null; + } + // Construct map from parameter name to matched value + var params = <String, String>{}; + for (var i = 0; i < _params.length; i++) { + // first group is always the full match, we ignore this group. + params[_params[i]] = m[i + 1]!; + } + return params; + } + + // invoke handler with given request and params + Future<Response> invoke(Request request, Map<String, String> params) async { + request = request.change(context: {'shelf_router/params': params}); + + return await _middleware((request) async { + if (_handler is Handler || _params.isEmpty) { + return await _handler(request); + } + return await Function.apply(_handler, [ + request, + ..._params.map((n) => params[n]), + ]); + })(request); + } +}
diff --git a/pkgs/shelf_web_socket/mono_pkg.yml b/pkgs/shelf_router/mono_pkg.yaml similarity index 78% copy from pkgs/shelf_web_socket/mono_pkg.yml copy to pkgs/shelf_router/mono_pkg.yaml index 75b1a04..84c7b48 100644 --- a/pkgs/shelf_web_socket/mono_pkg.yml +++ b/pkgs/shelf_router/mono_pkg.yaml
@@ -1,5 +1,5 @@ sdk: -- 2.12.0 +- 2.14.0 - dev stages: @@ -10,6 +10,3 @@ - dev - unit_test: - test: --test-randomize-ordering-seed=random - os: - - linux - - windows
diff --git a/pkgs/shelf_router/pubspec.yaml b/pkgs/shelf_router/pubspec.yaml new file mode 100644 index 0000000..99a1e46 --- /dev/null +++ b/pkgs/shelf_router/pubspec.yaml
@@ -0,0 +1,18 @@ +name: shelf_router +version: 1.1.2 +description: | + A convinent request router for the shelf web-framework, with support for + URL-parameters, nested routers and routers generated from source annotations. +homepage: https://github.com/google/dart-neats/tree/master/shelf_router +repository: https://github.com/google/dart-neats.git +issue_tracker: https://github.com/google/dart-neats/labels/pkg:shelf_router +dependencies: + meta: ^1.3.0 + shelf: ^1.0.0 + http_methods: ^1.1.0 +dev_dependencies: + test: ^1.16.0 + lints: ^1.0.0 + http: ^0.13.0 +environment: + sdk: '>=2.12.0 <3.0.0'
diff --git a/pkgs/shelf_router/test/route_entry_test.dart b/pkgs/shelf_router/test/route_entry_test.dart new file mode 100644 index 0000000..cb6a514 --- /dev/null +++ b/pkgs/shelf_router/test/route_entry_test.dart
@@ -0,0 +1,73 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:shelf_router/src/router_entry.dart' show RouterEntry; +import 'package:test/test.dart'; + +void main() { + void testPattern( + String pattern, { + Map<String, Map<String, String>> match = const {}, + List<String> notMatch = const [], + }) { + group('RouterEntry: "$pattern"', () { + final r = RouterEntry('GET', pattern, () => null); + for (final e in match.entries) { + test('Matches "${e.key}"', () { + expect(r.match(e.key), equals(e.value)); + }); + } + for (final v in notMatch) { + test('NotMatch "$v"', () { + expect(r.match(v), isNull); + }); + } + }); + } + + testPattern('/hello', match: { + '/hello': {}, + }, notMatch: [ + '/not-hello', + '/', + ]); + + testPattern(r'/user/<user>/groups/<group|\d+>', match: { + '/user/jonasfj/groups/42': { + 'user': 'jonasfj', + 'group': '42', + }, + '/user/jonasfj/groups/0': { + 'user': 'jonasfj', + 'group': '0', + }, + '/user/123/groups/101': { + 'user': '123', + 'group': '101', + }, + }, notMatch: [ + '/user/', + '/user/jonasfj/groups/5-3', + '/user/jonasfj/test/groups/5', + '/user/jonasfjtest/groups/4/', + '/user/jonasfj/groups/', + '/not-hello', + '/', + ]); + + test('non-capture regex only', () { + expect(() => RouterEntry('GET', '/users/<user|([^]*)>/info', () {}), + throwsA(anything)); + }); +}
diff --git a/pkgs/shelf_router/test/router_test.dart b/pkgs/shelf_router/test/router_test.dart new file mode 100644 index 0000000..f664f56 --- /dev/null +++ b/pkgs/shelf_router/test/router_test.dart
@@ -0,0 +1,205 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +@TestOn('vm') +import 'dart:async'; +import 'dart:io'; + +import 'package:http/http.dart' as http; +import 'package:shelf/shelf.dart'; +import 'package:shelf/shelf_io.dart' as io; +import 'package:shelf_router/shelf_router.dart'; +import 'package:test/test.dart'; + +void main() { + // Create a server that listens on localhost for testing + late io.IOServer server; + + setUp(() async { + try { + server = await io.IOServer.bind(InternetAddress.loopbackIPv6, 0); + } on SocketException catch (_) { + server = await io.IOServer.bind(InternetAddress.loopbackIPv4, 0); + } + }); + + tearDown(() => server.close()); + + Future<String> get(String path) => + http.read(Uri.parse(server.url.toString() + path)); + Future<int> head(String path) async => + (await http.head(Uri.parse(server.url.toString() + path))).statusCode; + + test('get sync/async handler', () async { + var app = Router(); + + app.get('/sync-hello', (Request request) { + return Response.ok('hello-world'); + }); + + app.get('/async-hello', (Request request) async { + return Future.microtask(() { + return Response.ok('hello-world'); + }); + }); + + // check that catch-alls work + app.all('/<path|[^]*>', (Request request) { + return Response.ok('not-found'); + }); + + server.mount(app); + + expect(await get('/sync-hello'), 'hello-world'); + expect(await get('/async-hello'), 'hello-world'); + expect(await get('/wrong-path'), 'not-found'); + + expect(await head('/sync-hello'), 200); + expect(await head('/async-hello'), 200); + expect(await head('/wrong-path'), 200); + }); + + test('params', () async { + var app = Router(); + + app.get(r'/user/<user>/groups/<group|\d+>', (Request request) { + final user = request.params['user']; + final group = request.params['group']; + return Response.ok('$user / $group'); + }); + + server.mount(app); + + expect(await get('/user/jonasfj/groups/42'), 'jonasfj / 42'); + }); + + test('params by arguments', () async { + var app = Router(); + + app.get(r'/user/<user>/groups/<group|\d+>', + (Request request, String user, String group) { + return Response.ok('$user / $group'); + }); + + server.mount(app); + + expect(await get('/user/jonasfj/groups/42'), 'jonasfj / 42'); + }); + + test('mount(Router)', () async { + var api = Router(); + api.get('/user/<user>/info', (Request request, String user) { + return Response.ok('Hello $user'); + }); + + var app = Router(); + app.get('/hello', (Request request) { + return Response.ok('hello-world'); + }); + + app.mount('/api/', api); + + app.all('/<_|[^]*>', (Request request) { + return Response.ok('catch-all-handler'); + }); + + server.mount(app); + + expect(await get('/hello'), 'hello-world'); + expect(await get('/api/user/jonasfj/info'), 'Hello jonasfj'); + expect(await get('/api/user/jonasfj/info-wrong'), 'catch-all-handler'); + }); + + test('mount(Handler) with middleware', () async { + var api = Router(); + api.get('/hello', (Request request) { + return Response.ok('Hello'); + }); + + final middleware = createMiddleware( + requestHandler: (request) { + if (request.url.queryParameters.containsKey('ok')) { + return Response.ok('middleware'); + } + return null; + }, + ); + + var app = Router(); + app.mount( + '/api/', + Pipeline().addMiddleware(middleware).addHandler(api), + ); + + server.mount(app); + + expect(await get('/api/hello'), 'Hello'); + expect(await get('/api/hello?ok'), 'middleware'); + }); + + test('mount(Router) does not require a trailing slash', () async { + var api = Router(); + api.get('/', (Request request) { + return Response.ok('Hello World!'); + }); + + api.get('/user/<user>/info', (Request request, String user) { + return Response.ok('Hello $user'); + }); + + var app = Router(); + app.get('/hello', (Request request) { + return Response.ok('hello-world'); + }); + + app.mount('/api', api); + + app.all('/<_|[^]*>', (Request request) { + return Response.ok('catch-all-handler'); + }); + + server.mount(app); + + expect(await get('/hello'), 'hello-world'); + expect(await get('/api'), 'Hello World!'); + expect(await get('/api/'), 'Hello World!'); + expect(await get('/api/user/jonasfj/info'), 'Hello jonasfj'); + expect(await get('/api/user/jonasfj/info-wrong'), 'catch-all-handler'); + }); + + test('responds with 404 if no handler matches', () { + var api = Router()..get('/hello', (request) => Response.ok('Hello')); + server.mount(api); + + expect( + get('/hi'), + throwsA(isA<http.ClientException>() + .having((e) => e.message, 'message', contains('404: Not Found.')))); + }); + + test('can invoke custom handler if no route matches', () { + var api = Router(notFoundHandler: (req) => Response.ok('Not found, but ok')) + ..get('/hello', (request) => Response.ok('Hello')); + server.mount(api); + + expect(get('/hi'), completion('Not found, but ok')); + }); + + test('can call Router.routeNotFound.read multiple times', () async { + final b1 = await Router.routeNotFound.readAsString(); + expect(b1, 'Route not found'); + final b2 = await Router.routeNotFound.readAsString(); + expect(b2, b1); + }); +}
diff --git a/pkgs/shelf_router_generator/CHANGELOG.md b/pkgs/shelf_router_generator/CHANGELOG.md new file mode 100644 index 0000000..a5ff54d --- /dev/null +++ b/pkgs/shelf_router_generator/CHANGELOG.md
@@ -0,0 +1,48 @@ +## v1.0.2 + +* Support update-to-date dependencies. + +## v1.0.1 + +* Support update-to-date dependencies. + +## v1.0.0 + + * Support update-to-date dependencies. + * Migrate implementation to null safety. + * Generate null-safe code. + +## v0.7.2+4 + + * Relax dependency constraint on `analyzer`. + +## v0.7.2+3 + + * Relax dependency constraint on `analyzer`. + +## v0.7.2+2 + + * Relax dependency constraint on `analyzer`. + +## v0.7.2+1 + + * Relax dependency constraint on `analyzer`. + +## v0.7.2 + + * Use `literalString('...', raw: true)` from `package:code_builder` to ensure + that generated strings are properly escaped (fixing [#10][issue-10]). + +[issue-10]: https://github.com/google/dart-neats/issues/10 + +## v0.7.1 + + * Bumped dependencies. + +## v0.7.0+1 + + * Updated `README.md` with references to package `build_runner`. + +## v0.7.0 + + * Initial release
diff --git a/pkgs/shelf_router_generator/LICENSE b/pkgs/shelf_router_generator/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/pkgs/shelf_router_generator/LICENSE
@@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file
diff --git a/pkgs/shelf_router_generator/README.md b/pkgs/shelf_router_generator/README.md new file mode 100644 index 0000000..75eb84d --- /dev/null +++ b/pkgs/shelf_router_generator/README.md
@@ -0,0 +1,70 @@ +Shelf Router Generator +====================== + +[Shelf](https://pub.dartlang.org/packages/shelf) makes it easy to build web +applications in Dart by composing request handlers. The `shelf_router` package +offers a request router for Shelf. this package enables generating a +`shelf_route.Router` from annotations in code. + +This package should be a _development dependency_ along with +[package `build_runner`](https://pub.dartlang.org/packages/build_runner), and +used with [package `shelf`](https://pub.dartlang.org/packages/shelf) and +[package `shelf_router`](https://pub.dartlang.org/packages/shelf_router) as +dependencies. + +```yaml +dependencies: + shelf: ^0.7.5 + shelf_router: ^0.7.0+1 +dev_dependencies: + shelf_router_generator: ^0.7.0+1 + build_runner: ^1.3.1 +``` + +Once your code have been annotated as illustrated in the example below the +generated part can be created with `pub run build_runner build`. + +## Example + +```dart +import 'package:shelf/shelf.dart'; +import 'package:shelf_router/shelf_router.dart'; + +part 'userservice.g.dart'; // generated with 'pub run build_runner build' + +class UserService { + final DatabaseConnection connection; + UserService(this.connection); + + @Route.get('/users/') + Future<Response> listUsers(Request request) async { + return Response.ok('["user1"]'); + } + + @Route.get('/users/<userId>') + Future<Response> fetchUser(Request request, String userId) async { + if (userId == 'user1') { + return Response.ok('user1'); + } + return Response.notFound('no such user'); + } + + // Create router using the generate function defined in 'userservice.g.dart'. + Router get router => _$UserServiceRouter(this); +} + +void main() async { + // You can setup context, database connections, cache connections, email + // services, before you create an instance of your service. + var connection = await DatabaseConnection.connect('localhost:1234'); + + // Create an instance of your service, usine one of the constructors you've + // defined. + var service = UserService(connection); + // Service request using the router, note the router can also be mounted. + var router = service.router; + var server = await io.serve(router.handler, 'localhost', 8080); +} +``` + +
diff --git a/pkgs/shelf_proxy/analysis_options.yaml b/pkgs/shelf_router_generator/analysis_options.yaml similarity index 75% rename from pkgs/shelf_proxy/analysis_options.yaml rename to pkgs/shelf_router_generator/analysis_options.yaml index 5467db8..8cc7cd1 100644 --- a/pkgs/shelf_proxy/analysis_options.yaml +++ b/pkgs/shelf_router_generator/analysis_options.yaml
@@ -6,45 +6,30 @@ linter: rules: - - avoid_bool_literals_in_conditional_expressions - - avoid_catching_errors - - avoid_classes_with_only_static_members - avoid_dynamic_calls - - avoid_empty_else + - sort_pub_dependencies - avoid_function_literals_in_foreach_calls - avoid_private_typedef_functions - avoid_redundant_argument_values - avoid_renaming_method_parameters - - avoid_returning_null - - avoid_returning_null_for_future - avoid_returning_null_for_void - - avoid_returning_this - avoid_unused_constructor_parameters - - avoid_void_async + - await_only_futures - camel_case_types - - cancel_subscriptions - - cascade_invocations - - comment_references - constant_identifier_names - - control_flow_in_finally - directives_ordering - empty_statements - file_names - - hash_and_equals - implementation_imports - - invariant_booleans - iterable_contains_unrelated_type - join_return_with_assignment - lines_longer_than_80_chars - list_remove_unrelated_type - - literal_only_boolean_expressions - missing_whitespace_between_adjacent_strings - - no_adjacent_strings_in_list - no_runtimeType_toString - non_constant_identifier_names - only_throw_errors - overridden_fields - - package_api_docs - package_names - package_prefixed_library_names - prefer_asserts_in_initializer_lists @@ -53,7 +38,9 @@ - prefer_expression_function_bodies - prefer_final_locals - prefer_function_declarations_over_variables + - prefer_if_null_operators - prefer_initializing_formals + - prefer_inlined_adds - prefer_interpolation_to_compose_strings - prefer_is_not_operator - prefer_null_aware_operators @@ -61,15 +48,16 @@ - prefer_typing_uninitialized_variables - prefer_void_to_null - provide_deprecation_message - - sort_pub_dependencies - test_types_in_equals - throw_in_finally - - unnecessary_await_in_return + - type_annotate_public_apis + - unnecessary_brace_in_string_interps - unnecessary_lambdas - unnecessary_null_aware_assignments - unnecessary_overrides - unnecessary_parenthesis - unnecessary_statements - unnecessary_string_interpolations + - use_is_even_rather_than_modulo - use_string_buffers - void_checks
diff --git a/pkgs/shelf_router_generator/build.yaml b/pkgs/shelf_router_generator/build.yaml new file mode 100644 index 0000000..d6561c0 --- /dev/null +++ b/pkgs/shelf_router_generator/build.yaml
@@ -0,0 +1,14 @@ +targets: + $default: + builders: + shelf_router_generator|shelf_router: + enabled: true + +builders: + shelf_router: + import: "package:shelf_router_generator/builder.dart" + builder_factories: ["shelfRouter"] + build_extensions: {".dart": [".shelf_router.g.part"]} + auto_apply: dependents + build_to: cache + applies_builders: ["source_gen|combining_builder"]
diff --git a/pkgs/shelf_router_generator/example/main.dart b/pkgs/shelf_router_generator/example/main.dart new file mode 100644 index 0000000..ca68090 --- /dev/null +++ b/pkgs/shelf_router_generator/example/main.dart
@@ -0,0 +1,82 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// @dart=2.12 + +import 'dart:async' show Future; + +import 'package:shelf/shelf.dart'; +import 'package:shelf/shelf_io.dart' as shelf_io; +import 'package:shelf_router/shelf_router.dart'; + +// Generated code will be written to 'main.g.dart' +part 'main.g.dart'; + +class Service { + // A handler is annotated with @Route.<verb>('<route>'), the '<route>' may + // embed URL-parameters, and these may be taken as parameters by the handler. + // But either all URL-parameters or none of the URL parameters must be taken + // as parameters by the handler. + @Route.get('/say-hi/<name>') + Response _hi(Request request, String name) => Response.ok('hi $name'); + + // Embedded URL parameters may also be associated with a regular-expression + // that the pattern must match. + @Route.get('/user/<userId|[0-9]+>') + Response _user(Request request, String userId) => + Response.ok('User has the user-number: $userId'); + + // Handlers can be asynchronous (returning `FutureOr` is also allowed). + @Route.get('/wave') + Future<Response> _wave(Request request) async { + await Future.delayed(const Duration(milliseconds: 100)); + return Response.ok('_o/'); + } + + // Other routers can be mounted... + @Route.mount('/api/') + Router get _api => Api().router; + + // You can catch all verbs and use a URL-parameter with a regular expression + // that matches everything to catch app. + @Route.all('/<ignored|.*>') + Response _notFound(Request request) => Response.notFound('Page not found'); + + // The generated function _$ServiceRouter can be used to get a [Handler] + // for this object. This can be used with [shelf_io.serve]. + Handler get handler => _$ServiceRouter(this); +} + +class Api { + // A handler can have more that one route :) + @Route.get('/messages') + @Route.get('/messages/') + Future<Response> _messages(Request request) async => Response.ok('[]'); + + // This nested catch-all, will only catch /api/.* when mounted above. + // Notice that ordering if annotated handlers and mounts is significant. + @Route.all('/<ignored|.*>') + Response _notFound(Request request) => Response.notFound('null'); + + // The generated function _$ApiRouter can be used to expose a [Router] for + // this object. + Router get router => _$ApiRouter(this); +} + +// Run shelf server and host a [Service] instance on port 8080. +void main() async { + final service = Service(); + final server = await shelf_io.serve(service.handler, 'localhost', 8080); + print('Server running on localhost:${server.port}'); +}
diff --git a/pkgs/shelf_router_generator/example/main.g.dart b/pkgs/shelf_router_generator/example/main.g.dart new file mode 100644 index 0000000..16d9a47 --- /dev/null +++ b/pkgs/shelf_router_generator/example/main.g.dart
@@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// @dart=2.12 + +part of 'main.dart'; + +// ************************************************************************** +// ShelfRouterGenerator +// ************************************************************************** + +Router _$ServiceRouter(Service service) { + final router = Router(); + router.add('GET', r'/say-hi/<name>', service._hi); + router.add('GET', r'/user/<userId|[0-9]+>', service._user); + router.add('GET', r'/wave', service._wave); + router.mount(r'/api/', service._api); + router.all(r'/<ignored|.*>', service._notFound); + return router; +} + +Router _$ApiRouter(Api service) { + final router = Router(); + router.add('GET', r'/messages', service._messages); + router.add('GET', r'/messages/', service._messages); + router.all(r'/<ignored|.*>', service._notFound); + return router; +}
diff --git a/pkgs/shelf_router_generator/lib/builder.dart b/pkgs/shelf_router_generator/lib/builder.dart new file mode 100644 index 0000000..4430571 --- /dev/null +++ b/pkgs/shelf_router_generator/lib/builder.dart
@@ -0,0 +1,35 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// This library provides a [Builder] for generating functions that can create +/// a [shelf_router.Router] based on annotated members. +/// +/// This is **not intended** for consumption, this library should be used by +/// running `pub run build_runner build`. Using this library through other means +/// is not supported and may break arbitrarily. +library builder; + +import 'package:build/build.dart'; +import 'package:shelf_router/shelf_router.dart' as shelf_router; +import 'package:source_gen/source_gen.dart'; + +import 'src/shelf_router_generator.dart'; + +/// A [Builder] that generates a `_$<className>Router(<className> service)` +/// function for each class `<className>` containing a member annotated with +/// [shelf_router.Route]. +Builder shelfRouter(BuilderOptions _) => SharedPartBuilder( + [ShelfRouterGenerator()], + 'shelf_router', + );
diff --git a/pkgs/shelf_router_generator/lib/src/shelf_router_generator.dart b/pkgs/shelf_router_generator/lib/src/shelf_router_generator.dart new file mode 100644 index 0000000..3050ed9 --- /dev/null +++ b/pkgs/shelf_router_generator/lib/src/shelf_router_generator.dart
@@ -0,0 +1,302 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:async' show Future; + +import 'package:analyzer/dart/element/element.dart' + show ClassElement, ElementKind, ExecutableElement; +import 'package:analyzer/dart/element/type.dart' show ParameterizedType; +import 'package:build/build.dart' show BuildStep, log; +import 'package:code_builder/code_builder.dart' as code; +import 'package:http_methods/http_methods.dart' show isHttpMethod; +import 'package:shelf/shelf.dart' as shelf; +import 'package:shelf_router/shelf_router.dart' as shelf_router; + +// ignore: implementation_imports +import 'package:shelf_router/src/router_entry.dart' show RouterEntry; +import 'package:source_gen/source_gen.dart' as g; + +// Type checkers that we need later +const _routeType = g.TypeChecker.fromRuntime(shelf_router.Route); +const _routerType = g.TypeChecker.fromRuntime(shelf_router.Router); +const _responseType = g.TypeChecker.fromRuntime(shelf.Response); +const _requestType = g.TypeChecker.fromRuntime(shelf.Request); +const _stringType = g.TypeChecker.fromRuntime(String); + +/// A representation of a handler that was annotated with [Route]. +class _Handler { + final String verb, route; + final ExecutableElement element; + + _Handler(this.verb, this.route, this.element); +} + +/// Find members of a class annotated with [shelf_router.Route]. +List<ExecutableElement> getAnnotatedElementsOrderBySourceOffset( + ClassElement cls) => + <ExecutableElement>[ + ...cls.methods.where(_routeType.hasAnnotationOfExact), + ...cls.accessors.where(_routeType.hasAnnotationOfExact) + ]..sort((a, b) => (a.nameOffset).compareTo(b.nameOffset)); + +/// Generate a `_$<className>Router(<className> service)` method that returns a +/// [shelf_router.Router] configured based on annotated handlers. +code.Method _buildRouterMethod({ + required ClassElement classElement, + required List<_Handler> handlers, +}) => + code.Method( + (b) => b + ..name = '_\$${classElement.name}Router' + ..requiredParameters.add( + code.Parameter((b) => b + ..name = 'service' + ..type = code.refer(classElement.name)), + ) + ..returns = code.refer('Router') + ..body = code.Block( + (b) => b + ..addExpression( + code.refer('Router').newInstance([]).assignFinal('router')) + ..statements.addAll(handlers.map((h) => _buildAddHandlerCode( + router: code.refer('router'), + service: code.refer('service'), + handler: h, + ))) + ..addExpression(code.refer('router').returned), + ), + ); + +/// Generate the code statement that adds [handler] from [service] to [router]. +code.Code _buildAddHandlerCode({ + required code.Reference router, + required code.Reference service, + required _Handler handler, +}) { + switch (handler.verb) { + case r'$mount': + return router.property('mount').call([ + code.literalString(handler.route, raw: true), + service.property(handler.element.name), + ]).statement; + case r'$all': + return router.property('all').call([ + code.literalString(handler.route, raw: true), + service.property(handler.element.name), + ]).statement; + default: + return router.property('add').call([ + code.literalString(handler.verb.toUpperCase()), + code.literalString(handler.route, raw: true), + service.property(handler.element.name), + ]).statement; + } +} + +class ShelfRouterGenerator extends g.Generator { + @override + Future<String?> generate(g.LibraryReader library, BuildStep buildStep) async { + // Create a map from ClassElement to list of annotated elements sorted by + // offset in source code, this is not type checked yet. + final classes = <ClassElement, List<_Handler>>{}; + for (final cls in library.classes) { + final elements = getAnnotatedElementsOrderBySourceOffset(cls); + if (elements.isEmpty) { + continue; + } + log.info('found shelf_router.Route annotations in ${cls.name}'); + + classes[cls] = elements + .map((e) => _routeType.annotationsOfExact(e).map((a) => _Handler( + a.getField('verb')!.toStringValue()!, + a.getField('route')!.toStringValue()!, + e, + ))) + .expand((i) => i) + .toList(); + } + if (classes.isEmpty) { + return null; // nothing to do if nothing was annotated + } + + // Run type check to ensure method and getters have the right signatures. + for (final handler in classes.values.expand((i) => i)) { + // If the verb is $mount, then it's not a handler, but a mount. + if (handler.verb.toLowerCase() == r'$mount') { + _typeCheckMount(handler); + } else { + _typeCheckHandler(handler); + } + } + + // Build library and emit code with all generate methods. + final methods = classes.entries.map((e) => _buildRouterMethod( + classElement: e.key, + handlers: e.value, + )); + return code.Library((b) => b.body.addAll(methods)) + .accept(code.DartEmitter()) + .toString(); + } +} + +/// Type checks for the case where [shelf_router.Route] is used to annotate +/// shelf request handler. +void _typeCheckHandler(_Handler h) { + if (h.element.isStatic) { + throw g.InvalidGenerationSourceError( + 'The shelf_router.Route annotation cannot be used on static members', + element: h.element); + } + + // Check the verb, note that $all is a special value for handling all verbs. + if (!isHttpMethod(h.verb) && h.verb != r'$all') { + throw g.InvalidGenerationSourceError( + 'The verb "${h.verb}" used in shelf_router.Route annotation must be ' + 'a valid HTTP method', + element: h.element); + } + + // Check that this shouldn't have been annotated with Route.mount + if (h.element.kind == ElementKind.GETTER) { + throw g.InvalidGenerationSourceError( + 'Only the shelf_router.Route.mount annotation can only be used on a ' + 'getter, and only if it returns a shelf_router.Router', + element: h.element); + } + + // Check that this is indeed a method + if (h.element.kind != ElementKind.METHOD) { + throw g.InvalidGenerationSourceError( + 'The shelf_router.Route annotation can only be used on request ' + 'handling methods', + element: h.element); + } + + // Check the route can parse + List<String> params; + try { + params = RouterEntry(h.verb, h.route, () => null).params; + } on ArgumentError catch (e) { + throw g.InvalidGenerationSourceError( + e.toString(), + element: h.element, + ); + } + + // Ensure that the first parameter is shelf.Request + if (h.element.parameters.isEmpty) { + throw g.InvalidGenerationSourceError( + 'The shelf_router.Route annotation can only be used on shelf request ' + 'handlers accept a shelf.Request parameter', + element: h.element); + } + for (final p in h.element.parameters) { + if (p.isOptional) { + throw g.InvalidGenerationSourceError( + 'The shelf_router.Route annotation can only be used on shelf ' + 'request handlers accept a shelf.Request parameter and/or a ' + 'shelf.Request parameter and all string parameters in the route, ' + 'optional parameters are not permitted', + element: p); + } + } + if (!_requestType.isExactlyType(h.element.parameters.first.type)) { + throw g.InvalidGenerationSourceError( + 'The shelf_router.Route annotation can only be used on shelf request ' + 'handlers accept a shelf.Request parameter as first parameter', + element: h.element); + } + if (h.element.parameters.length > 1) { + if (h.element.parameters.length != params.length + 1) { + throw g.InvalidGenerationSourceError( + 'The shelf_router.Route annotation can only be used on shelf ' + 'request handlers accept a shelf.Request parameter and/or a ' + 'shelf.Request parameter and all string parameters in the route', + element: h.element); + } + for (var i = 0; i < params.length; i++) { + final p = h.element.parameters[i + 1]; + if (p.name != params[i]) { + throw g.InvalidGenerationSourceError( + 'The shelf_router.Route annotation can only be used on shelf ' + 'request handlers accept a shelf.Request parameter and/or a ' + 'shelf.Request parameter and all string parameters in the route, ' + 'the "${p.name}" parameter should be named "${params[i]}"', + element: p); + } + if (!_stringType.isExactlyType(p.type)) { + throw g.InvalidGenerationSourceError( + 'The shelf_router.Route annotation can only be used on shelf ' + 'request handlers accept a shelf.Request parameter and/or a ' + 'shelf.Request parameter and all string parameters in the route, ' + 'the "${p.name}" parameter is not of type string', + element: p); + } + } + } + + // Check the return value of the method. + var returnType = h.element.returnType; + // Unpack Future<T> and FutureOr<T> wrapping of responseType + if (returnType.isDartAsyncFuture || returnType.isDartAsyncFutureOr) { + returnType = (returnType as ParameterizedType).typeArguments.first; + } + if (!_responseType.isAssignableFromType(returnType)) { + throw g.InvalidGenerationSourceError( + 'The shelf_router.Route annotation can only be used on shelf request ' + 'handlers that return shelf.Response, Future<shelf.Response> or ' + 'FutureOr<shelf.Response>, and not "${h.element.returnType}"', + element: h.element); + } +} + +/// Type checks for the case where [shelf_router.Route.mount] is used to +/// annotate a getter that returns a [shelf_router.Router]. +void _typeCheckMount(_Handler h) { + if (h.element.isStatic) { + throw g.InvalidGenerationSourceError( + 'The shelf_router.Route annotation cannot be used on static members', + element: h.element); + } + + // Check that this should have been annotated with Route.mount + if (h.element.kind != ElementKind.GETTER) { + throw g.InvalidGenerationSourceError( + 'The shelf_router.Route.mount annotation can only be used on a ' + 'getter that returns shelf_router.Router', + element: h.element); + } + + // Sanity checks for the prefix + if (!h.route.startsWith('/') || !h.route.endsWith('/')) { + throw g.InvalidGenerationSourceError( + 'The prefix "${h.route}" in shelf_router.Route.mount(prefix) ' + 'annotation must begin and end with a slash', + element: h.element); + } + if (h.route.contains('<')) { + throw g.InvalidGenerationSourceError( + 'The prefix "${h.route}" in shelf_router.Route.mount(prefix) ' + 'annotation cannot contain <', + element: h.element); + } + + if (!_routerType.isAssignableFromType(h.element.returnType)) { + throw g.InvalidGenerationSourceError( + 'The shelf_router.Route.mount annotation can only be used on a ' + 'getter that returns shelf_router.Router', + element: h.element); + } +}
diff --git a/pkgs/shelf_web_socket/mono_pkg.yml b/pkgs/shelf_router_generator/mono_pkg.yaml similarity index 78% copy from pkgs/shelf_web_socket/mono_pkg.yml copy to pkgs/shelf_router_generator/mono_pkg.yaml index 75b1a04..84c7b48 100644 --- a/pkgs/shelf_web_socket/mono_pkg.yml +++ b/pkgs/shelf_router_generator/mono_pkg.yaml
@@ -1,5 +1,5 @@ sdk: -- 2.12.0 +- 2.14.0 - dev stages: @@ -10,6 +10,3 @@ - dev - unit_test: - test: --test-randomize-ordering-seed=random - os: - - linux - - windows
diff --git a/pkgs/shelf_router_generator/pubspec.yaml b/pkgs/shelf_router_generator/pubspec.yaml new file mode 100644 index 0000000..9489f3c --- /dev/null +++ b/pkgs/shelf_router_generator/pubspec.yaml
@@ -0,0 +1,28 @@ +name: shelf_router_generator +version: 1.0.2 +description: >- + A package:build compatible builder for generating request routers for the + shelf web-framework based on source annotations. +homepage: https://github.com/google/dart-neats/tree/master/shelf_router_generator +repository: https://github.com/google/dart-neats.git +issue_tracker: https://github.com/google/dart-neats/labels/pkg:shelf_router_generator + +dependencies: + analyzer: '>=2.0.0 <4.0.0' + build: ^2.0.0 + build_config: ^1.0.0 + code_builder: ^4.0.0 + http_methods: ^1.0.0 + shelf: ^1.1.0 + shelf_router: ^1.0.0 + source_gen: ^1.0.0 + +dev_dependencies: + build_runner: ^2.0.0 + build_verify: ^3.0.0 + http: ^0.13.0 + lints: ^1.0.0 + test: ^1.5.3 + +environment: + sdk: '>=2.12.0 <3.0.0'
diff --git a/pkgs/shelf_router_generator/test/ensure_build_test.dart b/pkgs/shelf_router_generator/test/ensure_build_test.dart new file mode 100644 index 0000000..55a246d --- /dev/null +++ b/pkgs/shelf_router_generator/test/ensure_build_test.dart
@@ -0,0 +1,23 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// import 'package:build_verify/build_verify.dart'; +// import 'package:test/test.dart'; + +void main() { + // TODO: Run this test when the build_runner bug is fixed + // test('ensure_build', () { + // expectBuildClean(packageRelativeDirectory: 'shelf_router_generator'); + // }); +}
diff --git a/pkgs/shelf_router_generator/test/server/api.dart b/pkgs/shelf_router_generator/test/server/api.dart new file mode 100644 index 0000000..f27323a --- /dev/null +++ b/pkgs/shelf_router_generator/test/server/api.dart
@@ -0,0 +1,36 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// @dart=2.12 + +import 'dart:async' show Future; +import 'package:shelf/shelf.dart'; +import 'package:shelf_router/shelf_router.dart'; + +part 'api.g.dart'; + +class Api { + @Route.get('/time') + Response _time(Request request) => Response.ok('it is about now'); + + @Route.get('/to-uppercase/<word|.*>') + Future<Response> _toUpperCase(Request request, String word) async => + Response.ok(word.toUpperCase()); + + @Route.get(r'/$string-escape') + Response _stringEscapingWorks(Request request) => + Response.ok('Just testing string escaping'); + + Router get router => _$ApiRouter(this); +}
diff --git a/pkgs/shelf_router_generator/test/server/api.g.dart b/pkgs/shelf_router_generator/test/server/api.g.dart new file mode 100644 index 0000000..7c834d1 --- /dev/null +++ b/pkgs/shelf_router_generator/test/server/api.g.dart
@@ -0,0 +1,16 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// @dart=2.12 + +part of 'api.dart'; + +// ************************************************************************** +// ShelfRouterGenerator +// ************************************************************************** + +Router _$ApiRouter(Api service) { + final router = Router(); + router.add('GET', r'/time', service._time); + router.add('GET', r'/to-uppercase/<word|.*>', service._toUpperCase); + router.add('GET', r'/$string-escape', service._stringEscapingWorks); + return router; +}
diff --git a/pkgs/shelf_router_generator/test/server/server.dart b/pkgs/shelf_router_generator/test/server/server.dart new file mode 100644 index 0000000..b882bdf --- /dev/null +++ b/pkgs/shelf_router_generator/test/server/server.dart
@@ -0,0 +1,39 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// @dart=2.12 + +import 'dart:async' show Future; +import 'dart:io' show HttpServer; + +import 'package:shelf/shelf_io.dart' as shelf_io; + +import 'service.dart'; + +class Server { + final _service = Service(); + late HttpServer _server; + + Future<void> start() async { + _server = await shelf_io.serve(_service.router, 'localhost', 0); + } + + Future<void> stop() => _server.close(); + + Uri get uri => Uri( + scheme: 'http', + host: 'localhost', + port: _server.port, + ); +}
diff --git a/pkgs/shelf_router_generator/test/server/service.dart b/pkgs/shelf_router_generator/test/server/service.dart new file mode 100644 index 0000000..7fec7f6 --- /dev/null +++ b/pkgs/shelf_router_generator/test/server/service.dart
@@ -0,0 +1,61 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// @dart=2.12 + +import 'dart:async' show Future, FutureOr; + +import 'package:shelf/shelf.dart'; +import 'package:shelf_router/shelf_router.dart'; + +import 'api.dart'; +import 'unrelatedannotation.dart'; + +part 'service.g.dart'; + +class Service { + @Route.get('/say-hello') + @Route.get('/say-hello/') + Response _sayHello(Request request) => Response.ok('hello world'); + + @Route.get('/wave') + FutureOr<Response> _wave(Request request) async { + await Future.delayed(const Duration(milliseconds: 50)); + return Response.ok('_o/'); + } + + @Route.get('/greet/<user>') + Future<Response> _greet(Request request, String user) async => + Response.ok('Greetings, $user'); + + @Route.get('/hi/<user>') + Future<Response> _hi(Request request) async { + final name = request.params['user']; + return Response.ok('hi $name'); + } + + @Route.mount('/api/') + Router get _api => Api().router; + + @Route.all('/<_|.*>') + Response _index(Request request) => Response.ok('nothing-here'); + + Router get router => _$ServiceRouter(this); +} + +class UnrelatedThing { + @EndPoint.put('/api/test') + Future<Response> unrelatedMethod(Request request) async => + Response.ok('hello world'); +}
diff --git a/pkgs/shelf_router_generator/test/server/service.g.dart b/pkgs/shelf_router_generator/test/server/service.g.dart new file mode 100644 index 0000000..762773a --- /dev/null +++ b/pkgs/shelf_router_generator/test/server/service.g.dart
@@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// @dart=2.12 + +part of 'service.dart'; + +// ************************************************************************** +// ShelfRouterGenerator +// ************************************************************************** + +Router _$ServiceRouter(Service service) { + final router = Router(); + router.add('GET', r'/say-hello', service._sayHello); + router.add('GET', r'/say-hello/', service._sayHello); + router.add('GET', r'/wave', service._wave); + router.add('GET', r'/greet/<user>', service._greet); + router.add('GET', r'/hi/<user>', service._hi); + router.mount(r'/api/', service._api); + router.all(r'/<_|.*>', service._index); + return router; +}
diff --git a/pkgs/shelf_router_generator/test/server/unrelatedannotation.dart b/pkgs/shelf_router_generator/test/server/unrelatedannotation.dart new file mode 100644 index 0000000..505e199 --- /dev/null +++ b/pkgs/shelf_router_generator/test/server/unrelatedannotation.dart
@@ -0,0 +1,38 @@ +// @dart=2.12 + +/// Annotation for an API end-point. +class EndPoint { + /// HTTP verb for requests routed to the annotated method. + final String verb; + + /// HTTP route for request routed to the annotated method. + final String route; + + /// Create an annotation that routes requests matching [verb] and [route] to + /// the annotated method. + const EndPoint(this.verb, this.route); + + /// Route `GET` requests matching [route] to annotated method. + const EndPoint.get(this.route) : verb = 'GET'; + + /// Route `HEAD` requests matching [route] to annotated method. + const EndPoint.head(this.route) : verb = 'HEAD'; + + /// Route `POST` requests matching [route] to annotated method. + const EndPoint.post(this.route) : verb = 'POST'; + + /// Route `PUT` requests matching [route] to annotated method. + const EndPoint.put(this.route) : verb = 'PUT'; + + /// Route `DELETE` requests matching [route] to annotated method. + const EndPoint.delete(this.route) : verb = 'DELETE'; + + /// Route `CONNECT` requests matching [route] to annotated method. + const EndPoint.connect(this.route) : verb = 'CONNECT'; + + /// Route `OPTIONS` requests matching [route] to annotated method. + const EndPoint.options(this.route) : verb = 'OPTIONS'; + + /// Route `TRACE` requests matching [route] to annotated method. + const EndPoint.trace(this.route) : verb = 'TRACE'; +}
diff --git a/pkgs/shelf_router_generator/test/server_test.dart b/pkgs/shelf_router_generator/test/server_test.dart new file mode 100644 index 0000000..28199fc --- /dev/null +++ b/pkgs/shelf_router_generator/test/server_test.dart
@@ -0,0 +1,58 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// @dart=2.12 + +import 'package:http/http.dart' as http; +import 'package:test/test.dart'; + +import 'server/server.dart'; + +void main() { + final server = Server(); + setUpAll(server.start); + tearDownAll(server.stop); + + void testGet({ + required String path, + required String result, + }) => + test('GET $path', () async { + final result = await http.get(server.uri.resolve(path)); + expect(result, equals(result)); + }); + + // Test simple handlers + testGet(path: '/say-hello', result: 'hello world'); + testGet(path: '/say-hello/', result: 'hello world'); + testGet(path: '/wave', result: '_o/'); + testGet(path: '/greet/jonasfj', result: 'Greetings, jonasfj'); + testGet(path: '/greet/sigurdm', result: 'Greetings, sigurdm'); + testGet(path: '/hi/jonasfj', result: 'hi jonasfj'); + testGet(path: '/hi/sigurdm', result: 'hi sigurdm'); + + // Test /api/ + testGet(path: '/api/time', result: 'it is about now'); + testGet(path: '/api/to-uppercase/wEiRd%20Word', result: 'WEIRD WORD'); + testGet(path: '/api/to-uppercase/wEiRd Word', result: 'WEIRD WORD'); + + // Test the catch all handler + testGet(path: '/', result: 'nothing-here'); + testGet(path: '/wrong-path', result: 'nothing-here'); + testGet(path: '/hi/sigurdm/ups', result: 'nothing-here'); + testGet(path: '/api/to-uppercase/too/many/slashs', result: 'nothing-here'); + testGet(path: '/api/', result: 'nothing-here'); + testGet(path: '/api/time/', result: 'nothing-here'); // notice the extra slash + testGet(path: '/api/tim', result: 'nothing-here'); +}
diff --git a/pkgs/shelf_static/CHANGELOG.md b/pkgs/shelf_static/CHANGELOG.md index 8be7a56..b471192 100644 --- a/pkgs/shelf_static/CHANGELOG.md +++ b/pkgs/shelf_static/CHANGELOG.md
@@ -1,5 +1,7 @@ ## 1.1.1-dev +- Require Dart `2.14`. + ## 1.1.0 * Correctly handle `HEAD` requests. @@ -22,8 +24,8 @@ * Update SDK constraint to `>=2.3.0 <3.0.0`. * Allow `3.x` versions of `package:convert`. * Allow `4.x` versions of `package:http_parser`. -* Use file `modified` dates instead of `changed` for `304 Not Modified` checks - as `changed` returns creation dates on Windows. +* Use file `modified` dates instead of `changed` for `304 Not Modified` checks as `changed` returns creation dates on + Windows. ## 0.2.8
diff --git a/pkgs/shelf_static/analysis_options.yaml b/pkgs/shelf_static/analysis_options.yaml deleted file mode 100644 index bb10866..0000000 --- a/pkgs/shelf_static/analysis_options.yaml +++ /dev/null
@@ -1,80 +0,0 @@ -include: package:lints/recommended.yaml - -analyzer: - strong-mode: - implicit-casts: false - -linter: - rules: - - avoid_bool_literals_in_conditional_expressions - - avoid_catching_errors - - avoid_classes_with_only_static_members - - avoid_function_literals_in_foreach_calls - - avoid_private_typedef_functions - - avoid_redundant_argument_values - - avoid_renaming_method_parameters - - avoid_returning_null - - avoid_returning_null_for_future - - avoid_returning_null_for_void - - avoid_returning_this - - avoid_single_cascade_in_expression_statements - - avoid_unused_constructor_parameters - - avoid_void_async - - await_only_futures - - camel_case_types - - cancel_subscriptions - - cascade_invocations - - comment_references - - constant_identifier_names - - control_flow_in_finally - - directives_ordering - - empty_statements - - file_names - - hash_and_equals - - implementation_imports - - invariant_booleans - - iterable_contains_unrelated_type - - join_return_with_assignment - - lines_longer_than_80_chars - - list_remove_unrelated_type - - literal_only_boolean_expressions - - missing_whitespace_between_adjacent_strings - - no_adjacent_strings_in_list - - no_runtimeType_toString - - non_constant_identifier_names - - only_throw_errors - - overridden_fields - - package_api_docs - - package_names - - package_prefixed_library_names - - prefer_asserts_in_initializer_lists - - prefer_const_constructors - - prefer_const_declarations - - prefer_expression_function_bodies - - prefer_final_locals - - prefer_function_declarations_over_variables - - prefer_initializing_formals - - prefer_inlined_adds - - prefer_interpolation_to_compose_strings - - prefer_is_not_operator - - prefer_null_aware_operators - - prefer_relative_imports - - prefer_typing_uninitialized_variables - - prefer_void_to_null - - provide_deprecation_message - - sort_pub_dependencies - - test_types_in_equals - - throw_in_finally - - type_annotate_public_apis - - unnecessary_await_in_return - - unnecessary_brace_in_string_interps - - unnecessary_getters_setters - - unnecessary_lambdas - - unnecessary_null_aware_assignments - - unnecessary_overrides - - unnecessary_parenthesis - - unnecessary_statements - - unnecessary_string_interpolations - - use_is_even_rather_than_modulo - - use_string_buffers - - void_checks
diff --git a/pkgs/shelf_static/lib/src/static_handler.dart b/pkgs/shelf_static/lib/src/static_handler.dart index 463c731..f5d7006 100644 --- a/pkgs/shelf_static/lib/src/static_handler.dart +++ b/pkgs/shelf_static/lib/src/static_handler.dart
@@ -110,7 +110,7 @@ final byteSink = ByteAccumulatorSink(); - await file.openRead(0, length).listen(byteSink.add).asFuture(); + await file.openRead(0, length).listen(byteSink.add).asFuture<void>(); return mimeResolver.lookup(file.path, headerBytes: byteSink.bytes); } else {
diff --git a/pkgs/shelf_static/mono_pkg.yaml b/pkgs/shelf_static/mono_pkg.yaml index 75b1a04..80a16a3 100644 --- a/pkgs/shelf_static/mono_pkg.yaml +++ b/pkgs/shelf_static/mono_pkg.yaml
@@ -1,5 +1,5 @@ sdk: -- 2.12.0 +- 2.14.0 - dev stages:
diff --git a/pkgs/shelf_static/pubspec.yaml b/pkgs/shelf_static/pubspec.yaml index 5a395c4..1677c06 100644 --- a/pkgs/shelf_static/pubspec.yaml +++ b/pkgs/shelf_static/pubspec.yaml
@@ -4,7 +4,7 @@ repository: https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_static environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.14.0 <3.0.0' dependencies: convert: ^3.0.0
diff --git a/pkgs/shelf_static/test/sample_test.dart b/pkgs/shelf_static/test/sample_test.dart index ecae3d7..0a0249a 100644 --- a/pkgs/shelf_static/test/sample_test.dart +++ b/pkgs/shelf_static/test/sample_test.dart
@@ -52,7 +52,7 @@ return _request(Request('GET', uri)); } -Future _testFileContents(String filename) async { +Future<void> _testFileContents(String filename) async { final filePath = p.join(_samplePath, filename); final file = File(filePath); final fileContents = file.readAsBytesSync(); @@ -64,7 +64,7 @@ await _expectCompletesWithBytes(response, fileContents); } -Future _expectCompletesWithBytes( +Future<void> _expectCompletesWithBytes( Response response, List<int> expectedBytes) async { final bytes = await response.read().toList(); final flatBytes = bytes.expand((e) => e);
diff --git a/pkgs/shelf_static/test/test_util.dart b/pkgs/shelf_static/test/test_util.dart index 5c628d8..81d5ead 100644 --- a/pkgs/shelf_static/test/test_util.dart +++ b/pkgs/shelf_static/test/test_util.dart
@@ -55,7 +55,7 @@ : _target = toSecondResolution(target); @override - bool matches(dynamic item, Map matchState) { + bool matches(dynamic item, Map<dynamic, dynamic> matchState) { if (item is! DateTime) return false; return _datesEqualToSecond(_target, item);
diff --git a/pkgs/shelf_test_handler/CHANGELOG.md b/pkgs/shelf_test_handler/CHANGELOG.md index d450af6..4ca8a96 100644 --- a/pkgs/shelf_test_handler/CHANGELOG.md +++ b/pkgs/shelf_test_handler/CHANGELOG.md
@@ -1,5 +1,7 @@ ## 2.0.1-dev +- Require Dart `2.14`. + ## 2.0.0 * Migration to null safety.
diff --git a/pkgs/shelf_test_handler/analysis_options.yaml b/pkgs/shelf_test_handler/analysis_options.yaml deleted file mode 100644 index 0711aca..0000000 --- a/pkgs/shelf_test_handler/analysis_options.yaml +++ /dev/null
@@ -1,43 +0,0 @@ -include: package:pedantic/analysis_options.yaml -analyzer: - strong-mode: - implicit-casts: false -linter: - rules: - - avoid_empty_else - - avoid_init_to_null - - avoid_null_checks_in_equality_operators - - avoid_unused_constructor_parameters - - await_only_futures - - camel_case_types - - cancel_subscriptions - - constant_identifier_names - - control_flow_in_finally - - directives_ordering - - empty_catches - - empty_constructor_bodies - - empty_statements - - hash_and_equals - - implementation_imports - - iterable_contains_unrelated_type - - library_names - - library_prefixes - - list_remove_unrelated_type - - non_constant_identifier_names - - overridden_fields - - package_api_docs - - package_names - - package_prefixed_library_names - - prefer_equal_for_default_values - - prefer_final_fields - - prefer_generic_function_type_aliases - - prefer_is_not_empty - - slash_for_doc_comments - - test_types_in_equals - - throw_in_finally - - type_init_formals - - unnecessary_brace_in_string_interps - - unnecessary_const - - unnecessary_new - - unrelated_type_equality_checks - - valid_regexps
diff --git a/pkgs/shelf_test_handler/lib/src/handler.dart b/pkgs/shelf_test_handler/lib/src/handler.dart index 0d3eaa7..391066b 100644 --- a/pkgs/shelf_test_handler/lib/src/handler.dart +++ b/pkgs/shelf_test_handler/lib/src/handler.dart
@@ -10,7 +10,8 @@ import 'expectation.dart'; -/// A [Handler] that handles requests as specified by [expect] and [expectAnything]. +/// A [Handler] that handles requests as specified by [expect] and +/// [expectAnything]. class ShelfTestHandler { /// The description used in debugging output for this handler. final String description;
diff --git a/pkgs/shelf_test_handler/lib/src/server.dart b/pkgs/shelf_test_handler/lib/src/server.dart index 5bfb269..540649e 100644 --- a/pkgs/shelf_test_handler/lib/src/server.dart +++ b/pkgs/shelf_test_handler/lib/src/server.dart
@@ -44,5 +44,5 @@ /// Closes the server. /// /// If [force] is `true`, all active connections will be closed immediately. - Future close({bool force = false}) => _server.close(force: force); + Future<void> close({bool force = false}) => _server.close(force: force); }
diff --git a/pkgs/shelf_test_handler/mono_pkg.yml b/pkgs/shelf_test_handler/mono_pkg.yaml similarity index 96% rename from pkgs/shelf_test_handler/mono_pkg.yml rename to pkgs/shelf_test_handler/mono_pkg.yaml index d49105b..c7bde78 100644 --- a/pkgs/shelf_test_handler/mono_pkg.yml +++ b/pkgs/shelf_test_handler/mono_pkg.yaml
@@ -1,5 +1,5 @@ sdk: -- 2.12.0 +- 2.14.0 - dev stages:
diff --git a/pkgs/shelf_test_handler/pubspec.yaml b/pkgs/shelf_test_handler/pubspec.yaml index 1267771..f57ad93 100644 --- a/pkgs/shelf_test_handler/pubspec.yaml +++ b/pkgs/shelf_test_handler/pubspec.yaml
@@ -4,7 +4,7 @@ repository: https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_test_handler environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.14.0 <3.0.0' dependencies: http_multi_server: ^3.0.0 @@ -13,10 +13,6 @@ dev_dependencies: http: ^0.13.0 - pedantic: ^1.10.0 + lints: ^1.0.0 shelf_web_socket: ^1.0.0 - -dependency_overrides: - http_multi_server: ^3.0.0 - shelf_web_socket: ^1.0.0 - test: ^1.16.0 + web_socket_channel: ^2.0.0
diff --git a/pkgs/shelf_test_handler/test/handler_test.dart b/pkgs/shelf_test_handler/test/handler_test.dart index a7b0e77..af33450 100644 --- a/pkgs/shelf_test_handler/test/handler_test.dart +++ b/pkgs/shelf_test_handler/test/handler_test.dart
@@ -112,7 +112,7 @@ }); } -void _expectZoneFailure(Future Function() callback) { +void _expectZoneFailure(Future<void> Function() callback) { runZonedGuarded(callback, expectAsync2((error, stack) { expect(error, TypeMatcher<TestFailure>()); }));
diff --git a/pkgs/shelf_test_handler/test/server_test.dart b/pkgs/shelf_test_handler/test/server_test.dart index 2390b60..29208d9 100644 --- a/pkgs/shelf_test_handler/test/server_test.dart +++ b/pkgs/shelf_test_handler/test/server_test.dart
@@ -10,6 +10,7 @@ import 'package:shelf_test_handler/shelf_test_handler.dart'; import 'package:shelf_web_socket/shelf_web_socket.dart'; import 'package:test/test.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; void main() { test('serves a ShelfTestHandler', () async { @@ -25,7 +26,8 @@ var server = await ShelfTestServer.create(); addTearDown(server.close); - server.handler.expect('GET', '/', webSocketHandler((webSocket) { + server.handler.expect('GET', '/', + webSocketHandler((WebSocketChannel webSocket) { webSocket.sink.add('hello!'); webSocket.sink.close(); }));
diff --git a/pkgs/shelf_web_socket/CHANGELOG.md b/pkgs/shelf_web_socket/CHANGELOG.md index aa5f4e0..b4b67be 100644 --- a/pkgs/shelf_web_socket/CHANGELOG.md +++ b/pkgs/shelf_web_socket/CHANGELOG.md
@@ -1,5 +1,7 @@ ## 1.0.2-dev +- Require Dart `2.14`. + ## 1.0.1 * Require the latest shelf, remove dead code. @@ -16,13 +18,11 @@ * Support the latest shelf release (`1.x.x`). * Require at least Dart 2.1 -* Allow omitting `protocols` argument even if the `onConnection` callback takes - a second argument. +* Allow omitting `protocols` argument even if the `onConnection` callback takes a second argument. ## 0.2.3 -* Add `pingInterval` argument to `webSocketHandler`, to be passed through - to the created channel. +* Add `pingInterval` argument to `webSocketHandler`, to be passed through to the created channel. ## 0.2.2+5 @@ -30,8 +30,7 @@ ## 0.2.2+4 -* Fix the check for `onConnection` to check the number of arguments and not - that the arguments are `dynamic`. +* Fix the check for `onConnection` to check the number of arguments and not that the arguments are `dynamic`. ## 0.2.2+3 @@ -64,8 +63,8 @@ ## 0.1.0 -* **Breaking change**: `webSocketHandler()` now passes a `WebSocketChannel` to - the `onConnection()` callback, rather than a deprecated `CompatibleWebSocket`. +* **Breaking change**: `webSocketHandler()` now passes a `WebSocketChannel` to the `onConnection()` callback, rather + than a deprecated `CompatibleWebSocket`. ## 0.0.1+5 @@ -85,5 +84,4 @@ ## 0.0.1+1 -* Properly parse the `Connection` header. This fixes an issue where Firefox was - unable to connect. +* Properly parse the `Connection` header. This fixes an issue where Firefox was unable to connect.
diff --git a/pkgs/shelf_web_socket/analysis_options.yaml b/pkgs/shelf_web_socket/analysis_options.yaml deleted file mode 100644 index d183bf1..0000000 --- a/pkgs/shelf_web_socket/analysis_options.yaml +++ /dev/null
@@ -1,80 +0,0 @@ -include: package:pedantic/analysis_options.yaml - -analyzer: - strong-mode: - implicit-casts: false - -linter: - rules: - - avoid_bool_literals_in_conditional_expressions - - avoid_catching_errors - - avoid_classes_with_only_static_members - - avoid_function_literals_in_foreach_calls - - avoid_private_typedef_functions - - avoid_redundant_argument_values - - avoid_renaming_method_parameters - - avoid_returning_null - - avoid_returning_null_for_future - - avoid_returning_null_for_void - - avoid_returning_this - - avoid_single_cascade_in_expression_statements - - avoid_unused_constructor_parameters - - avoid_void_async - - await_only_futures - - camel_case_types - - cancel_subscriptions - - cascade_invocations - - comment_references - - constant_identifier_names - - control_flow_in_finally - - directives_ordering - - empty_statements - - file_names - - hash_and_equals - - implementation_imports - - invariant_booleans - - iterable_contains_unrelated_type - - join_return_with_assignment - - lines_longer_than_80_chars - - list_remove_unrelated_type - - literal_only_boolean_expressions - - missing_whitespace_between_adjacent_strings - - no_adjacent_strings_in_list - - no_runtimeType_toString - - non_constant_identifier_names - - only_throw_errors - - overridden_fields - - package_api_docs - - package_names - - package_prefixed_library_names - - prefer_asserts_in_initializer_lists - - prefer_const_constructors - - prefer_const_declarations - - prefer_expression_function_bodies - - prefer_final_locals - - prefer_function_declarations_over_variables - - prefer_initializing_formals - - prefer_inlined_adds - - prefer_interpolation_to_compose_strings - - prefer_is_not_operator - - prefer_null_aware_operators - - prefer_relative_imports - - prefer_typing_uninitialized_variables - - prefer_void_to_null - - provide_deprecation_message - - sort_pub_dependencies - - test_types_in_equals - - throw_in_finally - - type_annotate_public_apis - - unnecessary_await_in_return - - unnecessary_brace_in_string_interps - - unnecessary_getters_setters - - unnecessary_lambdas - - unnecessary_null_aware_assignments - - unnecessary_overrides - - unnecessary_parenthesis - - unnecessary_statements - - unnecessary_string_interpolations - - use_is_even_rather_than_modulo - - use_string_buffers - - void_checks
diff --git a/pkgs/shelf_web_socket/lib/shelf_web_socket.dart b/pkgs/shelf_web_socket/lib/shelf_web_socket.dart index 420b1bf..61ae886 100644 --- a/pkgs/shelf_web_socket/lib/shelf_web_socket.dart +++ b/pkgs/shelf_web_socket/lib/shelf_web_socket.dart
@@ -45,6 +45,7 @@ Duration? pingInterval}) { if (onConnection is! void Function(Null, Null)) { final innerOnConnection = onConnection; + // ignore: inference_failure_on_untyped_parameter onConnection = (webSocket, _) => innerOnConnection(webSocket); }
diff --git a/pkgs/shelf_web_socket/mono_pkg.yml b/pkgs/shelf_web_socket/mono_pkg.yaml similarity index 95% rename from pkgs/shelf_web_socket/mono_pkg.yml rename to pkgs/shelf_web_socket/mono_pkg.yaml index 75b1a04..80a16a3 100644 --- a/pkgs/shelf_web_socket/mono_pkg.yml +++ b/pkgs/shelf_web_socket/mono_pkg.yaml
@@ -1,5 +1,5 @@ sdk: -- 2.12.0 +- 2.14.0 - dev stages:
diff --git a/pkgs/shelf_web_socket/pubspec.yaml b/pkgs/shelf_web_socket/pubspec.yaml index c4c5828..c3397c2 100644 --- a/pkgs/shelf_web_socket/pubspec.yaml +++ b/pkgs/shelf_web_socket/pubspec.yaml
@@ -3,10 +3,10 @@ description: >- A shelf handler that wires up a listener for every connection. -repository: https://github.com/dart-lang/shelf/tree/master/shelf_web_socket +repository: https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_web_socket environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.14.0 <3.0.0" dependencies: shelf: ^1.1.0 @@ -15,5 +15,5 @@ dev_dependencies: http: ^0.13.0 - pedantic: ^1.10.0 + lints: ^1.0.0 test: ^1.16.0
diff --git a/pkgs/shelf_web_socket/test/web_socket_test.dart b/pkgs/shelf_web_socket/test/web_socket_test.dart index d2b6452..5f6b1d8 100644 --- a/pkgs/shelf_web_socket/test/web_socket_test.dart +++ b/pkgs/shelf_web_socket/test/web_socket_test.dart
@@ -2,6 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +// ignore_for_file: inference_failure_on_untyped_parameter + import 'dart:io'; import 'package:http/http.dart' as http; @@ -43,7 +45,7 @@ fail('Only expected two messages.'); } n++; - }).asFuture(); + }).asFuture<void>(); } finally { await server.close(); }
diff --git a/tool/readme.dart b/tool/readme.dart new file mode 100644 index 0000000..92b8179 --- /dev/null +++ b/tool/readme.dart
@@ -0,0 +1,22 @@ +import 'dart:io'; + +const _pkgsDir = 'pkgs'; + +void main() { + final dirs = Directory(_pkgsDir).listSync().whereType<Directory>(); + + final pkgs = dirs.map((e) => e.uri.pathSegments[1]).toList()..sort(); + + for (var pkg in pkgs) { + _printPkg(pkg); + } +} + +void _printPkg(String pkgName) { + print(''' +## $pkgName [![Pub Package](https://img.shields.io/pub/v/$pkgName.svg)](https://pub.dev/packages/$pkgName) + +- Package: <https://pub.dev/packages/$pkgName> +- [Source code]($_pkgsDir/$pkgName) +'''); +}