Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All notable changes to NetworkingKit are documented in this file.

## 2.4.6 - 2026-07-23

### Changed

- Show GraphQL `/graphql` and `POST` metadata together with statically discoverable `query`, `variables`, and `operationName` parameters in generated backend HTML documentation.
- Render GraphQL query and variables as formatted, indented code blocks.

## 2.4.5 - 2026-07-23

### Changed
Expand Down
2 changes: 1 addition & 1 deletion Docs/BackendReferencePlugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ struct GetProfileRequest: AccountRequest, RestfulRequest {

- A `NetworkClient` or `SharedNetworkClient` `baseURL` identifies one server.
- A request protocol constrained to one concrete client associates request types with that server.
- `RestfulRequest` and `GraphQLRequest` declarations become endpoints.
- `RestfulRequest` and `GraphQLRequest` declarations become endpoints. GraphQL endpoints use `/graphql` and `POST`; their `query`, `variables`, and `operationName` declarations are shown as request parameters when statically available.
- The closest preceding `// MARK: - Feature name` groups the endpoints.
- Stored request properties become parameters. The client `configuration` becomes the Configuration table.

Expand Down
2 changes: 1 addition & 1 deletion Docs/BackendReferencePlugin.zh-Hans.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ struct GetProfileRequest: AccountRequest, RestfulRequest {

- `NetworkClient` / `SharedNetworkClient` 的 `baseURL` 表示一个后端服务器。
- 绑定到某个具体 Client 的请求协议,会将 Request 关联到对应服务器。
- `RestfulRequest` 和 `GraphQLRequest` 声明会被识别为端点。
- `RestfulRequest` 和 `GraphQLRequest` 声明会被识别为端点。GraphQL 端点固定为 `/graphql` 和 `POST`;能够静态识别的 `query`、`variables` 与 `operationName` 会展示在参数列中。
- 请求声明上方最近的 `// MARK: - Feature 名称` 用于 Feature 分组。
- Request 的存储属性会列为参数;Client 的 `configuration` 会生成配置表。

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
<!doctype html><html lang="zh-Hans"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>AppNetworkClient</title><style>
:root{color-scheme:dark;--bg:#101112;--surface:#171819;--line:#343638;--text:#f7f7f5;--muted:#96999d;--blue:#8cbfff;--blue-bg:#172944}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:16px -apple-system,BlinkMacSystemFont,"SF Pro Display","PingFang SC",sans-serif}.page-width{max-width:1788px;margin:auto;padding-left:clamp(24px,5.4vw,104px);padding-right:clamp(24px,5.4vw,104px)}.hero{background:#0e1822;border-bottom:1px solid var(--line);padding:88px 0 68px}.hero.compact{padding:46px 0}.eyebrow,.card-kicker{margin:0 0 12px;color:var(--muted);font-size:13px;font-weight:750;letter-spacing:.12em}.hero h1{margin:0;font-size:clamp(44px,5vw,78px);line-height:1.05;letter-spacing:-.055em}.lede{margin:24px 0 0;color:var(--muted);font-size:21px;font-weight:600}.back{display:inline-block;margin-bottom:26px;color:var(--blue);font-weight:700;text-decoration:none}main{padding-top:62px;padding-bottom:100px}.search{width:100%;border:3px solid var(--blue);outline:0;border-radius:17px;background:#141516;color:var(--text);padding:20px 22px;font:600 21px inherit;box-shadow:0 0 0 1px #29486f}.search::placeholder{color:#6f7378}.chip-row{display:flex;gap:12px;margin:34px 0 42px}.chip,.count-badge{display:inline-flex;align-items:center;border-radius:999px;background:var(--blue-bg);color:#dceaff;font-weight:750}.chip{padding:10px 15px;border:1px solid #29486f}.count-badge{padding:9px 13px;font-size:14px;white-space:nowrap}.card-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:22px}.server-card,.feature-card{border:1px solid var(--line);border-radius:22px;background:var(--surface);box-shadow:0 15px 42px #0000002b}.server-card{display:block;min-height:220px;padding:31px;color:var(--text);text-decoration:none;transition:transform .15s,border-color .15s}.server-card:hover{transform:translateY(-3px);border-color:var(--blue)}.server-card h2,.feature-heading h2{margin:0;font-size:30px;letter-spacing:-.035em}.server-card code{display:block;margin-top:18px;color:#c8d6e9;overflow-wrap:anywhere}.server-card .count-badge{margin-top:26px}.feature-list{display:grid;gap:28px;margin-top:34px}.feature-card{margin-top:34px;padding:30px}.feature-heading{display:flex;justify-content:space-between;align-items:flex-start;gap:18px;margin-bottom:30px}.table-wrap{overflow:auto}table{width:100%;min-width:820px;border-collapse:collapse;text-align:left}th{color:var(--muted);font-size:14px;letter-spacing:.07em;text-transform:uppercase}th,td{padding:17px;border-bottom:1px solid var(--line);vertical-align:top}tbody tr:last-child td{border-bottom:0}code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.92em;overflow-wrap:anywhere}.method{display:inline-block;border-radius:7px;padding:6px 8px;background:#24364f;color:#cfe4ff;font:750 12px ui-monospace,SFMono-Regular,Menlo}.method-post{background:#263f32;color:#b6e5c6}.method-delete{background:#482c31;color:#ffc5cb}.empty{margin:40px 0;color:var(--muted);text-align:center}@media(max-width:700px){.hero{padding:58px 0 48px}.lede{font-size:17px}.feature-card{padding:21px}.feature-heading{display:block}.feature-heading .count-badge{margin-top:16px}.search{font-size:17px;padding:17px}}
</style></head><body><header class="hero compact"><div class="page-width"><a class="back" href="index.html">← 所有服务器</a><p class="eyebrow">BACKEND SERVER</p><h1>AppNetworkClient</h1><p class="lede"><code>https://rickandmortyapi.com</code> · 2 个端点</p></div></header><main class="page-width"><input class="search" type="search" placeholder="筛选 Feature、端点、参数、Request 或 Source…" aria-label="筛选端点" data-filter><section class="feature-card configuration"><div class="feature-heading"><div><p class="card-kicker">CLIENT CONFIGURATION</p><h2>配置</h2></div></div><div class="table-wrap"><table><thead><tr><th>配置项</th><th>值</th></tr></thead><tbody><tr><td>timeoutInterval</td><td><code>15</code></td></tr><tr><td>retryPolicy</td><td><code>RetryPolicy(maxAttempts: 2)</code></td></tr><tr><td>errorLocalizer</td><td><code>AppNetworkErrorLocalizer()</code></td></tr></tbody></table></div></section><section class="feature-list" data-filter-container><section class="feature-card" data-search="GraphQL FetchCharacterProfileRequest /graphql id: String"><div class="feature-heading"><div><p class="card-kicker">FEATURE</p><h2>GraphQL</h2></div><span class="count-badge">1 个端点</span></div><div class="table-wrap"><table><thead><tr><th>Method</th><th>Endpoint</th><th>Kind</th><th>Parameters</th><th>Request</th><th>Source</th></tr></thead><tbody><tr data-search="FetchCharacterProfileRequest /graphql id: String DemoViewModel.swift"><td><span class="method method-post">POST</span></td><td><code>/graphql</code></td><td>GraphQL</td><td>id: String</td><td><code>FetchCharacterProfileRequest</code></td><td><code>DemoViewModel.swift</code></td></tr></tbody></table></div></section>
</style><style>.graphql-input{display:grid;gap:7px}.graphql-input>span{color:var(--muted);font:750 12px ui-monospace,SFMono-Regular,Menlo}.graphql-input pre{max-width:560px;margin:0;padding:13px 15px;overflow:auto;border:1px solid #29486f;border-radius:10px;background:#101924;white-space:pre}.graphql-input pre code{color:#dceaff;font-size:13px;line-height:1.55}</style></head><body><header class="hero compact"><div class="page-width"><a class="back" href="index.html">← 所有服务器</a><p class="eyebrow">BACKEND SERVER</p><h1>AppNetworkClient</h1><p class="lede"><code>https://rickandmortyapi.com</code> · 2 个端点</p></div></header><main class="page-width"><input class="search" type="search" placeholder="筛选 Feature、端点、参数、Request 或 Source…" aria-label="筛选端点" data-filter><section class="feature-card configuration"><div class="feature-heading"><div><p class="card-kicker">CLIENT CONFIGURATION</p><h2>配置</h2></div></div><div class="table-wrap"><table><thead><tr><th>配置项</th><th>值</th></tr></thead><tbody><tr><td>timeoutInterval</td><td><code>15</code></td></tr><tr><td>retryPolicy</td><td><code>RetryPolicy(maxAttempts: 2)</code></td></tr><tr><td>errorLocalizer</td><td><code>AppNetworkErrorLocalizer()</code></td></tr></tbody></table></div></section><section class="feature-list" data-filter-container><section class="feature-card" data-search="GraphQL FetchCharacterProfileRequest /graphql id: String query: &quot;query Character($id: ID!) { character(id: $id) { name species status } }&quot; variables: [&quot;id&quot;: AnyEncodable(id)]"><div class="feature-heading"><div><p class="card-kicker">FEATURE</p><h2>GraphQL</h2></div><span class="count-badge">1 个端点</span></div><div class="table-wrap"><table><thead><tr><th>Method</th><th>Endpoint</th><th>Kind</th><th>Parameters</th><th>Request</th><th>Source</th></tr></thead><tbody><tr data-search="FetchCharacterProfileRequest /graphql id: String query: &quot;query Character($id: ID!) { character(id: $id) { name species status } }&quot; variables: [&quot;id&quot;: AnyEncodable(id)] DemoViewModel.swift"><td><span class="method method-post">POST</span></td><td><code>/graphql</code></td><td>GraphQL</td><td>id: String<br><div class="graphql-input"><span>query</span><pre><code>query Character($id: ID!) {
character(id: $id) {
name
species
status
}
}</code></pre></div><br><div class="graphql-input"><span>variables</span><pre><code>[
&quot;id&quot;: AnyEncodable(id)
]</code></pre></div></td><td><code>FetchCharacterProfileRequest</code></td><td><code>DemoViewModel.swift</code></td></tr></tbody></table></div></section>
<section class="feature-card" data-search="REST GetCharacterRequest &quot;/api/character/\(id)&quot; id: String"><div class="feature-heading"><div><p class="card-kicker">FEATURE</p><h2>REST</h2></div><span class="count-badge">1 个端点</span></div><div class="table-wrap"><table><thead><tr><th>Method</th><th>Endpoint</th><th>Kind</th><th>Parameters</th><th>Request</th><th>Source</th></tr></thead><tbody><tr data-search="GetCharacterRequest &quot;/api/character/\(id)&quot; id: String DemoViewModel.swift"><td><span class="method method-get">GET</span></td><td><code>&quot;/api/character/\(id)&quot;</code></td><td>REST</td><td>id: String</td><td><code>GetCharacterRequest</code></td><td><code>DemoViewModel.swift</code></td></tr></tbody></table></div></section></section><p class="empty" hidden>没有匹配的端点。</p></main><script>const input=document.querySelector('[data-filter]');if(input){const items=[...document.querySelectorAll('[data-search]')],empty=document.querySelector('.empty[hidden]');input.addEventListener('input',()=>{const q=input.value.trim().toLowerCase();let visible=0;items.forEach(item=>{const show=!q||item.dataset.search.toLowerCase().includes(q);item.hidden=!show;if(show)visible++});if(empty)empty.hidden=visible>0})}</script></body></html>
2 changes: 1 addition & 1 deletion Examples/NetworkingKitDemo/BackendAPIReference/index.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<!doctype html><html lang="zh-Hans"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Backend API Reference</title><style>
:root{color-scheme:dark;--bg:#101112;--surface:#171819;--line:#343638;--text:#f7f7f5;--muted:#96999d;--blue:#8cbfff;--blue-bg:#172944}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:16px -apple-system,BlinkMacSystemFont,"SF Pro Display","PingFang SC",sans-serif}.page-width{max-width:1788px;margin:auto;padding-left:clamp(24px,5.4vw,104px);padding-right:clamp(24px,5.4vw,104px)}.hero{background:#0e1822;border-bottom:1px solid var(--line);padding:88px 0 68px}.hero.compact{padding:46px 0}.eyebrow,.card-kicker{margin:0 0 12px;color:var(--muted);font-size:13px;font-weight:750;letter-spacing:.12em}.hero h1{margin:0;font-size:clamp(44px,5vw,78px);line-height:1.05;letter-spacing:-.055em}.lede{margin:24px 0 0;color:var(--muted);font-size:21px;font-weight:600}.back{display:inline-block;margin-bottom:26px;color:var(--blue);font-weight:700;text-decoration:none}main{padding-top:62px;padding-bottom:100px}.search{width:100%;border:3px solid var(--blue);outline:0;border-radius:17px;background:#141516;color:var(--text);padding:20px 22px;font:600 21px inherit;box-shadow:0 0 0 1px #29486f}.search::placeholder{color:#6f7378}.chip-row{display:flex;gap:12px;margin:34px 0 42px}.chip,.count-badge{display:inline-flex;align-items:center;border-radius:999px;background:var(--blue-bg);color:#dceaff;font-weight:750}.chip{padding:10px 15px;border:1px solid #29486f}.count-badge{padding:9px 13px;font-size:14px;white-space:nowrap}.card-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:22px}.server-card,.feature-card{border:1px solid var(--line);border-radius:22px;background:var(--surface);box-shadow:0 15px 42px #0000002b}.server-card{display:block;min-height:220px;padding:31px;color:var(--text);text-decoration:none;transition:transform .15s,border-color .15s}.server-card:hover{transform:translateY(-3px);border-color:var(--blue)}.server-card h2,.feature-heading h2{margin:0;font-size:30px;letter-spacing:-.035em}.server-card code{display:block;margin-top:18px;color:#c8d6e9;overflow-wrap:anywhere}.server-card .count-badge{margin-top:26px}.feature-list{display:grid;gap:28px;margin-top:34px}.feature-card{margin-top:34px;padding:30px}.feature-heading{display:flex;justify-content:space-between;align-items:flex-start;gap:18px;margin-bottom:30px}.table-wrap{overflow:auto}table{width:100%;min-width:820px;border-collapse:collapse;text-align:left}th{color:var(--muted);font-size:14px;letter-spacing:.07em;text-transform:uppercase}th,td{padding:17px;border-bottom:1px solid var(--line);vertical-align:top}tbody tr:last-child td{border-bottom:0}code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.92em;overflow-wrap:anywhere}.method{display:inline-block;border-radius:7px;padding:6px 8px;background:#24364f;color:#cfe4ff;font:750 12px ui-monospace,SFMono-Regular,Menlo}.method-post{background:#263f32;color:#b6e5c6}.method-delete{background:#482c31;color:#ffc5cb}.empty{margin:40px 0;color:var(--muted);text-align:center}@media(max-width:700px){.hero{padding:58px 0 48px}.lede{font-size:17px}.feature-card{padding:21px}.feature-heading{display:block}.feature-heading .count-badge{margin-top:16px}.search{font-size:17px;padding:17px}}
</style></head><body><header class="hero"><div class="page-width"><p class="eyebrow">NETWORKINGKIT</p><h1>后端 API 文档</h1><p class="lede">由 App 源码自动生成 · 共 1 个后端服务器,2 个端点</p></div></header><main class="page-width"><input class="search" type="search" placeholder="筛选服务器、URL 或端点…" aria-label="筛选服务器" data-filter><div class="chip-row"><span class="chip">1 个服务器</span><span class="chip">2 个端点</span></div><section class="card-grid" data-filter-container><a class="server-card" data-search="AppNetworkClient https://rickandmortyapi.com" href="AppNetworkClient.html"><span class="card-kicker">BACKEND SERVER</span><h2>AppNetworkClient</h2><code>https://rickandmortyapi.com</code><span class="count-badge">2 个端点</span></a></section><p class="empty" hidden>没有匹配的服务器。</p></main><script>const input=document.querySelector('[data-filter]');if(input){const items=[...document.querySelectorAll('[data-search]')],empty=document.querySelector('.empty[hidden]');input.addEventListener('input',()=>{const q=input.value.trim().toLowerCase();let visible=0;items.forEach(item=>{const show=!q||item.dataset.search.toLowerCase().includes(q);item.hidden=!show;if(show)visible++});if(empty)empty.hidden=visible>0})}</script></body></html>
</style><style>.graphql-input{display:grid;gap:7px}.graphql-input>span{color:var(--muted);font:750 12px ui-monospace,SFMono-Regular,Menlo}.graphql-input pre{max-width:560px;margin:0;padding:13px 15px;overflow:auto;border:1px solid #29486f;border-radius:10px;background:#101924;white-space:pre}.graphql-input pre code{color:#dceaff;font-size:13px;line-height:1.55}</style></head><body><header class="hero"><div class="page-width"><p class="eyebrow">NETWORKINGKIT</p><h1>后端 API 文档</h1><p class="lede">由 App 源码自动生成 · 共 1 个后端服务器,2 个端点</p></div></header><main class="page-width"><input class="search" type="search" placeholder="筛选服务器、URL 或端点…" aria-label="筛选服务器" data-filter><div class="chip-row"><span class="chip">1 个服务器</span><span class="chip">2 个端点</span></div><section class="card-grid" data-filter-container><a class="server-card" data-search="AppNetworkClient https://rickandmortyapi.com" href="AppNetworkClient.html"><span class="card-kicker">BACKEND SERVER</span><h2>AppNetworkClient</h2><code>https://rickandmortyapi.com</code><span class="count-badge">2 个端点</span></a></section><p class="empty" hidden>没有匹配的服务器。</p></main><script>const input=document.querySelector('[data-filter]');if(input){const items=[...document.querySelectorAll('[data-search]')],empty=document.querySelector('.empty[hidden]');input.addEventListener('input',()=>{const q=input.value.trim().toLowerCase();let visible=0;items.forEach(item=>{const show=!q||item.dataset.search.toLowerCase().includes(q);item.hidden=!show;if(show)visible++});if(empty)empty.hidden=visible>0})}</script></body></html>
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Or add the package manifest dependency:

```swift
dependencies: [
.package(url: "https://github.com/relaxfinger/NetworkingKit.git", from: "2.4.5")
.package(url: "https://github.com/relaxfinger/NetworkingKit.git", from: "2.4.6")
]
```

Expand Down
2 changes: 1 addition & 1 deletion README.zh-Hans.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ https://github.com/relaxfinger/NetworkingKit.git

```swift
dependencies: [
.package(url: "https://github.com/relaxfinger/NetworkingKit.git", from: "2.4.5")
.package(url: "https://github.com/relaxfinger/NetworkingKit.git", from: "2.4.6")
]
```

Expand Down
Loading
Loading