A Dart implementation of the Punycode (RFC 3492) encoding algorithm used for
Internationalized Domain Names in Applications (IDNA).
@@ -164,7 +165,7 @@
punycoder
punycoder
- 0.1.0-dev
+ 0.1.0
diff --git a/doc/api/index.json b/doc/api/index.json
index c465fc6..50deb4b 100644
--- a/doc/api/index.json
+++ b/doc/api/index.json
@@ -1 +1 @@
-[{"name":"punycoder","qualifiedName":"punycoder","href":"punycoder/","kind":9,"packageRank":0,"desc":"Provides a Dart implementation of the Punycode encoding algorithm\nspecified in RFC 3492."},{"name":"PunycodeCodec","qualifiedName":"punycoder.PunycodeCodec","href":"punycoder/PunycodeCodec-class.html","kind":3,"packageRank":0,"desc":"A codec for encoding and decoding strings using the Punycode algorithm.","enclosedBy":{"name":"punycoder","kind":9,"href":"punycoder/"}},{"name":"PunycodeCodec.new","qualifiedName":"punycoder.PunycodeCodec.PunycodeCodec.new","href":"punycoder/PunycodeCodec/PunycodeCodec.html","kind":2,"packageRank":0,"desc":"Creates a new instance of the Punycode codec.","enclosedBy":{"name":"PunycodeCodec","kind":3,"href":"punycoder/PunycodeCodec-class.html"}},{"name":"decoder","qualifiedName":"punycoder.PunycodeCodec.decoder","href":"punycoder/PunycodeCodec/decoder.html","kind":16,"overriddenDepth":0,"packageRank":0,"desc":"Returns the decoder of this, converting from T to S.","enclosedBy":{"name":"PunycodeCodec","kind":3,"href":"punycoder/PunycodeCodec-class.html"}},{"name":"encoder","qualifiedName":"punycoder.PunycodeCodec.encoder","href":"punycoder/PunycodeCodec/encoder.html","kind":16,"overriddenDepth":0,"packageRank":0,"desc":"Returns the encoder from S to T.","enclosedBy":{"name":"PunycodeCodec","kind":3,"href":"punycoder/PunycodeCodec-class.html"}},{"name":"PunycodeDecoder","qualifiedName":"punycoder.PunycodeDecoder","href":"punycoder/PunycodeDecoder-class.html","kind":3,"packageRank":0,"desc":"Converts a Punycode string of ASCII-only symbols to a string of\nUnicode symbols.","enclosedBy":{"name":"punycoder","kind":9,"href":"punycoder/"}},{"name":"convert","qualifiedName":"punycoder.PunycodeDecoder.convert","href":"punycoder/PunycodeDecoder/convert.html","kind":10,"overriddenDepth":1,"packageRank":0,"desc":"Converts input and returns the result of the conversion.","enclosedBy":{"name":"PunycodeDecoder","kind":3,"href":"punycoder/PunycodeDecoder-class.html"}},{"name":"toUnicode","qualifiedName":"punycoder.PunycodeDecoder.toUnicode","href":"punycoder/PunycodeDecoder/toUnicode.html","kind":10,"overriddenDepth":0,"packageRank":0,"desc":"Converts a Punycode string representing a domain name or an email address\nto Unicode. Only the Punycoded parts of the input will be converted, i.e.\nit doesn't matter if you call it on a string that has already been\nconverted to Unicode.","enclosedBy":{"name":"PunycodeDecoder","kind":3,"href":"punycoder/PunycodeDecoder-class.html"}},{"name":"PunycodeEncoder","qualifiedName":"punycoder.PunycodeEncoder","href":"punycoder/PunycodeEncoder-class.html","kind":3,"packageRank":0,"desc":"Converts a string of Unicode symbols (e.g. a domain name label) to a\nPunycode string of ASCII-only symbols.","enclosedBy":{"name":"punycoder","kind":9,"href":"punycoder/"}},{"name":"convert","qualifiedName":"punycoder.PunycodeEncoder.convert","href":"punycoder/PunycodeEncoder/convert.html","kind":10,"overriddenDepth":1,"packageRank":0,"desc":"Converts input and returns the result of the conversion.","enclosedBy":{"name":"PunycodeEncoder","kind":3,"href":"punycoder/PunycodeEncoder-class.html"}},{"name":"toAscii","qualifiedName":"punycoder.PunycodeEncoder.toAscii","href":"punycoder/PunycodeEncoder/toAscii.html","kind":10,"overriddenDepth":0,"packageRank":0,"desc":"Converts a Unicode string representing a domain name or an email address\nto Punycode. Only the non-ASCII parts of the domain name will be\nconverted, i.e. it doesn't matter if you call it with a domain that's\nalready in ASCII","enclosedBy":{"name":"PunycodeEncoder","kind":3,"href":"punycoder/PunycodeEncoder-class.html"}},{"name":"punycodeDecoder","qualifiedName":"punycoder.punycodeDecoder","href":"punycoder/punycodeDecoder-constant.html","kind":19,"packageRank":0,"desc":"The canonical version of the Punycode Decoder","enclosedBy":{"name":"punycoder","kind":9,"href":"punycoder/"}},{"name":"punycodeEncoder","qualifiedName":"punycoder.punycodeEncoder","href":"punycoder/punycodeEncoder-constant.html","kind":19,"packageRank":0,"desc":"The canonical version of the Punycode Encoder","enclosedBy":{"name":"punycoder","kind":9,"href":"punycoder/"}}]
+[{"name":"punycoder","qualifiedName":"punycoder","href":"punycoder/","kind":9,"packageRank":0,"desc":"Provides a Dart implementation of the Punycode encoding algorithm\nspecified in RFC 3492."},{"name":"PunycodeCodec","qualifiedName":"punycoder.PunycodeCodec","href":"punycoder/PunycodeCodec-class.html","kind":3,"packageRank":0,"desc":"A codec for encoding and decoding strings using the Punycode algorithm.","enclosedBy":{"name":"punycoder","kind":9,"href":"punycoder/"}},{"name":"PunycodeCodec.new","qualifiedName":"punycoder.PunycodeCodec.PunycodeCodec.new","href":"punycoder/PunycodeCodec/PunycodeCodec.html","kind":2,"packageRank":0,"desc":"Creates a new instance of the Punycode codec designed\nfor working with domains and email addresses where\nadditional rules apply about how it is converted","enclosedBy":{"name":"PunycodeCodec","kind":3,"href":"punycoder/PunycodeCodec-class.html"}},{"name":"PunycodeCodec.simple","qualifiedName":"punycoder.PunycodeCodec.PunycodeCodec.simple","href":"punycoder/PunycodeCodec/PunycodeCodec.simple.html","kind":2,"packageRank":0,"desc":"Creates a new instance of the Punycode codec just designed\nfor working with simple strings","enclosedBy":{"name":"PunycodeCodec","kind":3,"href":"punycoder/PunycodeCodec-class.html"}},{"name":"decoder","qualifiedName":"punycoder.PunycodeCodec.decoder","href":"punycoder/PunycodeCodec/decoder.html","kind":16,"overriddenDepth":0,"packageRank":0,"desc":"Returns the decoder of this, converting from T to S.","enclosedBy":{"name":"PunycodeCodec","kind":3,"href":"punycoder/PunycodeCodec-class.html"}},{"name":"encoder","qualifiedName":"punycoder.PunycodeCodec.encoder","href":"punycoder/PunycodeCodec/encoder.html","kind":16,"overriddenDepth":0,"packageRank":0,"desc":"Returns the encoder from S to T.","enclosedBy":{"name":"PunycodeCodec","kind":3,"href":"punycoder/PunycodeCodec-class.html"}}]
diff --git a/doc/api/punycoder/PunycodeCodec-class-sidebar.html b/doc/api/punycoder/PunycodeCodec-class-sidebar.html
index 247c95b..6aa7384 100644
--- a/doc/api/punycoder/PunycodeCodec-class-sidebar.html
+++ b/doc/api/punycoder/PunycodeCodec-class-sidebar.html
@@ -2,6 +2,7 @@
import 'package:punycoder/punycoder.dart';
void main() {
- const codec = PunycodeCodec();
- final encoded = codec.encoder.convert('münchen');
- print(encoded); // Output: mnchen-3ya
- final decoded = codec.decoder.convert('mnchen-3ya');
- print(decoded); // Output: münchen
+ // Designed to be used with domains and emails which have special rules
+ const domainCodec = PunycodeCodec();
+ // Designed to work with simple strings
+ const simpleCodec = PunycodeCodec.simple();
+
+ final encodedString = simpleCodec.encode('münchen');
+ final encodedDomain = domainCodec.encode('münchen.com');
+ final encodedEmail = domainCodec.encode('münchen@münchen.com');
+
+ print(encodedString); // Output: mnchen-3ya
+ // Uses the correct prefix for the domain
+ print(encodedDomain); // Output: xn--mnchen-3ya.com
+ // Only the domain should be encoded
+ print(encodedEmail); // Output: münchen@xn--mnchen-3ya.com
+
+ final decodedString = simpleCodec.decode('mnchen-3ya');
+ final decodecDomain = domainCodec.decode('xn--mnchen-3ya.com');
+ final decodedEmail = domainCodec.decode('münchen@xn--mnchen-3ya.com');
+
+ print(decodedString); // Output: münchen
+ print(decodecDomain); // Output: münchen.com
+ print(decodedEmail); // Output: münchen@münchen.com
}
- Creates a new instance of the Punycode codec.
+ Creates a new instance of the Punycode codec designed
+for working with domains and email addresses where
+additional rules apply about how it is converted
+
Creates a new instance of the Punycode codec designed
+for working with domains and email addresses where
+additional rules apply about how it is converted
string suitable for host name labels, using only letters, digits,
and hyphens. This library allows encoding Unicode strings to Punycode
ASCII strings and decoding them back to Unicode.
-
This library exports the main PunycodeCodec which provides convenient
-access to both the PunycodeEncoder and PunycodeDecoder. You can
-use the codec directly or instantiate the encoder/decoder separately.
+
This library exports the main PunycodeCodec which follows the
+standard codec interface from dart:convert to help ensure a
+smooth and idomatic Dart experience when encoding and decoding.
Usage
import 'package:punycoder/punycoder.dart';
void main() {
- // 1. Use the codec directly
final codec = PunycodeCodec();
// Encode a Unicode string (e.g., a domain label)
final encoded = codec.encode('bücher');
print(encoded); // Output: bcher-kva
- // Decode a Punycode string (typically prefixed with 'xn--' in IDNA)
- // Note: The decoder itself expects the raw Punycode without the prefix.
+ // Decode a Punycode string
final decoded = codec.decode('egbpdaj6bu4bxfgehfvwxn');
print(decoded); // Output: ليهماابتكلموشعربي؟
-
- // 2. Use the encoder/decoder individually
- const encoder = punycodeEncoder; // Access the singleton instance
- const decoder = punycodeDecoder; // Access the singleton instance
-
- // The encoder/decoder also provide helpers for domain/email processing
- // These handle the 'xn--' prefix and apply encoding/decoding only where needed.
- final encodedDomain = encoder.toAscii('mañana.com');
- print(encodedDomain); // Output: xn--maana-pta.com
-
- final decodedDomain = decoder.toUnicode('xn--ls8h.la');
- print(decodedDomain); // Output: 💩.la
-
- final email = 'bücher@xn--bcher-kva.com';
- final decodedEmail = decoder.toUnicode(email);
- print(decodedEmail); // Output: bücher@bücher.com
}
punycoder
punycoder
- 0.1.0-dev
+ 0.1.0
diff --git a/example/punycoder_example.dart b/example/punycoder_example.dart
index 24d1afc..f7db9ef 100644
--- a/example/punycoder_example.dart
+++ b/example/punycoder_example.dart
@@ -3,9 +3,9 @@ import 'dart:io';
import 'package:punycoder/punycoder.dart';
void main() {
- // Can account for what parts of a domain or email should be encoded
+ // Designed to be used with domains and emails which have special rules
const domainCodec = PunycodeCodec();
- // Makes no distinction based on the input content
+ // Designed to work with simple strings
const simpleCodec = PunycodeCodec.simple();
final encodedString = simpleCodec.encode('münchen');
diff --git a/lib/src/codec.dart b/lib/src/codec.dart
index 6e8e7c4..0467ed8 100644
--- a/lib/src/codec.dart
+++ b/lib/src/codec.dart
@@ -17,9 +17,9 @@ import 'package:punycoder/src/encoder.dart';
/// import 'package:punycoder/punycoder.dart';
///
/// void main() {
-/// // Can account for what parts of a domain or email should be encoded
+/// // Designed to be used with domains and emails which have special rules
/// const domainCodec = PunycodeCodec();
-/// // Makes no distinction based on the input content
+/// // Designed to work with simple strings
/// const simpleCodec = PunycodeCodec.simple();
///
/// final encodedString = simpleCodec.encode('münchen');
@@ -47,7 +47,7 @@ class PunycodeCodec extends Codec {
/// Creates a new instance of the Punycode codec designed
/// for working with domains and email addresses where
- /// additional rules apply about what needs to be converted
+ /// additional rules apply about how it is converted
const PunycodeCodec()
: _encoder = const PunycodeEncoder(),
_decoder = const PunycodeDecoder();
diff --git a/lib/src/encoder.dart b/lib/src/encoder.dart
index b6a85c9..9b51f71 100644
--- a/lib/src/encoder.dart
+++ b/lib/src/encoder.dart
@@ -1,3 +1,9 @@
+// SPDX-License-Identifier: MIT
+//
+// This code is partially based on the Punycode.js library by Mathias Bynens.
+// Original library: https://github.com/mathiasbynens/punycode.js/
+// Original library license: MIT
+
import 'dart:convert';
import 'package:punycoder/src/punycode_helper.dart';
diff --git a/lib/src/punycode_helper.dart b/lib/src/punycode_helper.dart
index fcecf21..d415ff4 100644
--- a/lib/src/punycode_helper.dart
+++ b/lib/src/punycode_helper.dart
@@ -1,3 +1,9 @@
+// SPDX-License-Identifier: MIT
+//
+// This code is partially based on the Punycode.js library by Mathias Bynens.
+// Original library: https://github.com/mathiasbynens/punycode.js/
+// Original library license: MIT
+
/// Bootstring parameters & constants from the Punycode
/// specification (RFC 3492)
const bootstrapValues = (
From 8bd5e532a0d03062ce75769d6798fe099b26b003 Mon Sep 17 00:00:00 2001
From: mark
Date: Fri, 2 May 2025 19:41:45 +0000
Subject: [PATCH 09/11] Update README
---
README.md | 68 ++++++++++++++++++++++---------------------------------
1 file changed, 27 insertions(+), 41 deletions(-)
diff --git a/README.md b/README.md
index 7f7e158..d2fc3d2 100644
--- a/README.md
+++ b/README.md
@@ -23,11 +23,11 @@ with the prefix `xn--`.
* **RFC 3492 Compliant:** Implements the Punycode encoding and decoding
algorithms as specified in the standard.
-* **IDNA Helpers:** Provides convenient methods (`toAscii`, `toUnicode`) for
- converting full domain names or email addresses, automatically handling
- the `xn--` prefix and processing only the necessary parts of the string.
-* **Core Codec:** Offers direct access to the raw Punycode `encode` and `decode`
- operations via `PunycodeCodec` for advanced use cases.
+* **IDNA Friendly:** Provides convenient ways of converting full domain names or
+ email addresses, automatically handling the `xn--` prefix and processing
+ only the necessary parts of the string.
+* **Idiomatic Dart:** This package implements the `Converter` interface
+ defined in `dart:convert` to make working with Punycode easy and familiar.
* **Efficient and Tested:** Based on a port of the well-regarded Punycode.js
library, including tests based on official RFC examples.
@@ -54,41 +54,27 @@ The easiest way to handle domain names or emails is using the singleton
import 'package:punycoder/punycoder.dart';
void main() {
- // Use the singleton encoder/decoder for common IDNA tasks
- const encoder = punycodeEncoder;
- const decoder = punycodeDecoder;
-
- // --- Convert domains/emails TO ASCII (ACE format) ---
- final domainsToEncode = ['bücher.example', 'example.com', '你好.test'];
- print('Encoding to ASCII:');
- for (final domain in domainsToEncode) {
- final asciiVersion = encoder.toAscii(domain);
- // toAscii adds 'xn--' prefix only if encoding actually happens
- print('"$domain" -> "$asciiVersion"');
- // Output:
- // "bücher.example" -> "xn--bcher-kva.example"
- // "example.com" -> "example.com"
- // "你好.test" -> "xn--6qq79v.test"
- }
-
- // --- Convert domains/emails FROM ASCII (ACE format) ---
- final domainsToDecode = ['xn--bcher-kva.example', 'example.com', 'xn--6qq79v.test'];
- print('Decoding from ASCII:');
- for (final domain in domainsToDecode) {
- final unicodeVersion = decoder.toUnicode(domain);
- // toUnicode decodes labels starting with 'xn--'
- print('"$domain" -> "$unicodeVersion"');
- // Output:
- // "xn--bcher-kva.example" -> "bücher.example"
- // "example.com" -> "example.com"
- // "xn--6qq79v.test" -> "你好.test"
- }
-
- // --- Raw Encoding/Decoding (Advanced) ---
- // For direct encoding/decoding without automatic prefix handling or domain splitting:
- final codec = PunycodeCodec();
- final rawEncoded = codec.encode('bücher'); // -> 'bcher-kva' (no prefix)
- final rawDecoded = codec.decode('bcher-kva'); // -> 'bücher'
- print('\nRaw codec example: "$rawDecoded"');
+ // Designed to be used with domains and emails which have special rules
+ const domainCodec = PunycodeCodec();
+ // Designed to work with simple strings
+ const simpleCodec = PunycodeCodec.simple();
+
+ final encodedString = simpleCodec.encode('münchen');
+ final encodedDomain = domainCodec.encode('münchen.com');
+ final encodedEmail = domainCodec.encode('münchen@münchen.com');
+
+ stdout.writeln(encodedString); // Output: mnchen-3ya
+ // Uses the correct prefix for the domain
+ stdout.writeln(encodedDomain); // Output: xn--mnchen-3ya.com
+ // Only the domain should be encoded
+ stdout.writeln(encodedEmail); // Output: münchen@xn--mnchen-3ya.com
+
+ final decodedString = simpleCodec.decode('mnchen-3ya');
+ final decodecDomain = domainCodec.decode('xn--mnchen-3ya.com');
+ final decodedEmail = domainCodec.decode('münchen@xn--mnchen-3ya.com');
+
+ stdout.writeln(decodedString); // Output: münchen
+ stdout.writeln(decodecDomain); // Output: münchen.com
+ stdout.writeln(decodedEmail); // Output: münchen@münchen.com
}
```
\ No newline at end of file
From 08872c3535f7c91538f859671a3c03b1b8b109e3 Mon Sep 17 00:00:00 2001
From: mark
Date: Fri, 2 May 2025 19:45:28 +0000
Subject: [PATCH 10/11] Regenerate docs
---
doc/api/index.html | 68 ++++++++++++++++++----------------------------
1 file changed, 27 insertions(+), 41 deletions(-)
diff --git a/doc/api/index.html b/doc/api/index.html
index ec4b425..e30c334 100644
--- a/doc/api/index.html
+++ b/doc/api/index.html
@@ -64,11 +64,11 @@
Features
RFC 3492 Compliant: Implements the Punycode encoding and decoding
algorithms as specified in the standard.
-
IDNA Helpers: Provides convenient methods (toAscii, toUnicode) for
- converting full domain names or email addresses, automatically handling
- the xn-- prefix and processing only the necessary parts of the string.
-
Core Codec: Offers direct access to the raw Punycode encode and decode
- operations via PunycodeCodec for advanced use cases.
+
IDNA Friendly: Provides convenient ways of converting full domain names or
+ email addresses, automatically handling the xn-- prefix and processing
+ only the necessary parts of the string.
+
Idiomatic Dart: This package implements the Converter<S, T> interface
+ defined in dart:convert to make working with Punycode easy and familiar.
Efficient and Tested: Based on a port of the well-regarded Punycode.js
library, including tests based on official RFC examples.
@@ -86,42 +86,28 @@
Usage
import 'package:punycoder/punycoder.dart';
void main() {
- // Use the singleton encoder/decoder for common IDNA tasks
- const encoder = punycodeEncoder;
- const decoder = punycodeDecoder;
-
- // --- Convert domains/emails TO ASCII (ACE format) ---
- final domainsToEncode = ['bücher.example', 'example.com', '你好.test'];
- print('Encoding to ASCII:');
- for (final domain in domainsToEncode) {
- final asciiVersion = encoder.toAscii(domain);
- // toAscii adds 'xn--' prefix only if encoding actually happens
- print('"$domain" -> "$asciiVersion"');
- // Output:
- // "bücher.example" -> "xn--bcher-kva.example"
- // "example.com" -> "example.com"
- // "你好.test" -> "xn--6qq79v.test"
- }
-
- // --- Convert domains/emails FROM ASCII (ACE format) ---
- final domainsToDecode = ['xn--bcher-kva.example', 'example.com', 'xn--6qq79v.test'];
- print('Decoding from ASCII:');
- for (final domain in domainsToDecode) {
- final unicodeVersion = decoder.toUnicode(domain);
- // toUnicode decodes labels starting with 'xn--'
- print('"$domain" -> "$unicodeVersion"');
- // Output:
- // "xn--bcher-kva.example" -> "bücher.example"
- // "example.com" -> "example.com"
- // "xn--6qq79v.test" -> "你好.test"
- }
-
- // --- Raw Encoding/Decoding (Advanced) ---
- // For direct encoding/decoding without automatic prefix handling or domain splitting:
- final codec = PunycodeCodec();
- final rawEncoded = codec.encode('bücher'); // -> 'bcher-kva' (no prefix)
- final rawDecoded = codec.decode('bcher-kva'); // -> 'bücher'
- print('\nRaw codec example: "$rawDecoded"');
+ // Designed to be used with domains and emails which have special rules
+ const domainCodec = PunycodeCodec();
+ // Designed to work with simple strings
+ const simpleCodec = PunycodeCodec.simple();
+
+ final encodedString = simpleCodec.encode('münchen');
+ final encodedDomain = domainCodec.encode('münchen.com');
+ final encodedEmail = domainCodec.encode('münchen@münchen.com');
+
+ stdout.writeln(encodedString); // Output: mnchen-3ya
+ // Uses the correct prefix for the domain
+ stdout.writeln(encodedDomain); // Output: xn--mnchen-3ya.com
+ // Only the domain should be encoded
+ stdout.writeln(encodedEmail); // Output: münchen@xn--mnchen-3ya.com
+
+ final decodedString = simpleCodec.decode('mnchen-3ya');
+ final decodecDomain = domainCodec.decode('xn--mnchen-3ya.com');
+ final decodedEmail = domainCodec.decode('münchen@xn--mnchen-3ya.com');
+
+ stdout.writeln(decodedString); // Output: münchen
+ stdout.writeln(decodecDomain); // Output: münchen.com
+ stdout.writeln(decodedEmail); // Output: münchen@münchen.com
}
From cdeb0547859a7df191fd8495d922f39cab998021 Mon Sep 17 00:00:00 2001
From: mark
Date: Fri, 2 May 2025 19:53:36 +0000
Subject: [PATCH 11/11] Update version and changelog
---
CHANGELOG.md | 4 ++++
pubspec.yaml | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a0712a7..dc606ad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.0-dev
+
+- Refactor the package to strictly follow the `Converter` interface established in `dart:convert` and clean up the API as a result.
+
## 0.1.0
- Initial version.
diff --git a/pubspec.yaml b/pubspec.yaml
index ac97aea..f2af69a 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: punycoder
description: Punycoder is a well tested Dart implementation of the RFC 3492 Punycode specification
-version: 0.1.0
+version: 0.2.0-dev
repository: https://github.com/dropbear-software/punycoder/
issue_tracker: https://github.com/dropbear-software/punycoder/issues
topics: