diff --git a/go.mod b/go.mod index c6984c812..55cde0de1 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/onsonr/sonr -go 1.23.3 +go 1.23.4 // overrides replace ( @@ -67,14 +67,16 @@ require ( github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/gtank/merlin v0.1.1 + github.com/ipfs/boxo v0.26.0 github.com/ipfs/go-cid v0.4.1 github.com/joho/godotenv v1.5.1 - github.com/libp2p/go-libp2p v0.37.2 + github.com/libp2p/go-libp2p v0.38.1 github.com/mr-tron/base58 v1.2.0 github.com/multiformats/go-multibase v0.2.0 github.com/multiformats/go-multicodec v0.9.0 github.com/multiformats/go-multihash v0.2.3 github.com/multiformats/go-varint v0.0.7 + github.com/onsonr/motr v0.0.0-20250106200953-0f9e40a14ad8 github.com/pkg/errors v0.9.1 github.com/spf13/cast v1.7.1 github.com/spf13/cobra v1.8.1 @@ -100,6 +102,7 @@ require ( github.com/99designs/keyring v1.2.1 // indirect github.com/DataDog/datadog-go v3.2.0+incompatible // indirect github.com/DataDog/zstd v1.5.5 // indirect + github.com/apple/pkl-go v0.9.0 // indirect github.com/aws/aws-sdk-go v1.55.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect @@ -124,6 +127,7 @@ require ( github.com/cosmos/iavl v1.1.2 // indirect github.com/cosmos/ics23/go v0.10.0 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect + github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf // indirect github.com/creachadair/atomicfile v0.3.1 // indirect github.com/creachadair/tomledit v0.0.24 // indirect github.com/danieljoos/wincred v1.1.2 // indirect @@ -204,7 +208,6 @@ require ( github.com/mtibben/percent v0.2.1 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multiaddr v0.14.0 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect github.com/oklog/run v1.1.0 // indirect github.com/onsi/ginkgo v1.16.5 // indirect @@ -231,6 +234,8 @@ require ( github.com/tendermint/go-amino v0.16.0 // indirect github.com/tidwall/btree v1.7.0 // indirect github.com/ulikunitz/xz v0.5.11 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect go.etcd.io/bbolt v1.3.10 // indirect @@ -239,18 +244,16 @@ require ( go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect go.opentelemetry.io/otel v1.31.0 // indirect go.opentelemetry.io/otel/metric v1.31.0 // indirect - go.opentelemetry.io/otel/sdk v1.31.0 // indirect go.opentelemetry.io/otel/trace v1.31.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d // indirect - golang.org/x/net v0.32.0 // indirect - golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.5.0 // indirect - golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect + golang.org/x/time v0.8.0 // indirect google.golang.org/api v0.172.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect diff --git a/go.sum b/go.sum index ed127f7ce..23d571823 100644 --- a/go.sum +++ b/go.sum @@ -843,6 +843,8 @@ github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0I github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +github.com/apple/pkl-go v0.9.0 h1:aA4Bh+WQ797p8nEnQhHzCahVuQP2HJ40ffSQWlAR5es= +github.com/apple/pkl-go v0.9.0/go.mod h1:5Hwil5tyZGrOekh7JXLZJvIAcGHb4gT19lnv4WEiKeI= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= @@ -977,6 +979,8 @@ github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5n github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf h1:dwGgBWn84wUS1pVikGiruW+x5XM4amhjaZO20vCjay4= +github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= github.com/creachadair/atomicfile v0.3.1 h1:yQORkHjSYySh/tv5th1dkKcn02NEW5JleB84sjt+W4Q= github.com/creachadair/atomicfile v0.3.1/go.mod h1:mwfrkRxFKwpNAflYZzytbSwxvbK6fdGRRlp0KEQc0qU= github.com/creachadair/tomledit v0.0.24 h1:5Xjr25R2esu1rKCbQEmjZYlrhFkDspoAbAKb6QKQDhQ= @@ -1351,6 +1355,8 @@ github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPt github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/ipfs/boxo v0.26.0 h1:RRxEon7rJMy8ScVaTLncSZ5/nA6majYhRSbzc80snO8= +github.com/ipfs/boxo v0.26.0/go.mod h1:iHyc9cjoF7/zoiKVY65d2fBWRhoS2zx4cMk8hKgqrac= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= @@ -1406,8 +1412,8 @@ github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/libp2p/go-libp2p v0.37.2 h1:Irh+n9aDPTLt9wJYwtlHu6AhMUipbC1cGoJtOiBqI9c= -github.com/libp2p/go-libp2p v0.37.2/go.mod h1:M8CRRywYkqC6xKHdZ45hmqVckBj5z4mRLIMLWReypz8= +github.com/libp2p/go-libp2p v0.38.1 h1:aT1K7IFWi+gZUsQGCzTHBTlKX5QVZQOahng8DnOr6tQ= +github.com/libp2p/go-libp2p v0.38.1/go.mod h1:QWV4zGL3O9nXKdHirIC59DoRcZ446dfkjbOJ55NEWFo= github.com/linxGnu/grocksdb v1.8.14 h1:HTgyYalNwBSG/1qCQUIott44wU5b2Y9Kr3z7SK5OfGQ= github.com/linxGnu/grocksdb v1.8.14/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= @@ -1515,6 +1521,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/onsonr/motr v0.0.0-20250106200953-0f9e40a14ad8 h1:mRyGBI8HbCtvBOy+glT47Tz2Hlz2BYUU/r2MOckgNgg= +github.com/onsonr/motr v0.0.0-20250106200953-0f9e40a14ad8/go.mod h1:lGU/f0rProgg/SzEko5NTD8n2uXjOodewcudSPf2GJQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= @@ -1676,6 +1684,10 @@ github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95 github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1782,8 +1794,8 @@ golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8H golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0= -golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1904,8 +1916,8 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1940,8 +1952,8 @@ golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2127,8 +2139,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/x/dwn/types/attns.go b/x/dwn/types/attns.go new file mode 100644 index 000000000..ab1254f4c --- /dev/null +++ b/x/dwn/types/attns.go @@ -0,0 +1 @@ +package types diff --git a/x/dwn/types/embed/codec.go b/x/dwn/types/embed/codec.go new file mode 100644 index 000000000..21f9dc252 --- /dev/null +++ b/x/dwn/types/embed/codec.go @@ -0,0 +1,48 @@ +package embed + +import ( + "encoding/json" + + "github.com/ipfs/boxo/files" + motr "github.com/onsonr/motr/pkg/config" +) + +const SchemaVersion = 1 +const ( + AppManifestFileName = "app.webmanifest" + DWNConfigFileName = "dwn.json" + IndexHTMLFileName = "index.html" + MainJSFileName = "main.js" + ServiceWorkerFileName = "sw.js" +) + +// spawnVaultDirectory creates a new directory with the default files +func NewVaultFS(cfg *motr.Config) (files.Directory, error) { + manifestBz, err := NewWebManifest() + if err != nil { + return nil, err + } + cnfBz, err := json.Marshal(cfg) + if err != nil { + return nil, err + } + return files.NewMapDirectory(map[string]files.Node{ + AppManifestFileName: files.NewBytesFile(manifestBz), + DWNConfigFileName: files.NewBytesFile(cnfBz), + IndexHTMLFileName: files.NewBytesFile(IndexHTML), + MainJSFileName: files.NewBytesFile(MainJS), + ServiceWorkerFileName: files.NewBytesFile(WorkerJS), + }), nil +} + +// NewVaultConfig returns the default vault config +func NewVaultConfig(addr string, ucanCID string) *motr.Config { + return &motr.Config{ + MotrToken: ucanCID, + MotrAddress: addr, + IpfsGatewayUrl: "http://localhost:80", + SonrApiUrl: "http://localhost:1317", + SonrRpcUrl: "http://localhost:26657", + SonrChainId: "sonr-testnet-1", + } +} diff --git a/x/dwn/types/embed/index.html b/x/dwn/types/embed/index.html new file mode 100644 index 000000000..b830f7a1b --- /dev/null +++ b/x/dwn/types/embed/index.html @@ -0,0 +1,138 @@ + + + + + + + Sonr DWN + + + + + + + + + + + + + + + + + + + + + +
+ You are currently offline. Some features may be limited. +
+ + + + +
+
+ Loading... +
+
+ + + + + + + diff --git a/x/dwn/types/embed/main.js b/x/dwn/types/embed/main.js new file mode 100644 index 000000000..b737ac852 --- /dev/null +++ b/x/dwn/types/embed/main.js @@ -0,0 +1,158 @@ +// MessageChannel for WASM communication +let wasmChannel; +let wasmPort; + +async function initWasmChannel() { + wasmChannel = new MessageChannel(); + wasmPort = wasmChannel.port1; + + // Setup message handling from WASM + wasmPort.onmessage = (event) => { + const { type, data } = event.data; + switch (type) { + case "WASM_READY": + console.log("WASM is ready"); + document.dispatchEvent(new CustomEvent("wasm-ready")); + break; + case "RESPONSE": + handleWasmResponse(data); + break; + case "SYNC_COMPLETE": + handleSyncComplete(data); + break; + } + }; +} + +// Initialize WebAssembly and Service Worker +async function init() { + try { + // Register service worker + if ("serviceWorker" in navigator) { + const registration = await navigator.serviceWorker.register("./sw.js"); + console.log("ServiceWorker registered"); + + // Wait for the service worker to be ready + await navigator.serviceWorker.ready; + + // Initialize MessageChannel + await initWasmChannel(); + + // Send the MessageChannel port to the service worker + navigator.serviceWorker.controller.postMessage( + { + type: "PORT_INITIALIZATION", + port: wasmChannel.port2, + }, + [wasmChannel.port2], + ); + + // Register for periodic sync if available + if ("periodicSync" in registration) { + try { + await registration.periodicSync.register("wasm-sync", { + minInterval: 24 * 60 * 60 * 1000, // 24 hours + }); + } catch (error) { + console.log("Periodic sync could not be registered:", error); + } + } + } + + // Initialize HTMX with custom config + htmx.config.withCredentials = true; + htmx.config.wsReconnectDelay = "full-jitter"; + + // Override HTMX's internal request handling + htmx.config.beforeRequest = function (config) { + // Add request ID for tracking + const requestId = "req_" + Date.now(); + config.headers["X-Wasm-Request-ID"] = requestId; + + // If offline, handle through service worker + if (!navigator.onLine) { + return false; // Let service worker handle it + } + return true; + }; + + // Handle HTMX after request + htmx.config.afterRequest = function (config) { + // Additional processing after request if needed + }; + + // Handle HTMX errors + htmx.config.errorHandler = function (error) { + console.error("HTMX Error:", error); + }; + } catch (error) { + console.error("Initialization failed:", error); + } +} + +function handleWasmResponse(data) { + const { requestId, response } = data; + // Process the WASM response + // This might update the UI or trigger HTMX swaps + const targetElement = document.querySelector( + `[data-request-id="${requestId}"]`, + ); + if (targetElement) { + htmx.process(targetElement); + } +} + +function handleSyncComplete(data) { + const { url } = data; + // Handle successful sync + // Maybe refresh the relevant part of the UI + htmx.trigger("body", "sync:complete", { url }); +} + +// Handle offline status changes +window.addEventListener("online", () => { + document.body.classList.remove("offline"); + // Trigger sync when back online + if (wasmPort) { + wasmPort.postMessage({ type: "SYNC_REQUEST" }); + } +}); + +window.addEventListener("offline", () => { + document.body.classList.add("offline"); +}); + +// Custom event handlers for HTMX +document.addEventListener("htmx:beforeRequest", (event) => { + const { elt, xhr } = event.detail; + // Add request tracking + const requestId = xhr.headers["X-Wasm-Request-ID"]; + elt.setAttribute("data-request-id", requestId); +}); + +document.addEventListener("htmx:afterRequest", (event) => { + const { elt, successful } = event.detail; + if (successful) { + elt.removeAttribute("data-request-id"); + } +}); + +// Initialize everything when the page loads +document.addEventListener("DOMContentLoaded", init); + +// Export functions that might be needed by WASM +window.wasmBridge = { + triggerUIUpdate: function (selector, content) { + const target = document.querySelector(selector); + if (target) { + htmx.process( + htmx.parse(content).forEach((node) => target.appendChild(node)), + ); + } + }, + + showNotification: function (message, type = "info") { + // Implement notification system + console.log(`${type}: ${message}`); + }, +}; diff --git a/x/dwn/types/embed/sw.js b/x/dwn/types/embed/sw.js new file mode 100644 index 000000000..072d46901 --- /dev/null +++ b/x/dwn/types/embed/sw.js @@ -0,0 +1,257 @@ +// Cache names for different types of resources +const CACHE_NAMES = { + wasm: "wasm-cache-v1", + static: "static-cache-v1", + dynamic: "dynamic-cache-v1", +}; + +// Import required scripts +importScripts( + "https://cdn.jsdelivr.net/gh/golang/go@go1.22.5/misc/wasm/wasm_exec.js", + "https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@v1.1.0/sw.js", +); + +// Initialize WASM HTTP listener +const wasmInstance = registerWasmHTTPListener( + "https://cdn.sonr.id/wasm/app.wasm", +); + +// MessageChannel port for WASM communication +let wasmPort; + +// Request queue for offline operations +let requestQueue = new Map(); + +// Setup message channel handler +self.addEventListener("message", async (event) => { + if (event.data.type === "PORT_INITIALIZATION") { + wasmPort = event.data.port; + setupWasmCommunication(); + } +}); + +function setupWasmCommunication() { + wasmPort.onmessage = async (event) => { + const { type, data } = event.data; + + switch (type) { + case "WASM_REQUEST": + handleWasmRequest(data); + break; + case "SYNC_REQUEST": + processSyncQueue(); + break; + } + }; + + // Notify that WASM is ready + wasmPort.postMessage({ type: "WASM_READY" }); +} + +// Enhanced install event +self.addEventListener("install", (event) => { + event.waitUntil( + Promise.all([ + skipWaiting(), + // Cache WASM binary and essential resources + caches + .open(CACHE_NAMES.wasm) + .then((cache) => + cache.addAll([ + "https://cdn.sonr.id/wasm/app.wasm", + "https://cdn.jsdelivr.net/gh/golang/go@go1.22.5/misc/wasm/wasm_exec.js", + ]), + ), + ]), + ); +}); + +// Enhanced activate event +self.addEventListener("activate", (event) => { + event.waitUntil( + Promise.all([ + clients.claim(), + // Clean up old caches + caches.keys().then((keys) => + Promise.all( + keys.map((key) => { + if (!Object.values(CACHE_NAMES).includes(key)) { + return caches.delete(key); + } + }), + ), + ), + ]), + ); +}); + +// Intercept fetch events +self.addEventListener("fetch", (event) => { + const request = event.request; + + // Handle API requests differently from static resources + if (request.url.includes("/api/")) { + event.respondWith(handleApiRequest(request)); + } else { + event.respondWith(handleStaticRequest(request)); + } +}); + +async function handleApiRequest(request) { + try { + // Try to make the request + const response = await fetch(request.clone()); + + // If successful, pass through WASM handler + if (response.ok) { + return await processWasmResponse(request, response); + } + + // If offline or failed, queue the request + await queueRequest(request); + + // Return cached response if available + const cachedResponse = await caches.match(request); + if (cachedResponse) { + return cachedResponse; + } + + // Return offline response + return new Response(JSON.stringify({ error: "Currently offline" }), { + status: 503, + headers: { "Content-Type": "application/json" }, + }); + } catch (error) { + await queueRequest(request); + return new Response(JSON.stringify({ error: "Request failed" }), { + status: 500, + headers: { "Content-Type": "application/json" }, + }); + } +} + +async function handleStaticRequest(request) { + // Check cache first + const cachedResponse = await caches.match(request); + if (cachedResponse) { + return cachedResponse; + } + + try { + const response = await fetch(request); + + // Cache successful responses + if (response.ok) { + const cache = await caches.open(CACHE_NAMES.static); + cache.put(request, response.clone()); + } + + return response; + } catch (error) { + // Return offline page for navigation requests + if (request.mode === "navigate") { + return caches.match("/offline.html"); + } + throw error; + } +} + +async function processWasmResponse(request, response) { + // Clone the response before processing + const responseClone = response.clone(); + + try { + // Process through WASM + const processedResponse = await wasmInstance.processResponse(responseClone); + + // Notify client through message channel + if (wasmPort) { + wasmPort.postMessage({ + type: "RESPONSE", + requestId: request.headers.get("X-Wasm-Request-ID"), + response: processedResponse, + }); + } + + return processedResponse; + } catch (error) { + console.error("WASM processing error:", error); + return response; + } +} + +async function queueRequest(request) { + const serializedRequest = await serializeRequest(request); + requestQueue.set(request.url, serializedRequest); + + // Register for background sync + try { + await self.registration.sync.register("wasm-sync"); + } catch (error) { + console.error("Sync registration failed:", error); + } +} + +async function serializeRequest(request) { + const headers = {}; + for (const [key, value] of request.headers.entries()) { + headers[key] = value; + } + + return { + url: request.url, + method: request.method, + headers, + body: await request.text(), + timestamp: Date.now(), + }; +} + +// Handle background sync +self.addEventListener("sync", (event) => { + if (event.tag === "wasm-sync") { + event.waitUntil(processSyncQueue()); + } +}); + +async function processSyncQueue() { + const requests = Array.from(requestQueue.values()); + + for (const serializedRequest of requests) { + try { + const response = await fetch( + new Request(serializedRequest.url, { + method: serializedRequest.method, + headers: serializedRequest.headers, + body: serializedRequest.body, + }), + ); + + if (response.ok) { + requestQueue.delete(serializedRequest.url); + + // Notify client of successful sync + if (wasmPort) { + wasmPort.postMessage({ + type: "SYNC_COMPLETE", + url: serializedRequest.url, + }); + } + } + } catch (error) { + console.error("Sync failed for request:", error); + } + } +} + +// Handle payment requests +self.addEventListener("canmakepayment", function (e) { + e.respondWith(Promise.resolve(true)); +}); + +// Handle periodic sync if available +self.addEventListener("periodicsync", (event) => { + if (event.tag === "wasm-sync") { + event.waitUntil(processSyncQueue()); + } +}); diff --git a/x/dwn/types/embed/utils.go b/x/dwn/types/embed/utils.go new file mode 100644 index 000000000..fac01a126 --- /dev/null +++ b/x/dwn/types/embed/utils.go @@ -0,0 +1,47 @@ +package embed + +import ( + _ "embed" + "reflect" + "strings" +) + +//go:embed index.html +var IndexHTML []byte + +//go:embed main.js +var MainJS []byte + +//go:embed sw.js +var WorkerJS []byte + +func getSchema(structType interface{}) string { + t := reflect.TypeOf(structType) + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + + if t.Kind() != reflect.Struct { + return "" + } + + var fields []string + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + fieldName := toCamelCase(field.Name) + fields = append(fields, fieldName) + } + + // Add "++" at the beginning, separated by a comma + return "++, " + strings.Join(fields, ", ") +} + +func toCamelCase(s string) string { + if s == "" { + return s + } + if len(s) == 1 { + return strings.ToLower(s) + } + return strings.ToLower(s[:1]) + s[1:] +} diff --git a/x/dwn/types/embed/webworker.go b/x/dwn/types/embed/webworker.go new file mode 100644 index 000000000..fd0e83d15 --- /dev/null +++ b/x/dwn/types/embed/webworker.go @@ -0,0 +1,124 @@ +package embed + +import "encoding/json" + +func NewWebManifest() ([]byte, error) { + return json.Marshal(baseWebManifest) +} + +var baseWebManifest = WebManifest{ + Name: "Sonr Vault", + ShortName: "Sonr.ID", + StartURL: "/index.html", + Display: "standalone", + DisplayOverride: []string{ + "fullscreen", + "minimal-ui", + }, + Icons: []IconDefinition{ + { + Src: "/icons/icon-192x192.png", + Sizes: "192x192", + Type: "image/png", + }, + }, + ServiceWorker: ServiceWorker{ + Scope: "/", + Src: "/sw.js", + UseCache: true, + }, + ProtocolHandlers: []ProtocolHandler{ + { + Scheme: "did.sonr", + URL: "/resolve/sonr/%s", + }, + { + Scheme: "did.eth", + URL: "/resolve/eth/%s", + }, + { + Scheme: "did.btc", + URL: "/resolve/btc/%s", + }, + { + Scheme: "did.usdc", + URL: "/resolve/usdc/%s", + }, + { + Scheme: "did.ipfs", + URL: "/resolve/ipfs/%s", + }, + }, +} + +type WebManifest struct { + // Required fields + Name string `json:"name"` // Full name of the application + ShortName string `json:"short_name"` // Short version of the name + + // Display and appearance + Description string `json:"description,omitempty"` // Purpose and features of the application + Display string `json:"display,omitempty"` // Preferred display mode: fullscreen, standalone, minimal-ui, browser + DisplayOverride []string `json:"display_override,omitempty"` + ThemeColor string `json:"theme_color,omitempty"` // Default theme color for the application + BackgroundColor string `json:"background_color,omitempty"` // Background color during launch + Orientation string `json:"orientation,omitempty"` // Default orientation: any, natural, landscape, portrait + + // URLs and scope + StartURL string `json:"start_url"` // Starting URL when launching + Scope string `json:"scope,omitempty"` // Navigation scope of the web application + ServiceWorker ServiceWorker `json:"service_worker,omitempty"` + + // Icons + Icons []IconDefinition `json:"icons,omitempty"` + + // Optional features + RelatedApplications []RelatedApplication `json:"related_applications,omitempty"` + PreferRelatedApplications bool `json:"prefer_related_applications,omitempty"` + Shortcuts []Shortcut `json:"shortcuts,omitempty"` + + // Experimental features (uncomment if needed) + FileHandlers []FileHandler `json:"file_handlers,omitempty"` + ProtocolHandlers []ProtocolHandler `json:"protocol_handlers,omitempty"` +} + +type FileHandler struct { + Action string `json:"action"` + Accept map[string][]string `json:"accept"` +} + +type LaunchHandler struct { + Action string `json:"action"` +} + +type IconDefinition struct { + Src string `json:"src"` + Sizes string `json:"sizes"` + Type string `json:"type,omitempty"` + Purpose string `json:"purpose,omitempty"` +} + +type ProtocolHandler struct { + Scheme string `json:"scheme"` + URL string `json:"url"` +} + +type RelatedApplication struct { + Platform string `json:"platform"` + URL string `json:"url,omitempty"` + ID string `json:"id,omitempty"` +} + +type Shortcut struct { + Name string `json:"name"` + ShortName string `json:"short_name,omitempty"` + Description string `json:"description,omitempty"` + URL string `json:"url"` + Icons []IconDefinition `json:"icons,omitempty"` +} + +type ServiceWorker struct { + Scope string `json:"scope"` + Src string `json:"src"` + UseCache bool `json:"use_cache"` +}