lanzou 0.1.0 → 0.1.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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 <your name>
3
+ Copyright (c) 2026 Akinohra
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # lanzou
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/lanzou)](https://www.npmjs.com/package/lanzou)
4
- [![CI](https://github.com/<your-username>/lanzou/actions/workflows/ci.yml/badge.svg)](https://github.com/<your-username>/lanzou/actions/workflows/ci.yml)
4
+ [![CI](https://github.com/Akinohra/lanzou/actions/workflows/ci.yml/badge.svg)](https://github.com/Akinohra/lanzou/actions/workflows/ci.yml)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
6
6
  [![node](https://img.shields.io/node/v/lanzou)](https://www.npmjs.com/package/lanzou)
7
7
 
@@ -18,6 +18,12 @@
18
18
  - **内置限速** — 相邻请求间隔至少 1 秒,降低触发风控的概率
19
19
  - **零异常抛出** — 主 API 永不 throw,失败信息通过返回值传递
20
20
 
21
+ ## 架构
22
+
23
+ ![lanzou 架构图](docs/architecture.svg)
24
+
25
+ > 图表随浏览器/系统自动切换深浅色 · [交互版(主题切换 / 导出 PNG/SVG)](docs/architecture.html)
26
+
21
27
  ## 安装
22
28
 
23
29
  ```sh
@@ -131,7 +137,7 @@ npm run example -- <分享链接> [密码] # 端到端实测
131
137
 
132
138
  仓库已配置 [Trusted Publishing](https://docs.npmjs.com/generating-provenance-statements/)(OIDC):
133
139
 
134
- 1. npmjs.com → Access Tokens → Trusted Publishers → 添加 ` <你的用户名>/lanzou ` + `release.yml`
140
+ 1. npmjs.com → Access Tokens → Trusted Publishers → 添加 `Akinohra/lanzou` + `release.yml`
135
141
  2. 修改 `package.json` 的 `version` 并提交
136
142
  3. 在 GitHub 创建对应 tag 的 Release,`release.yml` 自动执行 `npm publish --provenance`
137
143
 
@@ -0,0 +1,585 @@
1
+ <svg viewBox="0 0 1000 680" width="1000" height="680" xmlns="http://www.w3.org/2000/svg"><style>@font-face { font-family: 'JetBrains Mono'; font-weight: 400; src: local('JetBrains Mono'), local('JetBrainsMono-Regular'); }
2
+ @font-face { font-family: 'JetBrains Mono'; font-weight: 500; src: local('JetBrains Mono'), local('JetBrainsMono-Regular'); }
3
+ @font-face { font-family: 'JetBrains Mono'; font-weight: 600; src: local('JetBrains Mono'), local('JetBrainsMono-Regular'); }
4
+ @font-face { font-family: 'JetBrains Mono'; font-weight: 700; src: local('JetBrains Mono'), local('JetBrainsMono-Regular'); }
5
+ svg { font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace; }
6
+
7
+ /* ==========================================================
8
+ THEME VARIABLES — switch by toggling [data-theme] on &lt;html&gt;
9
+ ========================================================== */
10
+ :root,
11
+ [data-theme="dark"] {
12
+ --bg: #020617;
13
+ --grid: #1e293b;
14
+ --text: #ffffff;
15
+ --text-muted: #94a3b8;
16
+ --text-dim: #475569;
17
+
18
+ --panel: rgba(15, 23, 42, 0.5);
19
+ --panel-border: #1e293b;
20
+ --lane-fill: rgba(15, 23, 42, 0.22);
21
+ --lane-stroke: #334155;
22
+
23
+ --arrow: #64748b;
24
+ --arrow-emphasis: #34d399;
25
+
26
+ /* Opaque mask color used behind semi-transparent component fills
27
+ so the arrows drawn underneath are properly hidden. */
28
+ --mask: #0f172a;
29
+
30
+ --frontend-fill: rgba(8, 51, 68, 0.4);
31
+ --frontend-stroke: #22d3ee;
32
+ --backend-fill: rgba(6, 78, 59, 0.4);
33
+ --backend-stroke: #34d399;
34
+ --database-fill: rgba(76, 29, 149, 0.4);
35
+ --database-stroke: #a78bfa;
36
+ --cloud-fill: rgba(120, 53, 15, 0.3);
37
+ --cloud-stroke: #fbbf24;
38
+ --security-fill: rgba(136, 19, 55, 0.4);
39
+ --security-stroke: #fb7185;
40
+ --messagebus-fill: rgba(251, 146, 60, 0.3);
41
+ --messagebus-stroke:#fb923c;
42
+ --external-fill: rgba(30, 41, 59, 0.5);
43
+ --external-stroke: #94a3b8;
44
+
45
+ --toolbar-bg: rgba(15, 23, 42, 0.8);
46
+ --toolbar-border: #334155;
47
+ --toolbar-text: #e2e8f0;
48
+ --toolbar-hover: rgba(15, 23, 42, 0.95);
49
+ --toolbar-menu-bg: #0f172a;
50
+ }
51
+
52
+ [data-theme="light"] {
53
+ --bg: #f8fafc;
54
+ --grid: #e2e8f0;
55
+ --text: #0f172a;
56
+ --text-muted: #64748b;
57
+ --text-dim: #94a3b8;
58
+
59
+ --panel: #ffffff;
60
+ --panel-border: #e2e8f0;
61
+ --lane-fill: rgba(248, 250, 252, 0.65);
62
+ --lane-stroke: #cbd5e1;
63
+
64
+ --arrow: #94a3b8;
65
+ --arrow-emphasis: #059669;
66
+
67
+ --mask: #ffffff;
68
+
69
+ --frontend-fill: rgba(34, 211, 238, 0.15);
70
+ --frontend-stroke: #0891b2;
71
+ --backend-fill: rgba(52, 211, 153, 0.18);
72
+ --backend-stroke: #059669;
73
+ --database-fill: rgba(167, 139, 250, 0.2);
74
+ --database-stroke: #7c3aed;
75
+ --cloud-fill: rgba(251, 191, 36, 0.18);
76
+ --cloud-stroke: #d97706;
77
+ --security-fill: rgba(251, 113, 133, 0.15);
78
+ --security-stroke: #e11d48;
79
+ --messagebus-fill: rgba(251, 146, 60, 0.15);
80
+ --messagebus-stroke:#ea580c;
81
+ --external-fill: rgba(148, 163, 184, 0.18);
82
+ --external-stroke: #64748b;
83
+
84
+ --toolbar-bg: rgba(255, 255, 255, 0.92);
85
+ --toolbar-border: #cbd5e1;
86
+ --toolbar-text: #334155;
87
+ --toolbar-hover: #ffffff;
88
+ --toolbar-menu-bg: #ffffff;
89
+ }
90
+
91
+ * { margin: 0; padding: 0; box-sizing: border-box; }
92
+
93
+ body {
94
+ font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
95
+ background: var(--bg);
96
+ min-height: 100vh;
97
+ padding: 2rem;
98
+ color: var(--text);
99
+ transition: background 0.2s ease, color 0.2s ease;
100
+ }
101
+
102
+ .container { max-width: 1200px; margin: 0 auto; }
103
+
104
+ .header { margin-bottom: 2rem; }
105
+
106
+ .header-row {
107
+ display: flex;
108
+ align-items: center;
109
+ gap: 1rem;
110
+ margin-bottom: 0.5rem;
111
+ }
112
+
113
+ .pulse-dot {
114
+ width: 12px;
115
+ height: 12px;
116
+ background: var(--frontend-stroke);
117
+ border-radius: 50%;
118
+ animation: pulse 2s infinite;
119
+ }
120
+
121
+ @keyframes pulse {
122
+ 0%, 100% { opacity: 1; }
123
+ 50% { opacity: 0.5; }
124
+ }
125
+
126
+ h1 {
127
+ font-size: 1.5rem;
128
+ font-weight: 700;
129
+ letter-spacing: -0.025em;
130
+ }
131
+
132
+ .subtitle {
133
+ color: var(--text-muted);
134
+ font-size: 0.875rem;
135
+ margin-left: 1.75rem;
136
+ }
137
+
138
+ .diagram-container {
139
+ background: var(--panel);
140
+ border-radius: 1rem;
141
+ border: 1px solid var(--panel-border);
142
+ padding: 1.5rem;
143
+ overflow-x: auto;
144
+ }
145
+
146
+ svg {
147
+ width: 100%;
148
+ min-width: min(900px, 100%);
149
+ display: block;
150
+ }
151
+
152
+ .cards {
153
+ display: grid;
154
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
155
+ gap: 1rem;
156
+ margin-top: 2rem;
157
+ }
158
+
159
+ .card {
160
+ background: var(--panel);
161
+ border-radius: 0.75rem;
162
+ border: 1px solid var(--panel-border);
163
+ padding: 1.25rem;
164
+ }
165
+
166
+ .card-header {
167
+ display: flex;
168
+ align-items: center;
169
+ gap: 0.5rem;
170
+ margin-bottom: 0.75rem;
171
+ }
172
+
173
+ .card-dot {
174
+ width: 8px;
175
+ height: 8px;
176
+ border-radius: 50%;
177
+ }
178
+
179
+ .card-dot.cyan { background: var(--frontend-stroke); }
180
+ .card-dot.emerald { background: var(--backend-stroke); }
181
+ .card-dot.violet { background: var(--database-stroke); }
182
+ .card-dot.amber { background: var(--cloud-stroke); }
183
+ .card-dot.rose { background: var(--security-stroke); }
184
+ .card-dot.orange { background: var(--messagebus-stroke); }
185
+ .card-dot.slate { background: var(--external-stroke); }
186
+
187
+ .card h3 { font-size: 0.875rem; font-weight: 600; color: var(--text); }
188
+
189
+ .card ul {
190
+ list-style: none;
191
+ color: var(--text-muted);
192
+ font-size: 0.75rem;
193
+ }
194
+
195
+ .card li { margin-bottom: 0.375rem; }
196
+
197
+ .footer {
198
+ text-align: center;
199
+ margin-top: 1.5rem;
200
+ color: var(--text-dim);
201
+ font-size: 0.75rem;
202
+ }
203
+
204
+ /* ==========================================================
205
+ Print stylesheet — hide interactive controls, force light,
206
+ drop the grid so the diagram doesn't waste ink, use landscape
207
+ so wide diagrams fit, pack summary cards into 2 columns to
208
+ avoid an orphan card on a trailing page.
209
+ ========================================================== */
210
+ @page { size: landscape; margin: 1.5cm; }
211
+
212
+ @media print {
213
+ :root, [data-theme="dark"], [data-theme="light"] {
214
+ --bg: #ffffff;
215
+ --grid: transparent;
216
+ --panel: #ffffff;
217
+ --panel-border: #e2e8f0;
218
+ --text: #0f172a;
219
+ --text-muted: #475569;
220
+ --text-dim: #94a3b8;
221
+ --mask: #ffffff;
222
+ }
223
+ body { background: #ffffff !important; padding: 0; }
224
+ .container { max-width: none; }
225
+ .toolbar, .archify-toast { display: none !important; }
226
+ .diagram-container, .card { box-shadow: none; border-color: #e2e8f0; }
227
+ .cards { grid-template-columns: 1fr 1fr; }
228
+ /* Ensure elements break sensibly across pages */
229
+ .card { break-inside: avoid; page-break-inside: avoid; }
230
+ .diagram-container { break-inside: avoid; page-break-inside: avoid; }
231
+ h1, .subtitle { color: #0f172a; }
232
+ }
233
+
234
+ /* ==========================================================
235
+ TOOLBAR (theme toggle + export menu)
236
+ ========================================================== */
237
+ .toolbar {
238
+ position: fixed;
239
+ top: 1rem;
240
+ right: 1rem;
241
+ display: flex;
242
+ gap: 0.5rem;
243
+ z-index: 100;
244
+ }
245
+ .toolbar button {
246
+ background: var(--toolbar-bg);
247
+ color: var(--toolbar-text);
248
+ border: 1px solid var(--toolbar-border);
249
+ padding: 0.5rem 0.875rem;
250
+ border-radius: 0.5rem;
251
+ font-family: inherit;
252
+ font-size: 0.75rem;
253
+ font-weight: 500;
254
+ cursor: pointer;
255
+ backdrop-filter: blur(8px);
256
+ transition: background 0.15s, border-color 0.15s;
257
+ display: inline-flex;
258
+ align-items: center;
259
+ gap: 0.375rem;
260
+ }
261
+ .toolbar button:hover {
262
+ background: var(--toolbar-hover);
263
+ border-color: var(--arrow);
264
+ }
265
+ .toolbar button:focus-visible {
266
+ outline: 2px solid var(--frontend-stroke);
267
+ outline-offset: 2px;
268
+ }
269
+ .toolbar .export-wrap { position: relative; }
270
+ .toolbar .export-menu {
271
+ position: absolute;
272
+ top: calc(100% + 0.375rem);
273
+ right: 0;
274
+ background: var(--toolbar-menu-bg);
275
+ border: 1px solid var(--toolbar-border);
276
+ border-radius: 0.5rem;
277
+ padding: 0.375rem;
278
+ min-width: 160px;
279
+ display: none;
280
+ flex-direction: column;
281
+ box-shadow: 0 4px 20px rgba(0,0,0,0.25);
282
+ }
283
+ .toolbar .export-menu.open { display: flex; }
284
+ .toolbar .export-menu button {
285
+ background: transparent;
286
+ border: 0;
287
+ justify-content: space-between;
288
+ width: 100%;
289
+ text-align: left;
290
+ padding: 0.5rem 0.75rem;
291
+ }
292
+ .toolbar .export-menu button:hover {
293
+ background: var(--toolbar-hover);
294
+ }
295
+ .toolbar .export-menu .hint {
296
+ color: var(--text-dim);
297
+ font-size: 0.625rem;
298
+ }
299
+ .toolbar .export-menu hr {
300
+ border: 0;
301
+ border-top: 1px solid var(--toolbar-border);
302
+ margin: 0.25rem 0.375rem;
303
+ opacity: 0.5;
304
+ }
305
+
306
+ /* Toast — brief feedback for actions like "copied" */
307
+ .archify-toast {
308
+ position: fixed;
309
+ top: 1rem;
310
+ left: 50%;
311
+ transform: translateX(-50%) translateY(-8px);
312
+ background: var(--toolbar-menu-bg);
313
+ color: var(--text);
314
+ border: 1px solid var(--toolbar-border);
315
+ padding: 0.5rem 1rem;
316
+ border-radius: 0.5rem;
317
+ font-family: inherit;
318
+ font-size: 0.75rem;
319
+ opacity: 0;
320
+ transition: opacity 0.2s, transform 0.2s;
321
+ z-index: 200;
322
+ pointer-events: none;
323
+ box-shadow: 0 4px 20px rgba(0,0,0,0.25);
324
+ }
325
+ .archify-toast.show {
326
+ opacity: 1;
327
+ transform: translateX(-50%) translateY(0);
328
+ }
329
+
330
+ @media (max-width: 720px) {
331
+ body { padding: 1rem; }
332
+ .toolbar {
333
+ position: static;
334
+ justify-content: flex-end;
335
+ margin-bottom: 1rem;
336
+ }
337
+ .header { margin-bottom: 1.25rem; }
338
+ .header-row {
339
+ gap: 0.75rem;
340
+ align-items: flex-start;
341
+ }
342
+ h1 {
343
+ font-size: 1.25rem;
344
+ line-height: 1.25;
345
+ }
346
+ .subtitle {
347
+ margin-left: 0;
348
+ font-size: 0.8125rem;
349
+ line-height: 1.55;
350
+ }
351
+ .diagram-container {
352
+ padding: 0.75rem;
353
+ border-radius: 0.75rem;
354
+ }
355
+ .cards {
356
+ grid-template-columns: 1fr;
357
+ margin-top: 1rem;
358
+ }
359
+ }
360
+
361
+ /* ==========================================================
362
+ SVG SEMANTIC CLASSES — use these instead of inline fill/stroke
363
+ ========================================================== */
364
+ .c-grid { stroke: var(--grid); fill: none; }
365
+ .c-mask { fill: var(--mask); stroke: none; }
366
+
367
+ .c-frontend { fill: var(--frontend-fill); stroke: var(--frontend-stroke); }
368
+ .c-backend { fill: var(--backend-fill); stroke: var(--backend-stroke); }
369
+ .c-database { fill: var(--database-fill); stroke: var(--database-stroke); }
370
+ .c-cloud { fill: var(--cloud-fill); stroke: var(--cloud-stroke); }
371
+ .c-security { fill: var(--security-fill); stroke: var(--security-stroke); }
372
+ .c-messagebus { fill: var(--messagebus-fill); stroke: var(--messagebus-stroke); }
373
+ .c-external { fill: var(--external-fill); stroke: var(--external-stroke); }
374
+
375
+ /* Text helpers */
376
+ .t-primary { fill: var(--text); }
377
+ .t-muted { fill: var(--text-muted); }
378
+ .t-dim { fill: var(--text-dim); }
379
+ .t-frontend { fill: var(--frontend-stroke); }
380
+ .t-backend { fill: var(--backend-stroke); }
381
+ .t-database { fill: var(--database-stroke); }
382
+ .t-cloud { fill: var(--cloud-stroke); }
383
+ .t-security { fill: var(--security-stroke); }
384
+ .t-messagebus { fill: var(--messagebus-stroke); }
385
+ .t-external { fill: var(--external-stroke); }
386
+
387
+ /* Arrows */
388
+ .a-default { stroke: var(--arrow); fill: none; }
389
+ .a-emphasis { stroke: var(--arrow-emphasis); fill: none; }
390
+ .a-security { stroke: var(--security-stroke); fill: none; stroke-dasharray: 5,5; }
391
+ .a-dashed { stroke: var(--database-stroke); fill: none; stroke-dasharray: 4,4; }
392
+
393
+ /* Arrowhead markers reference these */
394
+ .m-default { fill: var(--arrow); }
395
+ .m-emphasis { fill: var(--arrow-emphasis); }
396
+ .m-security { fill: var(--security-stroke); }
397
+ .m-dashed { fill: var(--database-stroke); }
398
+
399
+ /* Dashed boundary variants */
400
+ .c-security-group { fill: transparent; stroke: var(--security-stroke); stroke-dasharray: 4,4; }
401
+ .c-region { fill: rgba(251, 191, 36, 0.05); stroke: var(--cloud-stroke); stroke-dasharray: 8,4; }
402
+ .c-lane { fill: var(--lane-fill); stroke: var(--lane-stroke); stroke-dasharray: 6,6; }
403
+
404
+ :root, svg { --bg: #020617; --grid: #1e293b; --text: #ffffff; --text-muted: #94a3b8; --text-dim: #475569; --arrow: #64748b; --arrow-emphasis: #34d399; --mask: #0f172a; --frontend-fill: rgba(8, 51, 68, 0.4); --frontend-stroke: #22d3ee; --backend-fill: rgba(6, 78, 59, 0.4); --backend-stroke: #34d399; --database-fill: rgba(76, 29, 149, 0.4); --database-stroke: #a78bfa; --cloud-fill: rgba(120, 53, 15, 0.3); --cloud-stroke: #fbbf24; --security-fill: rgba(136, 19, 55, 0.4); --security-stroke: #fb7185; --messagebus-fill: rgba(251, 146, 60, 0.3); --messagebus-stroke: #fb923c; --external-fill: rgba(30, 41, 59, 0.5); --external-stroke: #94a3b8; }
405
+ @media (prefers-color-scheme: light) { :root, svg { --bg: #f8fafc; --grid: #e2e8f0; --text: #0f172a; --text-muted: #64748b; --text-dim: #94a3b8; --arrow: #94a3b8; --arrow-emphasis: #059669; --mask: #ffffff; --frontend-fill: rgba(34, 211, 238, 0.15); --frontend-stroke: #0891b2; --backend-fill: rgba(52, 211, 153, 0.18); --backend-stroke: #059669; --database-fill: rgba(167, 139, 250, 0.2); --database-stroke: #7c3aed; --cloud-fill: rgba(251, 191, 36, 0.18); --cloud-stroke: #d97706; --security-fill: rgba(251, 113, 133, 0.15); --security-stroke: #e11d48; --messagebus-fill: rgba(251, 146, 60, 0.15); --messagebus-stroke: #ea580c; --external-fill: rgba(148, 163, 184, 0.18); --external-stroke: #64748b; } }
406
+ svg[data-theme="light"] { --bg: #f8fafc; --grid: #e2e8f0; --text: #0f172a; --text-muted: #64748b; --text-dim: #94a3b8; --arrow: #94a3b8; --arrow-emphasis: #059669; --mask: #ffffff; --frontend-fill: rgba(34, 211, 238, 0.15); --frontend-stroke: #0891b2; --backend-fill: rgba(52, 211, 153, 0.18); --backend-stroke: #059669; --database-fill: rgba(167, 139, 250, 0.2); --database-stroke: #7c3aed; --cloud-fill: rgba(251, 191, 36, 0.18); --cloud-stroke: #d97706; --security-fill: rgba(251, 113, 133, 0.15); --security-stroke: #e11d48; --messagebus-fill: rgba(251, 146, 60, 0.15); --messagebus-stroke: #ea580c; --external-fill: rgba(148, 163, 184, 0.18); --external-stroke: #64748b; }
407
+ svg[data-theme="dark"] { --bg: #020617; --grid: #1e293b; --text: #ffffff; --text-muted: #94a3b8; --text-dim: #475569; --arrow: #64748b; --arrow-emphasis: #34d399; --mask: #0f172a; --frontend-fill: rgba(8, 51, 68, 0.4); --frontend-stroke: #22d3ee; --backend-fill: rgba(6, 78, 59, 0.4); --backend-stroke: #34d399; --database-fill: rgba(76, 29, 149, 0.4); --database-stroke: #a78bfa; --cloud-fill: rgba(120, 53, 15, 0.3); --cloud-stroke: #fbbf24; --security-fill: rgba(136, 19, 55, 0.4); --security-stroke: #fb7185; --messagebus-fill: rgba(251, 146, 60, 0.3); --messagebus-stroke: #fb923c; --external-fill: rgba(30, 41, 59, 0.5); --external-stroke: #94a3b8; }
408
+ rect.c-bg-rect { fill: var(--bg); }
409
+ </style><rect width="100%" height="100%" class="c-bg-rect"/>
410
+ <!-- Definitions -->
411
+ <defs>
412
+ <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
413
+ <polygon points="0 0, 10 3.5, 0 7" class="m-default"/>
414
+ </marker>
415
+ <marker id="arrowhead-emphasis" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
416
+ <polygon points="0 0, 10 3.5, 0 7" class="m-emphasis"/>
417
+ </marker>
418
+ <marker id="arrowhead-security" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
419
+ <polygon points="0 0, 10 3.5, 0 7" class="m-security"/>
420
+ </marker>
421
+ <marker id="arrowhead-dashed" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
422
+ <polygon points="0 0, 10 3.5, 0 7" class="m-dashed"/>
423
+ </marker>
424
+ <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
425
+ <path d="M 40 0 L 0 0 0 40" class="c-grid" stroke-width="0.5"/>
426
+ </pattern>
427
+ </defs>
428
+
429
+ <!-- Background Grid -->
430
+ <rect width="100%" height="100%" fill="url(#grid)"/>
431
+
432
+ <!-- ============ ARROWS (drawn before boxes so they sit behind) ============ -->
433
+
434
+ <!-- 调用方 -> API 层 -->
435
+ <path d="M 145 275 L 158 275 L 158 112 L 186 112" class="a-default" stroke-width="1.5" marker-end="url(#arrowhead)"/>
436
+
437
+ <!-- API 层 -> 流程层 -->
438
+ <line x1="265" y1="140" x2="265" y2="197" class="a-default" stroke-width="1.5" marker-end="url(#arrowhead)"/>
439
+ <line x1="425" y1="140" x2="425" y2="197" class="a-default" stroke-width="1.5" marker-end="url(#arrowhead)"/>
440
+ <path d="M 375 140 L 375 163 L 295 163 L 295 197" class="a-default" stroke-width="1.5" marker-end="url(#arrowhead)"/>
441
+
442
+ <!-- 列表 -> 直链(逐文件) -->
443
+ <line x1="400" y1="237" x2="416" y2="237" class="a-default" stroke-width="1.5" marker-end="url(#arrowhead)"/>
444
+
445
+ <!-- 流程层 -> httpSend -->
446
+ <line x1="295" y1="275" x2="295" y2="337" class="a-default" stroke-width="1.5" marker-end="url(#arrowhead)"/>
447
+ <path d="M 555 275 L 555 296 L 410 296 L 410 337" class="a-default" stroke-width="1.5" marker-end="url(#arrowhead)"/>
448
+
449
+ <!-- getAllFileList -> filemoreajax(下方走廊绕行) -->
450
+ <path d="M 380 275 L 380 316 L 730 316 L 730 225 L 756 225" class="a-default" stroke-width="1.5" marker-end="url(#arrowhead)"/>
451
+
452
+ <!-- getFinalLink -> 文件页(上方走廊绕行) -->
453
+ <path d="M 555 200 L 555 172 L 745 172 L 745 355 L 756 355" class="a-default" stroke-width="1.5" marker-end="url(#arrowhead)"/>
454
+
455
+ <!-- httpSend -> 蓝奏云(HTTPS 传输) -->
456
+ <line x1="690" y1="370" x2="756" y2="370" class="a-emphasis" stroke-width="1.5" marker-end="url(#arrowhead-emphasis)"/>
457
+ <text x="725" y="364" class="t-muted" font-size="7" text-anchor="middle">HTTPS</text>
458
+
459
+ <!-- httpSend -> rateLimit / CookieJar -->
460
+ <line x1="265" y1="400" x2="265" y2="442" class="a-default" stroke-width="1.5" marker-end="url(#arrowhead)"/>
461
+ <line x1="420" y1="400" x2="420" y2="442" class="a-dashed" stroke-width="1" marker-end="url(#arrowhead-dashed)"/>
462
+ <text x="414" y="424" class="t-muted" font-size="7" text-anchor="end">Cookie</text>
463
+
464
+ <!-- calcAcwScV2 -> CookieJar(写入挑战答案) -->
465
+ <path d="M 615 445 L 615 425 L 445 425 L 445 442" class="a-security" stroke-width="1.5" marker-end="url(#arrowhead-security)"/>
466
+ <text x="530" y="414" class="t-security" font-size="7" text-anchor="middle">写入 acw_sc__v2</text>
467
+
468
+ <!-- WAF 挑战页 -> calcAcwScV2 -->
469
+ <line x1="758" y1="482" x2="698" y2="482" class="a-security" stroke-width="1.5" marker-end="url(#arrowhead-security)"/>
470
+ <text x="728" y="495" class="t-security" font-size="7" text-anchor="middle">arg1</text>
471
+
472
+ <!-- ============ 调用方 ============ -->
473
+ <rect x="30" y="255" width="115" height="70" rx="6" class="c-mask"/>
474
+ <rect x="30" y="255" width="115" height="70" rx="6" class="c-external" stroke-width="1.5"/>
475
+ <text x="87" y="278" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">调用方代码</text>
476
+ <text x="87" y="296" class="t-muted" font-size="9" text-anchor="middle">Node.js ≥ 18</text>
477
+ <text x="87" y="312" class="t-muted" font-size="8" text-anchor="middle">ESM / CJS</text>
478
+
479
+ <!-- ============ lanzou 库边界 ============ -->
480
+ <rect x="170" y="40" width="540" height="540" rx="12" class="c-region" stroke-width="1"/>
481
+ <text x="182" y="58" class="t-cloud" font-size="10" font-weight="600">lanzou 库 · src/index.ts</text>
482
+
483
+ <!-- API 层 -->
484
+ <rect x="190" y="85" width="150" height="55" rx="6" class="c-mask"/>
485
+ <rect x="190" y="85" width="150" height="55" rx="6" class="c-backend" stroke-width="1.5"/>
486
+ <text x="265" y="107" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">getLanzouFiles</text>
487
+ <text x="265" y="124" class="t-muted" font-size="9" text-anchor="middle">全部文件直链</text>
488
+
489
+ <rect x="355" y="85" width="140" height="55" rx="6" class="c-mask"/>
490
+ <rect x="355" y="85" width="140" height="55" rx="6" class="c-backend" stroke-width="1.5"/>
491
+ <text x="425" y="107" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">getLatestFile</text>
492
+ <text x="425" y="124" class="t-muted" font-size="9" text-anchor="middle">仅最新文件</text>
493
+
494
+ <rect x="510" y="85" width="180" height="55" rx="6" class="c-mask"/>
495
+ <rect x="510" y="85" width="180" height="55" rx="6" class="c-backend" stroke-width="1.5"/>
496
+ <text x="600" y="107" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">checkLanzouUrl</text>
497
+ <text x="600" y="124" class="t-muted" font-size="9" text-anchor="middle">链接校验 · 不发请求</text>
498
+
499
+ <!-- 流程层 -->
500
+ <rect x="190" y="200" width="210" height="75" rx="6" class="c-mask"/>
501
+ <rect x="190" y="200" width="210" height="75" rx="6" class="c-backend" stroke-width="1.5"/>
502
+ <text x="295" y="222" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">getAllFileList</text>
503
+ <text x="295" y="240" class="t-muted" font-size="8" text-anchor="middle">prepareData · extractParam</text>
504
+ <text x="295" y="256" class="t-muted" font-size="8" text-anchor="middle">翻页 · 单页重试 ×4</text>
505
+
506
+ <rect x="420" y="200" width="270" height="75" rx="6" class="c-mask"/>
507
+ <rect x="420" y="200" width="270" height="75" rx="6" class="c-backend" stroke-width="1.5"/>
508
+ <text x="555" y="222" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">getFinalLink</text>
509
+ <text x="555" y="240" class="t-muted" font-size="8" text-anchor="middle">processDownloadPage</text>
510
+ <text x="555" y="256" class="t-muted" font-size="8" text-anchor="middle">downurl → vkjxld/hyggid → 直链</text>
511
+
512
+ <!-- HTTP 层 -->
513
+ <rect x="190" y="340" width="500" height="60" rx="6" class="c-mask"/>
514
+ <rect x="190" y="340" width="500" height="60" rx="6" class="c-backend" stroke-width="1.5"/>
515
+ <text x="440" y="365" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">httpSend · axios</text>
516
+ <text x="440" y="383" class="t-muted" font-size="9" text-anchor="middle">UA 伪装 · Referer · 超时 15s · WAF 挑战自动重试 ≤3</text>
517
+
518
+ <!-- 基础组件层 -->
519
+ <rect x="190" y="445" width="150" height="75" rx="6" class="c-mask"/>
520
+ <rect x="190" y="445" width="150" height="75" rx="6" class="c-backend" stroke-width="1.5"/>
521
+ <text x="265" y="470" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">rateLimit</text>
522
+ <text x="265" y="488" class="t-muted" font-size="8" text-anchor="middle">全局 1s/请求</text>
523
+ <text x="265" y="504" class="t-muted" font-size="8" text-anchor="middle">进程内共享</text>
524
+
525
+ <rect x="355" y="445" width="150" height="75" rx="6" class="c-mask"/>
526
+ <rect x="355" y="445" width="150" height="75" rx="6" class="c-database" stroke-width="1.5"/>
527
+ <text x="430" y="470" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">CookieJar</text>
528
+ <text x="430" y="488" class="t-muted" font-size="8" text-anchor="middle">按域名存储 Cookie</text>
529
+ <text x="430" y="504" class="t-muted" font-size="8" text-anchor="middle">跨请求复用 WAF 状态</text>
530
+
531
+ <rect x="540" y="445" width="150" height="75" rx="6" class="c-mask"/>
532
+ <rect x="540" y="445" width="150" height="75" rx="6" class="c-security" stroke-width="1.5"/>
533
+ <text x="615" y="470" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">calcAcwScV2</text>
534
+ <text x="615" y="488" class="t-muted" font-size="8" text-anchor="middle">本地计算挑战答案</text>
535
+ <text x="615" y="504" class="t-muted" font-size="8" text-anchor="middle">不执行远端 JS</text>
536
+
537
+ <!-- ============ 蓝奏云站点边界 ============ -->
538
+ <rect x="740" y="40" width="230" height="540" rx="12" class="c-region" stroke-width="1"/>
539
+ <text x="752" y="58" class="t-cloud" font-size="10" font-weight="600">蓝奏云站点(外部)</text>
540
+
541
+ <rect x="760" y="90" width="190" height="65" rx="6" class="c-mask"/>
542
+ <rect x="760" y="90" width="190" height="65" rx="6" class="c-cloud" stroke-width="1.5"/>
543
+ <text x="855" y="113" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">分享页 HTML</text>
544
+ <text x="855" y="131" class="t-muted" font-size="8" text-anchor="middle">prepareData 提取参数</text>
545
+ <text x="855" y="147" class="t-muted" font-size="8" text-anchor="middle">fid · t · k · pwd</text>
546
+
547
+ <rect x="760" y="190" width="190" height="70" rx="6" class="c-mask"/>
548
+ <rect x="760" y="190" width="190" height="70" rx="6" class="c-cloud" stroke-width="1.5"/>
549
+ <text x="855" y="213" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">filemoreajax.php</text>
550
+ <text x="855" y="231" class="t-muted" font-size="8" text-anchor="middle">文件列表 JSON · zt</text>
551
+ <text x="855" y="247" class="t-muted" font-size="8" text-anchor="middle">兜底域名池 ×3</text>
552
+
553
+ <rect x="760" y="315" width="190" height="80" rx="6" class="c-mask"/>
554
+ <rect x="760" y="315" width="190" height="80" rx="6" class="c-cloud" stroke-width="1.5"/>
555
+ <text x="855" y="340" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">文件页 → 跳转页</text>
556
+ <text x="855" y="358" class="t-muted" font-size="8" text-anchor="middle">downurl</text>
557
+ <text x="855" y="374" class="t-muted" font-size="8" text-anchor="middle">vkjxld + hyggid</text>
558
+
559
+ <rect x="760" y="435" width="190" height="65" rx="6" class="c-mask"/>
560
+ <rect x="760" y="435" width="190" height="65" rx="6" class="c-security" stroke-width="1.5"/>
561
+ <text x="855" y="460" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">WAF 挑战页</text>
562
+ <text x="855" y="478" class="t-muted" font-size="8" text-anchor="middle">acw_sc__v2 · arg1</text>
563
+
564
+ <!-- ============ LEGEND(边界外) ============ -->
565
+ <rect x="30" y="615" width="16" height="10" rx="2" class="c-external" stroke-width="1"/>
566
+ <text x="52" y="624" class="t-muted" font-size="8">调用方</text>
567
+
568
+ <rect x="105" y="615" width="16" height="10" rx="2" class="c-backend" stroke-width="1"/>
569
+ <text x="127" y="624" class="t-muted" font-size="8">库组件</text>
570
+
571
+ <rect x="180" y="615" width="16" height="10" rx="2" class="c-cloud" stroke-width="1"/>
572
+ <text x="202" y="624" class="t-muted" font-size="8">蓝奏云站点</text>
573
+
574
+ <rect x="275" y="615" width="16" height="10" rx="2" class="c-security" stroke-width="1"/>
575
+ <text x="297" y="624" class="t-muted" font-size="8">WAF / 安全</text>
576
+
577
+ <line x1="365" y1="620" x2="385" y2="620" class="a-dashed" stroke-width="1" marker-end="url(#arrowhead-dashed)"/>
578
+ <text x="391" y="624" class="t-muted" font-size="8">Cookie 读写</text>
579
+
580
+ <line x1="470" y1="620" x2="490" y2="620" class="a-security" stroke-width="1" marker-end="url(#arrowhead-security)"/>
581
+ <text x="496" y="624" class="t-muted" font-size="8">挑战应答</text>
582
+
583
+ <line x1="570" y1="620" x2="590" y2="620" class="a-emphasis" stroke-width="1" marker-end="url(#arrowhead-emphasis)"/>
584
+ <text x="596" y="624" class="t-muted" font-size="8">HTTPS 请求</text>
585
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lanzou",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "蓝奏云分享链接解析库:文件列表 + 直链提取,自动处理 acw_sc__v2 WAF 挑战、密码分享、翻页与域名回退",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -21,6 +21,7 @@
21
21
  },
22
22
  "files": [
23
23
  "dist",
24
+ "docs/architecture.svg",
24
25
  "README.md",
25
26
  "LICENSE"
26
27
  ],
@@ -45,8 +46,16 @@
45
46
  "crawler",
46
47
  "acw_sc__v2"
47
48
  ],
48
- "author": "",
49
+ "author": "Akinohra",
49
50
  "license": "MIT",
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "git+https://github.com/Akinohra/lanzou.git"
54
+ },
55
+ "bugs": {
56
+ "url": "https://github.com/Akinohra/lanzou/issues"
57
+ },
58
+ "homepage": "https://github.com/Akinohra/lanzou#readme",
50
59
  "dependencies": {
51
60
  "axios": "^1.7.0"
52
61
  },