refactor(core): restructure module organization and enhance documentation

Remove standalone DID, handle, NSID, rKey and TID modules and reorganize as submodules. Update URI module with comprehensive documentation explaining AT Protocol URI concepts. Improve import statements to use relative imports for better module encapsulation.

BREAKING CHANGE: Removed standalone modules for DID, handle, NSID, rKey and TID. These components are now accessible as submodules. Users must update their import statements to reflect the new module structure.

+127 -108
poetry.lock
···
[[package]]
name = "cffi"
-
version = "1.17.1"
+
version = "2.0.0"
description = "Foreign Function Interface for Python calling C code."
optional = false
-
python-versions = ">=3.8"
+
python-versions = ">=3.9"
groups = ["main"]
markers = "platform_python_implementation != \"PyPy\""
files = [
-
{file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
-
{file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
-
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"},
-
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"},
-
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"},
-
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"},
-
{file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"},
-
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"},
-
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"},
-
{file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"},
-
{file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"},
-
{file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"},
-
{file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"},
-
{file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"},
-
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"},
-
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"},
-
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"},
-
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"},
-
{file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"},
-
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"},
-
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"},
-
{file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"},
-
{file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"},
-
{file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"},
-
{file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"},
-
{file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"},
-
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"},
-
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"},
-
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"},
-
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"},
-
{file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"},
-
{file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"},
-
{file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"},
-
{file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"},
-
{file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"},
-
{file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"},
-
{file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"},
-
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"},
-
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"},
-
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"},
-
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"},
-
{file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"},
-
{file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"},
-
{file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"},
-
{file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"},
-
{file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"},
-
{file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"},
-
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"},
-
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"},
-
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"},
-
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"},
-
{file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"},
-
{file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"},
-
{file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"},
-
{file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"},
-
{file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"},
-
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"},
-
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"},
-
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"},
-
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"},
-
{file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"},
-
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"},
-
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"},
-
{file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"},
-
{file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"},
-
{file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
-
{file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
+
{file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"},
+
{file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"},
+
{file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"},
+
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"},
+
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"},
+
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"},
+
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"},
+
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"},
+
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"},
+
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"},
+
{file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"},
+
{file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"},
+
{file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"},
+
{file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"},
+
{file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"},
+
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"},
+
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"},
+
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"},
+
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"},
+
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"},
+
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"},
+
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"},
+
{file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"},
+
{file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"},
+
{file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"},
+
{file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"},
+
{file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"},
+
{file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"},
+
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"},
+
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"},
+
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"},
+
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"},
+
{file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"},
+
{file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"},
+
{file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"},
+
{file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"},
+
{file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"},
+
{file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"},
+
{file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"},
+
{file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"},
+
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"},
+
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"},
+
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"},
+
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"},
+
{file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"},
+
{file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"},
+
{file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"},
+
{file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"},
+
{file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"},
+
{file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"},
+
{file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"},
+
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"},
+
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"},
+
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"},
+
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"},
+
{file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"},
+
{file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"},
+
{file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"},
+
{file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"},
+
{file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"},
+
{file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"},
+
{file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"},
+
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"},
+
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"},
+
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"},
+
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"},
+
{file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"},
+
{file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"},
+
{file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"},
+
{file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"},
+
{file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"},
+
{file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"},
+
{file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"},
+
{file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"},
+
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"},
+
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"},
+
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"},
+
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"},
+
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"},
+
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"},
+
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"},
+
{file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"},
+
{file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"},
+
{file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"},
]
[package.dependencies]
-
pycparser = "*"
+
pycparser = {version = "*", markers = "implementation_name != \"PyPy\""}
[[package]]
name = "charset-normalizer"
···
[[package]]
name = "dnspython"
-
version = "2.7.0"
+
version = "2.8.0"
description = "DNS toolkit"
optional = false
-
python-versions = ">=3.9"
+
python-versions = ">=3.10"
groups = ["main"]
files = [
-
{file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"},
-
{file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"},
+
{file = "dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af"},
+
{file = "dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f"},
]
[package.extras]
-
dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.16.0)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "quart-trio (>=0.11.0)", "sphinx (>=7.2.0)", "sphinx-rtd-theme (>=2.0.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"]
-
dnssec = ["cryptography (>=43)"]
-
doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"]
-
doq = ["aioquic (>=1.0.0)"]
-
idna = ["idna (>=3.7)"]
-
trio = ["trio (>=0.23)"]
-
wmi = ["wmi (>=1.5.1)"]
+
dev = ["black (>=25.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.17.0)", "mypy (>=1.17)", "pylint (>=3)", "pytest (>=8.4)", "pytest-cov (>=6.2.0)", "quart-trio (>=0.12.0)", "sphinx (>=8.2.0)", "sphinx-rtd-theme (>=3.0.0)", "twine (>=6.1.0)", "wheel (>=0.45.0)"]
+
dnssec = ["cryptography (>=45)"]
+
doh = ["h2 (>=4.2.0)", "httpcore (>=1.0.0)", "httpx (>=0.28.0)"]
+
doq = ["aioquic (>=1.2.0)"]
+
idna = ["idna (>=3.10)"]
+
trio = ["trio (>=0.30)"]
+
wmi = ["wmi (>=1.5.1) ; platform_system == \"Windows\""]
[[package]]
name = "frozendict"
···
[[package]]
name = "py-cid"
-
version = "0.3.0"
+
version = "0.3.1"
description = "Self-describing content-addressed identifiers for distributed systems"
optional = false
-
python-versions = "*"
+
python-versions = ">=3.10"
groups = ["main"]
files = [
-
{file = "py-cid-0.3.0.tar.gz", hash = "sha256:22f432cc6fb68d12a9c35dbdc92c95484fc49e31dfcb9e0efb0082233c5394e3"},
-
{file = "py_cid-0.3.0-py2.py3-none-any.whl", hash = "sha256:7c48a6ee0bc50fd114d4b24849cd689a31d3ad5bdf8fa073bf68f846fd58c5da"},
+
{file = "py_cid-0.3.1-py3-none-any.whl", hash = "sha256:bd60f54372704b7f2c0565432bf052e9ba7d42f260d39e9f7dc9566802a445a6"},
+
{file = "py_cid-0.3.1.tar.gz", hash = "sha256:eb0d14f35430787b978ffb66067d70ffaa9d85ecf62c950e792293193e705a75"},
]
[package.dependencies]
···
morphys = ">=1.0,<2.0"
py-multibase = ">=1.0.0,<2.0.0"
py-multicodec = "<0.3.0"
-
py-multihash = ">=0.2.0,<1.0.0"
+
pymultihash = ">=0.8.0,<1.0.0"
+
+
[package.extras]
+
dev = ["build", "bump_my_version (>=0.19.0)", "codecov (>=2.0.0)", "coverage (>=7.0.0)", "cryptography (>=44.0.1)", "hypothesis (>=6.0.0)", "mypy (>=1.5.0)", "pre-commit (>=3.0.0)", "pyrefly (>=0.17.1,<0.18.0)", "pytest (>=7.0.0)", "pytest-cov (>=4.0.0)", "pytest-runner (>=6.0.0)", "ruff (>=0.1.0)", "sphinx (>=7.0.0)", "towncrier (>=24,<25)", "tox (>=4.0.0)", "twine (>=4.0.0)", "watchdog[watchmedo] (>=3.0.0)", "wheel"]
+
docs = ["sphinx (>=7.0.0)"]
[[package]]
name = "py-multibase"
···
varint = ">=1.0.2,<2.0.0"
[[package]]
-
name = "py-multihash"
-
version = "0.2.3"
-
description = "Multihash implementation in Python"
-
optional = false
-
python-versions = "*"
-
groups = ["main"]
-
files = [
-
{file = "py-multihash-0.2.3.tar.gz", hash = "sha256:f0ade4de820afdc4b4aaa40464ec86c9da5cae3a4578cda2daab4b0eb7e5b18d"},
-
{file = "py_multihash-0.2.3-py2.py3-none-any.whl", hash = "sha256:a0602c99093587dfbf1634e2e8c7726de39374b0d68587a36093b4c237af6969"},
-
]
-
-
[package.dependencies]
-
base58 = ">=1.0.2,<2.0"
-
morphys = ">=1.0,<2.0"
-
six = ">=1.10.0,<2.0"
-
varint = ">=1.0.2,<2.0"
-
-
[[package]]
name = "pycparser"
-
version = "2.22"
+
version = "2.23"
description = "C parser in Python"
optional = false
python-versions = ">=3.8"
groups = ["main"]
-
markers = "platform_python_implementation != \"PyPy\""
+
markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\""
files = [
-
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
-
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
+
{file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"},
+
{file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"},
]
[[package]]
···
cachetools = ["cachetools"]
frozendict = ["frozendict"]
requests = ["requests"]
+
+
[[package]]
+
name = "pymultihash"
+
version = "0.8.2"
+
description = "Python implementation of the multihash specification"
+
optional = false
+
python-versions = "*"
+
groups = ["main"]
+
files = [
+
{file = "pymultihash-0.8.2-py3-none-any.whl", hash = "sha256:f7fa840b24bd6acbd6b073fcd330f10e15619387297babf1dd13ca4dae6e8209"},
+
{file = "pymultihash-0.8.2.tar.gz", hash = "sha256:49c75a1ae9ecc6d22d259064d4597b3685da3f0258f4ded632e03a3a79af215b"},
+
]
+
+
[package.extras]
+
blake2 = ["pyblake2"]
+
sha3 = ["pysha3"]
[[package]]
name = "python-baseconv"
-54
src/atpasser/did/__init__.py
···
-
import re
-
from pyld import jsonld
-
-
-
class DID:
-
"""
-
A class representing a DID.
-
-
Attributes:
-
uri (str): The DID URI.
-
"""
-
-
def __init__(self, uri: str) -> None:
-
"""
-
Initalizes an DID object.
-
-
Parameters:
-
uri (str): The DID URI.
-
"""
-
pattern = re.compile("^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$")
-
patternMatch = pattern.match(uri)
-
if patternMatch and len(uri) <= 2048:
-
self.uri = patternMatch[0]
-
else:
-
raise ValueError
-
-
def __str__(self) -> str:
-
"""
-
Convert the TID to a string by given the URI.
-
"""
-
return self.uri
-
-
def __eq__(self, value: object, /) -> bool:
-
"""
-
Check if the 2 values are exactly the same.
-
"""
-
if isinstance(value, DID):
-
return str(self) == str(value)
-
else:
-
return False
-
-
def fetch(self):
-
"""
-
Fetch the metadata of the DID.
-
-
Returns:
-
list: the pseudo-list which can be read by `PyLD` library
-
"""
-
if self.uri.startswith("did:plc:"):
-
return jsonld.expand(f"https://plc.directory/{self.uri}")
-
elif self.uri.startswith("did:web"):
-
return jsonld.expand(
-
f"https://{self.uri.replace("did:web:","")}/.well-known/did.json"
-
)
-96
src/atpasser/handle/__init__.py
···
-
import dns.resolver, requests
-
-
from atpasser import did
-
-
-
class Handle:
-
"""
-
A class representing a Handle.
-
-
-
Attributes:
-
handle (str): The Handle.
-
"""
-
-
def __init__(self, handle: str) -> None:
-
"""
-
Initalizes an Handle object.
-
-
Parameters:
-
handle (str): The Handle.
-
"""
-
-
if len(handle) > 253:
-
raise ValueError("handle is more than 253 chars")
-
-
labels = handle.lower().split(".")
-
-
if len(labels) < 2:
-
raise ValueError("are you tld?")
-
-
if labels[0] == "" or labels[-1] == "":
-
raise ValueError("proceeding or tariling ascii periods")
-
-
for label in labels:
-
if len(label) not in range(1, 64):
-
raise ValueError("two periods or segment longer than 63 char")
-
charset = set(label)
-
validset = set("abcdefghijklmnopqrstuvwxyz0123456789-")
-
if not charset.issubset(validset):
-
raise ValueError("invalid char used in segment")
-
if label.startswith("-") or label.endswith("-"):
-
raise ValueError("segments starts or ends with hyphen")
-
-
tld = labels[-1]
-
if tld[0] in "0123456789":
-
raise ValueError("tld starts with digit")
-
-
self.handle = handle
-
-
def __str__(self) -> str:
-
"""
-
-
Convert the TID to a string by given the URI.
-
"""
-
return self.handle
-
-
def __eq__(self, value: object, /) -> bool:
-
"""
-
-
Check if the 2 values are exactly the same.
-
"""
-
-
if isinstance(value, Handle):
-
-
return str(self) == str(value)
-
else:
-
-
return False
-
-
def toTID(self):
-
"""
-
Convert the handle to TID.
-
-
Returns:
-
An DID object, or `None` if the handle is invalid.
-
"""
-
try:
-
answers = dns.resolver.resolve("_atproto." + self.handle, "TXT")
-
except:
-
answers = []
-
for answer in answers:
-
if str(answer).startswith('"did='):
-
try:
-
uri = str(answer)[5:-1]
-
return did.DID(uri)
-
except:
-
pass # cannot resolve via dns
-
response = requests.get(f"https://{self.handle}/.well-known/atproto-did")
-
if response.status_code // 100 != 2:
-
return None
-
if response.headers.get("Content-Type") != "text/plain":
-
pass # Pass for now, because some sites like neocities, breaks this rule
-
try:
-
return did.DID(response.text)
-
except:
-
return None
+30 -15
src/atpasser/nsid/__init__.py src/atpasser/uri/nsid.py
···
class NSID:
-
"""
-
A class representing a NSID.
+
"""A class representing a NSID (Namespace Identifier) in the AT Protocol.
+
NSIDs are used to identify record types and methods in the AT Protocol's Lexicon
+
schema system. They follow a reverse domain name notation format like
+
'com.example.recordName' which provides a hierarchical namespace for organizing
+
schema definitions across different domains and applications.
Attributes:
-
nsid (str): The NSID URI.
+
nsid (str): The complete NSID string including optional fragment.
+
domainAuthority (list): Domain authority segments as list (e.g., ['com', 'example']).
+
domainAuthorityAsText (str): Domain authority as dot-separated text.
+
name (str): The record name or method name segment.
+
fragment (str or None): Optional fragment for variant specifications.
"""
def __init__(self, nsid: str) -> None:
-
"""
-
Initalizes an NSID object.
+
"""Initializes an NSID object with validation.
-
Parameters:
-
domainAuthority (list): Domain Authority as list.
-
domainAuthorityAsText (str): Domain Authority as text.
-
name (str): The name.
-
fragment (str | none): The fragment.
-
nsid (str): The NSID.
+
Parses and validates an NSID string according to AT Protocol Lexicon specifications.
+
NSIDs must follow reverse domain notation with proper segment structure,
+
character restrictions, and length limits.
+
+
Args:
+
nsid (str): The NSID string to parse and validate.
+
+
Raises:
+
ValueError: If the NSID contains invalid characters, exceeds maximum length,
+
has improper segment structure, or violates other naming rules.
"""
if "#" in nsid:
···
self.nsid = ".".join(domainAuthority) + f".{name}" + f"#{fragment}"
def __str__(self) -> str:
-
"""
+
"""Convert the NSID object to its string representation.
-
Convert the NSID to a string by given the URI.
+
Returns:
+
str: The canonical NSID string including optional fragment.
"""
return self.nsid
def __eq__(self, value: object, /) -> bool:
-
"""
+
"""Check if two NSID objects represent the same namespace identifier.
+
+
Args:
+
value (object): The object to compare with.
-
Check if the 2 values are exactly the same.
+
Returns:
+
bool: True if the objects represent the same NSID, False otherwise.
"""
if isinstance(value, NSID):
-72
src/atpasser/rKey/__init__.py
···
-
class RKey:
-
"""
-
-
A class representing a RecordKey.
-
-
-
-
Attributes:
-
-
recordKey (str): The RecordKey URI.
-
"""
-
-
-
def __init__(self, recordKey: str) -> None:
-
"""
-
-
Initalizes an RecordKey object.
-
-
-
Parameters:
-
-
recordKey (str): The RecordKey.
-
"""
-
-
-
if recordKey == "" or len(recordKey) > 512:
-
-
raise ValueError("null record key or record key longer than 512 chars")
-
-
-
if recordKey == ".." or recordKey == ".":
-
-
raise ValueError("reserved value . and ..")
-
-
-
if not set(recordKey).issubset(
-
-
set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_:~")
-
):
-
-
raise ValueError("invalid char")
-
-
-
self.recordKey = recordKey
-
-
-
def __str__(self) -> str:
-
"""
-
-
-
Convert the RecordKey to a string by given the URI.
-
"""
-
return self.recordKey
-
-
-
def __eq__(self, value: object, /) -> bool:
-
"""
-
-
-
Check if the 2 values are exactly the same.
-
"""
-
-
-
if isinstance(value, RKey):
-
-
-
return str(self) == str(value)
-
else:
-
-
-
return False
-
-99
src/atpasser/tid/__init__.py
···
-
import datetime, random
-
-
-
class TID:
-
"""
-
A class representing a TID.
-
-
Attributes:
-
timestamp (datetime.datetime): The time of a TID.
-
clockIdentifier (int): what it said.
-
"""
-
-
def __init__(
-
self, time: datetime.datetime | None = None, clockIdentifier: int | None = None
-
) -> None:
-
"""
-
Initalizes an TID object.
-
-
Parameters:
-
time (datetime.datetime): The time of a TID.
-
clockIdentifier (int): what it said.
-
"""
-
if time == None:
-
self.timestamp = datetime.datetime.now()
-
else:
-
self.timestamp = time
-
if clockIdentifier == None:
-
self.clockIdentifier = random.randrange(0, 1024)
-
else:
-
self.clockIdentifier = clockIdentifier
-
-
def __int__(self):
-
"""
-
Convert the TID to integer.
-
"""
-
timestamp = int(self.timestamp.timestamp() * 1000000)
-
return timestamp * 1024 + self.clockIdentifier
-
-
def __str__(self):
-
"""
-
Convert the TID to a base32-sortable string.
-
"""
-
integer = int(self)
-
binary = f"{integer:065b}"
-
return "".join(
-
[
-
"234567abcdefghijklmnopqrstuvwxyz"[int(binary[i : i + 5], base=2)]
-
for i in range(0, len(binary), 5)
-
]
-
)
-
-
def __eq__(self, value: object, /) -> bool:
-
"""
-
Check if the 2 values are exactly the same.
-
"""
-
if isinstance(value, TID):
-
return int(self) == int(value)
-
else:
-
return False
-
-
-
def importTIDfromInteger(value: int | None = None):
-
"""
-
This function imports from Integer to TID.
-
-
Parameters:
-
value (int): The integer.
-
-
Returns:
-
TID: The TID of given integer.
-
"""
-
if value == None:
-
value = int(TID())
-
clockIdentifier = value % 1024
-
timestamp = (value >> 10) / 1000000
-
return TID(datetime.datetime.fromtimestamp(timestamp), clockIdentifier)
-
-
-
def importTIDfromBase32(value: str | None = None):
-
"""
-
This function imports from Integer to TID.
-
-
Parameters:
-
value (int): The integer.
-
-
Returns:
-
TID: The TID of given integer.
-
"""
-
if value == None:
-
value = str(TID())
-
b32s = "234567abcdefghijklmnopqrstuvwxyz"
-
return importTIDfromInteger(
-
sum(
-
[
-
b32s.find(value[i]) * (32 ** (len(value) - i - 1))
-
for i in range(len(value))
-
]
-
)
-
)
+35 -24
src/atpasser/uri/__init__.py
···
import urllib.parse as up
-
from atpasser import handle, did
+
from . import handle, did
import jsonpath_ng
class URI:
-
"""
-
A class representing an AT URI.
+
"""A class representing an AT Protocol URI.
-
Attributes:
-
uri (str): The AT URI.
+
The AT Protocol URI scheme provides a standardized way to address content within the
+
decentralized AT Protocol network. URIs follow the format 'at://authority/path'
+
where authority can be either a DID (Decentralized Identifier) or a handle, and
+
path specifies the location of content within a repository.
-
fragment: Parsed fragment
-
fragmentAsText (str): fragment as text
-
-
query (dict): Parsed query as dict
-
queryAsText (str): Query as text
-
-
path (list): Parsed path as list
-
pathAsText (str): Path as text
-
-
authority (atpasser.handle.Handle | atpasser.did.DID | None): Domain authority as handle or DID
-
authorityAsText (str): Domain authority as text
+
Attributes:
+
uri (str): The complete AT Protocol URI string.
+
fragment: Parsed JSONPath fragment for querying specific data within records.
+
fragmentAsText (str): URL-encoded fragment as text.
+
query (dict): Parsed query parameters as dictionary.
+
queryAsText (str): URL-encoded query string.
+
path (list): Parsed path segments as list.
+
pathAsText (str): URL-encoded path string.
+
authority (atpasser.handle.Handle | atpasser.did.DID | None): Repository authority as handle or DID.
+
authorityAsText (str): Authority as text string.
"""
def __init__(self, uri: str) -> None:
-
"""
-
Initalizes an AT URI.
+
"""Initializes an AT Protocol URI object.
+
+
Parses and validates an AT Protocol URI string according to the AT Protocol specification.
+
The URI must start with 'at://' and contain valid ASCII characters only.
+
+
Args:
+
uri (str): The AT Protocol URI string to parse.
-
Parameters:
-
uri (str): The AT URI.
+
Raises:
+
ValueError: If the URI contains invalid characters, exceeds maximum length,
+
doesn't start with 'at://', or has other formatting issues.
"""
if not set(uri).issubset(set([chr(i) for i in range(0x80)])):
···
)
def __str__(self) -> str:
-
"""
+
"""Convert the URI object to its string representation.
-
Convert the RecordKey to a string by given the URI.
+
Returns:
+
str: The canonical AT Protocol URI string.
"""
return self.uri
def __eq__(self, value: object, /) -> bool:
-
"""
+
"""Check if two URI objects represent the same AT Protocol URI.
+
+
Args:
+
value (object): The object to compare with.
-
Check if the 2 values are exactly the same.
+
Returns:
+
bool: True if the objects represent the same URI, False otherwise.
"""
if isinstance(value, URI):
+79
src/atpasser/uri/did.py
···
+
import re
+
from pyld import jsonld
+
+
+
class DID:
+
"""A class representing a DID (Decentralized Identifier) in the AT Protocol.
+
+
Decentralized Identifiers (DIDs) are a key component of the AT Protocol's identity system.
+
They provide cryptographically verifiable, persistent identifiers for users and services
+
in the decentralized network. DIDs follow the format 'did:method:specific-identifier'
+
where method defines the resolution mechanism (e.g., 'plc' for PLC directory, 'web' for web-based).
+
+
Attributes:
+
uri (str): The complete DID URI string.
+
"""
+
+
def __init__(self, uri: str) -> None:
+
"""Initializes a DID object with validation.
+
+
Parses and validates a DID URI string according to the W3C DID specification
+
and AT Protocol requirements. The DID must follow the format 'did:method:identifier'
+
and contain only valid characters.
+
+
Args:
+
uri (str): The DID URI string to validate.
+
+
Raises:
+
ValueError: If the URI doesn't match the DID format or exceeds maximum length.
+
"""
+
pattern = re.compile("^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$")
+
patternMatch = pattern.match(uri)
+
if patternMatch and len(uri) <= 2048:
+
self.uri = patternMatch[0]
+
else:
+
raise ValueError
+
+
def __str__(self) -> str:
+
"""Convert the DID object to its string representation.
+
+
Returns:
+
str: The canonical DID URI string.
+
"""
+
return self.uri
+
+
def __eq__(self, value: object, /) -> bool:
+
"""Check if two DID objects represent the same identifier.
+
+
Args:
+
value (object): The object to compare with.
+
+
Returns:
+
bool: True if the objects represent the same DID, False otherwise.
+
"""
+
if isinstance(value, DID):
+
return str(self) == str(value)
+
else:
+
return False
+
+
def fetch(self):
+
"""Fetch the DID document metadata from the appropriate resolver.
+
+
Resolves the DID to its associated DID document by querying the appropriate
+
resolver based on the DID method. Currently supports 'did:plc:' (resolved via
+
PLC directory) and 'did:web:' (resolved via well-known HTTP endpoint).
+
+
Returns:
+
list: The expanded JSON-LD document representing the DID document,
+
compatible with the PyLD library for further processing.
+
+
Note:
+
- For 'did:plc:' DIDs, queries https://plc.directory/{did}
+
- For 'did:web:' DIDs, queries https://{domain}/.well-known/did.json
+
"""
+
if self.uri.startswith("did:plc:"):
+
return jsonld.expand(f"https://plc.directory/{self.uri}")
+
elif self.uri.startswith("did:web"):
+
return jsonld.expand(
+
f"https://{self.uri.replace("did:web:","")}/.well-known/did.json"
+
)
+122
src/atpasser/uri/handle.py
···
+
import dns.resolver, requests
+
+
from .did import DID
+
+
+
class Handle:
+
"""A class representing a Handle in the AT Protocol.
+
+
Handles are human-readable identifiers that map to DIDs in the AT Protocol.
+
They provide a user-friendly way to reference accounts and repositories in the
+
decentralized network. Handles follow DNS naming conventions and can be resolved
+
to their corresponding DIDs through DNS TXT records or HTTP well-known endpoints.
+
+
Attributes:
+
handle (str): The handle string following DNS naming conventions.
+
"""
+
+
def __init__(self, handle: str) -> None:
+
"""Initializes a Handle object with validation.
+
+
Parses and validates a handle string according to AT Protocol specifications.
+
Handles must follow DNS naming conventions with proper domain structure,
+
character restrictions, and length limits.
+
+
Args:
+
handle (str): The handle string to validate.
+
+
Raises:
+
ValueError: If the handle exceeds maximum length, has invalid domain structure,
+
contains invalid characters, or violates other naming rules.
+
"""
+
+
if len(handle) > 253:
+
raise ValueError("handle is more than 253 chars")
+
+
labels = handle.lower().split(".")
+
+
if len(labels) < 2:
+
raise ValueError("are you tld?")
+
+
if labels[0] == "" or labels[-1] == "":
+
raise ValueError("proceeding or tariling ascii periods")
+
+
for label in labels:
+
if len(label) not in range(1, 64):
+
raise ValueError("two periods or segment longer than 63 char")
+
charset = set(label)
+
validset = set("abcdefghijklmnopqrstuvwxyz0123456789-")
+
if not charset.issubset(validset):
+
raise ValueError("invalid char used in segment")
+
if label.startswith("-") or label.endswith("-"):
+
raise ValueError("segments starts or ends with hyphen")
+
+
tld = labels[-1]
+
if tld[0] in "0123456789":
+
raise ValueError("tld starts with digit")
+
+
self.handle = handle
+
+
def __str__(self) -> str:
+
"""Convert the Handle object to its string representation.
+
+
Returns:
+
str: The canonical handle string.
+
"""
+
return self.handle
+
+
def __eq__(self, value: object, /) -> bool:
+
"""Check if two Handle objects represent the same identifier.
+
+
Args:
+
value (object): The object to compare with.
+
+
Returns:
+
bool: True if the objects represent the same handle, False otherwise.
+
"""
+
+
if isinstance(value, Handle):
+
+
return str(self) == str(value)
+
else:
+
+
return False
+
+
def toTID(self):
+
"""Resolve the handle to its corresponding DID (Decentralized Identifier).
+
+
Attempts to resolve the handle to a DID by first checking DNS TXT records
+
at '_atproto.{handle}' and falling back to HTTP well-known endpoint at
+
'https://{handle}/.well-known/atproto-did'. This enables mapping from
+
human-readable handles to cryptographically verifiable DIDs.
+
+
Returns:
+
atpasser.uri.did.DID or None: A DID object if resolution succeeds,
+
None if resolution fails.
+
+
Note:
+
Resolution follows AT Protocol specifications:
+
1. First attempts DNS TXT record lookup for 'did={did}' value
+
2. Falls back to HTTP GET request to well-known endpoint
+
3. Returns None if both methods fail
+
"""
+
try:
+
answers = dns.resolver.resolve("_atproto." + self.handle, "TXT")
+
except:
+
answers = []
+
for answer in answers:
+
if str(answer).startswith('"did='):
+
try:
+
uri = str(answer)[5:-1]
+
return DID(uri)
+
except:
+
pass # cannot resolve via dns
+
response = requests.get(f"https://{self.handle}/.well-known/atproto-did")
+
if response.status_code // 100 != 2:
+
return None
+
if response.headers.get("Content-Type") != "text/plain":
+
pass # Pass for now, because some sites like neocities, breaks this rule
+
try:
+
return DID(response.text)
+
except:
+
return None
+22 -15
src/atpasser/uri/restricted.py
···
-
from atpasser import uri, handle, nsid, rKey
+
from . import handle, nsid
+
from . import rkey as rKey
+
from . import URI
-
class RestrictedURI(uri.URI):
-
"""
+
class RestrictedURI(URI):
+
"""A class representing a restricted AT Protocol URI for record access.
-
A class representing a restricted URI.
-
+
RestrictedURIs provide a specialized form of AT Protocol URIs that specifically
+
address records within repositories. They follow the format 'at://authority/collection/rkey'
+
where collection identifies the record type (via NSID) and rkey identifies the specific
+
record instance. This format is commonly used for referencing specific social records
+
like posts, profiles, and other user-generated content.
Attributes:
-
-
collection (atpasser.nsid.NSID): Collection as NSID.
-
-
rkey (atpasser.rKey.RKey): Record key as a RKey object.
+
collection (atpasser.nsid.NSID): The record collection identified by NSID.
+
rkey (atpasser.rKey.RKey): The record key identifying a specific record instance.
"""
-
def __init__(self, uri: str) -> None:
-
"""
+
"""Initializes a restricted URI with validation.
-
Initalizes a restricted URI.
-
+
Parses and validates an AT Protocol URI specifically for record access.
+
Restricted URIs must have a valid authority (DID or handle), may include
+
a collection (NSID), and optionally a record key (rkey). Query parameters
+
and fragments are not allowed in restricted URIs.
-
Parameters:
+
Args:
+
uri (str): The AT Protocol URI string to parse as a restricted URI.
-
uri (str): The AT URI.
+
Raises:
+
ValueError: If the URI has query parameters or fragments, invalid authority,
+
or too many path segments for a restricted URI format.
"""
super().__init__(uri)
+74
src/atpasser/uri/rkey.py
···
+
class RKey:
+
"""A class representing a Record Key (RKey) in the AT Protocol.
+
+
Record Keys are unique identifiers for individual records within a collection
+
in the AT Protocol. They are used in URIs to address specific records and
+
must follow specific character and length restrictions. RKeys can be
+
user-defined or auto-generated and are essential for referencing specific
+
content within repositories.
+
+
Attributes:
+
recordKey (str): The record key string identifying a specific record.
+
"""
+
+
def __init__(self, recordKey: str) -> None:
+
"""Initializes a Record Key object with validation.
+
+
Parses and validates a record key string according to AT Protocol specifications.
+
Record keys must not be empty, cannot exceed maximum length, cannot be
+
reserved values ('.' or '..'), and must contain only valid characters.
+
+
Args:
+
recordKey (str): The record key string to validate.
+
+
Raises:
+
ValueError: If the record key is empty, exceeds maximum length,
+
is a reserved value, or contains invalid characters.
+
"""
+
+
+
if recordKey == "" or len(recordKey) > 512:
+
+
raise ValueError("null record key or record key longer than 512 chars")
+
+
+
if recordKey == ".." or recordKey == ".":
+
+
raise ValueError("reserved value . and ..")
+
+
+
if not set(recordKey).issubset(
+
+
set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_:~")
+
):
+
+
raise ValueError("invalid char")
+
+
+
self.recordKey = recordKey
+
+
+
def __str__(self) -> str:
+
"""Convert the Record Key object to its string representation.
+
+
Returns:
+
str: The canonical record key string.
+
"""
+
return self.recordKey
+
+
+
def __eq__(self, value: object, /) -> bool:
+
"""Check if two RKey objects represent the same record key.
+
+
Args:
+
value (object): The object to compare with.
+
+
Returns:
+
bool: True if the objects represent the same record key, False otherwise.
+
"""
+
+
if isinstance(value, RKey):
+
return str(self) == str(value)
+
else:
+
return False
+
+136
src/atpasser/uri/tid.py
···
+
import datetime, random
+
+
+
class TID:
+
"""A class representing a TID (Time-based Identifier) in the AT Protocol.
+
+
TIDs are time-based identifiers used for ordering and uniquely identifying
+
records in the AT Protocol. They combine a microsecond-precision timestamp
+
with a clock identifier to ensure uniqueness even when multiple records
+
are created in the same microsecond. TIDs are sortable and provide both
+
temporal ordering and uniqueness guarantees.
+
+
Attributes:
+
timestamp (datetime.datetime): The timestamp component of the TID.
+
clockIdentifier (int): Clock identifier (0-1023) for disambiguation.
+
"""
+
+
def __init__(
+
self, time: datetime.datetime | None = None, clockIdentifier: int | None = None
+
) -> None:
+
"""Initializes a TID object with timestamp and clock identifier.
+
+
Creates a new TID with the specified timestamp and clock identifier.
+
If no timestamp is provided, uses the current time. If no clock identifier
+
is provided, generates a random value between 0-1023.
+
+
Args:
+
time (datetime.datetime, optional): The timestamp for the TID.
+
Defaults to current time if not provided.
+
clockIdentifier (int, optional): Clock identifier (0-1023) for
+
disambiguation. Defaults to random value if not provided.
+
"""
+
if time == None:
+
self.timestamp = datetime.datetime.now()
+
else:
+
self.timestamp = time
+
if clockIdentifier == None:
+
self.clockIdentifier = random.randrange(0, 1024)
+
else:
+
self.clockIdentifier = clockIdentifier
+
+
def __int__(self):
+
"""Convert the TID to its integer representation.
+
+
Combines the timestamp (in microseconds) and clock identifier into
+
a single 64-bit integer where the high bits represent the timestamp
+
and the low 10 bits represent the clock identifier.
+
+
Returns:
+
int: The integer representation of the TID.
+
"""
+
timestamp = int(self.timestamp.timestamp() * 1000000)
+
return timestamp * 1024 + self.clockIdentifier
+
+
def __str__(self):
+
"""Convert the TID to a base32-sortable string representation.
+
+
Encodes the TID as a base32 string using a custom character set that
+
maintains lexicographical sort order corresponding to temporal order.
+
This format is commonly used in the AT Protocol for compact,
+
sortable identifiers.
+
+
Returns:
+
str: The base32 string representation of the TID.
+
"""
+
integer = int(self)
+
binary = f"{integer:065b}"
+
return "".join(
+
[
+
"234567abcdefghijklmnopqrstuvwxyz"[int(binary[i : i + 5], base=2)]
+
for i in range(0, len(binary), 5)
+
]
+
)
+
+
def __eq__(self, value: object, /) -> bool:
+
"""Check if two TID objects represent the same identifier.
+
+
Args:
+
value (object): The object to compare with.
+
+
Returns:
+
bool: True if the objects represent the same TID, False otherwise.
+
"""
+
if isinstance(value, TID):
+
return int(self) == int(value)
+
else:
+
return False
+
+
+
def importTIDfromInteger(value: int | None = None):
+
"""Create a TID object from an integer representation.
+
+
Converts a 64-bit integer back into a TID object by extracting the
+
timestamp and clock identifier components. If no value is provided,
+
creates a TID for the current time.
+
+
Args:
+
value (int, optional): The integer value to convert to a TID.
+
Defaults to creating a TID for the current time if not provided.
+
+
Returns:
+
TID: The TID object created from the integer value.
+
"""
+
if value == None:
+
value = int(TID())
+
clockIdentifier = value % 1024
+
timestamp = (value >> 10) / 1000000
+
return TID(datetime.datetime.fromtimestamp(timestamp), clockIdentifier)
+
+
+
def importTIDfromBase32(value: str | None = None):
+
"""Create a TID object from a base32 string representation.
+
+
Converts a base32-encoded TID string back into a TID object by decoding
+
the string to its integer representation and then extracting the timestamp
+
and clock identifier components. If no value is provided, creates a TID
+
for the current time.
+
+
Args:
+
value (str, optional): The base32 string to convert to a TID.
+
Defaults to creating a TID for the current time if not provided.
+
+
Returns:
+
TID: The TID object created from the base32 string.
+
"""
+
if value == None:
+
value = str(TID())
+
b32s = "234567abcdefghijklmnopqrstuvwxyz"
+
return importTIDfromInteger(
+
sum(
+
[
+
b32s.find(value[i]) * (32 ** (len(value) - i - 1))
+
for i in range(len(value))
+
]
+
)
+
)