mirror of
https://github.com/Such-Software/such.software.git
synced 2026-10-17 05:32:05 +02:00
fix(blog): render tables, drop typography's backticks, correct the transport claim
TABLES. next-mdx-remote had no remark plugins, so GFM tables rendered as literal pipe characters in a paragraph. Four published posts were affected, not just the new one: bauhaus-echo-launch, flutter-3d-physics-dice, vegan-iq-data-pipeline and smirk-v0-3-0. remark-gfm is now wired into MDXRemote's mdxOptions. BACKTICKS. @tailwindcss/typography wraps every inline <code> in literal backtick characters via code::before / code::after. The served HTML was already clean; the marks were injected by CSS. Monospace already marks code as code, so the backticks were a redundant second marker that reads as a typo in running prose. Suppressed across .prose. TRANSPORT. The previous version's "transports" section covered only Grin slate channels. Tor is a separate mechanism and was undocumented: the wallet accepts a .onion backend origin over plain http, alongside private ranges and .local, because onion services carry their own transport authentication and no CA issues for them. The onion pattern matches the label immediately before .onion, so an onion-lookalike public host is refused. On the backend, the admin plane binds to loopback and TOR_ADMIN_ONION adds exactly one onion host to its Host allowlist as a DNS-rebinding guard. There is no bundled Tor daemon and no I2P support anywhere in either repo. The post now says so rather than leaving it implied. COPY. Removed the editorial voice throughout: no more "the part worth explaining", "the instructive one", "worth dwelling on". A technical report states what a thing does and lets the reader decide what matters. Headings are now descriptive rather than argumentative.
This commit is contained in:
@@ -1,29 +1,27 @@
|
|||||||
---
|
---
|
||||||
title: "Smirk Wallet v0.3.0: Nostr identity, three slate transports, and a backend you can run"
|
title: "Smirk Wallet v0.3.0: Nostr identity, federated backends, and Grin slate transports"
|
||||||
date: "2026-08-20"
|
date: "2026-08-20"
|
||||||
excerpt: "v0.3.0 makes Smirk federated end to end: a NIP-07 provider with tiered signing, Grin slates that travel over Nostr instead of a shared server, and smirk-backend-core released so you can host the whole thing yourself."
|
excerpt: "v0.3.0 makes Smirk federated end to end: a configurable backend host, a NIP-07 provider with tiered signing, Grin slates over NIP-59 gift-wraps, and smirk-backend-core released under MIT."
|
||||||
draft: false
|
draft: false
|
||||||
---
|
---
|
||||||
|
|
||||||
# Smirk Wallet v0.3.0
|
# Smirk Wallet v0.3.0
|
||||||
|
|
||||||
v0.3.0 is out. It is the release that removes the last places Smirk assumed our
|
v0.3.0 removes the remaining assumptions about our infrastructure. The backend host is
|
||||||
infrastructure: the backend host is configurable, Grin is fully non-custodial, and the
|
per-wallet configuration, Grin is fully non-custodial, and the server is a public repo.
|
||||||
server that used to be ours is now a public repo you can run yourself.
|
|
||||||
|
|
||||||
The wallet ships as a Chrome MV3 and Firefox extension, plus a Tauri 2.x desktop shell for
|
The wallet ships as a Chrome MV3 and Firefox extension plus a Tauri 2.x desktop shell for
|
||||||
Windows, macOS and Linux built from the same code path. Capacitor mobile is deferred to
|
Windows, macOS and Linux, built from the same code path. Capacitor mobile is deferred to
|
||||||
v0.4.
|
v0.4.
|
||||||
|
|
||||||
## Federation
|
## Federation
|
||||||
|
|
||||||
Nothing in the wallet points at `api.smirk.cash` any more. `homeDomain` is per-wallet
|
No component points at `api.smirk.cash`. `homeDomain` is per-wallet configuration naming an
|
||||||
configuration, and the backend it names is one instance of
|
instance of [smirk-backend-core](https://github.com/Such-Software/smirk-backend-core),
|
||||||
[smirk-backend-core](https://github.com/Such-Software/smirk-backend-core), which is now
|
released under MIT.
|
||||||
public under MIT.
|
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<svg viewBox="0 0 720 250" role="img" aria-label="Two wallets pointing at two independent backends, each delegating to its own chain nodes, with a Nostr relay layer connecting the wallets directly." style={{width:"100%",height:"auto"}}>
|
<svg viewBox="0 0 720 250" role="img" aria-label="Two wallets pointing at two independent backends, each delegating to its own chain nodes, with a Nostr relay layer connecting the two wallets directly." style={{width:"100%",height:"auto"}}>
|
||||||
<defs>
|
<defs>
|
||||||
<marker id="fedArrow" markerWidth="9" markerHeight="9" refX="8" refY="3" orient="auto">
|
<marker id="fedArrow" markerWidth="9" markerHeight="9" refX="8" refY="3" orient="auto">
|
||||||
<polygon points="0 0, 9 3, 0 6" fill="currentColor" opacity="0.55" />
|
<polygon points="0 0, 9 3, 0 6" fill="currentColor" opacity="0.55" />
|
||||||
@@ -65,24 +63,43 @@ public under MIT.
|
|||||||
</svg>
|
</svg>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
The two backends share nothing. A wallet on `a.example` can pay a wallet on `b.example`
|
The two backends share no state. A wallet on `a.example` pays a wallet on `b.example`
|
||||||
because identity and message transport ride Nostr relays, not a shared database.
|
because identity and message transport ride Nostr relays rather than a shared database.
|
||||||
|
|
||||||
## Nostr identity, and why signing is tiered
|
## Backend origins: onion and LAN
|
||||||
|
|
||||||
|
A backend URL must be `https`, with three exemptions where requiring TLS would make a
|
||||||
|
legitimate instance unreachable:
|
||||||
|
|
||||||
|
| Origin | Accepted over plain http | Reason |
|
||||||
|
|---|---|---|
|
||||||
|
| `.onion` (16 or 56 char) | yes | Onion services carry their own transport authentication and encryption, and no CA issues certificates for them |
|
||||||
|
| Private ranges, `.local` | yes | A self-hoster running a box on their own LAN |
|
||||||
|
| `localhost` | yes | Local development |
|
||||||
|
|
||||||
|
Everything else is rejected. The onion pattern matches the label immediately before
|
||||||
|
`.onion`, so `abcdefghijklmnop.onion.example.com` is a normal public host and is refused.
|
||||||
|
|
||||||
|
The backend's admin plane binds to loopback by default. Exposing it requires an SSH tunnel
|
||||||
|
or an onion: `TOR_ADMIN_ONION=<hash>.onion` adds that one host to the admin `Host`
|
||||||
|
allowlist, which otherwise accepts only loopback names. A request arriving with any other
|
||||||
|
`Host` gets a 403, which is the DNS-rebinding guard. A non-loopback bind additionally
|
||||||
|
requires `ADMIN_ALLOW_PUBLIC_BIND=true`.
|
||||||
|
|
||||||
|
There is no bundled Tor daemon and no I2P support. The wallet accepts onion origins; routing
|
||||||
|
to them is the operator's Tor instance.
|
||||||
|
|
||||||
|
## Nostr identity and tiered signing
|
||||||
|
|
||||||
The wallet exposes a NIP-07 `window.nostr` provider, so Nostr-native sites request a public
|
The wallet exposes a NIP-07 `window.nostr` provider, so Nostr-native sites request a public
|
||||||
key and get events signed without a second extension. A multi-identity vault keeps separate
|
key and have events signed without a second extension. A multi-identity vault holds separate
|
||||||
keypairs apart, with an in-wallet switcher.
|
keypairs with an in-wallet switcher.
|
||||||
|
|
||||||
The part worth explaining is what a connected site may sign *without asking again*. A Nostr
|
A Nostr signature is a bearer credential, so which events a connected origin may sign
|
||||||
signature is a bearer credential, so that is a security decision rather than a convenience
|
without a fresh prompt is a security decision. Kinds are classified into three tiers.
|
||||||
setting. Kinds are sorted into three tiers, and the wallet handler is the enforcement point:
|
|
||||||
it computes `sessionCovered=false` for anything not session-grantable and refuses to persist
|
|
||||||
a money-tier kind into a session at all, so the rule holds even if the approval UI is
|
|
||||||
compromised.
|
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<svg viewBox="0 0 720 210" role="img" aria-label="A sign-event request routed by kind into three tiers: money kinds always prompt, session-grantable kinds may use a time-boxed grant, and everything else prompts by default." style={{width:"100%",height:"auto"}}>
|
<svg viewBox="0 0 720 210" role="img" aria-label="A sign-event request routed by kind into three tiers: money kinds always prompt, session-grantable kinds may use a time-boxed grant, and everything else prompts per event." style={{width:"100%",height:"auto"}}>
|
||||||
<defs>
|
<defs>
|
||||||
<marker id="tierArrow" markerWidth="9" markerHeight="9" refX="8" refY="3" orient="auto">
|
<marker id="tierArrow" markerWidth="9" markerHeight="9" refX="8" refY="3" orient="auto">
|
||||||
<polygon points="0 0, 9 3, 0 6" fill="currentColor" opacity="0.55" />
|
<polygon points="0 0, 9 3, 0 6" fill="currentColor" opacity="0.55" />
|
||||||
@@ -120,17 +137,17 @@ compromised.
|
|||||||
</svg>
|
</svg>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
Kind 27235 is the instructive one. NIP-98 HTTP auth reads like plumbing, and it was
|
The wallet handler is the enforcement point. It computes `sessionCovered=false` for any kind
|
||||||
session-grantable in an early draft. But the backend mints a full session from a NIP-98 event
|
that is not session-grantable and refuses to persist a money-tier kind into a session, so the
|
||||||
via `POST /auth/nostr`, so a grant meant for social posting would have let a site collect an
|
classification holds independently of the approval UI.
|
||||||
auth token for the user's own backend. It is money-tier now, with an origin guard on the
|
|
||||||
event's `u` tag.
|
|
||||||
|
|
||||||
## Three transports for a Grin slate
|
Kind 27235 is NIP-98 HTTP auth. The backend mints a full session from a NIP-98 event via
|
||||||
|
`POST /auth/nostr`, so it is money-tier, with an origin guard on the event's `u` tag.
|
||||||
|
|
||||||
Grin is interactive: a payment needs slatepack legs to travel between sender and receiver.
|
## Grin slate transports
|
||||||
A slate is one payload, and how it gets there is a separate choice with different trust
|
|
||||||
properties.
|
Grin is interactive: a payment requires slatepack legs to travel between sender and receiver.
|
||||||
|
The slate is one payload; the channel is a separate choice.
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<svg viewBox="0 0 720 200" role="img" aria-label="Three Grin slate transports: Nostr gift-wrap as the federated default, manual clipboard as the universal fallback, and the backend relay as a same-instance shortcut." style={{width:"100%",height:"auto"}}>
|
<svg viewBox="0 0 720 200" role="img" aria-label="Three Grin slate transports: Nostr gift-wrap as the federated default, manual clipboard as the universal fallback, and the backend relay as a same-instance shortcut." style={{width:"100%",height:"auto"}}>
|
||||||
@@ -144,30 +161,32 @@ properties.
|
|||||||
<text x="30" y="55" fill="#2f9e68" opacity="0.85" fontSize="12">federated default, crosses backends, relays learn neither sender, amount, nor slate</text>
|
<text x="30" y="55" fill="#2f9e68" opacity="0.85" fontSize="12">federated default, crosses backends, relays learn neither sender, amount, nor slate</text>
|
||||||
<text x="560" y="36" fill="#2f9e68" fontSize="12">npub / NIP-05</text>
|
<text x="560" y="36" fill="#2f9e68" fontSize="12">npub / NIP-05</text>
|
||||||
<text x="30" y="98" fill="currentColor" fontSize="14">Manual clipboard</text>
|
<text x="30" y="98" fill="currentColor" fontSize="14">Manual clipboard</text>
|
||||||
<text x="30" y="117" fill="currentColor" opacity="0.7" fontSize="12">universal fallback, no network at all, age-encrypted to the recipient address</text>
|
<text x="30" y="117" fill="currentColor" opacity="0.7" fontSize="12">universal fallback, no network dependency, age-encrypted to the recipient address</text>
|
||||||
<text x="560" y="98" fill="currentColor" opacity="0.8" fontSize="12">grin1… address</text>
|
<text x="560" y="98" fill="currentColor" opacity="0.8" fontSize="12">grin1… address</text>
|
||||||
<text x="30" y="160" fill="currentColor" opacity="0.75" fontSize="14">Backend relay</text>
|
<text x="30" y="160" fill="currentColor" opacity="0.75" fontSize="14">Backend relay</text>
|
||||||
<text x="30" y="179" fill="currentColor" opacity="0.55" fontSize="12">same-instance shortcut only, both parties on one backend, never a requirement</text>
|
<text x="30" y="179" fill="currentColor" opacity="0.55" fontSize="12">same-instance only, both parties registered on one backend, never required</text>
|
||||||
<text x="560" y="160" fill="currentColor" opacity="0.6" fontSize="12">backend user_id</text>
|
<text x="560" y="160" fill="currentColor" opacity="0.6" fontSize="12">backend user_id</text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
What you type in the send box picks the transport. An npub or NIP-05 handle routes over
|
The address in the send box selects the transport. An npub or NIP-05 handle routes over
|
||||||
Nostr. A bare `grin1…` address falls back to clipboard, unless a same-instance lookup can
|
Nostr. A bare `grin1…` address defaults to clipboard, and is upgraded to gift-wrap when a
|
||||||
bridge that address to its owner's npub, in which case it is upgraded to gift-wrap. The
|
same-instance lookup resolves that address to its owner's npub. The backend relay serves two
|
||||||
backend relay is a convenience for two users on one host and is never on the critical path,
|
users on one host; there is no cross-backend relay federation.
|
||||||
because there is no cross-backend relay federation and there deliberately isn't going to be.
|
|
||||||
|
|
||||||
## The backend
|
Counterparty keys are pinned on first use, so a later key change for the same NIP-05 handle
|
||||||
|
is surfaced rather than trusted.
|
||||||
|
|
||||||
Rust, Axum, PostgreSQL. It gives a wallet chain reads, fee estimation and broadcast, Nostr
|
## smirk-backend-core
|
||||||
identity, a price feed, a Grin slatepack mailbox and the public social-tips subsystem,
|
|
||||||
without ever holding a spend key or a seed.
|
|
||||||
|
|
||||||
It stores public addresses, public keys, and for Monero and Wownero the incoming view keys.
|
Rust, Axum, PostgreSQL. It provides chain reads, fee estimation, broadcast, Nostr identity, a
|
||||||
View credentials are forwarded per request to whatever scans with them and are not persisted.
|
price feed, a Grin slatepack mailbox and the public social-tips subsystem, without holding a
|
||||||
Signing and broadcast happen in the wallet.
|
spend key or a seed.
|
||||||
|
|
||||||
|
Stored: public addresses, public keys, and for Monero and Wownero the incoming view keys.
|
||||||
|
View credentials are forwarded per request to the chain backends that scan with them and are
|
||||||
|
not persisted. Signing and broadcast happen in the wallet.
|
||||||
|
|
||||||
| Chain | Source |
|
| Chain | Source |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -175,30 +194,29 @@ Signing and broadcast happen in the wallet.
|
|||||||
| Monero, Wownero | light-wallet-server, stateless view-key forwarding |
|
| Monero, Wownero | light-wallet-server, stateless view-key forwarding |
|
||||||
| Grin | grin-lws, or grin-wallet view-only plus a node |
|
| Grin | grin-lws, or grin-wallet view-only plus a node |
|
||||||
|
|
||||||
Chains are independently feature-flagged. One that is not configured reports
|
Chains are independently feature-flagged. An unconfigured chain reports `enabled: false` from
|
||||||
`enabled: false` from `/capabilities` instead of failing when something calls it, so a
|
`/capabilities` rather than failing at call time, so a Bitcoin-only deployment is a supported
|
||||||
Bitcoin-only deployment is a supported outcome rather than a half-broken one.
|
configuration.
|
||||||
|
|
||||||
The HTTP contract is generated from the handlers into `openapi.json`, which is also what the
|
The HTTP contract is generated from the handlers into `openapi.json`, which is also the
|
||||||
wallet's client is generated from, with a CI gate that fails on drift.
|
source for the wallet's generated client. A CI gate fails the build on drift.
|
||||||
|
|
||||||
Grin became fully non-custodial in this release. The backend no longer holds Grin balances,
|
Grin is non-custodial in this release. The backend no longer holds Grin balances, outputs or
|
||||||
outputs or transaction records; those endpoints and the server-side wallets table are gone.
|
transaction records; those endpoints and the server-side wallets table are removed. Grin
|
||||||
Grin outputs use the standard deterministic rewind-nonce scheme, so a wallet restored from a
|
outputs use the standard deterministic rewind-nonce scheme, so a wallet restored from a seed
|
||||||
seed phrase rediscovers its outputs from the chain the way Bitcoin and Monero restores
|
phrase rediscovers its outputs from the chain, matching how Bitcoin and Monero already
|
||||||
already did. Outputs from earlier builds stay spendable.
|
restored. Outputs created by earlier builds remain spendable.
|
||||||
|
|
||||||
Two supporting pieces are public for the same reason:
|
Two supporting components are public: [grin-lws](https://github.com/Such-Software/grin-lws),
|
||||||
[grin-lws](https://github.com/Such-Software/grin-lws), a standalone Grin light wallet server,
|
a standalone Grin light wallet server, and a non-custodial BTC/LTC payment processor exposing
|
||||||
and a small non-custodial BTC/LTC payment processor exposing a BTCPay-style invoice surface
|
a BTCPay-style invoice surface where funds go to operator-controlled addresses.
|
||||||
where funds go straight to operator-controlled addresses.
|
|
||||||
|
|
||||||
## Notes
|
## Also in this release
|
||||||
|
|
||||||
v0.3.0 is feature-complete and has been through a security review, but the schema and API are
|
Taproot (`bc1p…`, `ltc1p…`) recipients in Send and Swap. Cross-chain sends blocked, where a
|
||||||
young and may still move. If you self-host, run your own chain backends where you can and
|
Monero address could previously be used as a Wownero destination. Public tip keys encrypted
|
||||||
read the code first.
|
at rest. A custom fee rate below the relay minimum now fails rather than being silently
|
||||||
|
raised at broadcast. Connected sites visible and revocable in Settings.
|
||||||
|
|
||||||
Also in this release: taproot (`bc1p…`, `ltc1p…`) recipients, cross-chain sends blocked
|
v0.3.0 is feature-complete and security-reviewed. The schema and API may still change; run
|
||||||
before they can destroy funds, encrypted public tip keys at rest, a fee you sign that matches
|
your own chain backends where possible.
|
||||||
the fee you were shown, and connected sites made visible and revocable in Settings.
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
|
"remark-gfm": "^4.0.1",
|
||||||
"resend": "^4.0.0",
|
"resend": "^4.0.0",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import Link from "next/link";
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { MDXRemote } from "next-mdx-remote/rsc";
|
import { MDXRemote } from "next-mdx-remote/rsc";
|
||||||
|
import remarkGfm from "remark-gfm";
|
||||||
import { getAllSlugs, getPost } from "@/lib/blog";
|
import { getAllSlugs, getPost } from "@/lib/blog";
|
||||||
import { JsonLd, articleLd, breadcrumbLd } from "@/components/seo/json-ld";
|
import { JsonLd, articleLd, breadcrumbLd } from "@/components/seo/json-ld";
|
||||||
|
|
||||||
@@ -73,7 +74,13 @@ export default async function BlogPost({ params }: Props) {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className="prose dark:prose-invert max-w-none">
|
<div className="prose dark:prose-invert max-w-none">
|
||||||
<MDXRemote source={post.content} components={{
|
<MDXRemote
|
||||||
|
source={post.content}
|
||||||
|
// GFM, for tables. Without it a markdown table renders as literal
|
||||||
|
// pipe characters in a paragraph, which four published posts were
|
||||||
|
// doing.
|
||||||
|
options={{ mdxOptions: { remarkPlugins: [remarkGfm] } }}
|
||||||
|
components={{
|
||||||
StoreButtons,
|
StoreButtons,
|
||||||
VeganIqStoreButtons,
|
VeganIqStoreButtons,
|
||||||
SuchoiceStoreButtons,
|
SuchoiceStoreButtons,
|
||||||
|
|||||||
@@ -364,3 +364,12 @@ html[data-splash="skip"] #splash-overlay {
|
|||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.splash-burst-blob { display: none; }
|
.splash-burst-blob { display: none; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* @tailwindcss/typography wraps every inline <code> in literal backtick characters via
|
||||||
|
code::before / code::after. The monospace treatment already marks code as code, so the
|
||||||
|
backticks are a second, redundant marker that reads as a typo in running prose:
|
||||||
|
`homeDomain` instead of homeDomain. Turn them off across the blog. */
|
||||||
|
.prose :where(code):not(:where([class~="not-prose"] *))::before,
|
||||||
|
.prose :where(code):not(:where([class~="not-prose"] *))::after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
|||||||
Generated
+201
@@ -78,6 +78,9 @@ importers:
|
|||||||
react-icons:
|
react-icons:
|
||||||
specifier: ^5.5.0
|
specifier: ^5.5.0
|
||||||
version: 5.5.0(react@19.2.4)
|
version: 5.5.0(react@19.2.4)
|
||||||
|
remark-gfm:
|
||||||
|
specifier: ^4.0.1
|
||||||
|
version: 4.0.1
|
||||||
resend:
|
resend:
|
||||||
specifier: ^4.0.0
|
specifier: ^4.0.0
|
||||||
version: 4.8.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 4.8.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
@@ -1165,6 +1168,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
escape-string-regexp@5.0.0:
|
||||||
|
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
eslint-config-next@15.5.9:
|
eslint-config-next@15.5.9:
|
||||||
resolution: {integrity: sha512-852JYI3NkFNzW8CqsMhI0K2CDRxTObdZ2jQJj5CtpEaOkYHn13107tHpNuD/h0WRpU4FAbCdUaxQsrfBtNK9Kw==}
|
resolution: {integrity: sha512-852JYI3NkFNzW8CqsMhI0K2CDRxTObdZ2jQJj5CtpEaOkYHn13107tHpNuD/h0WRpU4FAbCdUaxQsrfBtNK9Kw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1739,13 +1746,37 @@ packages:
|
|||||||
resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==}
|
resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
|
markdown-table@3.0.4:
|
||||||
|
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
|
||||||
|
|
||||||
math-intrinsics@1.1.0:
|
math-intrinsics@1.1.0:
|
||||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
mdast-util-find-and-replace@3.0.2:
|
||||||
|
resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
|
||||||
|
|
||||||
mdast-util-from-markdown@2.0.3:
|
mdast-util-from-markdown@2.0.3:
|
||||||
resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==}
|
resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==}
|
||||||
|
|
||||||
|
mdast-util-gfm-autolink-literal@2.0.1:
|
||||||
|
resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==}
|
||||||
|
|
||||||
|
mdast-util-gfm-footnote@2.1.0:
|
||||||
|
resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==}
|
||||||
|
|
||||||
|
mdast-util-gfm-strikethrough@2.0.0:
|
||||||
|
resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
|
||||||
|
|
||||||
|
mdast-util-gfm-table@2.0.0:
|
||||||
|
resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
|
||||||
|
|
||||||
|
mdast-util-gfm-task-list-item@2.0.0:
|
||||||
|
resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
|
||||||
|
|
||||||
|
mdast-util-gfm@3.1.0:
|
||||||
|
resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==}
|
||||||
|
|
||||||
mdast-util-mdx-expression@2.0.1:
|
mdast-util-mdx-expression@2.0.1:
|
||||||
resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==}
|
resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==}
|
||||||
|
|
||||||
@@ -1777,6 +1808,27 @@ packages:
|
|||||||
micromark-core-commonmark@2.0.3:
|
micromark-core-commonmark@2.0.3:
|
||||||
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
|
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
|
||||||
|
|
||||||
|
micromark-extension-gfm-autolink-literal@2.1.0:
|
||||||
|
resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==}
|
||||||
|
|
||||||
|
micromark-extension-gfm-footnote@2.1.0:
|
||||||
|
resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==}
|
||||||
|
|
||||||
|
micromark-extension-gfm-strikethrough@2.1.0:
|
||||||
|
resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==}
|
||||||
|
|
||||||
|
micromark-extension-gfm-table@2.1.1:
|
||||||
|
resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==}
|
||||||
|
|
||||||
|
micromark-extension-gfm-tagfilter@2.0.0:
|
||||||
|
resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
|
||||||
|
|
||||||
|
micromark-extension-gfm-task-list-item@2.1.0:
|
||||||
|
resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==}
|
||||||
|
|
||||||
|
micromark-extension-gfm@3.0.0:
|
||||||
|
resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
|
||||||
|
|
||||||
micromark-extension-mdx-expression@3.0.1:
|
micromark-extension-mdx-expression@3.0.1:
|
||||||
resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==}
|
resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==}
|
||||||
|
|
||||||
@@ -2180,6 +2232,9 @@ packages:
|
|||||||
rehype-recma@1.0.0:
|
rehype-recma@1.0.0:
|
||||||
resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==}
|
resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==}
|
||||||
|
|
||||||
|
remark-gfm@4.0.1:
|
||||||
|
resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
|
||||||
|
|
||||||
remark-mdx@3.1.1:
|
remark-mdx@3.1.1:
|
||||||
resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==}
|
resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==}
|
||||||
|
|
||||||
@@ -2189,6 +2244,9 @@ packages:
|
|||||||
remark-rehype@11.1.2:
|
remark-rehype@11.1.2:
|
||||||
resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==}
|
resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==}
|
||||||
|
|
||||||
|
remark-stringify@11.0.0:
|
||||||
|
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
|
||||||
|
|
||||||
resend@4.8.0:
|
resend@4.8.0:
|
||||||
resolution: {integrity: sha512-R8eBOFQDO6dzRTDmaMEdpqrkmgSjPpVXt4nGfWsZdYOet0kqra0xgbvTES6HmCriZEXbmGk3e0DiGIaLFTFSHA==}
|
resolution: {integrity: sha512-R8eBOFQDO6dzRTDmaMEdpqrkmgSjPpVXt4nGfWsZdYOet0kqra0xgbvTES6HmCriZEXbmGk3e0DiGIaLFTFSHA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -3593,6 +3651,8 @@ snapshots:
|
|||||||
|
|
||||||
escape-string-regexp@4.0.0: {}
|
escape-string-regexp@4.0.0: {}
|
||||||
|
|
||||||
|
escape-string-regexp@5.0.0: {}
|
||||||
|
|
||||||
eslint-config-next@15.5.9(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3):
|
eslint-config-next@15.5.9(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/eslint-plugin-next': 15.5.9
|
'@next/eslint-plugin-next': 15.5.9
|
||||||
@@ -4308,8 +4368,17 @@ snapshots:
|
|||||||
|
|
||||||
markdown-extensions@2.0.0: {}
|
markdown-extensions@2.0.0: {}
|
||||||
|
|
||||||
|
markdown-table@3.0.4: {}
|
||||||
|
|
||||||
math-intrinsics@1.1.0: {}
|
math-intrinsics@1.1.0: {}
|
||||||
|
|
||||||
|
mdast-util-find-and-replace@3.0.2:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
escape-string-regexp: 5.0.0
|
||||||
|
unist-util-is: 6.0.1
|
||||||
|
unist-util-visit-parents: 6.0.2
|
||||||
|
|
||||||
mdast-util-from-markdown@2.0.3:
|
mdast-util-from-markdown@2.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.4
|
'@types/mdast': 4.0.4
|
||||||
@@ -4327,6 +4396,63 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
mdast-util-gfm-autolink-literal@2.0.1:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
ccount: 2.0.1
|
||||||
|
devlop: 1.1.0
|
||||||
|
mdast-util-find-and-replace: 3.0.2
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
|
||||||
|
mdast-util-gfm-footnote@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
devlop: 1.1.0
|
||||||
|
mdast-util-from-markdown: 2.0.3
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
micromark-util-normalize-identifier: 2.0.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
mdast-util-gfm-strikethrough@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
mdast-util-from-markdown: 2.0.3
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
mdast-util-gfm-table@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
devlop: 1.1.0
|
||||||
|
markdown-table: 3.0.4
|
||||||
|
mdast-util-from-markdown: 2.0.3
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
mdast-util-gfm-task-list-item@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
devlop: 1.1.0
|
||||||
|
mdast-util-from-markdown: 2.0.3
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
mdast-util-gfm@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
mdast-util-from-markdown: 2.0.3
|
||||||
|
mdast-util-gfm-autolink-literal: 2.0.1
|
||||||
|
mdast-util-gfm-footnote: 2.1.0
|
||||||
|
mdast-util-gfm-strikethrough: 2.0.0
|
||||||
|
mdast-util-gfm-table: 2.0.0
|
||||||
|
mdast-util-gfm-task-list-item: 2.0.0
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
mdast-util-mdx-expression@2.0.1:
|
mdast-util-mdx-expression@2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree-jsx': 1.0.5
|
'@types/estree-jsx': 1.0.5
|
||||||
@@ -4430,6 +4556,64 @@ snapshots:
|
|||||||
micromark-util-symbol: 2.0.1
|
micromark-util-symbol: 2.0.1
|
||||||
micromark-util-types: 2.0.2
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
micromark-extension-gfm-autolink-literal@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-sanitize-uri: 2.0.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
micromark-extension-gfm-footnote@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-core-commonmark: 2.0.3
|
||||||
|
micromark-factory-space: 2.0.1
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-normalize-identifier: 2.0.1
|
||||||
|
micromark-util-sanitize-uri: 2.0.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
micromark-extension-gfm-strikethrough@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-util-chunked: 2.0.1
|
||||||
|
micromark-util-classify-character: 2.0.1
|
||||||
|
micromark-util-resolve-all: 2.0.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
micromark-extension-gfm-table@2.1.1:
|
||||||
|
dependencies:
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-factory-space: 2.0.1
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
micromark-extension-gfm-tagfilter@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
micromark-extension-gfm-task-list-item@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-factory-space: 2.0.1
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
micromark-extension-gfm@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
micromark-extension-gfm-autolink-literal: 2.1.0
|
||||||
|
micromark-extension-gfm-footnote: 2.1.0
|
||||||
|
micromark-extension-gfm-strikethrough: 2.1.0
|
||||||
|
micromark-extension-gfm-table: 2.1.1
|
||||||
|
micromark-extension-gfm-tagfilter: 2.0.0
|
||||||
|
micromark-extension-gfm-task-list-item: 2.1.0
|
||||||
|
micromark-util-combine-extensions: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
micromark-extension-mdx-expression@3.0.1:
|
micromark-extension-mdx-expression@3.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree': 1.0.8
|
'@types/estree': 1.0.8
|
||||||
@@ -4964,6 +5148,17 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
remark-gfm@4.0.1:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
mdast-util-gfm: 3.1.0
|
||||||
|
micromark-extension-gfm: 3.0.0
|
||||||
|
remark-parse: 11.0.0
|
||||||
|
remark-stringify: 11.0.0
|
||||||
|
unified: 11.0.5
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
remark-mdx@3.1.1:
|
remark-mdx@3.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
mdast-util-mdx: 3.0.0
|
mdast-util-mdx: 3.0.0
|
||||||
@@ -4988,6 +5183,12 @@ snapshots:
|
|||||||
unified: 11.0.5
|
unified: 11.0.5
|
||||||
vfile: 6.0.3
|
vfile: 6.0.3
|
||||||
|
|
||||||
|
remark-stringify@11.0.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
unified: 11.0.5
|
||||||
|
|
||||||
resend@4.8.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
resend@4.8.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@react-email/render': 1.1.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
'@react-email/render': 1.1.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
|
|||||||
Reference in New Issue
Block a user