@laitszkin/apollo-toolkit 3.10.0 → 3.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/analyse-app-logs/scripts/__pycache__/filter_logs_by_time.cpython-312.pyc +0 -0
  3. package/analyse-app-logs/scripts/__pycache__/log_cli_utils.cpython-312.pyc +0 -0
  4. package/analyse-app-logs/scripts/__pycache__/search_logs.cpython-312.pyc +0 -0
  5. package/docs-to-voice/scripts/__pycache__/docs_to_voice.cpython-312.pyc +0 -0
  6. package/generate-spec/SKILL.md +17 -15
  7. package/generate-spec/agents/openai.yaml +1 -1
  8. package/generate-spec/references/TEMPLATE_SPEC.md +103 -84
  9. package/generate-spec/scripts/__pycache__/create-specscpython-312.pyc +0 -0
  10. package/init-project-html/SKILL.md +117 -125
  11. package/init-project-html/agents/openai.yaml +18 -9
  12. package/init-project-html/lib/atlas/assets/architecture.css +161 -0
  13. package/init-project-html/lib/atlas/assets/viewer.client.js +136 -0
  14. package/init-project-html/lib/atlas/cli.js +1023 -0
  15. package/init-project-html/lib/atlas/layout.js +330 -0
  16. package/init-project-html/lib/atlas/render.js +583 -0
  17. package/init-project-html/lib/atlas/schema.js +347 -0
  18. package/init-project-html/lib/atlas/state.js +402 -0
  19. package/init-project-html/references/TEMPLATE_SPEC.md +140 -83
  20. package/init-project-html/sample-demo/resources/project-architecture/assets/architecture.css +160 -1058
  21. package/init-project-html/sample-demo/resources/project-architecture/assets/viewer.client.js +136 -0
  22. package/init-project-html/sample-demo/resources/project-architecture/atlas/atlas.index.yaml +34 -0
  23. package/init-project-html/sample-demo/resources/project-architecture/atlas/features/get-invite-codes.yaml +172 -0
  24. package/init-project-html/sample-demo/resources/project-architecture/atlas/features/invite-code-registration.yaml +160 -0
  25. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/index.html +67 -52
  26. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/invite-code-generator.html +64 -163
  27. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/invite-issuance-service.html +102 -196
  28. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/postgresql.html +82 -163
  29. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/public-api.html +88 -150
  30. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/web-get-invite-ui.html +83 -138
  31. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/index.html +61 -51
  32. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/postgresql.html +84 -159
  33. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/public-api.html +81 -143
  34. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/registration-service.html +98 -188
  35. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/web-register-ui.html +83 -138
  36. package/init-project-html/sample-demo/resources/project-architecture/index.html +256 -335
  37. package/init-project-html/scripts/architecture.js +65 -247
  38. package/katex/scripts/__pycache__/render_katex.cpython-312.pyc +0 -0
  39. package/open-github-issue/scripts/__pycache__/open_github_issue.cpython-312.pyc +0 -0
  40. package/package.json +6 -2
  41. package/read-github-issue/scripts/__pycache__/find_issues.cpython-312.pyc +0 -0
  42. package/read-github-issue/scripts/__pycache__/read_issue.cpython-312.pyc +0 -0
  43. package/resolve-review-comments/scripts/__pycache__/review_threads.cpython-312.pyc +0 -0
  44. package/spec-to-project-html/SKILL.md +74 -67
  45. package/spec-to-project-html/agents/openai.yaml +14 -8
  46. package/spec-to-project-html/references/TEMPLATE_SPEC.md +98 -83
  47. package/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
@@ -1,161 +1,86 @@
1
1
  <!DOCTYPE html>
2
- <html lang="zh-Hant">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>子模組 · postgresql — 邀請碼註冊(reg 視角)</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com" />
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
- <link
10
- href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;600&family=Plus+Jakarta+Sans:wght@400;500;600&display=swap"
11
- rel="stylesheet"
12
- />
13
- <link rel="stylesheet" href="../../assets/architecture.css" />
14
- </head>
15
- <body>
16
- <main class="atlas-page atlas-page--feature">
17
- <nav class="breadcrumb" aria-label="階層導航">
18
- <a href="../../index.html">宏觀架構</a> · <a href="./index.html">邀請碼註冊</a> · <span>postgresql</span>
19
- </nav>
20
-
21
- <header class="atlas-header">
22
- <p class="atlas-kicker">Submodule · postgresql(reg 視角)</p>
23
- <h1 class="atlas-title">postgresql 註冊側操作</h1>
24
- <p class="atlas-meta">
25
- 自身職責:以 SQL 對 <code>invite_codes</code>/<code>users</code> 表執行讀鎖、寫入與條件更新。本子模組同時是<strong>讀者</strong>(查邀請碼)與<strong>寫者</strong>(建 user、核銷邀請)。本頁只列自身 SQL 函式的 I/O。
26
- </p>
27
- </header>
28
-
29
- <section class="sub-io">
30
- <h2>SQL 函式 I/O</h2>
31
- <table>
32
- <thead><tr><th>函式</th><th>簽名/SQL</th><th>副作用</th><th>用途</th></tr></thead>
33
- <tbody>
34
- <tr>
35
- <td><code>GetInviteForUpdate</code></td>
36
- <td class="sub-io__signature">
37
- <strong>in:</strong> <code>tx</code>, <code>code: text</code><br>
38
- <strong>out:</strong> <code>InviteRow</code> | <code>NotFound</code><br>
39
- SQL: <code>SELECT FROM invite_codes WHERE code=$1 FOR UPDATE</code>
40
- </td>
41
- <td><span class="sub-io__side sub-io__side--io">lock</span> 對該列加 row lock 直到 tx 結束</td>
42
- <td>取得邀請碼狀態並阻止他人並行核銷。</td>
43
- </tr>
44
- <tr>
45
- <td><code>InsertUser</code></td>
46
- <td class="sub-io__signature">
47
- <strong>in:</strong> <code>tx</code>, <code>{email, passwordHash, inviteID}</code><br>
48
- <strong>out:</strong> <code>UserID</code> | <code>UniqueViolation</code><br>
49
- SQL: <code>INSERT INTO users (...) RETURNING id</code>
50
- </td>
51
- <td><span class="sub-io__side sub-io__side--write">write</span> 新列(待 commit)</td>
52
- <td>建立使用者列;<code>users.email UNIQUE</code>。</td>
53
- </tr>
54
- <tr>
55
- <td><code>MarkInviteConsumed</code></td>
56
- <td class="sub-io__signature">
57
- <strong>in:</strong> <code>tx</code>, <code>inviteID</code>, <code>userID</code>, <code>now</code><br>
58
- <strong>out:</strong> <code>RowsAffected</code><br>
59
- SQL: <code>UPDATE invite_codes SET consumed_at=$3, consumed_by=$2 WHERE id=$1 AND consumed_at IS NULL</code>
60
- </td>
61
- <td><span class="sub-io__side sub-io__side--write">write</span> 條件更新</td>
62
- <td>標記核銷;<code>RowsAffected=0</code> 表示已被搶先核銷。</td>
63
- </tr>
64
- </tbody>
65
- </table>
66
- </section>
67
-
68
- <section class="sub-vars">
69
- <h2>變數與業務用途</h2>
70
- <p class="sub-vars__intro">本表分兩段:上半為 SQL 函式參數,下半為 <code>invite_codes</code> / <code>users</code> 表上的關鍵列欄位(被本子模組讀寫)。</p>
71
- <table>
72
- <thead><tr><th>變數</th><th>型別</th><th>作用域</th><th>業務用途</th></tr></thead>
73
- <tbody>
74
- <tr>
75
- <td class="sub-vars__name">tx</td>
76
- <td class="sub-vars__type">*sql.Tx</td>
77
- <td><span class="sub-vars__scope sub-vars__scope--tx">tx</span></td>
78
- <td>上層管理的隔離單元;本子模組所有 SQL 都掛在這個 <code>tx</code> 上,未 commit 即視為「無事發生」,保證註冊原子性。</td>
79
- </tr>
80
- <tr>
81
- <td class="sub-vars__name">code</td>
82
- <td class="sub-vars__type">text</td>
83
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
84
- <td><code>GetInviteForUpdate</code> 的查鎖鍵;對應到列上欄位 <code>invite_codes.code</code> 的 UNIQUE 索引,避免並行核銷相同邀請。</td>
85
- </tr>
86
- <tr>
87
- <td class="sub-vars__name">inviteID</td>
88
- <td class="sub-vars__type">UUID</td>
89
- <td><span class="sub-vars__scope sub-vars__scope--tx">tx</span></td>
90
- <td>從 SELECT 取得後跨多個寫入;把「核銷哪一張邀請」與「新 user 屬於哪張邀請」綁定,作為日後審計鏈。</td>
91
- </tr>
92
- <tr>
93
- <td class="sub-vars__name">userID</td>
94
- <td class="sub-vars__type">UUID</td>
95
- <td><span class="sub-vars__scope sub-vars__scope--tx">tx</span></td>
96
- <td><code>InsertUser RETURNING id</code> 的結果;同一 <code>tx</code> 內回填到 <code>consumed_by</code>,完成「誰用了邀請」的紀錄。</td>
97
- </tr>
98
- <tr>
99
- <td class="sub-vars__name">now</td>
100
- <td class="sub-vars__type">timestamp</td>
101
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
102
- <td>核銷時的官方時間;由應用層提供以避免應用與 DB 時鐘漂移影響審計順序。</td>
103
- </tr>
104
- <tr>
105
- <td class="sub-vars__name">RowsAffected</td>
106
- <td class="sub-vars__type">int</td>
107
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
108
- <td><code>MarkInviteConsumed</code> 條件更新結果;<code>0</code> 表示已被搶先核銷,本次必須回滾,否則會發生雙倍消費。</td>
109
- </tr>
110
- <tr>
111
- <td class="sub-vars__name">consumed_at</td>
112
- <td class="sub-vars__type">timestamp NULL</td>
113
- <td><span class="sub-vars__scope sub-vars__scope--persist">persist</span></td>
114
- <td><code>invite_codes</code> 列上欄位;非空代表「這張邀請已被消費」,與 <code>WHERE consumed_at IS NULL</code> 一起防雙重核銷。</td>
115
- </tr>
116
- <tr>
117
- <td class="sub-vars__name">consumed_by</td>
118
- <td class="sub-vars__type">FK → users.id</td>
119
- <td><span class="sub-vars__scope sub-vars__scope--persist">persist</span></td>
120
- <td><code>invite_codes</code> 列上欄位;紀錄是哪個 user 用掉這張邀請,是「邀請碼註冊」這條業務路徑留下的最終痕跡。</td>
121
- </tr>
122
- <tr>
123
- <td class="sub-vars__name">users.email</td>
124
- <td class="sub-vars__type">text UNIQUE</td>
125
- <td><span class="sub-vars__scope sub-vars__scope--persist">persist</span></td>
126
- <td>使用者唯一鍵;UNIQUE 違例代表「相同信箱已註冊」,業務上以 409 回應,呼叫端不應靜默忽略。</td>
127
- </tr>
128
- </tbody>
129
- </table>
130
- </section>
131
-
132
- <section class="sub-dataflow">
133
- <h2>內部資料流(單一交易視角)</h2>
134
- <ol>
135
- <li>呼叫端開 <code>tx</code> → 本子模組以該 <code>tx</code> 執行 SQL;交易控制不在本子模組內。</li>
136
- <li><code>GetInviteForUpdate</code> 取得 <code>InviteRow</code> 並把 <strong>列鎖</strong> 綁在 <code>tx</code> 上。</li>
137
- <li><code>InsertUser</code> 取得 <code>UserID</code>,作為下一步 <code>consumed_by</code> 的 FK 值。</li>
138
- <li><code>MarkInviteConsumed</code> 以 <code>WHERE consumed_at IS NULL</code> 防雙重核銷。</li>
139
- <li>所有寫入皆在 <code>tx</code> 內;commit/rollback 由呼叫端決定,本子模組對外只報告每個 SQL 的結果。</li>
140
- </ol>
141
- </section>
142
-
143
- <section class="sub-errors">
144
- <h2>本子模組會回傳的錯誤</h2>
145
- <table>
146
- <thead><tr><th>錯誤</th><th>觸發 SQL</th><th>含義</th></tr></thead>
147
- <tbody>
148
- <tr><td><code>NotFound</code></td><td><code>GetInviteForUpdate</code></td><td>無該邀請碼列。</td></tr>
149
- <tr><td><code>UniqueViolation</code></td><td><code>InsertUser</code></td><td>email 重複。</td></tr>
150
- <tr><td><code>LockWaitTimeout</code></td><td><code>GetInviteForUpdate</code></td><td>其他並發交易長時間持有鎖。</td></tr>
151
- <tr><td><code>ConnTransient</code></td><td>任意</td><td>網路/restart;交易不會 partial。</td></tr>
152
- </tbody>
153
- </table>
154
- </section>
155
-
156
- <footer class="atlas-meta" style="margin-top: 2rem">
157
- <p><a href="../../index.html">← 宏觀架構</a> · <a href="./index.html">← 功能總覽</a></p>
158
- </footer>
159
- </main>
160
- </body>
2
+ <html lang="en" data-atlas-page="submodule">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Invite-code registration · postgresql</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <link rel="stylesheet" href="../../assets/architecture.css">
8
+ </head>
9
+ <body>
10
+ <header class="submodule-header">
11
+ <nav class="submodule-breadcrumb"><a href="../../index.html">← Atlas</a> · <a href="index.html">← Invite-code registration</a></nav>
12
+ <h1>postgresql <small class="submodule-kind submodule-kind--db">DB</small></h1>
13
+ <p class="submodule-role">Stores `users` rows and applies invite-code state transitions inside the registration tx.</p>
14
+ </header>
15
+ <main class="submodule-main">
16
+ <section class="sub-io" aria-label="Function I/O">
17
+ <h2>Function I/O</h2>
18
+ <table class="sub-table">
19
+ <thead><tr><th scope="col">Name</th><th scope="col">In</th><th scope="col">Out</th><th scope="col">Side</th><th scope="col">Purpose</th></tr></thead>
20
+ <tbody>
21
+ <tr><td>SELECT_invite_for_update</td><td>code</td><td>InviteCodeRow | nil</td><td>lock</td><td>Locks the invite row for the duration of the transaction.</td></tr>
22
+ <tr><td>INSERT_users</td><td>userRow</td><td>userId</td><td>write</td><td>Persists the new account.</td></tr>
23
+ <tr><td>UPDATE_invite_consumed</td><td>code, now</td><td>rows_affected</td><td>write</td><td>Marks the invite as consumed.</td></tr>
24
+ </tbody>
25
+ </table>
26
+ </section>
27
+ <section class="sub-vars" aria-label="Variables">
28
+ <h2>Variables</h2>
29
+ <table class="sub-table">
30
+ <thead><tr><th scope="col">Name</th><th scope="col">Type</th><th scope="col">Scope</th><th scope="col">Purpose</th></tr></thead>
31
+ <tbody>
32
+ <tr><td>users.email</td><td>text</td><td>persist</td><td>Unique account identity.</td></tr>
33
+ <tr><td>invite_codes.consumed_at</td><td>timestamptz</td><td>persist</td><td>Timestamp set the moment the invite row is consumed.</td></tr>
34
+ </tbody>
35
+ </table>
36
+ </section>
37
+ <section class="sub-dataflow" aria-label="Internal data flow">
38
+ <h2>Internal data flow</h2>
39
+ <div class="sub-dataflow__canvas" data-pan-zoom-container>
40
+ <div class="sub-dataflow__toolbar" role="toolbar" aria-label="Diagram controls">
41
+ <button type="button" data-pan-zoom="zoom-in" aria-label="Zoom in">+</button>
42
+ <button type="button" data-pan-zoom="zoom-out" aria-label="Zoom out">−</button>
43
+ <button type="button" data-pan-zoom="fit" aria-label="Reset view">Fit</button>
44
+ </div>
45
+ <div class="sub-dataflow__viewport" data-pan-zoom-viewport>
46
+ <svg class="sub-dataflow__svg" data-atlas-svg="sub-dataflow" viewBox="0 0 628 368" role="img" aria-label="Internal dataflow">
47
+ <defs>
48
+ <marker id="sub-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="9" markerHeight="9" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 Z" /></marker>
49
+ </defs>
50
+ <g class="sub-dataflow__step">
51
+ <circle class="sub-dataflow__badge" cx="42" cy="68" r="18" />
52
+ <text class="sub-dataflow__badge-text" x="42" y="73" text-anchor="middle">1</text>
53
+ <rect class="sub-dataflow__box" x="80" y="32" width="520" height="72" rx="14" ry="14" />
54
+ <text class="sub-dataflow__text" x="340" y="74" text-anchor="middle">Apply row lock on invite_codes.</text>
55
+ </g>
56
+ <line class="sub-dataflow__arrow" x1="340" y1="110" x2="340" y2="140" marker-end="url(#sub-arrow)" />
57
+ <g class="sub-dataflow__step">
58
+ <circle class="sub-dataflow__badge" cx="42" cy="184" r="18" />
59
+ <text class="sub-dataflow__badge-text" x="42" y="189" text-anchor="middle">2</text>
60
+ <rect class="sub-dataflow__box" x="80" y="148" width="520" height="72" rx="14" ry="14" />
61
+ <text class="sub-dataflow__text" x="340" y="190" text-anchor="middle">Validate uniqueness of users.email.</text>
62
+ </g>
63
+ <line class="sub-dataflow__arrow" x1="340" y1="226" x2="340" y2="256" marker-end="url(#sub-arrow)" />
64
+ <g class="sub-dataflow__step">
65
+ <circle class="sub-dataflow__badge" cx="42" cy="300" r="18" />
66
+ <text class="sub-dataflow__badge-text" x="42" y="305" text-anchor="middle">3</text>
67
+ <rect class="sub-dataflow__box" x="80" y="264" width="520" height="72" rx="14" ry="14" />
68
+ <text class="sub-dataflow__text" x="340" y="306" text-anchor="middle">Write users row and update invite_codes.consumed_at.</text>
69
+ </g>
70
+ </svg>
71
+ </div>
72
+ </div>
73
+ </section>
74
+ <section class="sub-errors" aria-label="Errors">
75
+ <h2>Errors</h2>
76
+ <table class="sub-table">
77
+ <thead><tr><th scope="col">Name</th><th scope="col">When</th><th scope="col">Means</th></tr></thead>
78
+ <tbody>
79
+ <tr><td>ErrUniqueEmail</td><td>users.email unique constraint violated.</td><td>Bubble up so service can return ErrEmailTaken.</td></tr>
80
+ </tbody>
81
+ </table>
82
+ </section>
83
+ </main>
84
+ <script src="../../assets/viewer.client.js" defer></script>
85
+ </body>
161
86
  </html>
@@ -1,145 +1,83 @@
1
1
  <!DOCTYPE html>
2
- <html lang="zh-Hant">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>子模組 · public-api — 邀請碼註冊</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com" />
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
- <link
10
- href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;600&family=Plus+Jakarta+Sans:wght@400;500;600&display=swap"
11
- rel="stylesheet"
12
- />
13
- <link rel="stylesheet" href="../../assets/architecture.css" />
14
- </head>
15
- <body>
16
- <main class="atlas-page atlas-page--feature">
17
- <nav class="breadcrumb" aria-label="階層導航">
18
- <a href="../../index.html">宏觀架構</a> · <a href="./index.html">邀請碼註冊</a> · <span>public-api</span>
19
- </nav>
20
-
21
- <header class="atlas-header">
22
- <p class="atlas-kicker">Submodule · public-api</p>
23
- <h1 class="atlas-title">public-api(reg)</h1>
24
- <p class="atlas-meta">自身職責:HTTP 邊界。處理綁定、輸入 schema 檢查、把網域結果映射為狀態碼/錯誤 payload。</p>
25
- </header>
26
-
27
- <section class="sub-io">
28
- <h2>函式 I/O</h2>
29
- <table>
30
- <thead><tr><th>函式</th><th>簽名</th><th>副作用</th><th>用途</th></tr></thead>
31
- <tbody>
32
- <tr>
33
- <td><code>RegisterHandler</code></td>
34
- <td class="sub-io__signature">
35
- <strong>in:</strong> <code>http.Request</code><br>
36
- <strong>out:</strong> <code>http.Response</code>
37
- </td>
38
- <td><span class="sub-io__side sub-io__side--io">io</span> 讀/寫 socket</td>
39
- <td>路由綁定的對外入口。</td>
40
- </tr>
41
- <tr>
42
- <td><code>decodeRegisterInput</code></td>
43
- <td class="sub-io__signature">
44
- <strong>in:</strong> <code>io.Reader</code><br>
45
- <strong>out:</strong> <code>RegisterInput</code> | <code>ErrBadRequest</code>
46
- </td>
47
- <td><span class="sub-io__side sub-io__side--io">io</span> 讀 body</td>
48
- <td>JSON 解碼 + 基本 schema 校驗。</td>
49
- </tr>
50
- <tr>
51
- <td><code>mapDomainError</code></td>
52
- <td class="sub-io__signature">
53
- <strong>in:</strong> <code>error</code><br>
54
- <strong>out:</strong> <code>(httpStatus, ErrorBody)</code>
55
- </td>
56
- <td><span class="sub-io__side sub-io__side--pure">pure</span></td>
57
- <td>網域 error HTTP 對映表。</td>
58
- </tr>
59
- <tr>
60
- <td><code>writeSuccess</code></td>
61
- <td class="sub-io__signature">
62
- <strong>in:</strong> <code>NewUserID</code>, <code>http.ResponseWriter</code><br>
63
- <strong>out:</strong>
64
- </td>
65
- <td><span class="sub-io__side sub-io__side--write">io</span> 201 body</td>
66
- <td>序列化 user id + 必要 header。</td>
67
- </tr>
68
- </tbody>
69
- </table>
70
- </section>
71
-
72
- <section class="sub-vars">
73
- <h2>變數與業務用途</h2>
74
- <p class="sub-vars__intro">HTTP 邊界子模組持有的識別子,幾乎全在單次 handler 呼叫中存活。</p>
75
- <table>
76
- <thead><tr><th>變數</th><th>型別</th><th>作用域</th><th>業務用途</th></tr></thead>
77
- <tbody>
78
- <tr>
79
- <td class="sub-vars__name">http.Request</td>
80
- <td class="sub-vars__type">request</td>
81
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
82
- <td>邊界輸入;只在 handler 期間有效,handler 結束後資源歸還,不可在背景任務繼續使用。</td>
83
- </tr>
84
- <tr>
85
- <td class="sub-vars__name">RegisterInput</td>
86
- <td class="sub-vars__type">{Email, Password, InviteCode}</td>
87
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
88
- <td>HTTP body 解碼後的網域層承載;schema 校驗通過代表「結構面可進入業務」,避免錯誤輸入觸發無謂的 DB 開銷。</td>
89
- </tr>
90
- <tr>
91
- <td class="sub-vars__name">httpStatus</td>
92
- <td class="sub-vars__type">int</td>
93
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
94
- <td><code>mapDomainError</code> 的輸出之一;業務面決定協議語意(409 與 503 不可混用,前者要使用者改、後者要使用者稍後再試)。</td>
95
- </tr>
96
- <tr>
97
- <td class="sub-vars__name">ErrorBody</td>
98
- <td class="sub-vars__type">{code, message}</td>
99
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
100
- <td>結構化錯誤體;<code>code</code> 給 UI 對映本地化文字,<code>message</code> 僅供開發者排錯。</td>
101
- </tr>
102
- <tr>
103
- <td class="sub-vars__name">http.ResponseWriter</td>
104
- <td class="sub-vars__type">writer</td>
105
- <td><span class="sub-vars__scope sub-vars__scope--call">call</span></td>
106
- <td>對外副作用通道;本子模組保證一次回應只寫一次,避免重複寫入造成協議違例與使用者看到的不一致結果。</td>
107
- </tr>
108
- </tbody>
109
- </table>
110
- </section>
111
-
112
- <section class="sub-dataflow">
113
- <h2>內部資料流</h2>
114
- <ol>
115
- <li><code>http.Request.Body</code> → <code>decodeRegisterInput</code> → <code>RegisterInput</code>(失敗 → <code>ErrBadRequest</code>)。</li>
116
- <li><code>RegisterInput</code> 作為傳入網域層的參數;本子模組<strong>不</strong>持有交易、密碼明文也只在這一函式內存在。</li>
117
- <li>從網域層拿到 <code>NewUserID | error</code>:
118
- <ul>
119
- <li><code>NewUserID</code> → <code>writeSuccess</code>。</li>
120
- <li><code>error</code> → <code>mapDomainError</code> → 對應 HTTP 狀態 + JSON。</li>
121
- </ul>
122
- </li>
123
- <li>處理結束 / response 完成寫入後生命週期終止。</li>
124
- </ol>
125
- </section>
126
-
127
- <section class="sub-errors">
128
- <h2>本子模組會回應/轉化的錯誤</h2>
129
- <table>
130
- <thead><tr><th>條件</th><th>HTTP</th></tr></thead>
131
- <tbody>
132
- <tr><td>schema 失敗</td><td><code>400 BAD_REQUEST</code></td></tr>
133
- <tr><td>網域回 <code>ErrInviteInvalid</code></td><td><code>409 INVITE_INVALID</code></td></tr>
134
- <tr><td>網域回 <code>ErrTransient</code></td><td><code>503 SERVICE_UNAVAILABLE</code></td></tr>
135
- <tr><td>panic 或未分類</td><td><code>500 INTERNAL</code></td></tr>
136
- </tbody>
137
- </table>
138
- </section>
139
-
140
- <footer class="atlas-meta" style="margin-top: 2rem">
141
- <p><a href="../../index.html">← 宏觀架構</a> · <a href="./index.html">← 功能總覽</a></p>
142
- </footer>
143
- </main>
144
- </body>
2
+ <html lang="en" data-atlas-page="submodule">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Invite-code registration · public-api</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <link rel="stylesheet" href="../../assets/architecture.css">
8
+ </head>
9
+ <body>
10
+ <header class="submodule-header">
11
+ <nav class="submodule-breadcrumb"><a href="../../index.html">← Atlas</a> · <a href="index.html">← Invite-code registration</a></nav>
12
+ <h1>public-api <small class="submodule-kind submodule-kind--api">API</small></h1>
13
+ <p class="submodule-role">HTTP boundary for `/api/register` POST requests.</p>
14
+ </header>
15
+ <main class="submodule-main">
16
+ <section class="sub-io" aria-label="Function I/O">
17
+ <h2>Function I/O</h2>
18
+ <table class="sub-table">
19
+ <thead><tr><th scope="col">Name</th><th scope="col">In</th><th scope="col">Out</th><th scope="col">Side</th><th scope="col">Purpose</th></tr></thead>
20
+ <tbody>
21
+ <tr><td>postRegister</td><td>HttpRequest</td><td>HttpResponse</td><td>network</td><td>Parses payload and delegates to registration-service.</td></tr>
22
+ </tbody>
23
+ </table>
24
+ </section>
25
+ <section class="sub-vars" aria-label="Variables">
26
+ <h2>Variables</h2>
27
+ <table class="sub-table">
28
+ <thead><tr><th scope="col">Name</th><th scope="col">Type</th><th scope="col">Scope</th><th scope="col">Purpose</th></tr></thead>
29
+ <tbody>
30
+ <tr><td>payload</td><td>RegisterDTO</td><td>call</td><td>Inbound registration intent.</td></tr>
31
+ </tbody>
32
+ </table>
33
+ </section>
34
+ <section class="sub-dataflow" aria-label="Internal data flow">
35
+ <h2>Internal data flow</h2>
36
+ <div class="sub-dataflow__canvas" data-pan-zoom-container>
37
+ <div class="sub-dataflow__toolbar" role="toolbar" aria-label="Diagram controls">
38
+ <button type="button" data-pan-zoom="zoom-in" aria-label="Zoom in">+</button>
39
+ <button type="button" data-pan-zoom="zoom-out" aria-label="Zoom out">−</button>
40
+ <button type="button" data-pan-zoom="fit" aria-label="Reset view">Fit</button>
41
+ </div>
42
+ <div class="sub-dataflow__viewport" data-pan-zoom-viewport>
43
+ <svg class="sub-dataflow__svg" data-atlas-svg="sub-dataflow" viewBox="0 0 628 368" role="img" aria-label="Internal dataflow">
44
+ <defs>
45
+ <marker id="sub-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="9" markerHeight="9" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 Z" /></marker>
46
+ </defs>
47
+ <g class="sub-dataflow__step">
48
+ <circle class="sub-dataflow__badge" cx="42" cy="68" r="18" />
49
+ <text class="sub-dataflow__badge-text" x="42" y="73" text-anchor="middle">1</text>
50
+ <rect class="sub-dataflow__box" x="80" y="32" width="520" height="72" rx="14" ry="14" />
51
+ <text class="sub-dataflow__text" x="340" y="74" text-anchor="middle">Decode JSON body.</text>
52
+ </g>
53
+ <line class="sub-dataflow__arrow" x1="340" y1="110" x2="340" y2="140" marker-end="url(#sub-arrow)" />
54
+ <g class="sub-dataflow__step">
55
+ <circle class="sub-dataflow__badge" cx="42" cy="184" r="18" />
56
+ <text class="sub-dataflow__badge-text" x="42" y="189" text-anchor="middle">2</text>
57
+ <rect class="sub-dataflow__box" x="80" y="148" width="520" height="72" rx="14" ry="14" />
58
+ <text class="sub-dataflow__text" x="340" y="190" text-anchor="middle">Call registration-service.RegisterWithInvite.</text>
59
+ </g>
60
+ <line class="sub-dataflow__arrow" x1="340" y1="226" x2="340" y2="256" marker-end="url(#sub-arrow)" />
61
+ <g class="sub-dataflow__step">
62
+ <circle class="sub-dataflow__badge" cx="42" cy="300" r="18" />
63
+ <text class="sub-dataflow__badge-text" x="42" y="305" text-anchor="middle">3</text>
64
+ <rect class="sub-dataflow__box" x="80" y="264" width="520" height="72" rx="14" ry="14" />
65
+ <text class="sub-dataflow__text" x="340" y="306" text-anchor="middle">Serialize success or 422 / 5xx error.</text>
66
+ </g>
67
+ </svg>
68
+ </div>
69
+ </div>
70
+ </section>
71
+ <section class="sub-errors" aria-label="Errors">
72
+ <h2>Errors</h2>
73
+ <table class="sub-table">
74
+ <thead><tr><th scope="col">Name</th><th scope="col">When</th><th scope="col">Means</th></tr></thead>
75
+ <tbody>
76
+ <tr><td>ErrMalformedPayload</td><td>JSON parse fails.</td><td>400 response with `payload` reason.</td></tr>
77
+ </tbody>
78
+ </table>
79
+ </section>
80
+ </main>
81
+ <script src="../../assets/viewer.client.js" defer></script>
82
+ </body>
145
83
  </html>