keiro-core 0.6.0.0 → 0.7.0.0
raw patch · 3 files changed
+138/−2 lines, 3 filesdep +mmzk-typeiddep ~keikiPVP ok
version bump matches the API change (PVP)
Dependencies added: mmzk-typeid
Dependency ranges changed: keiki
API changes (from Hackage documentation)
+ Keiro.Codec.IdDomain: CanonicalLowercase :: IdNormalization
+ Keiro.Codec.IdDomain: IdDomainContract :: !Text -> !Text -> !Char -> !Int -> !Int -> !IdNormalization -> !Text -> IdDomainContract
+ Keiro.Codec.IdDomain: IdDomainMalformed :: !Text -> IdDomainFailure
+ Keiro.Codec.IdDomain: IdDomainNonCanonical :: IdDomainFailure
+ Keiro.Codec.IdDomain: IdDomainNotUuidV7 :: !Text -> IdDomainFailure
+ Keiro.Codec.IdDomain: IdDomainWrongPrefix :: !Text -> !Text -> IdDomainFailure
+ Keiro.Codec.IdDomain: [idDomainJsonRepresentation] :: IdDomainContract -> !Text
+ Keiro.Codec.IdDomain: [idDomainMaxLength] :: IdDomainContract -> !Int
+ Keiro.Codec.IdDomain: [idDomainNormalization] :: IdDomainContract -> !IdNormalization
+ Keiro.Codec.IdDomain: [idDomainPrefix] :: IdDomainContract -> !Text
+ Keiro.Codec.IdDomain: [idDomainSeparator] :: IdDomainContract -> !Char
+ Keiro.Codec.IdDomain: [idDomainSuffixLength] :: IdDomainContract -> !Int
+ Keiro.Codec.IdDomain: [idDomainVersion] :: IdDomainContract -> !Text
+ Keiro.Codec.IdDomain: data IdDomainContract
+ Keiro.Codec.IdDomain: data IdDomainFailure
+ Keiro.Codec.IdDomain: data IdNormalization
+ Keiro.Codec.IdDomain: enforcedIdDomainVersion :: Text
+ Keiro.Codec.IdDomain: idDomainAcceptsText :: IdDomainContract -> Text -> Bool
+ Keiro.Codec.IdDomain: idDomainSampleText :: IdDomainContract -> Text
+ Keiro.Codec.IdDomain: idDomainTextPattern :: IdDomainContract -> Either DomainConstructionError TextPattern
+ Keiro.Codec.IdDomain: instance GHC.Classes.Eq Keiro.Codec.IdDomain.IdDomainContract
+ Keiro.Codec.IdDomain: instance GHC.Classes.Eq Keiro.Codec.IdDomain.IdDomainFailure
+ Keiro.Codec.IdDomain: instance GHC.Classes.Eq Keiro.Codec.IdDomain.IdNormalization
+ Keiro.Codec.IdDomain: instance GHC.Classes.Ord Keiro.Codec.IdDomain.IdDomainContract
+ Keiro.Codec.IdDomain: instance GHC.Classes.Ord Keiro.Codec.IdDomain.IdDomainFailure
+ Keiro.Codec.IdDomain: instance GHC.Classes.Ord Keiro.Codec.IdDomain.IdNormalization
+ Keiro.Codec.IdDomain: instance GHC.Internal.Show.Show Keiro.Codec.IdDomain.IdDomainContract
+ Keiro.Codec.IdDomain: instance GHC.Internal.Show.Show Keiro.Codec.IdDomain.IdDomainFailure
+ Keiro.Codec.IdDomain: instance GHC.Internal.Show.Show Keiro.Codec.IdDomain.IdNormalization
+ Keiro.Codec.IdDomain: typeIdV7Domain :: Text -> IdDomainContract
+ Keiro.Codec.IdDomain: validateIdDomainText :: IdDomainContract -> Text -> Either IdDomainFailure ()
Files
- CHANGELOG.md +22/−0
- keiro-core.cabal +4/−2
- src/Keiro/Codec/IdDomain.hs +112/−0
CHANGELOG.md view
@@ -6,6 +6,28 @@ ## [Unreleased] +## 0.7.0.0 — 2026-08-01++### Breaking Changes++- Requires the conservative-projection Keiki release (`keiki >=0.7 && <0.8`),+ replacing the previous `>=0.6 && <0.7` bound. Keiki 0.7 treats a predicate+ that crosses a one-way generated projection as opaque to symbolic proof, so+ verification may return `UnverifiedOpaque` where an earlier release reported a+ `Verified*` result. Runtime codec and event-stream behavior is unchanged.++### New Features++- Adds the public `Keiro.Codec.IdDomain` module: the frozen+ `keiro-dsl/id-domain/typeid-v7/1` runtime contract for prefix-bearing+ TypeID-v7 identifiers. Exposes `IdDomainContract`, `IdDomainFailure`,+ `IdNormalization`, `enforcedIdDomainVersion`, `typeIdV7Domain`,+ `idDomainAcceptsText`, `validateIdDomainText`, `idDomainTextPattern`, and+ `idDomainSampleText`. Admission requires canonical lowercase text, the+ declared prefix and one underscore, a 26-character Crockford suffix, and+ UUIDv7 version/variant bits; `idDomainTextPattern` yields the matching exact+ Keiki projection domain. Adds a `mmzk-typeid >=0.7 && <0.8` dependency.+ ## 0.6.0.0 — 2026-07-31 ### Breaking Changes
keiro-core.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: keiro-core-version: 0.6.0.0+version: 0.7.0.0 synopsis: Core contracts for Keiro packages description: Stable stream, codec, event-stream, and integration-event contracts@@ -40,6 +40,7 @@ import: warnings, shared exposed-modules: Keiro.Codec+ Keiro.Codec.IdDomain Keiro.Codec.Nominal Keiro.Codec.Structural Keiro.Codec.Structural.Generic@@ -59,9 +60,10 @@ , bytestring >=0.11 && <0.13 , deepseq >=1.5 && <1.6 , generic-lens >=2.2 && <2.4- , keiki >=0.6 && <0.7+ , keiki >=0.7 && <0.8 , kiroku-store >=0.3 && <0.4 , lens >=5.2 && <5.4+ , mmzk-typeid >=0.7 && <0.8 , scientific >=0.3 && <0.4 , text >=2.1 && <2.2 , time >=1.12 && <1.15
+ src/Keiro/Codec/IdDomain.hs view
@@ -0,0 +1,112 @@+-- | Published runtime contract for canonical prefix-bearing TypeID-v7 values.+module Keiro.Codec.IdDomain+ ( IdNormalization (..),+ IdDomainContract (..),+ IdDomainFailure (..),+ enforcedIdDomainVersion,+ typeIdV7Domain,+ idDomainAcceptsText,+ validateIdDomainText,+ idDomainTextPattern,+ idDomainSampleText,+ )+where++import Data.List.NonEmpty (NonEmpty (..))+import Data.Text (Text)+import Data.Text qualified as T+import Data.TypeID qualified as TypeID+import Keiki.ProjectionDomain+ ( DomainConstructionError,+ TextPattern,+ textCharSet,+ textConcat,+ textLiteral,+ textRepeatBetween,+ )++data IdNormalization = CanonicalLowercase+ deriving stock (Eq, Ord, Show)++data IdDomainContract = IdDomainContract+ { idDomainVersion :: !Text,+ idDomainPrefix :: !Text,+ idDomainSeparator :: !Char,+ idDomainSuffixLength :: !Int,+ idDomainMaxLength :: !Int,+ idDomainNormalization :: !IdNormalization,+ idDomainJsonRepresentation :: !Text+ }+ deriving stock (Eq, Ord, Show)++data IdDomainFailure+ = IdDomainNonCanonical+ | IdDomainWrongPrefix !Text !Text+ | IdDomainMalformed !Text+ | IdDomainNotUuidV7 !Text+ deriving stock (Eq, Ord, Show)++enforcedIdDomainVersion :: Text+enforcedIdDomainVersion = "keiro-dsl/id-domain/typeid-v7/1"++typeIdV7Domain :: Text -> IdDomainContract+typeIdV7Domain prefix =+ IdDomainContract+ { idDomainVersion = enforcedIdDomainVersion,+ idDomainPrefix = prefix,+ idDomainSeparator = '_',+ idDomainSuffixLength = 26,+ idDomainMaxLength = if T.null prefix then 26 else T.length prefix + 27,+ idDomainNormalization = CanonicalLowercase,+ idDomainJsonRepresentation = "canonical-json-text"+ }++idDomainAcceptsText :: IdDomainContract -> Text -> Bool+idDomainAcceptsText contract = either (const False) (const True) . validateIdDomainText contract++-- | @mmzk-typeid@ intentionally separates canonical parsing from the UUID+-- version check, so both operations are part of this frozen contract.+validateIdDomainText :: IdDomainContract -> Text -> Either IdDomainFailure ()+validateIdDomainText contract input = do+ parsed <- either (Left . IdDomainMalformed . T.pack . show) Right (TypeID.parseText input)+ let actualPrefix = TypeID.getPrefix parsed+ if actualPrefix == idDomainPrefix contract+ then pure ()+ else Left (IdDomainWrongPrefix (idDomainPrefix contract) actualPrefix)+ if TypeID.toText parsed == input+ then pure ()+ else Left IdDomainNonCanonical+ maybe (Right ()) (Left . IdDomainNotUuidV7 . T.pack . show) (TypeID.checkTypeID parsed)++idDomainTextPattern :: IdDomainContract -> Either DomainConstructionError TextPattern+idDomainTextPattern contract = do+ prefix <-+ textLiteral+ ( if T.null (idDomainPrefix contract)+ then ""+ else idDomainPrefix contract <> T.singleton (idDomainSeparator contract)+ )+ leading <- textCharSet ('0' :| "1234567")+ crockford <- textCharSet ('0' :| "123456789abcdefghjkmnpqrstvwxyz")+ version <- textCharSet ('e' :| "f")+ variant <- textCharSet ('8' :| "9abrstv")+ beforeVersion <- textRepeatBetween 9 9 crockford+ beforeVariant <- textRepeatBetween 2 2 crockford+ afterVariant <- textRepeatBetween 12 12 crockford+ pure+ ( textConcat+ ( prefix+ :| [ leading,+ beforeVersion,+ version,+ beforeVariant,+ variant,+ afterVariant+ ]+ )+ )++idDomainSampleText :: IdDomainContract -> Text+idDomainSampleText contract =+ (if T.null (idDomainPrefix contract) then "" else idDomainPrefix contract <> "_")+ <> "01h455vb4pex5vsknk084sn02q"