diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..a9fc8c5 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "pub" + directory: "/" # Location of package manifests + schedule: + interval: "daily" \ No newline at end of file diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml new file mode 100644 index 0000000..e480ae1 --- /dev/null +++ b/.github/workflows/dart.yml @@ -0,0 +1,42 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Dart + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + # Note: This workflow uses the latest stable version of the Dart SDK. + # You can specify other versions if desired, see documentation here: + # https://github.com/dart-lang/setup-dart/blob/main/README.md + # - uses: dart-lang/setup-dart@v1 + - uses: dart-lang/setup-dart@v1 + + - name: Install dependencies + run: dart pub get + + # Verify the use of 'dart format' on each commit. + - name: Verify formatting + run: dart format --output=none --set-exit-if-changed . + + # Ensure code quality meets package standards. + - name: Analyze project source + run: dart analyze --fatal-infos + + # Your project will need to have tests in test/ and a dependency on + # package:test for this step to succeed. Note that Flutter projects will + # want to change this to 'flutter test'. + - name: Run tests + run: dart test \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..d24f850 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,14 @@ +name: Publish to pub.dev + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+*' + +jobs: + publish: + permissions: + id-token: write # Required for authentication using OIDC + uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1 + with: + environment: 'pub.dev' \ No newline at end of file diff --git a/.gitignore b/.gitignore index bf71f4d..d90f09e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ # Avoid committing pubspec.lock for library packages; see # https://dart.dev/tools/pub/private-files#pubspec-lock. pubspec.lock + +# Ignore generated API documentation +doc/api/ diff --git a/.pubignore b/.pubignore new file mode 100644 index 0000000..ce22204 --- /dev/null +++ b/.pubignore @@ -0,0 +1,13 @@ +# Ignore local dart and pub tools config/caches +.dart_tool/ +pubspec.lock + +# Ignore generated API documentation +doc/api/ + +# Ignore development tools and benchmarks +tool/ +benchmark/ + +# Ignore conformance tests submodule +structured-field-tests/ diff --git a/LICENSE b/LICENSE index e69de29..f6d93d6 100644 --- a/LICENSE +++ b/LICENSE @@ -0,0 +1,8 @@ +The MIT License (MIT) +Original code: Copyright © 2026 Dropbear Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index b8ad888..ac3f98e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,16 @@ -# Structured Field Values for HTTP (RFC 9651) +# Structured Field Values for HTTP -A fully compliant implementation of **RFC 9651 (Structured Field Values for HTTP)** in Dart. This package handles parsing and serialization of structured headers and trailers, obsoleting the older RFC 8941 specification. +[![pub package](https://img.shields.io/badge/pub-1.0.0-blue.svg)](https://pub.dev/packages/structured_field_values) +[![Dart CI](https://github.com/dropbear-software/structured_field_values/actions/workflows/dart.yml/badge.svg)](https://github.com/dropbear-software/structured_field_values/actions) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -It provides a strongly typed representation of structured fields and adheres strictly to the step-by-step parsing and serialization algorithms outlined in the RFC. +A fully compliant implementation of **RFC 9651 (Structured Field Values for +HTTP)** in native Dart. This package handles the parsing and serialization of +structured headers and trailers, replacing the older RFC 8941 specification. + +It provides a strongly typed representation of structured fields and adheres +strictly to the step-by-step parsing and serialization algorithms outlined in +the RFC. --- @@ -14,26 +22,41 @@ It provides a strongly typed representation of structured fields and adheres str - `StructuredItem` (parameterized single values) - **Inner Lists**: `StructuredInnerList` (parenthesized space-separated lists). - **Parameters**: `StructuredParameters` (semicolon-delimited key-value pairs). -- **All RFC 9651 Bare Item Types**: - - **Integer**: range `-999,999,999,999,999` to `999,999,999,999,999`. - - **Decimal**: Up to 12 integer digits and 3 fractional digits. - - **String**: Printable ASCII characters (`%x20-7E`). - - **Token**: Identifiers beginning with `ALPHA` or `*`, plus `:` and `/`. - - **Byte Sequence**: Base64 encoded binary data. - - **Boolean**: `?1` (true) or `?0` (false). - - **Date**: Unix timestamps preceded by `@` (e.g. `@1659578233`). - - **Display String**: Unicode strings formatted with percent encoding (e.g. `%"This is intended for display to %c3%bcsers."`). + +### RFC 9651 Bare Item Types + +This library provides full support for all bare item types defined in the RFC: + +| RFC Type | Dart Class | Description / Constraints | Example Syntax | +| :--- | :--- | :--- | :--- | +| **Integer** | `StructuredInteger` | Range `-999,999,999,999,999` to `999,999,999,999,999` | `42` | +| **Decimal** | `StructuredDecimal` | Up to 12 integer digits and 3 fractional component digits | `3.14` | +| **String** | `StructuredString` | Zero or more printable ASCII characters (`%x20-7E`) | `"hello"` | +| **Token** | `StructuredToken` | Identifiers starting with `ALPHA` or `*` | `text/html` | +| **Byte Sequence** | `StructuredByteSequence` | Base64 encoded binary data enclosed in colons | `:aGVsbG8=:` | +| **Boolean** | `StructuredBoolean` | `?1` (true) or `?0` (false) | `?1` | +| **Date** | `StructuredDate` | Unix timestamps preceded by `@` | `@1659578233` | +| **Display String** | `StructuredDisplayString` | Unicode strings formatted with percent-encoding | `%"hello"` | --- ## Compliance Highlights -- **Official Test Suite Compliance**: 100% compliant with the official IETF HTTP Working Group [structured-field-tests](https://github.com/httpwg/structured-field-tests) suite (tested against all 2,135 parse and serialization test cases). -- **Order Preservation**: Dictionary and Parameter maps preserve key insertion order. -- **Round-to-Even**: Decimals with $>3$ fractional digits are rounded using the round-to-nearest-even strategy (IEEE 754 round half to even) during serialization. -- **Strict Parsing**: Invalid syntaxes fail immediately throwing a `StructuredFieldParseException`. -- **Base64 Tolerances**: Adheres to the RFC advice to tolerate missing base64 padding or non-zero pad bits. -- **Token / String Distinction**: Explicit classes (`StructuredToken` vs `StructuredString`) ensure they are not conflated. +- **Official Test Suite Compliance**: 100% compliant with the official IETF + HTTP Working Group + [structured-field-tests](https://github.com/httpwg/structured-field-tests) + suite (tested against all 2,135 parse and serialization test cases). +- **Order Preservation**: Dictionary and Parameter maps preserve key insertion + order. +- **Round-to-Even**: Decimals with $>3$ fractional digits are rounded using the + round-to-nearest-even strategy (IEEE 754 round half to even) during + serialization. +- **Strict Parsing**: Invalid syntaxes fail immediately throwing a + `StructuredFieldParseException`. +- **Base64 Tolerances**: Adheres to the RFC advice to tolerate missing base64 + padding or non-zero pad bits. +- **Token / String Distinction**: Explicit classes (`StructuredToken` vs + `StructuredString`) ensure they are not conflated. --- @@ -43,9 +66,7 @@ Add the package to your `pubspec.yaml`: ```yaml dependencies: - structured_field_values: - hosted: https://homebar.dropbear.dev - version: ^1.0.0 + structured_field_values: ^1.0.0 ``` Then run `dart pub get`. @@ -56,7 +77,9 @@ Then run `dart pub get`. ### Parsing Field Values -To parse an HTTP header value, use standard codecs: `structuredList`, `structuredDictionary`, or `structuredItem`. Decoding is fully type-safe and does not require manual casting: +To parse an HTTP header value, use standard codecs: `structuredList`, +`structuredDictionary`, or `structuredItem`. Decoding is fully type-safe and +does not require manual casting: ```dart import 'package:structured_field_values/structured_field_values.dart'; @@ -64,24 +87,25 @@ import 'package:structured_field_values/structured_field_values.dart'; void main() { // Parsing a List of Inner Lists const listHeader = '("sugar" "tea"), ("rum")'; - final parsedList = structuredList.decode(listHeader); // Statically typed as StructuredList + final parsedList = structuredList.decode(listHeader); // StructuredList print(parsedList); // Parsing a Dictionary with Boolean Parameters const dictHeader = 'a=?0, b, c;foo=bar'; - final parsedDict = structuredDictionary.decode(dictHeader); // Statically typed as StructuredDictionary + final parsedDict = structuredDictionary.decode(dictHeader); // StructuredDictionary print(parsedDict); // Parsing an Item const itemHeader = '@1659578233;expires=3600'; - final parsedItem = structuredItem.decode(itemHeader); // Statically typed as StructuredItem + final parsedItem = structuredItem.decode(itemHeader); // StructuredItem print(parsedItem); } ``` ### Serializing Structured Fields -To serialize a structured field back to its HTTP header string representation, use the codec's `encode` method: +To serialize a structured field back to its HTTP header string representation, +use the codec's `encode` method: ```dart import 'package:structured_field_values/structured_field_values.dart'; @@ -106,8 +130,10 @@ void main() { ### Exception Handling -- `StructuredFieldParseException` is thrown if the input string contains invalid syntax. -- `StructuredFieldSerializationException` is thrown if you try to serialize an out-of-range integer, an invalid key, or prohibited surrogate code points. +- `StructuredFieldParseException` is thrown if the input string contains + invalid syntax. +- `StructuredFieldSerializationException` is thrown if you try to serialize + an out-of-range integer, an invalid key, or prohibited surrogate code points. ```dart import 'package:structured_field_values/structured_field_values.dart'; @@ -126,7 +152,9 @@ void main() { ## Conformance Testing -This package integrates the official IETF HTTP Working Group [structured-field-tests](https://github.com/httpwg/structured-field-tests) suite as a Git submodule. +This package integrates the official IETF HTTP Working Group +[structured-field-tests](https://github.com/httpwg/structured-field-tests) +suite as a Git submodule. To run the conformance tests: @@ -145,9 +173,4 @@ To run the conformance tests: --- -## Design and API References - -Detailed classes can be found under: -* [types.dart](file:///home/mark/Code/structured_field_values/lib/src/types.dart) - Strong typing models. -* [parser.dart](file:///home/mark/Code/structured_field_values/lib/src/parser.dart) - Decoders and validators. -* [serializer.dart](file:///home/mark/Code/structured_field_values/lib/src/serializer.dart) - Encoders and formatters. +For support, feedback, or to report issues, please visit our [Issue Tracker](https://github.com/dropbear-software/structured_field_values/issues). diff --git a/pubspec.yaml b/pubspec.yaml index 04197ec..e159a59 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,6 @@ description: A Dart native implementation of Structured Field Values for HTTP (R version: 1.0.0 repository: https://github.com/dropbear-software/structured_field_values issue_tracker: https://github.com/dropbear-software/structured_field_values/issues -publish_to: https://homebar.dropbear.dev environment: sdk: ^3.12.0 @@ -13,6 +12,6 @@ dependencies: meta: ^1.19.0 dev_dependencies: - benchmark_harness: any + benchmark_harness: ^2.4.0 lints: ^6.0.0 test: ^1.25.6