diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,5 +1,32 @@
 # Changelog for `proto-lens`
 
+## v0.7.1.7
+- Relax upper bounds for ghc-9.10.
+- Bump upper bound to allow ghc-prim-0.12 (ghc-9.10.2).
+- Support GHC 9.12.
+- Support GHC 9.14.
+
+## v0.7.1.2
+- Support GHC 9.4
+
+## v0.7.1.1
+- Relax upper bounds for ghc-9.2
+
+## v0.7.1.0
+- Support GHC 9.0.
+- Fixed parsing of UTF8 chars in text format proto string literals.
+- Support all valid text format boolean field values
+- Remove redundant (.&. 127) from putVarInt (#396)
+
+## v0.7.0.0
+- Support GHC 8.10.
+- Add a method to `Data.ProtoLens.Message` for getting the `DescriptorProto`
+  of a given message.  For a simpler API, see `Data.ProtoLens.Descriptor`
+  from `proto-lens-protobuf-types`.
+- Bump ghc-source-gen to version 0.4.0.0.
+- Bump upper bound to allow base-4.14.
+- Bump upper bound to allow ghc-prim-0.6.
+
 ## v0.6.0.0
 
 ### Breaking Changes
diff --git a/proto-lens-imports/google/protobuf/compiler/plugin.proto b/proto-lens-imports/google/protobuf/compiler/plugin.proto
--- a/proto-lens-imports/google/protobuf/compiler/plugin.proto
+++ b/proto-lens-imports/google/protobuf/compiler/plugin.proto
@@ -1,38 +1,12 @@
 // Protocol Buffers - Google's data interchange format
 // Copyright 2008 Google Inc.  All rights reserved.
-// https://developers.google.com/protocol-buffers/
 //
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
 
 // Author: kenton@google.com (Kenton Varda)
 //
-// WARNING:  The plugin interface is currently EXPERIMENTAL and is subject to
-//   change.
-//
 // protoc (aka the Protocol Compiler) can be extended via plugins.  A plugin is
 // just a program that reads a CodeGeneratorRequest from stdin and writes a
 // CodeGeneratorResponse to stdout.
@@ -45,11 +19,13 @@
 // flag "--${NAME}_out" is passed to protoc.
 
 syntax = "proto2";
+
 package google.protobuf.compiler;
 option java_package = "com.google.protobuf.compiler";
 option java_outer_classname = "PluginProtos";
 
-option go_package = "github.com/golang/protobuf/protoc-gen-go/plugin;plugin_go";
+option csharp_namespace = "Google.Protobuf.Compiler";
+option go_package = "google.golang.org/protobuf/types/pluginpb";
 
 import "google/protobuf/descriptor.proto";
 
@@ -77,6 +53,11 @@
   // they import.  The files will appear in topological order, so each file
   // appears before any file that imports it.
   //
+  // Note: the files listed in files_to_generate will include runtime-retention
+  // options only, but all other files will include source-retention options.
+  // The source_file_descriptors field below is available in case you need
+  // source-retention options for files_to_generate.
+  //
   // protoc guarantees that all proto_files will be written after
   // the fields above, even though this is not technically guaranteed by the
   // protobuf wire format.  This theoretically could allow a plugin to stream
@@ -89,9 +70,13 @@
   // fully qualified.
   repeated FileDescriptorProto proto_file = 15;
 
+  // File descriptors with all options, including source-retention options.
+  // These descriptors are only provided for the files listed in
+  // files_to_generate.
+  repeated FileDescriptorProto source_file_descriptors = 17;
+
   // The version number of protocol compiler.
   optional Version compiler_version = 3;
-
 }
 
 // The plugin writes an encoded CodeGeneratorResponse to stdout.
@@ -106,6 +91,29 @@
   // exiting with a non-zero status code.
   optional string error = 1;
 
+  // A bitmask of supported features that the code generator supports.
+  // This is a bitwise "or" of values from the Feature enum.
+  optional uint64 supported_features = 2;
+
+  // Sync with code_generator.h.
+  enum Feature {
+    FEATURE_NONE = 0;
+    FEATURE_PROTO3_OPTIONAL = 1;
+    FEATURE_SUPPORTS_EDITIONS = 2;
+  }
+
+  // The minimum edition this plugin supports.  This will be treated as an
+  // Edition enum, but we want to allow unknown values.  It should be specified
+  // according the edition enum value, *not* the edition number.  Only takes
+  // effect for plugins that have FEATURE_SUPPORTS_EDITIONS set.
+  optional int32 minimum_edition = 3;
+
+  // The maximum edition this plugin supports.  This will be treated as an
+  // Edition enum, but we want to allow unknown values.  It should be specified
+  // according the edition enum value, *not* the edition number.  Only takes
+  // effect for plugins that have FEATURE_SUPPORTS_EDITIONS set.
+  optional int32 maximum_edition = 4;
+
   // Represents a single generated file.
   message File {
     // The file name, relative to the output directory.  The name must not
@@ -162,6 +170,11 @@
 
     // The file contents.
     optional string content = 15;
+
+    // Information describing the file content being inserted. If an insertion
+    // point is used, this information will be appropriately offset and inserted
+    // into the code generation metadata for the generated files.
+    optional GeneratedCodeInfo generated_code_info = 16;
   }
   repeated File file = 15;
 }
diff --git a/proto-lens-imports/google/protobuf/descriptor.proto b/proto-lens-imports/google/protobuf/descriptor.proto
--- a/proto-lens-imports/google/protobuf/descriptor.proto
+++ b/proto-lens-imports/google/protobuf/descriptor.proto
@@ -36,11 +36,11 @@
 // A valid .proto file can be translated directly to a FileDescriptorProto
 // without any other information (e.g. without reading its imports).
 
-
 syntax = "proto2";
 
 package google.protobuf;
-option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor";
+
+option go_package = "google.golang.org/protobuf/types/descriptorpb";
 option java_package = "com.google.protobuf";
 option java_outer_classname = "DescriptorProtos";
 option csharp_namespace = "Google.Protobuf.Reflection";
@@ -57,10 +57,46 @@
   repeated FileDescriptorProto file = 1;
 }
 
+// The full set of known editions.
+enum Edition {
+  // A placeholder for an unknown edition value.
+  EDITION_UNKNOWN = 0;
+
+  // A placeholder edition for specifying default behaviors *before* a feature
+  // was first introduced.  This is effectively an "infinite past".
+  EDITION_LEGACY = 900;
+
+  // Legacy syntax "editions".  These pre-date editions, but behave much like
+  // distinct editions.  These can't be used to specify the edition of proto
+  // files, but feature definitions must supply proto2/proto3 defaults for
+  // backwards compatibility.
+  EDITION_PROTO2 = 998;
+  EDITION_PROTO3 = 999;
+
+  // Editions that have been released.  The specific values are arbitrary and
+  // should not be depended on, but they will always be time-ordered for easy
+  // comparison.
+  EDITION_2023 = 1000;
+  EDITION_2024 = 1001;
+
+  // Placeholder editions for testing feature resolution.  These should not be
+  // used or relyed on outside of tests.
+  EDITION_1_TEST_ONLY = 1;
+  EDITION_2_TEST_ONLY = 2;
+  EDITION_99997_TEST_ONLY = 99997;
+  EDITION_99998_TEST_ONLY = 99998;
+  EDITION_99999_TEST_ONLY = 99999;
+
+  // Placeholder for specifying unbounded edition support.  This should only
+  // ever be used by plugins that can expect to never require any changes to
+  // support a new edition.
+  EDITION_MAX = 0x7FFFFFFF;
+}
+
 // Describes a complete .proto file.
 message FileDescriptorProto {
-  optional string name = 1;       // file name, relative to root of source tree
-  optional string package = 2;    // e.g. "foo", "foo.bar", etc.
+  optional string name = 1;     // file name, relative to root of source tree
+  optional string package = 2;  // e.g. "foo", "foo.bar", etc.
 
   // Names of files imported by this file.
   repeated string dependency = 3;
@@ -85,8 +121,13 @@
   optional SourceCodeInfo source_code_info = 9;
 
   // The syntax of the proto file.
-  // The supported values are "proto2" and "proto3".
+  // The supported values are "proto2", "proto3", and "editions".
+  //
+  // If `edition` is present, this value must be "editions".
   optional string syntax = 12;
+
+  // The edition of the proto file.
+  optional Edition edition = 14;
 }
 
 // Describes a message type.
@@ -100,8 +141,8 @@
   repeated EnumDescriptorProto enum_type = 4;
 
   message ExtensionRange {
-    optional int32 start = 1;
-    optional int32 end = 2;
+    optional int32 start = 1;  // Inclusive.
+    optional int32 end = 2;    // Exclusive.
 
     optional ExtensionRangeOptions options = 3;
   }
@@ -115,8 +156,8 @@
   // fields or extension ranges in the same message. Reserved ranges may
   // not overlap.
   message ReservedRange {
-    optional int32 start = 1; // Inclusive.
-    optional int32 end = 2;   // Exclusive.
+    optional int32 start = 1;  // Inclusive.
+    optional int32 end = 2;    // Exclusive.
   }
   repeated ReservedRange reserved_range = 9;
   // Reserved field names, which may not be used by fields in the same message.
@@ -128,6 +169,52 @@
   // The parser stores options it doesn't recognize here. See above.
   repeated UninterpretedOption uninterpreted_option = 999;
 
+  message Declaration {
+    // The extension number declared within the extension range.
+    optional int32 number = 1;
+
+    // The fully-qualified name of the extension field. There must be a leading
+    // dot in front of the full name.
+    optional string full_name = 2;
+
+    // The fully-qualified type name of the extension field. Unlike
+    // Metadata.type, Declaration.type must have a leading dot for messages
+    // and enums.
+    optional string type = 3;
+
+    // If true, indicates that the number is reserved in the extension range,
+    // and any extension field with the number will fail to compile. Set this
+    // when a declared extension field is deleted.
+    optional bool reserved = 5;
+
+    // If true, indicates that the extension must be defined as repeated.
+    // Otherwise the extension must be defined as optional.
+    optional bool repeated = 6;
+
+    reserved 4;  // removed is_repeated
+  }
+
+  // For external users: DO NOT USE. We are in the process of open sourcing
+  // extension declaration and executing internal cleanups before it can be
+  // used externally.
+  repeated Declaration declaration = 2 [retention = RETENTION_SOURCE];
+
+  // Any features defined in the specific edition.
+  optional FeatureSet features = 50;
+
+  // The verification state of the extension range.
+  enum VerificationState {
+    // All the extensions of the range must be declared.
+    DECLARATION = 0;
+    UNVERIFIED = 1;
+  }
+
+  // The verification state of the range.
+  // TODO: flip the default to DECLARATION once all empty ranges
+  // are marked as UNVERIFIED.
+  optional VerificationState verification = 3
+      [default = UNVERIFIED, retention = RETENTION_SOURCE];
+
   // Clients can define custom options in extensions of this message. See above.
   extensions 1000 to max;
 }
@@ -137,42 +224,46 @@
   enum Type {
     // 0 is reserved for errors.
     // Order is weird for historical reasons.
-    TYPE_DOUBLE         = 1;
-    TYPE_FLOAT          = 2;
+    TYPE_DOUBLE = 1;
+    TYPE_FLOAT = 2;
     // Not ZigZag encoded.  Negative numbers take 10 bytes.  Use TYPE_SINT64 if
     // negative values are likely.
-    TYPE_INT64          = 3;
-    TYPE_UINT64         = 4;
+    TYPE_INT64 = 3;
+    TYPE_UINT64 = 4;
     // Not ZigZag encoded.  Negative numbers take 10 bytes.  Use TYPE_SINT32 if
     // negative values are likely.
-    TYPE_INT32          = 5;
-    TYPE_FIXED64        = 6;
-    TYPE_FIXED32        = 7;
-    TYPE_BOOL           = 8;
-    TYPE_STRING         = 9;
+    TYPE_INT32 = 5;
+    TYPE_FIXED64 = 6;
+    TYPE_FIXED32 = 7;
+    TYPE_BOOL = 8;
+    TYPE_STRING = 9;
     // Tag-delimited aggregate.
-    // Group type is deprecated and not supported in proto3. However, Proto3
+    // Group type is deprecated and not supported after google.protobuf. However, Proto3
     // implementations should still be able to parse the group wire format and
-    // treat group fields as unknown fields.
-    TYPE_GROUP          = 10;
-    TYPE_MESSAGE        = 11;  // Length-delimited aggregate.
+    // treat group fields as unknown fields.  In Editions, the group wire format
+    // can be enabled via the `message_encoding` feature.
+    TYPE_GROUP = 10;
+    TYPE_MESSAGE = 11;  // Length-delimited aggregate.
 
     // New in version 2.
-    TYPE_BYTES          = 12;
-    TYPE_UINT32         = 13;
-    TYPE_ENUM           = 14;
-    TYPE_SFIXED32       = 15;
-    TYPE_SFIXED64       = 16;
-    TYPE_SINT32         = 17;  // Uses ZigZag encoding.
-    TYPE_SINT64         = 18;  // Uses ZigZag encoding.
-  };
+    TYPE_BYTES = 12;
+    TYPE_UINT32 = 13;
+    TYPE_ENUM = 14;
+    TYPE_SFIXED32 = 15;
+    TYPE_SFIXED64 = 16;
+    TYPE_SINT32 = 17;  // Uses ZigZag encoding.
+    TYPE_SINT64 = 18;  // Uses ZigZag encoding.
+  }
 
   enum Label {
     // 0 is reserved for errors
-    LABEL_OPTIONAL      = 1;
-    LABEL_REQUIRED      = 2;
-    LABEL_REPEATED      = 3;
-  };
+    LABEL_OPTIONAL = 1;
+    LABEL_REPEATED = 3;
+    // The required label is only allowed in google.protobuf.  In proto3 and Editions
+    // it's explicitly prohibited.  In Editions, the `field_presence` feature
+    // can be used to get this behavior.
+    LABEL_REQUIRED = 2;
+  }
 
   optional string name = 1;
   optional int32 number = 3;
@@ -197,7 +288,6 @@
   // For booleans, "true" or "false".
   // For strings, contains the default text contents (not escaped in any way).
   // For bytes, contains the C escaped value.  All bytes >= 128 are escaped.
-  // TODO(kenton):  Base-64 encode?
   optional string default_value = 7;
 
   // If set, gives the index of a oneof in the containing type's oneof_decl
@@ -211,6 +301,29 @@
   optional string json_name = 10;
 
   optional FieldOptions options = 8;
+
+  // If true, this is a proto3 "optional". When a proto3 field is optional, it
+  // tracks presence regardless of field type.
+  //
+  // When proto3_optional is true, this field must belong to a oneof to signal
+  // to old proto3 clients that presence is tracked for this field. This oneof
+  // is known as a "synthetic" oneof, and this field must be its sole member
+  // (each proto3 optional field gets its own synthetic oneof). Synthetic oneofs
+  // exist in the descriptor only, and do not generate any API. Synthetic oneofs
+  // must be ordered after all "real" oneofs.
+  //
+  // For message fields, proto3_optional doesn't create any semantic change,
+  // since non-repeated message fields always track presence. However it still
+  // indicates the semantic detail of whether the user wrote "optional" or not.
+  // This can be useful for round-tripping the .proto file. For consistency we
+  // give message fields a synthetic oneof also, even though it is not required
+  // to track presence. This is especially important because the parser can't
+  // tell if a field is a message or an enum, so it must always create a
+  // synthetic oneof.
+  //
+  // Proto2 optional fields do not set this flag, because they already indicate
+  // optional with `LABEL_OPTIONAL`.
+  optional bool proto3_optional = 17;
 }
 
 // Describes a oneof.
@@ -234,8 +347,8 @@
   // is inclusive such that it can appropriately represent the entire int32
   // domain.
   message EnumReservedRange {
-    optional int32 start = 1; // Inclusive.
-    optional int32 end = 2;   // Inclusive.
+    optional int32 start = 1;  // Inclusive.
+    optional int32 end = 2;    // Inclusive.
   }
 
   // Range of reserved numeric values. Reserved numeric values may not be used
@@ -276,12 +389,11 @@
   optional MethodOptions options = 4;
 
   // Identifies if client streams multiple client messages
-  optional bool client_streaming = 5 [default=false];
+  optional bool client_streaming = 5 [default = false];
   // Identifies if server streams multiple server messages
-  optional bool server_streaming = 6 [default=false];
+  optional bool server_streaming = 6 [default = false];
 }
 
-
 // ===================================================================
 // Options
 
@@ -314,7 +426,6 @@
 //   If this turns out to be popular, a web service will be set up
 //   to automatically assign option numbers.
 
-
 message FileOptions {
 
   // Sets the Java package where classes generated from this .proto will be
@@ -323,42 +434,44 @@
   // domain names.
   optional string java_package = 1;
 
-
-  // If set, all the classes from the .proto file are wrapped in a single
-  // outer class with the given name.  This applies to both Proto1
-  // (equivalent to the old "--one_java_file" option) and Proto2 (where
-  // a .proto always translates to a single class, but you may want to
-  // explicitly choose the class name).
+  // Controls the name of the wrapper Java class generated for the .proto file.
+  // That class will always contain the .proto file's getDescriptor() method as
+  // well as any top-level extensions defined in the .proto file.
+  // If java_multiple_files is disabled, then all the other classes from the
+  // .proto file will be nested inside the single wrapper outer class.
   optional string java_outer_classname = 8;
 
-  // If set true, then the Java code generator will generate a separate .java
+  // If enabled, then the Java code generator will generate a separate .java
   // file for each top-level message, enum, and service defined in the .proto
-  // file.  Thus, these types will *not* be nested inside the outer class
-  // named by java_outer_classname.  However, the outer class will still be
+  // file.  Thus, these types will *not* be nested inside the wrapper class
+  // named by java_outer_classname.  However, the wrapper class will still be
   // generated to contain the file's getDescriptor() method as well as any
   // top-level extensions defined in the file.
-  optional bool java_multiple_files = 10 [default=false];
+  optional bool java_multiple_files = 10 [default = false];
 
   // This option does nothing.
   optional bool java_generate_equals_and_hash = 20 [deprecated=true];
 
-  // If set true, then the Java2 code generator will generate code that
-  // throws an exception whenever an attempt is made to assign a non-UTF-8
-  // byte sequence to a string field.
-  // Message reflection will do the same.
-  // However, an extension field still accepts non-UTF-8 byte sequences.
-  // This option has no effect on when used with the lite runtime.
-  optional bool java_string_check_utf8 = 27 [default=false];
-
+  // A proto2 file can set this to true to opt in to UTF-8 checking for Java,
+  // which will throw an exception if invalid UTF-8 is parsed from the wire or
+  // assigned to a string field.
+  //
+  // TODO: clarify exactly what kinds of field types this option
+  // applies to, and update these docs accordingly.
+  //
+  // Proto3 files already perform these checks. Setting the option explicitly to
+  // false has no effect: it cannot be used to opt proto3 files out of UTF-8
+  // checks.
+  optional bool java_string_check_utf8 = 27 [default = false];
 
   // Generated classes can be optimized for speed or code size.
   enum OptimizeMode {
-    SPEED = 1;        // Generate complete code for parsing, serialization,
-                      // etc.
-    CODE_SIZE = 2;    // Use ReflectionOps to implement these methods.
-    LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
+    SPEED = 1;         // Generate complete code for parsing, serialization,
+                       // etc.
+    CODE_SIZE = 2;     // Use ReflectionOps to implement these methods.
+    LITE_RUNTIME = 3;  // Generate code using MessageLite and the lite runtime.
   }
-  optional OptimizeMode optimize_for = 9 [default=SPEED];
+  optional OptimizeMode optimize_for = 9 [default = SPEED];
 
   // Sets the Go package where structs generated from this .proto will be
   // placed. If omitted, the Go package will be derived from the following:
@@ -367,8 +480,6 @@
   //   - Otherwise, the basename of the .proto file, without extension.
   optional string go_package = 11;
 
-
-
   // Should generic services be generated in each language?  "Generic" services
   // are not specific to any particular RPC system.  They are generated by the
   // main code generators in each language (without additional plugins).
@@ -379,21 +490,21 @@
   // that generate code specific to your particular RPC system.  Therefore,
   // these default to false.  Old code which depends on generic services should
   // explicitly set them to true.
-  optional bool cc_generic_services = 16 [default=false];
-  optional bool java_generic_services = 17 [default=false];
-  optional bool py_generic_services = 18 [default=false];
-  optional bool php_generic_services = 42 [default=false];
+  optional bool cc_generic_services = 16 [default = false];
+  optional bool java_generic_services = 17 [default = false];
+  optional bool py_generic_services = 18 [default = false];
+  reserved 42;  // removed php_generic_services
+  reserved "php_generic_services";
 
   // Is this file deprecated?
   // Depending on the target platform, this can emit Deprecated annotations
   // for everything in the file, or it will be completely ignored; in the very
   // least, this is a formalization for deprecating files.
-  optional bool deprecated = 23 [default=false];
+  optional bool deprecated = 23 [default = false];
 
   // Enables the use of arenas for the proto messages in this file. This applies
   // only to generated classes for C++.
-  optional bool cc_enable_arenas = 31 [default=false];
-
+  optional bool cc_enable_arenas = 31 [default = true];
 
   // Sets the objective c class prefix which is prepended to all objective c
   // generated classes from this .proto. There is no default.
@@ -417,10 +528,9 @@
   // determining the namespace.
   optional string php_namespace = 41;
 
-
   // Use this option to change the namespace of php generated metadata classes.
-  // Default is empty. When this option is empty, the proto file name will be used
-  // for determining the namespace.
+  // Default is empty. When this option is empty, the proto file name will be
+  // used for determining the namespace.
   optional string php_metadata_namespace = 44;
 
   // Use this option to change the package of ruby generated classes. Default
@@ -428,6 +538,9 @@
   // determining the ruby package.
   optional string ruby_package = 45;
 
+  // Any features defined in the specific edition.
+  optional FeatureSet features = 50;
+
   // The parser stores options it doesn't recognize here.
   // See the documentation for the "Options" section above.
   repeated UninterpretedOption uninterpreted_option = 999;
@@ -458,19 +571,21 @@
   //
   // Because this is an option, the above two restrictions are not enforced by
   // the protocol compiler.
-  optional bool message_set_wire_format = 1 [default=false];
+  optional bool message_set_wire_format = 1 [default = false];
 
   // Disables the generation of the standard "descriptor()" accessor, which can
   // conflict with a field of the same name.  This is meant to make migration
   // from proto1 easier; new code should avoid fields named "descriptor".
-  optional bool no_standard_descriptor_accessor = 2 [default=false];
+  optional bool no_standard_descriptor_accessor = 2 [default = false];
 
   // Is this message deprecated?
   // Depending on the target platform, this can emit Deprecated annotations
   // for the message, or it will be completely ignored; in the very least,
   // this is a formalization for deprecating messages.
-  optional bool deprecated = 3 [default=false];
+  optional bool deprecated = 3 [default = false];
 
+  reserved 4, 5, 6;
+
   // Whether the message is an automatically generated map entry type for the
   // maps field.
   //
@@ -486,7 +601,7 @@
   //
   // Implementations may choose not to generate the map_entry=true message, but
   // use a native map in the target language to hold the keys and values.
-  // The reflection APIs in such implementions still need to work as
+  // The reflection APIs in such implementations still need to work as
   // if the field is a repeated message field.
   //
   // NOTE: Do not set the option in .proto files. Always use the maps syntax
@@ -497,6 +612,21 @@
   reserved 8;  // javalite_serializable
   reserved 9;  // javanano_as_lite
 
+  // Enable the legacy handling of JSON field name conflicts.  This lowercases
+  // and strips underscored from the fields before comparison in proto3 only.
+  // The new behavior takes `json_name` into account and applies to proto2 as
+  // well.
+  //
+  // This should only be used as a temporary measure against broken builds due
+  // to the change in behavior for JSON field name conflicts.
+  //
+  // TODO This is legacy behavior we plan to remove once downstream
+  // teams have had time to migrate.
+  optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true];
+
+  // Any features defined in the specific edition.
+  optional FeatureSet features = 12;
+
   // The parser stores options it doesn't recognize here. See above.
   repeated UninterpretedOption uninterpreted_option = 999;
 
@@ -505,15 +635,24 @@
 }
 
 message FieldOptions {
+  // NOTE: ctype is deprecated. Use `features.(pb.cpp).string_type` instead.
   // The ctype option instructs the C++ code generator to use a different
   // representation of the field than it normally would.  See the specific
-  // options below.  This option is not yet implemented in the open source
-  // release -- sorry, we'll try to include it in a future version!
-  optional CType ctype = 1 [default = STRING];
+  // options below.  This option is only implemented to support use of
+  // [ctype=CORD] and [ctype=STRING] (the default) on non-repeated fields of
+  // type "bytes" in the open source release.
+  // TODO: make ctype actually deprecated.
+  optional CType ctype = 1 [/*deprecated = true,*/ default = STRING];
   enum CType {
     // Default mode.
     STRING = 0;
 
+    // The option [ctype=CORD] may be applied to a non-repeated field of type
+    // "bytes". It indicates that in C++, the data should be stored in a Cord
+    // instead of a string.  For very large strings, this may reduce memory
+    // fragmentation. It may also allow better performance when parsing from a
+    // Cord, or when parsing with aliasing enabled, as the parsed Cord may then
+    // alias the original buffer.
     CORD = 1;
 
     STRING_PIECE = 2;
@@ -522,7 +661,9 @@
   // a more efficient representation on the wire. Rather than repeatedly
   // writing the tag and type for each element, the entire array is encoded as
   // a single length-delimited blob. In proto3, only explicit setting it to
-  // false will avoid using packed encoding.
+  // false will avoid using packed encoding.  This option is prohibited in
+  // Editions, but the `repeated_field_encoding` feature can be used to control
+  // the behavior.
   optional bool packed = 2;
 
   // The jstype option determines the JavaScript type used for values of the
@@ -565,39 +706,106 @@
   // call from multiple threads concurrently, while non-const methods continue
   // to require exclusive access.
   //
-  //
-  // Note that implementations may choose not to check required fields within
-  // a lazy sub-message.  That is, calling IsInitialized() on the outer message
-  // may return true even if the inner message has missing required fields.
-  // This is necessary because otherwise the inner message would have to be
-  // parsed in order to perform the check, defeating the purpose of lazy
-  // parsing.  An implementation which chooses not to check required fields
-  // must be consistent about it.  That is, for any particular sub-message, the
-  // implementation must either *always* check its required fields, or *never*
-  // check its required fields, regardless of whether or not the message has
-  // been parsed.
-  optional bool lazy = 5 [default=false];
+  // Note that lazy message fields are still eagerly verified to check
+  // ill-formed wireformat or missing required fields. Calling IsInitialized()
+  // on the outer message would fail if the inner message has missing required
+  // fields. Failed verification would result in parsing failure (except when
+  // uninitialized messages are acceptable).
+  optional bool lazy = 5 [default = false];
 
+  // unverified_lazy does no correctness checks on the byte stream. This should
+  // only be used where lazy with verification is prohibitive for performance
+  // reasons.
+  optional bool unverified_lazy = 15 [default = false];
+
   // Is this field deprecated?
   // Depending on the target platform, this can emit Deprecated annotations
   // for accessors, or it will be completely ignored; in the very least, this
   // is a formalization for deprecating fields.
-  optional bool deprecated = 3 [default=false];
+  optional bool deprecated = 3 [default = false];
 
   // For Google-internal migration only. Do not use.
-  optional bool weak = 10 [default=false];
+  optional bool weak = 10 [default = false];
 
+  // Indicate that the field value should not be printed out when using debug
+  // formats, e.g. when the field contains sensitive credentials.
+  optional bool debug_redact = 16 [default = false];
 
+  // If set to RETENTION_SOURCE, the option will be omitted from the binary.
+  // Note: as of January 2023, support for this is in progress and does not yet
+  // have an effect (b/264593489).
+  enum OptionRetention {
+    RETENTION_UNKNOWN = 0;
+    RETENTION_RUNTIME = 1;
+    RETENTION_SOURCE = 2;
+  }
+
+  optional OptionRetention retention = 17;
+
+  // This indicates the types of entities that the field may apply to when used
+  // as an option. If it is unset, then the field may be freely used as an
+  // option on any kind of entity. Note: as of January 2023, support for this is
+  // in progress and does not yet have an effect (b/264593489).
+  enum OptionTargetType {
+    TARGET_TYPE_UNKNOWN = 0;
+    TARGET_TYPE_FILE = 1;
+    TARGET_TYPE_EXTENSION_RANGE = 2;
+    TARGET_TYPE_MESSAGE = 3;
+    TARGET_TYPE_FIELD = 4;
+    TARGET_TYPE_ONEOF = 5;
+    TARGET_TYPE_ENUM = 6;
+    TARGET_TYPE_ENUM_ENTRY = 7;
+    TARGET_TYPE_SERVICE = 8;
+    TARGET_TYPE_METHOD = 9;
+  }
+
+  repeated OptionTargetType targets = 19;
+
+  message EditionDefault {
+    optional Edition edition = 3;
+    optional string value = 2;  // Textproto value.
+  }
+  repeated EditionDefault edition_defaults = 20;
+
+  // Any features defined in the specific edition.
+  optional FeatureSet features = 21;
+
+  // Information about the support window of a feature.
+  message FeatureSupport {
+    // The edition that this feature was first available in.  In editions
+    // earlier than this one, the default assigned to EDITION_LEGACY will be
+    // used, and proto files will not be able to override it.
+    optional Edition edition_introduced = 1;
+
+    // The edition this feature becomes deprecated in.  Using this after this
+    // edition may trigger warnings.
+    optional Edition edition_deprecated = 2;
+
+    // The deprecation warning text if this feature is used after the edition it
+    // was marked deprecated in.
+    optional string deprecation_warning = 3;
+
+    // The edition this feature is no longer available in.  In editions after
+    // this one, the last default assigned will be used, and proto files will
+    // not be able to override it.
+    optional Edition edition_removed = 4;
+  }
+  optional FeatureSupport feature_support = 22;
+
   // The parser stores options it doesn't recognize here. See above.
   repeated UninterpretedOption uninterpreted_option = 999;
 
   // Clients can define custom options in extensions of this message. See above.
   extensions 1000 to max;
 
-  reserved 4;  // removed jtype
+  reserved 4;   // removed jtype
+  reserved 18;  // reserve target, target_obsolete_do_not_use
 }
 
 message OneofOptions {
+  // Any features defined in the specific edition.
+  optional FeatureSet features = 1;
+
   // The parser stores options it doesn't recognize here. See above.
   repeated UninterpretedOption uninterpreted_option = 999;
 
@@ -615,10 +823,21 @@
   // Depending on the target platform, this can emit Deprecated annotations
   // for the enum, or it will be completely ignored; in the very least, this
   // is a formalization for deprecating enums.
-  optional bool deprecated = 3 [default=false];
+  optional bool deprecated = 3 [default = false];
 
   reserved 5;  // javanano_as_lite
 
+  // Enable the legacy handling of JSON field name conflicts.  This lowercases
+  // and strips underscored from the fields before comparison in proto3 only.
+  // The new behavior takes `json_name` into account and applies to proto2 as
+  // well.
+  // TODO Remove this legacy behavior once downstream teams have
+  // had time to migrate.
+  optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true];
+
+  // Any features defined in the specific edition.
+  optional FeatureSet features = 7;
+
   // The parser stores options it doesn't recognize here. See above.
   repeated UninterpretedOption uninterpreted_option = 999;
 
@@ -631,8 +850,19 @@
   // Depending on the target platform, this can emit Deprecated annotations
   // for the enum value, or it will be completely ignored; in the very least,
   // this is a formalization for deprecating enum values.
-  optional bool deprecated = 1 [default=false];
+  optional bool deprecated = 1 [default = false];
 
+  // Any features defined in the specific edition.
+  optional FeatureSet features = 2;
+
+  // Indicate that fields annotated with this enum value should not be printed
+  // out when using debug formats, e.g. when the field contains sensitive
+  // credentials.
+  optional bool debug_redact = 3 [default = false];
+
+  // Information about the support window of a feature value.
+  optional FieldOptions.FeatureSupport feature_support = 4;
+
   // The parser stores options it doesn't recognize here. See above.
   repeated UninterpretedOption uninterpreted_option = 999;
 
@@ -642,6 +872,9 @@
 
 message ServiceOptions {
 
+  // Any features defined in the specific edition.
+  optional FeatureSet features = 34;
+
   // Note:  Field numbers 1 through 32 are reserved for Google's internal RPC
   //   framework.  We apologize for hoarding these numbers to ourselves, but
   //   we were already using them long before we decided to release Protocol
@@ -651,7 +884,7 @@
   // Depending on the target platform, this can emit Deprecated annotations
   // for the service, or it will be completely ignored; in the very least,
   // this is a formalization for deprecating services.
-  optional bool deprecated = 33 [default=false];
+  optional bool deprecated = 33 [default = false];
 
   // The parser stores options it doesn't recognize here. See above.
   repeated UninterpretedOption uninterpreted_option = 999;
@@ -671,19 +904,22 @@
   // Depending on the target platform, this can emit Deprecated annotations
   // for the method, or it will be completely ignored; in the very least,
   // this is a formalization for deprecating methods.
-  optional bool deprecated = 33 [default=false];
+  optional bool deprecated = 33 [default = false];
 
   // Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
   // or neither? HTTP based RPC implementation may choose GET verb for safe
   // methods, and PUT verb for idempotent methods instead of the default POST.
   enum IdempotencyLevel {
     IDEMPOTENCY_UNKNOWN = 0;
-    NO_SIDE_EFFECTS     = 1; // implies idempotent
-    IDEMPOTENT          = 2; // idempotent, but may have side effects
+    NO_SIDE_EFFECTS = 1;  // implies idempotent
+    IDEMPOTENT = 2;       // idempotent, but may have side effects
   }
-  optional IdempotencyLevel idempotency_level =
-      34 [default=IDEMPOTENCY_UNKNOWN];
+  optional IdempotencyLevel idempotency_level = 34
+      [default = IDEMPOTENCY_UNKNOWN];
 
+  // Any features defined in the specific edition.
+  optional FeatureSet features = 35;
+
   // The parser stores options it doesn't recognize here. See above.
   repeated UninterpretedOption uninterpreted_option = 999;
 
@@ -691,7 +927,6 @@
   extensions 1000 to max;
 }
 
-
 // A message representing a option the parser does not recognize. This only
 // appears in options protos created by the compiler::Parser class.
 // DescriptorPool resolves these when building Descriptor objects. Therefore,
@@ -702,8 +937,8 @@
   // The name of the uninterpreted option.  Each string represents a segment in
   // a dot-separated name.  is_extension is true iff a segment represents an
   // extension (denoted with parentheses in options specs in .proto files).
-  // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
-  // "foo.(bar.baz).qux".
+  // E.g.,{ ["foo", false], ["bar.baz", true], ["moo", false] } represents
+  // "foo.(bar.baz).moo".
   message NamePart {
     required string name_part = 1;
     required bool is_extension = 2;
@@ -721,6 +956,172 @@
 }
 
 // ===================================================================
+// Features
+
+// TODO Enums in C++ gencode (and potentially other languages) are
+// not well scoped.  This means that each of the feature enums below can clash
+// with each other.  The short names we've chosen maximize call-site
+// readability, but leave us very open to this scenario.  A future feature will
+// be designed and implemented to handle this, hopefully before we ever hit a
+// conflict here.
+message FeatureSet {
+  enum FieldPresence {
+    FIELD_PRESENCE_UNKNOWN = 0;
+    EXPLICIT = 1;
+    IMPLICIT = 2;
+    LEGACY_REQUIRED = 3;
+  }
+  optional FieldPresence field_presence = 1 [
+    retention = RETENTION_RUNTIME,
+    targets = TARGET_TYPE_FIELD,
+    targets = TARGET_TYPE_FILE,
+    feature_support = {
+      edition_introduced: EDITION_2023,
+    },
+    edition_defaults = { edition: EDITION_LEGACY, value: "EXPLICIT" },
+    edition_defaults = { edition: EDITION_PROTO3, value: "IMPLICIT" },
+    edition_defaults = { edition: EDITION_2023, value: "EXPLICIT" }
+  ];
+
+  enum EnumType {
+    ENUM_TYPE_UNKNOWN = 0;
+    OPEN = 1;
+    CLOSED = 2;
+  }
+  optional EnumType enum_type = 2 [
+    retention = RETENTION_RUNTIME,
+    targets = TARGET_TYPE_ENUM,
+    targets = TARGET_TYPE_FILE,
+    feature_support = {
+      edition_introduced: EDITION_2023,
+    },
+    edition_defaults = { edition: EDITION_LEGACY, value: "CLOSED" },
+    edition_defaults = { edition: EDITION_PROTO3, value: "OPEN" }
+  ];
+
+  enum RepeatedFieldEncoding {
+    REPEATED_FIELD_ENCODING_UNKNOWN = 0;
+    PACKED = 1;
+    EXPANDED = 2;
+  }
+  optional RepeatedFieldEncoding repeated_field_encoding = 3 [
+    retention = RETENTION_RUNTIME,
+    targets = TARGET_TYPE_FIELD,
+    targets = TARGET_TYPE_FILE,
+    feature_support = {
+      edition_introduced: EDITION_2023,
+    },
+    edition_defaults = { edition: EDITION_LEGACY, value: "EXPANDED" },
+    edition_defaults = { edition: EDITION_PROTO3, value: "PACKED" }
+  ];
+
+  enum Utf8Validation {
+    UTF8_VALIDATION_UNKNOWN = 0;
+    VERIFY = 2;
+    NONE = 3;
+    reserved 1;
+  }
+  optional Utf8Validation utf8_validation = 4 [
+    retention = RETENTION_RUNTIME,
+    targets = TARGET_TYPE_FIELD,
+    targets = TARGET_TYPE_FILE,
+    feature_support = {
+      edition_introduced: EDITION_2023,
+    },
+    edition_defaults = { edition: EDITION_LEGACY, value: "NONE" },
+    edition_defaults = { edition: EDITION_PROTO3, value: "VERIFY" }
+  ];
+
+  enum MessageEncoding {
+    MESSAGE_ENCODING_UNKNOWN = 0;
+    LENGTH_PREFIXED = 1;
+    DELIMITED = 2;
+  }
+  optional MessageEncoding message_encoding = 5 [
+    retention = RETENTION_RUNTIME,
+    targets = TARGET_TYPE_FIELD,
+    targets = TARGET_TYPE_FILE,
+    feature_support = {
+      edition_introduced: EDITION_2023,
+    },
+    edition_defaults = { edition: EDITION_LEGACY, value: "LENGTH_PREFIXED" }
+  ];
+
+  enum JsonFormat {
+    JSON_FORMAT_UNKNOWN = 0;
+    ALLOW = 1;
+    LEGACY_BEST_EFFORT = 2;
+  }
+  optional JsonFormat json_format = 6 [
+    retention = RETENTION_RUNTIME,
+    targets = TARGET_TYPE_MESSAGE,
+    targets = TARGET_TYPE_ENUM,
+    targets = TARGET_TYPE_FILE,
+    feature_support = {
+      edition_introduced: EDITION_2023,
+    },
+    edition_defaults = { edition: EDITION_LEGACY, value: "LEGACY_BEST_EFFORT" },
+    edition_defaults = { edition: EDITION_PROTO3, value: "ALLOW" }
+  ];
+
+  reserved 999;
+
+  extensions 1000 to 9994 [
+    declaration = {
+      number: 1000,
+      full_name: ".pb.cpp",
+      type: ".pb.CppFeatures"
+    },
+    declaration = {
+      number: 1001,
+      full_name: ".pb.java",
+      type: ".pb.JavaFeatures"
+    },
+    declaration = { number: 1002, full_name: ".pb.go", type: ".pb.GoFeatures" },
+    declaration = {
+      number: 9990,
+      full_name: ".pb.proto1",
+      type: ".pb.Proto1Features"
+    }
+  ];
+
+  extensions 9995 to 9999;  // For internal testing
+  extensions 10000;         // for https://github.com/bufbuild/protobuf-es
+}
+
+// A compiled specification for the defaults of a set of features.  These
+// messages are generated from FeatureSet extensions and can be used to seed
+// feature resolution. The resolution with this object becomes a simple search
+// for the closest matching edition, followed by proto merges.
+message FeatureSetDefaults {
+  // A map from every known edition with a unique set of defaults to its
+  // defaults. Not all editions may be contained here.  For a given edition,
+  // the defaults at the closest matching edition ordered at or before it should
+  // be used.  This field must be in strict ascending order by edition.
+  message FeatureSetEditionDefault {
+    optional Edition edition = 3;
+
+    // Defaults of features that can be overridden in this edition.
+    optional FeatureSet overridable_features = 4;
+
+    // Defaults of features that can't be overridden in this edition.
+    optional FeatureSet fixed_features = 5;
+
+    reserved 1, 2;
+    reserved "features";
+  }
+  repeated FeatureSetEditionDefault defaults = 1;
+
+  // The minimum supported edition (inclusive) when this was constructed.
+  // Editions before this will not have defaults.
+  optional Edition minimum_edition = 4;
+
+  // The maximum known edition (inclusive) when this was constructed. Editions
+  // after this will not have reliable defaults.
+  optional Edition maximum_edition = 5;
+}
+
+// ===================================================================
 // Optional source code info
 
 // Encapsulates information about the original source file from which a
@@ -763,7 +1164,7 @@
   //   beginning of the "extend" block and is shared by all extensions within
   //   the block.
   // - Just because a location's span is a subset of some other location's span
-  //   does not mean that it is a descendent.  For example, a "group" defines
+  //   does not mean that it is a descendant.  For example, a "group" defines
   //   both a type and a field in a single declaration.  Thus, the locations
   //   corresponding to the type and field and their components will overlap.
   // - Code which tries to interpret locations should probably be designed to
@@ -775,8 +1176,8 @@
     // location.
     //
     // Each element is a field number or an index.  They form a path from
-    // the root FileDescriptorProto to the place where the definition.  For
-    // example, this path:
+    // the root FileDescriptorProto to the place where the definition appears.
+    // For example, this path:
     //   [ 4, 3, 2, 7, 1 ]
     // refers to:
     //   file.message_type(3)  // 4, 3
@@ -794,14 +1195,14 @@
     //   [ 4, 3, 2, 7 ]
     // this path refers to the whole field declaration (from the beginning
     // of the label to the terminating semicolon).
-    repeated int32 path = 1 [packed=true];
+    repeated int32 path = 1 [packed = true];
 
     // Always has exactly three or four elements: start line, start column,
     // end line (optional, otherwise assumed same as start line), end column.
     // These are packed into a single field for efficiency.  Note that line
     // and column numbers are zero-based -- typically you will want to add
     // 1 to each before displaying to a user.
-    repeated int32 span = 2 [packed=true];
+    repeated int32 span = 2 [packed = true];
 
     // If this SourceCodeInfo represents a complete declaration, these are any
     // comments appearing before and after the declaration which appear to be
@@ -830,13 +1231,13 @@
     //   // Comment attached to baz.
     //   // Another line attached to baz.
     //
-    //   // Comment attached to qux.
+    //   // Comment attached to moo.
     //   //
-    //   // Another line attached to qux.
-    //   optional double qux = 4;
+    //   // Another line attached to moo.
+    //   optional double moo = 4;
     //
     //   // Detached comment for corge. This is not leading or trailing comments
-    //   // to qux or corge because there are blank lines separating it from
+    //   // to moo or corge because there are blank lines separating it from
     //   // both.
     //
     //   // Detached comment for corge paragraph 2.
@@ -866,7 +1267,7 @@
   message Annotation {
     // Identifies the element in the original source .proto file. This field
     // is formatted the same as SourceCodeInfo.Location.path.
-    repeated int32 path = 1 [packed=true];
+    repeated int32 path = 1 [packed = true];
 
     // Identifies the filesystem path to the original source .proto.
     optional string source_file = 2;
@@ -876,8 +1277,20 @@
     optional int32 begin = 3;
 
     // Identifies the ending offset in bytes in the generated code that
-    // relates to the identified offset. The end offset should be one past
+    // relates to the identified object. The end offset should be one past
     // the last relevant byte (so the length of the text = end - begin).
     optional int32 end = 4;
+
+    // Represents the identified object's effect on the element in the original
+    // .proto file.
+    enum Semantic {
+      // There is no effect or the effect is indescribable.
+      NONE = 0;
+      // The element is set or otherwise mutated.
+      SET = 1;
+      // An alias to the element is returned.
+      ALIAS = 2;
+    }
+    optional Semantic semantic = 5;
   }
 }
diff --git a/proto-lens.cabal b/proto-lens.cabal
--- a/proto-lens.cabal
+++ b/proto-lens.cabal
@@ -1,13 +1,11 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.31.2.
+-- This file has been generated from package.yaml by hpack version 0.39.1.
 --
 -- see: https://github.com/sol/hpack
---
--- hash: 5006405422ef969b41707de8219c7bbf95f72fb7bb41c12a2f22d30086de9113
 
 name:           proto-lens
-version:        0.6.0.0
+version:        0.7.1.7
 synopsis:       A lens-based implementation of protocol buffers in Haskell.
 description:    The proto-lens library provides an API for protocol buffers using modern Haskell language and library patterns.  Specifically, it provides:
                 .
@@ -60,20 +58,20 @@
   hs-source-dirs:
       src
   build-depends:
-      base >=4.10 && <4.14
-    , bytestring ==0.10.*
-    , containers >=0.5 && <0.7
-    , deepseq ==1.4.*
-    , ghc-prim >=0.4 && <0.6
-    , lens-family >=1.2 && <2.1
+      base >=4.10 && <4.23
+    , bytestring >=0.10 && <0.14
+    , containers >=0.5 && <0.9
+    , deepseq >=1.4 && <1.6
+    , ghc-prim >=0.4 && <0.14
+    , lens-family >=1.2 && <2.2
     , parsec ==3.1.*
     , pretty ==1.1.*
-    , primitive >=0.6 && <0.8
-    , profunctors >=5.2 && <5.6
+    , primitive >=0.6 && <0.10
+    , profunctors >=5.2 && <6.0
     , tagged ==0.8.*
-    , text ==1.2.*
-    , transformers >=0.4 && <0.6
-    , vector >=0.11 && <0.13
+    , text >=1.2 && <2.2
+    , transformers >=0.4 && <0.7
+    , vector >=0.11 && <0.14
   default-language: Haskell2010
 
 test-suite growing_test
diff --git a/src/Data/ProtoLens/Combinators.hs b/src/Data/ProtoLens/Combinators.hs
--- a/src/Data/ProtoLens/Combinators.hs
+++ b/src/Data/ProtoLens/Combinators.hs
@@ -29,7 +29,7 @@
 
 -- | Sets the provided lens in @a@ to @Nothing@.
 clear :: Setter a a' b (Maybe b') -> a -> a'
-clear = (.~ Nothing)
+clear setter = setter .~ Nothing
 
 -- | Creates a 'Default' and then applies the provided `State` to it.  This is
 -- convenient for creating complicated structures.
diff --git a/src/Data/ProtoLens/Encoding/Bytes.hs b/src/Data/ProtoLens/Encoding/Bytes.hs
--- a/src/Data/ProtoLens/Encoding/Bytes.hs
+++ b/src/Data/ProtoLens/Encoding/Bytes.hs
@@ -6,7 +6,6 @@
 
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE CPP #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE LambdaCase #-}
@@ -22,6 +21,8 @@
     -- * Bytestrings
     getBytes,
     putBytes,
+    -- * Text
+    getText,
     -- * Integral types
     getVarInt,
     getVarIntH,
@@ -51,7 +52,7 @@
 import Control.Monad.Trans.Except (throwE, ExceptT)
 import Data.Bits
 import Data.ByteString (ByteString)
-import Data.ByteString.Lazy.Builder as Builder
+import Data.ByteString.Builder as Builder
 import qualified Data.ByteString.Builder.Internal as Internal
 import qualified Data.ByteString.Lazy as L
 import Data.Int (Int32, Int64)
@@ -61,14 +62,14 @@
 import qualified Data.Vector.Generic as V
 import Data.Word (Word8, Word32, Word64)
 import Foreign.Marshal (malloc, free)
-import Foreign.Storable (peek)
 import System.IO (Handle, hGetBuf)
 #if MIN_VERSION_base(4,11,0)
 import qualified GHC.Float as Float
+import Foreign.Storable (peek)
 #else
 import Foreign.Ptr (castPtr)
 import Foreign.Marshal.Alloc (alloca)
-import Foreign.Storable (Storable, poke)
+import Foreign.Storable (Storable, peek, poke)
 import System.IO.Unsafe (unsafePerformIO)
 #endif
 
@@ -94,9 +95,9 @@
 getVarIntH h = do
     buf <- liftIO malloc
     let loopStart !n !s =
-          (liftIO $ hGetBuf h buf 1) >>=
+          liftIO (hGetBuf h buf 1) >>=
           \case
-            1 -> (liftIO $ peek buf) >>=
+            1 -> liftIO (peek buf) >>=
                  getVarIntLoopFinish loopStart n s
             _ -> throwE "Unexpected end of file"
     res <- loopStart 0 1
@@ -126,7 +127,7 @@
 putVarInt :: Word64 -> Builder
 putVarInt n
     | n < 128 = Builder.word8 (fromIntegral n)
-    | otherwise = Builder.word8 (fromIntegral $ n .&. 127 .|. 128)
+    | otherwise = Builder.word8 (fromIntegral $ n .|. 128)
                       <> putVarInt (n `shiftR` 7)
 
 getFixed32 :: Parser Word32
diff --git a/src/Data/ProtoLens/Encoding/Parser.hs b/src/Data/ProtoLens/Encoding/Parser.hs
--- a/src/Data/ProtoLens/Encoding/Parser.hs
+++ b/src/Data/ProtoLens/Encoding/Parser.hs
@@ -1,6 +1,4 @@
 -- | A custom parsing monad, optimized for speed.
-{-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 module Data.ProtoLens.Encoding.Parser
@@ -11,6 +9,7 @@
     , getWord8
     , getWord32le
     , getBytes
+    , getText
     , (<?>)
     ) where
 
@@ -18,6 +17,8 @@
 import Data.Word (Word8, Word32)
 import Data.ByteString (ByteString, packCStringLen)
 import qualified Data.ByteString.Unsafe as B
+import Data.Text (Text)
+import Data.Text.Encoding (decodeUtf8')
 import Foreign.Ptr
 import Foreign.Storable
 import System.IO.Unsafe (unsafePerformIO)
@@ -68,6 +69,12 @@
 getBytes n = withSized n "getBytes: Unexpected end of input"
                     $ \pos -> packCStringLen (castPtr pos, n)
 
+getText :: Int -> Parser Text
+getText n = do
+  r <- withSized n "getText: Unexpected end of input" $ \pos ->
+          decodeUtf8' <$> B.unsafePackCStringLen (castPtr pos, n)
+  either (fail . show) pure r
+
 -- | Helper function for reading bytes from the current position and
 -- advancing the pointer.
 --
@@ -86,7 +93,7 @@
     | otherwise = fail "withSized: negative length"
 {-# INLINE withSized #-}
 
--- | Run the given parsing action as if there are only 
+-- | Run the given parsing action as if there are only
 -- @len@ bytes remaining.  That is, once @len@ bytes have been
 -- consumed, 'atEnd' will return 'True' and other actions
 -- like 'getWord8' will act like there is no input remaining.
diff --git a/src/Data/ProtoLens/Encoding/Wire.hs b/src/Data/ProtoLens/Encoding/Wire.hs
--- a/src/Data/ProtoLens/Encoding/Wire.hs
+++ b/src/Data/ProtoLens/Encoding/Wire.hs
@@ -16,7 +16,9 @@
     , joinTypeAndTag
     , parseFieldSet
     , buildFieldSet
+    , buildMessageSet
     , parseTaggedValueFromWire
+    , parseMessageSetTaggedValueFromWire
     ) where
 
 import Control.DeepSeq (NFData(..))
@@ -63,6 +65,15 @@
     putVarInt (joinTypeAndTag tag (wireValueToInt wv))
     <> buildWireValue wv
 
+-- builds in legacy MessageSet format.
+-- See https://github.com/protocolbuffers/protobuf/blob/dec4939439d9ca2adf2bb14edccf876c2587faf2/src/google/protobuf/descriptor.proto#L444
+buildTaggedValueAsMessageSet :: TaggedValue -> Builder
+buildTaggedValueAsMessageSet (TaggedValue (Tag t) wv) =
+    buildTaggedValue ( TaggedValue 1 StartGroup)
+    <> buildTaggedValue (TaggedValue 2 (VarInt $ fromIntegral t))
+    <> buildTaggedValue (TaggedValue 3 wv)
+    <> buildTaggedValue (TaggedValue 1 EndGroup)
+
 buildWireValue :: WireValue -> Builder
 buildWireValue (VarInt w) = putVarInt w
 buildWireValue (Fixed64 w) = putFixed64 w
@@ -98,6 +109,18 @@
         5 -> Fixed32 <$> getFixed32
         _ -> fail $ "Unknown wire type " ++ show w
 
+parseMessageSetTaggedValueFromWire :: Word64 -> Parser TaggedValue
+parseMessageSetTaggedValueFromWire t =
+    parseTaggedValueFromWire t >>= \v -> case v of
+        TaggedValue 1 StartGroup -> parseTaggedValue >>= \ft -> case ft of
+            TaggedValue 2 (VarInt f) -> parseTaggedValue >>= \dt -> case dt of
+                TaggedValue 3 (Lengthy b) -> parseTaggedValue >>= \et -> case et of
+                    TaggedValue 1 EndGroup -> return $ TaggedValue (Tag $ fromIntegral f) (Lengthy b)
+                    _ -> fail "missing end_group"
+                _ -> fail "missing message"
+            _ -> fail "missing field tag"
+        _ -> return v
+
 splitTypeAndTag :: Word64 -> (Tag, Word8)
 splitTypeAndTag w = (fromIntegral $ w `shiftR` 3, fromIntegral (w .&. 7))
 
@@ -117,3 +140,6 @@
 
 buildFieldSet :: FieldSet -> Builder
 buildFieldSet = mconcat . map buildTaggedValue 
+
+buildMessageSet :: FieldSet -> Builder
+buildMessageSet = mconcat . map buildTaggedValueAsMessageSet
diff --git a/src/Data/ProtoLens/Field.hs b/src/Data/ProtoLens/Field.hs
--- a/src/Data/ProtoLens/Field.hs
+++ b/src/Data/ProtoLens/Field.hs
@@ -17,7 +17,6 @@
 {-# LANGUAGE FunctionalDependencies #-}
 {-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE MagicHash #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE UndecidableInstances #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 module Data.ProtoLens.Field
diff --git a/src/Data/ProtoLens/Labels.hs b/src/Data/ProtoLens/Labels.hs
--- a/src/Data/ProtoLens/Labels.hs
+++ b/src/Data/ProtoLens/Labels.hs
@@ -3,6 +3,7 @@
 {-# LANGUAGE MagicHash #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
 {-# LANGUAGE UndecidableInstances #-}
 #if __GLASGOW_HASKELL__ >= 802
 {-# LANGUAGE ScopedTypeVariables #-}
diff --git a/src/Data/ProtoLens/Message.hs b/src/Data/ProtoLens/Message.hs
--- a/src/Data/ProtoLens/Message.hs
+++ b/src/Data/ProtoLens/Message.hs
@@ -75,6 +75,20 @@
     -- @"packagename.messagename"@.
     messageName :: Proxy msg -> T.Text
 
+    -- | The serialized protobuffer message descriptor for this type.
+    --
+    -- For a friendlier version which returns the actual descriptor type,
+    -- use @Data.ProtoLens.Descriptor.messageDescriptor@
+    -- from the @proto-lens-protobuf-types@ package.
+    packedMessageDescriptor :: Proxy msg -> B.ByteString
+
+    -- | The serialized protobuffer file message descriptor containing this type.
+    --
+    -- For a friendlier version which returns the actual file descriptor type,
+    -- use @Data.ProtoLens.Descriptor.fileDescriptor@
+    -- from the @proto-lens-protobuf-types@ package.
+    packedFileDescriptor :: Proxy msg -> B.ByteString
+
     -- | A message with all fields set to their default values.
     --
     -- Satisfies @encodeMessage defMessage == ""@ and @decodeMessage "" == Right defMessage@.
diff --git a/src/Data/ProtoLens/Service/Types.hs b/src/Data/ProtoLens/Service/Types.hs
--- a/src/Data/ProtoLens/Service/Types.hs
+++ b/src/Data/ProtoLens/Service/Types.hs
@@ -3,7 +3,6 @@
 {-# LANGUAGE FlexibleContexts      #-}
 {-# LANGUAGE FlexibleInstances     #-}
 {-# LANGUAGE GADTs                 #-}
-{-# LANGUAGE KindSignatures        #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE PolyKinds             #-}
 {-# LANGUAGE RankNTypes            #-}
@@ -23,8 +22,10 @@
   , StreamingType (..)
   ) where
 
-import Data.Kind (Constraint)
+import qualified Data.ByteString as B
+import Data.Kind (Constraint, Type)
 import Data.ProtoLens.Message (Message)
+import Data.Proxy (Proxy(..))
 import GHC.TypeLits
 
 
@@ -53,6 +54,7 @@
   type ServicePackage s :: Symbol
   type ServiceMethods s :: [Symbol]
 
+  packedServiceDescriptor :: Proxy s -> B.ByteString
 
 ------------------------------------------------------------------------------
 -- | Data type to be used as a promoted type for 'MethodStreamingType'.
@@ -82,8 +84,8 @@
       , Message (MethodOutput s m)
       ) => HasMethodImpl s (m :: Symbol) where
   type MethodName          s m :: Symbol
-  type MethodInput         s m :: *
-  type MethodOutput        s m :: *
+  type MethodInput         s m :: Type
+  type MethodOutput        s m :: Type
   type MethodStreamingType s m :: StreamingType
 
 
diff --git a/src/Data/ProtoLens/TextFormat.hs b/src/Data/ProtoLens/TextFormat.hs
--- a/src/Data/ProtoLens/TextFormat.hs
+++ b/src/Data/ProtoLens/TextFormat.hs
@@ -28,7 +28,8 @@
 import Data.Bifunctor (first)
 import qualified Data.ByteString
 import Data.Char (isPrint, isAscii, chr)
-import Data.Foldable (foldlM, foldl')
+import Data.Foldable (foldlM)
+import qualified Data.Foldable as F
 import qualified Data.Map as Map
 import Data.Maybe (catMaybes)
 import Data.Proxy (Proxy(Proxy))
@@ -228,7 +229,7 @@
                       <$> buildMessageFromDescriptor reg defMessage fields
 
 missingFields :: forall msg . Message msg => Proxy msg -> Parser.Message -> [String]
-missingFields _ = Set.toList . foldl' deleteField requiredFieldNames
+missingFields _ = Set.toList . F.foldl' deleteField requiredFieldNames
   where
     requiredFieldNames :: Set.Set String
     requiredFieldNames = Set.fromList $ Map.keys
@@ -317,8 +318,8 @@
 makeScalarValue DoubleField (Parser.DoubleValue x) = Right x
 makeScalarValue FloatField (Parser.DoubleValue x) = Right (realToFrac x)
 makeScalarValue BoolField (Parser.EnumValue x)
-    | x == "true" = Right True
-    | x == "false" = Right False
+    | x `elem` ["true", "True", "t"] = Right True
+    | x `elem` ["false", "False", "f"] = Right False
     | otherwise = Left $ "Unrecognized bool value " ++ show x
 makeScalarValue StringField (Parser.ByteStringValue x) = Right (Text.decodeUtf8 x)
 makeScalarValue BytesField (Parser.ByteStringValue x) = Right x
diff --git a/src/Data/ProtoLens/TextFormat/Parser.hs b/src/Data/ProtoLens/TextFormat/Parser.hs
--- a/src/Data/ProtoLens/TextFormat/Parser.hs
+++ b/src/Data/ProtoLens/TextFormat/Parser.hs
@@ -16,23 +16,23 @@
     , parser
     ) where
 
-import Data.ByteString (ByteString, pack)
-import Data.Char (ord, isSpace)
+import Control.Applicative ((<|>), many)
+import Control.Monad (liftM, liftM2, mzero, unless)
+import Data.ByteString (ByteString)
+import Data.ByteString.Builder (Builder, char8, charUtf8, toLazyByteString, word8)
+import Data.ByteString.Lazy (toStrict)
+import Data.Char (digitToInt, isSpace)
+import Data.Functor (($>))
 import Data.Functor.Identity (Identity)
 import Data.List (intercalate)
-import Data.Maybe (catMaybes)
-import Data.Text.Lazy (Text)
 import qualified Data.Text as StrictText
-import Data.Word (Word8)
-import Numeric (readOct, readHex)
+import Data.Text.Lazy (Text)
 import Text.Parsec ((<?>))
 import Text.Parsec.Char
   (alphaNum, char, hexDigit, letter, octDigit, oneOf, satisfy)
 import Text.Parsec.Text.Lazy (Parser)
-import Text.Parsec.Combinator (choice, eof, many1, optionMaybe, sepBy1)
+import Text.Parsec.Combinator (choice, eof, many1, sepBy1)
 import Text.Parsec.Token hiding (octal)
-import Control.Applicative ((<|>), many)
-import Control.Monad (liftM, liftM2, mzero)
 
 -- | A 'TokenParser' for the protobuf text format.
 ptp :: GenTokenParser Text () Identity
@@ -55,7 +55,6 @@
   , caseSensitive = True
   }
 
-
 type Message = [Field]
 
 data Field = Field Key Value
@@ -103,7 +102,7 @@
         value <- naturalOrFloat ptp
         return $ makeNumberValue negative value
     parseString = liftM (ByteStringValue . mconcat)
-        $ many1 $ lexeme ptp $ protoStringLiteral
+        $ many1 $ lexeme ptp protoStringLiteral
     parseEnumValue = liftM EnumValue (identifier ptp)
     parseMessageValue =
         braces ptp (parseAny <|>
@@ -131,39 +130,43 @@
 protoStringLiteral :: Parser ByteString
 protoStringLiteral = do
     initialQuoteChar <- char '\'' <|> char '\"'
-    word8s <- many $ stringChar initialQuoteChar
+    let quoted = do
+          _ <- char '\\'
+          choice
+            [ char 'a'   $> char8 '\a'
+            , char 'b'   $> char8 '\b'
+            , char 'f'   $> char8 '\f'
+            , char 'n'   $> char8 '\n'
+            , char 'r'   $> char8 '\r'
+            , char 't'   $> char8 '\t'
+            , char 'v'   $> char8 '\v'
+            , char '\\'  $> char8 '\\'
+            , char '\''  $> char8 '\''
+            , char '\"'  $> char8 '\"'
+            , oneOf "xX" *> parse8BitToBuilder hexDigit 16 (1, 2)
+            , oneOf "uU" *> fail "Unicode in string literals not yet supported"
+            ,               parse8BitToBuilder octDigit 8 (1, 3)
+            ]
+        unquoted = charUtf8 <$> satisfy (/= initialQuoteChar)
+    builders <- many $ quoted <|> unquoted
     _ <- char initialQuoteChar
-    return $ pack word8s
+    return $ toStrict $ toLazyByteString $ mconcat builders
   where
-    stringChar :: Char -> Parser Word8
-    stringChar quote = (nonEscape quote) <|> stringEscape
-    nonEscape quote = fmap (fromIntegral . ord)
-        $ satisfy (\c -> c `notElem` "\\" ++ [quote] && ord c < 256)
-    stringEscape = char '\\' >> (octal <|> hex <|> unicode <|> simple)
-    octal = do d0 <- octDigit
-               d1 <- optionMaybe octDigit
-               d2 <- optionMaybe octDigit
-               readMaybeDigits readOct [Just d0, d1, d2]
-    readMaybeDigits :: ReadS Word8 -> [Maybe Char] -> Parser Word8
-    readMaybeDigits reader
-        = return . (\str -> let [(v, "")] = reader str in v) . catMaybes
-    hex = do _ <- oneOf "xX"
-             d0 <- hexDigit
-             d1 <- optionMaybe hexDigit
-             readMaybeDigits readHex [Just d0, d1]
-    unicode = oneOf "uU" >> fail "Unicode in string literals not yet supported"
-    simple = choice $ map charRet [ ('a', '\a')
-                                  , ('b', '\b')
-                                  , ('f', '\f')
-                                  , ('n', '\n')
-                                  , ('r', '\r')
-                                  , ('t', '\t')
-                                  , ('v', '\v')
-                                  , ('\\', '\\')
-                                  , ('\'', '\'')
-                                  , ('\"', '\"')
-                                  ]
-      where
-        charRet :: (Char, Char) -> Parser Word8
-        charRet (escapeCh, ch) = do _ <- char escapeCh
-                                    return $ fromIntegral $ ord ch
+    -- | Apply a parser between 'min' and 'max' times, failing otherwise.
+    manyN :: Parser a -> (Int, Int) -> Parser [a]
+    manyN _ (_, 0) = return []
+    manyN p (0, maX) = ((:) <$> p <*> manyN p (0, maX - 1)) <|> pure []
+    manyN p (miN, maX) = (:) <$> p <*> manyN p (miN - 1, maX - 1)
+
+    -- | Parse a number in 'base' with between 'min' and 'max' digits.
+    parseNum :: Parser Char -> Int -> (Int, Int) -> Parser Int
+    parseNum p base range = do
+      digits <- map digitToInt <$> manyN p range
+      return $ foldl (\a d -> a * base + d) 0 digits
+
+    -- | Parse a number and return a builder for the 8-bit char it represents.
+    parse8BitToBuilder :: Parser Char -> Int -> (Int, Int) -> Parser Builder
+    parse8BitToBuilder p base range = do
+      value <- parseNum p base range
+      unless (value < 256) $ fail "Escaped number is not 8-bit"
+      return $ word8 $ fromIntegral value
diff --git a/tests/parser_test.hs b/tests/parser_test.hs
--- a/tests/parser_test.hs
+++ b/tests/parser_test.hs
@@ -1,7 +1,6 @@
 -- | Unit and property tests for our custom parsing monad.
 module Main (main) where
 
-import Control.Applicative (liftA2)
 import qualified Data.ByteString as B
 import Data.Either (isLeft)
 
@@ -74,8 +73,8 @@
 testIsolate :: [TestTree]
 testIsolate =
     [ testProperty "many" $ \bs bs' ->
-        runParser (liftA2 (,) (isolate (length bs) $ manyTillEnd getWord8)
-                        (manyTillEnd getWord8))
+        runParser ((,) <$> isolate (length bs) (manyTillEnd getWord8) <*>
+                        manyTillEnd getWord8)
             (B.pack (bs ++ bs'))
             == Right (bs, bs')
     , testProperty "negative length" $ \n ws ->
