packages feed

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 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"