diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..1e04acc
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,10 @@
+name: CI
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+
+jobs:
+ lint:
+ uses: lnbits/lnbits/.github/workflows/lint.yml@dev
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..7ec9b48
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,59 @@
+on:
+ push:
+ tags:
+ - "v[0-9]+.[0-9]+.[0-9]+"
+
+jobs:
+
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Create github release
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ tag: ${{ github.ref_name }}
+ run: |
+ gh release create "$tag" --generate-notes
+
+ pullrequest:
+ needs: [release]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ token: ${{ secrets.EXT_GITHUB }}
+ repository: lnbits/lnbits-extensions
+ path: './lnbits-extensions'
+
+ - name: setup git user
+ run: |
+ git config --global user.name "alan"
+ git config --global user.email "alan@lnbits.com"
+
+ - name: Create pull request in extensions repo
+ env:
+ GH_TOKEN: ${{ secrets.EXT_GITHUB }}
+ repo_name: "${{ github.event.repository.name }}"
+ tag: "${{ github.ref_name }}"
+ branch: "update-${{ github.event.repository.name }}-${{ github.ref_name }}"
+ title: "[UPDATE] ${{ github.event.repository.name }} to ${{ github.ref_name }}"
+ body: "https://github.com/lnbits/${{ github.event.repository.name }}/releases/${{ github.ref_name }}"
+ archive: "https://github.com/lnbits/${{ github.event.repository.name }}/archive/refs/tags/${{ github.ref_name }}.zip"
+ run: |
+ cd lnbits-extensions
+ git checkout -b $branch
+
+ # if there is another open PR
+ git pull origin $branch || echo "branch does not exist"
+
+ sh util.sh update_extension $repo_name $tag
+
+ git add -A
+ git commit -am "$title"
+ git push origin $branch
+
+ # check if pr exists before creating it
+ gh config set pager cat
+ check=$(gh pr list -H $branch | wc -l)
+ test $check -ne 0 || gh pr create --title "$title" --body "$body" --repo lnbits/lnbits-extensions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0171c60
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,32 @@
+.DS_Store
+._*
+
+__pycache__
+*.py[cod]
+*$py.class
+.mypy_cache
+.vscode
+*-lock.json
+
+*.egg
+*.egg-info
+.coverage
+.pytest_cache
+.webassets-cache
+htmlcov
+test-reports
+tests/data/*.sqlite3
+node_modules
+
+*.swo
+*.swp
+*.pyo
+*.pyc
+*.env
+env.dev
+.venv/
+.ruff_cache/
+
+# Claude Code config
+CLAUDE.md
+.claude/
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..931dc7f
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,14 @@
+**/.git
+**/.svn
+**/.hg
+**/node_modules
+
+*.yml
+
+**/static/market/*
+**/static/js/nostr.bundle.js*
+
+
+flake.lock
+
+.venv
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..725c398
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,12 @@
+{
+ "semi": false,
+ "arrowParens": "avoid",
+ "insertPragma": false,
+ "printWidth": 80,
+ "proseWrap": "preserve",
+ "singleQuote": true,
+ "trailingComma": "none",
+ "useTabs": false,
+ "bracketSameLine": false,
+ "bracketSpacing": false
+}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ddc0714
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,48 @@
+all: format check
+
+format: prettier black ruff
+
+# Note: mypy/pyright skipped due to hyphen in directory name "diagon-alley"
+check: checkblack checkruff checkprettier
+
+prettier:
+ uv run ./node_modules/.bin/prettier --write .
+pyright:
+ uv run ./node_modules/.bin/pyright
+
+mypy:
+ uv run mypy .
+
+black:
+ uv run black .
+
+ruff:
+ uv run ruff check . --fix
+
+checkruff:
+ uv run ruff check .
+
+checkprettier:
+ uv run ./node_modules/.bin/prettier --check .
+
+checkblack:
+ uv run black --check .
+
+checkeditorconfig:
+ editorconfig-checker
+
+test:
+ PYTHONUNBUFFERED=1 \
+ DEBUG=true \
+ uv run pytest
+install-pre-commit-hook:
+ @echo "Installing pre-commit hook to git"
+ @echo "Uninstall the hook with uv run pre-commit uninstall"
+ uv run pre-commit install
+
+pre-commit:
+ uv run pre-commit run --all-files
+
+
+checkbundle:
+ @echo "skipping checkbundle"
diff --git a/README.md b/README.md
index a4ef6a6..eb6b1b9 100644
--- a/README.md
+++ b/README.md
@@ -1,87 +1,114 @@
-
+# Diagon Alley
+
+
+
+
+ Public Nostr Marketplace Aggregator +
+
-
+ An LNbits extension for browsing and discovering products from multiple Nostr merchants.
h?0:h+i),o=o>h?h:o,o<0&&(o+=h),h=i>o?0:o-i>>>0,i>>>=0;for(var p=R(h);++c =c?n:In(n,i,o)}var Kd=Pv||function(n){return Ut.clearTimeout(n)};function Gd(n,i){if(i)return n.slice();var o=n.length,c=md?md(o):new n.constructor(o);return n.copy(c),c}function Fs(n){var i=new n.constructor(n.byteLength);return new Sl(i).set(new Sl(n)),i}function Vp(n,i){var o=i?Fs(n.buffer):n.buffer;return new n.constructor(o,n.byteOffset,n.byteLength)}function Yp(n){var i=new n.constructor(n.source,qi.exec(n));return i.lastIndex=n.lastIndex,i}function Wp(n){return ya?dt(ya.call(n)):{}}function Qd(n,i){var o=i?Fs(n.buffer):n.buffer;return new n.constructor(o,n.byteOffset,n.length)}function jd(n,i){if(n!==i){var o=n!==r,c=n===null,h=n===n,p=_n(n),w=i!==r,k=i===null,P=i===i,U=_n(i);if(!k&&!U&&!p&&n>i||p&&w&&P&&!k&&!U||c&&w&&P||!o&&P||!h)return 1;if(!c&&!p&&!U&&n=k)return P;var U=o[c];return P*(U=="desc"?-1:1)}}return n.index-i.index}function Zd(n,i,o,c){for(var h=-1,p=n.length,w=o.length,k=-1,P=i.length,U=Ot(p-w,0),z=R(P+U),W=!c;++k 1?o[h-1]:r,w=h>2?o[2]:r;for(p=n.length>3&&typeof p=="function"?(h--,p):r,w&&ln(o[0],o[1],w)&&(p=h<3?r:p,h=1),i=dt(i);++c =i}),hi=Ad(function(){return arguments}())?Ad:function(n){return Tt(n)&&ot.call(n,"callee")&&!pd.call(n,"callee")},Ee=R.isArray,p1=Jc?pn(Jc):Tp;function fn(n){return n!=null&&jl(n.length)&&!_r(n)}function xt(n){return Tt(n)&&fn(n)}function y1(n){return n===!0||n===!1||Tt(n)&&an(n)==ft}var Gr=Ev||fu,_1=Xc?pn(Xc):Mp;function b1(n){return Tt(n)&&n.nodeType===1&&!Da(n)}function w1(n){if(n==null)return!0;if(fn(n)&&(Ee(n)||typeof n=="string"||typeof n.splice=="function"||Gr(n)||zi(n)||hi(n)))return!n.length;var i=Zt(n);if(i==qt||i==K)return!n.size;if(Ta(n))return!xs(n).length;for(var o in n)if(ot.call(n,o))return!1;return!0}function S1(n,i){return Sa(n,i)}function k1(n,i,o){o=typeof o=="function"?o:r;var c=o?o(n,i):r;return c===r?Sa(n,i,r,o):!!c}function nu(n){if(!Tt(n))return!1;var i=an(n);return i==nn||i==wt||typeof n.message=="string"&&typeof n.name=="string"&&!Da(n)}function C1(n){return typeof n=="number"&&_d(n)}function _r(n){if(!Ct(n))return!1;var i=an(n);return i==un||i==rn||i==Me||i==$r}function Ff(n){return typeof n=="number"&&n==Oe(n)}function jl(n){return typeof n=="number"&&n>-1&&n%1==0&&n<=me}function Ct(n){var i=typeof n;return n!=null&&(i=="object"||i=="function")}function Tt(n){return n!=null&&typeof n=="object"}var Uf=ed?pn(ed):qp;function T1(n,i){return n===i||Ps(n,i,Hs(i))}function M1(n,i,o){return o=typeof o=="function"?o:r,Ps(n,i,Hs(i),o)}function D1(n){return zf(n)&&n!=+n}function q1(n){if(d0(n))throw new Ae(s);return Ed(n)}function P1(n){return n===null}function x1(n){return n==null}function zf(n){return typeof n=="number"||Tt(n)&&an(n)==Kt}function Da(n){if(!Tt(n)||an(n)!=Gt)return!1;var i=kl(n);if(i===null)return!0;var o=ot.call(i,"constructor")&&i.constructor;return typeof o=="function"&&o instanceof o&&_l.call(o)==Mv}var ru=td?pn(td):Pp;function A1(n){return Ff(n)&&n>=-me&&n<=me}var Vf=nd?pn(nd):xp;function Zl(n){return typeof n=="string"||!Ee(n)&&Tt(n)&&an(n)==de}function _n(n){return typeof n=="symbol"||Tt(n)&&an(n)==qe}var zi=rd?pn(rd):Ap;function E1(n){return n===r}function I1(n){return Tt(n)&&Zt(n)==it}function $1(n){return Tt(n)&&an(n)==Ze}var O1=Ul(As),R1=Ul(function(n,i){return n<=i});function Yf(n){if(!n)return[];if(fn(n))return Zl(n)?zn(n):dn(n);if(ga&&n[ga])return gv(n[ga]());var i=Zt(n),o=i==qt?ys:i==K?vl:Vi;return o(n)}function br(n){if(!n)return n===0?n:0;if(n=On(n),n===G||n===-G){var i=n<0?-1:1;return i*rt}return n===n?n:0}function Oe(n){var i=br(n),o=i%1;return i===i?o?i-o:i:0}function Wf(n){return n?ui(Oe(n),0,Z):0}function On(n){if(typeof n=="number")return n;if(_n(n))return X;if(Ct(n)){var i=typeof n.valueOf=="function"?n.valueOf():n;n=Ct(i)?i+"":i}if(typeof n!="string")return n===0?n:+n;n=ud(n);var o=ul.test(n);return o||dl.test(n)?Zm(n.slice(2),o?2:8):ns.test(n)?X:+n}function Hf(n){return nr(n,hn(n))}function L1(n){return n?ui(Oe(n),-me,me):n===0?n:0}function at(n){return n==null?"":yn(n)}var N1=Bi(function(n,i){if(Ta(i)||fn(i)){nr(i,Ft(i),n);return}for(var o in i)ot.call(i,o)&&_a(n,o,i[o])}),Kf=Bi(function(n,i){nr(i,hn(i),n)}),Jl=Bi(function(n,i,o,c){nr(i,hn(i),n,c)}),B1=Bi(function(n,i,o,c){nr(i,Ft(i),n,c)}),F1=pr(Cs);function U1(n,i){var o=Ni(n);return i==null?o:Cd(o,i)}var z1=Ue(function(n,i){n=dt(n);var o=-1,c=i.length,h=c>2?i[2]:r;for(h&&ln(i[0],i[1],h)&&(c=1);++o{e[t]=function(...n){gn();const o=ie(this)[t].apply(this,n);return mn(),o}}),e}function qu(e){const t=ie(this);return Ie(t,"has",e),t.hasOwnProperty(e)}function Zr(e=!1,t=!1){return function(o,r,i){if(r==="__v_isReactive")return!e;if(r==="__v_isReadonly")return e;if(r==="__v_isShallow")return t;if(r==="__v_raw"&&i===(e?t?Uu:gl:t?hl:dl).get(o))return o;const s=te(o);if(!e){if(s&&ce(Ci,r))return Reflect.get(Ci,r,i);if(r==="hasOwnProperty")return qu}const l=Reflect.get(o,r,i);return(zr(r)?ul.has(r):Eu(r))||(e||Ie(o,"get",r),t)?l:Oe(l)?s&&Kr(r)?l:l.value:ye(l)?e?pl(l):pn(l):l}}const Au=cl(),Mu=cl(!0);function cl(e=!1){return function(n,o,r,i){let s=n[o];if(ln(s)&&Oe(s)&&!Oe(r))return!1;if(!e&&(!yo(r)&&!ln(r)&&(s=ie(s),r=ie(r)),!te(n)&&Oe(s)&&!Oe(r)))return s.value=r,!0;const l=te(n)&&Kr(o)?Number(o)
+
+ /diagonalley/market
+