@anthusai/papyrus 1.0.0-next.2 → 1.0.0-next.3

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 (42) hide show
  1. package/amplify/data/resource.js +26 -0
  2. package/amplify/data/resource.ts +30 -0
  3. package/amplify/data/schema.js +26 -0
  4. package/amplify/data/schema.ts +30 -0
  5. package/amplify/functions/content-actions/handler.py +107 -0
  6. package/amplify/functions/content-actions/requirements.txt +4 -0
  7. package/amplify/functions/content-actions/resource.js +40 -0
  8. package/amplify/functions/content-actions/resource.ts +54 -0
  9. package/amplify/functions/shared/python-bundle.js +17 -2
  10. package/amplify/functions/shared/python-bundle.ts +23 -2
  11. package/amplify/site-backend.js +10 -0
  12. package/amplify/site-backend.ts +15 -0
  13. package/app/[year]/[month]/[day]/[articleSlug]/page.tsx +2 -2
  14. package/app/globals.css +1 -4
  15. package/app/layout.tsx +4 -12
  16. package/components/news-desk-page.tsx +1 -4
  17. package/lib/content-actions-client.ts +98 -0
  18. package/lib/empty-theme.css +1 -0
  19. package/lib/layout-scenarios.ts +8 -8
  20. package/lib/site-brand-tenant.ts +3 -22
  21. package/lib/site-brand.ts +44 -42
  22. package/middleware.ts +10 -35
  23. package/package.json +2 -1
  24. package/renderers/pretext/article-page.tsx +1 -6
  25. package/renderers/pretext/presentation-shell.tsx +2 -9
  26. package/routes.manifest.json +1 -1
  27. package/src/with-papyrus.mjs +15 -2
  28. package/publications/anth_us/brand.ts +0 -68
  29. package/publications/anth_us/theme.css +0 -66
  30. package/publications/pilobol_us/brand.ts +0 -37
  31. package/publications/pilobol_us/theme.css +0 -140
  32. package/publications/threat_intelligence/blog-defense/graph.ts +0 -636
  33. package/publications/threat_intelligence/blog-defense/layout.ts +0 -934
  34. package/publications/threat_intelligence/blog-defense/page-background.tsx +0 -590
  35. package/publications/threat_intelligence/brand.ts +0 -40
  36. package/publications/threat_intelligence/pictograms/art.tsx +0 -608
  37. package/publications/threat_intelligence/pictograms/figure.tsx +0 -82
  38. package/publications/threat_intelligence/pictograms/registry.ts +0 -30
  39. package/publications/threat_intelligence/pictograms/system.tsx +0 -305
  40. package/publications/threat_intelligence/seed/seed-edition-content.json +0 -629
  41. package/publications/threat_intelligence/theme.css +0 -1440
  42. /package/{publications/papyrus/theme.css → app/papyrus-theme.css} +0 -0
@@ -1,636 +0,0 @@
1
- export const BLOG_DEFENSE_VIEWBOX_WIDTH = 1000;
2
- export const BLOG_DEFENSE_VIEWBOX_HEIGHT = 700;
3
-
4
- export type BlogDefenseNodeRole = "core" | "attack" | "perimeter" | "interior";
5
- export type BlogDefenseZone = "core" | "left_arm" | "right_arm" | "right_corridor";
6
-
7
- export type BlogDefenseNode = {
8
- id: string;
9
- x: number;
10
- y: number;
11
- radius: number;
12
- role: BlogDefenseNodeRole;
13
- zone: BlogDefenseZone;
14
- protectedIngress?: boolean;
15
- };
16
-
17
- export type BlogDefenseEdge = {
18
- id: string;
19
- from: string;
20
- to: string;
21
- };
22
-
23
- export type VisibleAttackPathInput = {
24
- nodes: BlogDefenseNode[];
25
- edges: BlogDefenseEdge[];
26
- coreId?: string;
27
- stochastic?: boolean;
28
- };
29
-
30
- export type VisibleAttackPath = {
31
- nodeIds: string[];
32
- edgeIds: string[];
33
- };
34
-
35
- export const BLOG_DEFENSE_CORE_NODE_ID = "core";
36
-
37
- export const BLOG_DEFENSE_NODES: BlogDefenseNode[] = [
38
- { id: "core", x: 770, y: 175, radius: 12, role: "core", zone: "core" },
39
- { id: "halo_n", x: 770, y: 32, radius: 7, role: "perimeter", zone: "core" },
40
- { id: "halo_ne", x: 913, y: 58, radius: 7, role: "perimeter", zone: "core" },
41
- { id: "halo_e", x: 913, y: 175, radius: 7, role: "perimeter", zone: "core" },
42
- { id: "halo_se", x: 878, y: 284, radius: 7, role: "perimeter", zone: "core" },
43
- { id: "halo_s", x: 770, y: 318, radius: 7, role: "perimeter", zone: "core" },
44
- { id: "halo_sw", x: 662, y: 284, radius: 7, role: "perimeter", zone: "core" },
45
- { id: "halo_w", x: 627, y: 175, radius: 7, role: "perimeter", zone: "core" },
46
- { id: "halo_nw", x: 662, y: 66, radius: 7, role: "perimeter", zone: "core" },
47
- { id: "outer_n", x: 770, y: 56, radius: 7, role: "perimeter", zone: "core" },
48
- { id: "outer_ne", x: 854, y: 91, radius: 7, role: "perimeter", zone: "core" },
49
- { id: "outer_e", x: 889, y: 175, radius: 7, role: "perimeter", zone: "core" },
50
- { id: "outer_se", x: 854, y: 259, radius: 7, role: "perimeter", zone: "core" },
51
- { id: "outer_s", x: 770, y: 294, radius: 7, role: "perimeter", zone: "core" },
52
- { id: "outer_sw", x: 686, y: 259, radius: 7, role: "perimeter", zone: "core" },
53
- { id: "outer_w", x: 651, y: 175, radius: 7, role: "perimeter", zone: "core" },
54
- { id: "outer_nw", x: 686, y: 91, radius: 7, role: "perimeter", zone: "core" },
55
- { id: "ring_n", x: 770, y: 84, radius: 7, role: "perimeter", zone: "core" },
56
- { id: "ring_ne", x: 833, y: 111, radius: 7, role: "perimeter", zone: "core" },
57
- { id: "ring_e", x: 858, y: 175, radius: 7, role: "perimeter", zone: "core" },
58
- { id: "ring_se", x: 833, y: 240, radius: 7, role: "perimeter", zone: "core" },
59
- { id: "ring_s", x: 770, y: 266, radius: 7, role: "perimeter", zone: "core" },
60
- { id: "ring_sw", x: 706, y: 240, radius: 7, role: "perimeter", zone: "core" },
61
- { id: "ring_w", x: 682, y: 175, radius: 7, role: "perimeter", zone: "core" },
62
- { id: "ring_nw", x: 706, y: 111, radius: 7, role: "perimeter", zone: "core" },
63
-
64
- { id: "mid_n", x: 770, y: 111, radius: 5, role: "interior", zone: "core" },
65
- { id: "mid_ne", x: 814, y: 130, radius: 5, role: "interior", zone: "core" },
66
- { id: "mid_e", x: 832, y: 175, radius: 5, role: "interior", zone: "core" },
67
- { id: "mid_se", x: 814, y: 220, radius: 5, role: "interior", zone: "core" },
68
- { id: "mid_s", x: 770, y: 239, radius: 5, role: "interior", zone: "core" },
69
- { id: "mid_sw", x: 726, y: 220, radius: 5, role: "interior", zone: "core" },
70
- { id: "mid_w", x: 708, y: 175, radius: 5, role: "interior", zone: "core" },
71
- { id: "mid_nw", x: 726, y: 130, radius: 5, role: "interior", zone: "core" },
72
-
73
- { id: "left_attach_top", x: 640, y: 42, radius: 6, role: "interior", zone: "left_arm" },
74
- { id: "left_attach_bottom", x: 626, y: 114, radius: 6, role: "interior", zone: "left_arm" },
75
- { id: "left_bridge_top", x: 678, y: 32, radius: 6, role: "perimeter", zone: "left_arm" },
76
- { id: "left_a0", x: 640, y: 32, radius: 6, role: "perimeter", zone: "left_arm" },
77
- { id: "left_a1", x: 600, y: 32, radius: 6, role: "perimeter", zone: "left_arm" },
78
- { id: "left_a2", x: 556, y: 32, radius: 6, role: "perimeter", zone: "left_arm" },
79
- { id: "left_a3", x: 514, y: 32, radius: 6, role: "perimeter", zone: "left_arm" },
80
- { id: "left_b0", x: 628, y: 72, radius: 6, role: "interior", zone: "left_arm" },
81
- { id: "left_b3", x: 534, y: 44, radius: 6, role: "interior", zone: "left_arm" },
82
- { id: "left_c0", x: 632, y: 120, radius: 6, role: "interior", zone: "left_arm" },
83
- { id: "left_c3", x: 548, y: 60, radius: 6, role: "interior", zone: "left_arm" },
84
- { id: "left_tip", x: 492, y: 32, radius: 5, role: "perimeter", zone: "left_arm" },
85
- { id: "left_tail_1", x: 450, y: 32, radius: 5, role: "perimeter", zone: "left_arm" },
86
- { id: "left_tail_2", x: 408, y: 32, radius: 5, role: "perimeter", zone: "left_arm" },
87
- { id: "left_tail_3", x: 366, y: 32, radius: 5, role: "perimeter", zone: "left_arm" },
88
- { id: "left_tail_4", x: 326, y: 32, radius: 5, role: "perimeter", zone: "left_arm" },
89
-
90
- { id: "right_attach_bottom", x: 820, y: 308, radius: 6, role: "interior", zone: "right_arm" },
91
- { id: "right_bridge_top", x: 905, y: 74, radius: 6, role: "perimeter", zone: "right_arm" },
92
- { id: "right_bridge_peak", x: 913, y: 42, radius: 6, role: "perimeter", zone: "right_arm" },
93
- { id: "right_a0", x: 906, y: 240, radius: 7, role: "perimeter", zone: "right_arm" },
94
- { id: "right_a1", x: 910, y: 316, radius: 7, role: "perimeter", zone: "right_arm" },
95
- { id: "right_a2", x: 912, y: 390, radius: 7, role: "perimeter", zone: "right_arm" },
96
- { id: "right_a3", x: 912, y: 470, radius: 7, role: "perimeter", zone: "right_arm" },
97
- { id: "right_a4", x: 912, y: 552, radius: 7, role: "perimeter", zone: "right_arm" },
98
- { id: "right_a5", x: 912, y: 632, radius: 7, role: "perimeter", zone: "right_arm" },
99
- { id: "right_b0", x: 834, y: 286, radius: 6, role: "interior", zone: "right_arm" },
100
- { id: "right_b1", x: 848, y: 350, radius: 6, role: "interior", zone: "right_arm" },
101
- { id: "right_b2", x: 872, y: 424, radius: 6, role: "interior", zone: "right_arm" },
102
- { id: "right_b3", x: 907, y: 502, radius: 6, role: "interior", zone: "right_arm" },
103
- { id: "right_b4", x: 910, y: 582, radius: 6, role: "interior", zone: "right_arm" },
104
- { id: "right_b5", x: 911, y: 654, radius: 6, role: "interior", zone: "right_arm" },
105
- { id: "right_c0", x: 790, y: 328, radius: 6, role: "interior", zone: "right_arm" },
106
- { id: "right_c1", x: 812, y: 366, radius: 6, role: "interior", zone: "right_arm" },
107
- { id: "right_c2", x: 836, y: 444, radius: 6, role: "interior", zone: "right_arm" },
108
- { id: "right_c3", x: 900, y: 528, radius: 6, role: "interior", zone: "right_arm" },
109
- { id: "right_c4", x: 908, y: 608, radius: 6, role: "interior", zone: "right_arm" },
110
- { id: "right_c5", x: 910, y: 676, radius: 6, role: "interior", zone: "right_arm" },
111
-
112
- { id: "corridor_0", x: 970, y: 260, radius: 7, role: "attack", zone: "right_corridor", protectedIngress: true },
113
- { id: "corridor_1", x: 972, y: 328, radius: 7, role: "attack", zone: "right_corridor", protectedIngress: true },
114
- { id: "corridor_2", x: 974, y: 398, radius: 7, role: "attack", zone: "right_corridor", protectedIngress: true },
115
- { id: "corridor_3", x: 976, y: 472, radius: 8, role: "attack", zone: "right_corridor", protectedIngress: true },
116
- { id: "corridor_4", x: 978, y: 548, radius: 8, role: "attack", zone: "right_corridor", protectedIngress: true },
117
- { id: "corridor_5", x: 980, y: 620, radius: 9, role: "attack", zone: "right_corridor", protectedIngress: true },
118
- { id: "corridor_6", x: 982, y: 684, radius: 10, role: "attack", zone: "right_corridor", protectedIngress: true },
119
- ];
120
-
121
- export const BLOG_DEFENSE_EDGES: BlogDefenseEdge[] = [
122
- { id: "halo_1", from: "halo_n", to: "halo_ne" },
123
- { id: "halo_2", from: "halo_ne", to: "halo_e" },
124
- { id: "halo_3", from: "halo_e", to: "halo_se" },
125
- { id: "halo_4", from: "halo_se", to: "halo_s" },
126
- { id: "halo_5", from: "halo_s", to: "halo_sw" },
127
- { id: "halo_6", from: "halo_sw", to: "halo_w" },
128
- { id: "halo_7", from: "halo_w", to: "halo_nw" },
129
- { id: "halo_8", from: "halo_nw", to: "halo_n" },
130
- { id: "halo_to_outer_n", from: "halo_n", to: "outer_n" },
131
- { id: "halo_to_outer_ne", from: "halo_ne", to: "outer_ne" },
132
- { id: "halo_to_outer_e", from: "halo_e", to: "outer_e" },
133
- { id: "halo_to_outer_se", from: "halo_se", to: "outer_se" },
134
- { id: "halo_to_outer_s", from: "halo_s", to: "outer_s" },
135
- { id: "halo_to_outer_sw", from: "halo_sw", to: "outer_sw" },
136
- { id: "halo_to_outer_w", from: "halo_w", to: "outer_w" },
137
- { id: "halo_to_outer_nw", from: "halo_nw", to: "outer_nw" },
138
- { id: "outer_1", from: "outer_n", to: "outer_ne" },
139
- { id: "outer_2", from: "outer_ne", to: "outer_e" },
140
- { id: "outer_3", from: "outer_e", to: "outer_se" },
141
- { id: "outer_4", from: "outer_se", to: "outer_s" },
142
- { id: "outer_5", from: "outer_s", to: "outer_sw" },
143
- { id: "outer_6", from: "outer_sw", to: "outer_w" },
144
- { id: "outer_7", from: "outer_w", to: "outer_nw" },
145
- { id: "outer_8", from: "outer_nw", to: "outer_n" },
146
- { id: "outer_to_ring_n", from: "outer_n", to: "ring_n" },
147
- { id: "outer_to_ring_ne", from: "outer_ne", to: "ring_ne" },
148
- { id: "outer_to_ring_e", from: "outer_e", to: "ring_e" },
149
- { id: "outer_to_ring_se", from: "outer_se", to: "ring_se" },
150
- { id: "outer_to_ring_s", from: "outer_s", to: "ring_s" },
151
- { id: "outer_to_ring_sw", from: "outer_sw", to: "ring_sw" },
152
- { id: "outer_to_ring_w", from: "outer_w", to: "ring_w" },
153
- { id: "outer_to_ring_nw", from: "outer_nw", to: "ring_nw" },
154
- { id: "ring_1", from: "ring_n", to: "ring_ne" },
155
- { id: "ring_2", from: "ring_ne", to: "ring_e" },
156
- { id: "ring_3", from: "ring_e", to: "ring_se" },
157
- { id: "ring_4", from: "ring_se", to: "ring_s" },
158
- { id: "ring_5", from: "ring_s", to: "ring_sw" },
159
- { id: "ring_6", from: "ring_sw", to: "ring_w" },
160
- { id: "ring_7", from: "ring_w", to: "ring_nw" },
161
- { id: "ring_8", from: "ring_nw", to: "ring_n" },
162
- { id: "spoke_n", from: "ring_n", to: "core" },
163
- { id: "spoke_e", from: "ring_e", to: "core" },
164
- { id: "spoke_s", from: "ring_s", to: "core" },
165
- { id: "spoke_w", from: "ring_w", to: "core" },
166
- { id: "mid_ring_1", from: "mid_n", to: "mid_ne" },
167
- { id: "mid_ring_2", from: "mid_ne", to: "mid_e" },
168
- { id: "mid_ring_3", from: "mid_e", to: "mid_se" },
169
- { id: "mid_ring_4", from: "mid_se", to: "mid_s" },
170
- { id: "mid_ring_5", from: "mid_s", to: "mid_sw" },
171
- { id: "mid_ring_6", from: "mid_sw", to: "mid_w" },
172
- { id: "mid_ring_7", from: "mid_w", to: "mid_nw" },
173
- { id: "mid_ring_8", from: "mid_nw", to: "mid_n" },
174
- { id: "mid_spoke_n", from: "mid_n", to: "core" },
175
- { id: "mid_spoke_e", from: "mid_e", to: "core" },
176
- { id: "mid_spoke_s", from: "mid_s", to: "core" },
177
- { id: "mid_spoke_w", from: "mid_w", to: "core" },
178
- { id: "mid_to_outer_n", from: "mid_n", to: "ring_n" },
179
- { id: "mid_to_outer_e", from: "mid_e", to: "ring_e" },
180
- { id: "mid_to_outer_s", from: "mid_s", to: "ring_s" },
181
- { id: "mid_to_outer_w", from: "mid_w", to: "ring_w" },
182
- { id: "left_a_chain_1", from: "left_a0", to: "left_a1" },
183
- { id: "left_a_chain_0", from: "left_bridge_top", to: "left_a0" },
184
- { id: "left_a_chain_2", from: "left_a1", to: "left_a2" },
185
- { id: "left_a_chain_3", from: "left_a2", to: "left_a3" },
186
- { id: "left_a_chain_4", from: "left_a3", to: "left_tip" },
187
- { id: "left_b_chain_1", from: "left_b0", to: "left_b3" },
188
- { id: "left_b_chain_4", from: "left_b3", to: "left_tip" },
189
- { id: "left_c_chain_1", from: "left_c0", to: "left_c3" },
190
- { id: "left_c_chain_4", from: "left_c3", to: "left_tip" },
191
- { id: "left_tail_chain_1", from: "left_tip", to: "left_tail_1" },
192
- { id: "left_tail_chain_2", from: "left_tail_1", to: "left_tail_2" },
193
- { id: "left_tail_chain_3", from: "left_tail_2", to: "left_tail_3" },
194
- { id: "left_tail_chain_4", from: "left_tail_3", to: "left_tail_4" },
195
- { id: "left_tri_1", from: "left_a0", to: "left_b0" },
196
- { id: "left_tri_2", from: "left_b0", to: "left_c0" },
197
- { id: "left_tri_7", from: "left_a3", to: "left_b3" },
198
- { id: "left_tri_8", from: "left_b3", to: "left_c3" },
199
- { id: "left_tri_9", from: "left_a3", to: "left_tip" },
200
- { id: "left_tri_10", from: "left_c3", to: "left_tip" },
201
- { id: "left_cross_1", from: "left_b0", to: "left_a1" },
202
- { id: "left_cross_7", from: "left_b3", to: "left_tip" },
203
- { id: "left_cross_8", from: "left_c3", to: "left_tip" },
204
- { id: "left_attach_1", from: "left_attach_top", to: "left_a0" },
205
- { id: "left_attach_1b", from: "left_attach_top", to: "left_bridge_top" },
206
- { id: "left_attach_2", from: "left_attach_top", to: "left_b0" },
207
- { id: "left_attach_3", from: "left_attach_bottom", to: "left_b0" },
208
- { id: "left_attach_4", from: "left_attach_bottom", to: "left_c0" },
209
- { id: "left_to_outer_1", from: "left_attach_top", to: "halo_nw" },
210
- { id: "left_to_outer_2", from: "left_attach_bottom", to: "halo_nw" },
211
- { id: "left_to_outer_3", from: "left_c0", to: "halo_w" },
212
- { id: "left_to_outer_4", from: "left_c0", to: "halo_nw" },
213
- { id: "left_to_outer_5", from: "left_a0", to: "halo_nw" },
214
- { id: "left_to_outer_11", from: "left_bridge_top", to: "halo_nw" },
215
- { id: "left_to_outer_12", from: "left_bridge_top", to: "halo_n" },
216
- { id: "left_to_outer_6", from: "left_b0", to: "halo_nw" },
217
- { id: "left_to_outer_10", from: "left_c3", to: "halo_nw" },
218
-
219
- { id: "right_a_chain_1", from: "right_a0", to: "right_a1" },
220
- { id: "right_a_chain_2", from: "right_a1", to: "right_a2" },
221
- { id: "right_a_chain_3", from: "right_a2", to: "right_a3" },
222
- { id: "right_a_chain_4", from: "right_a3", to: "right_a4" },
223
- { id: "right_a_chain_5", from: "right_a4", to: "right_a5" },
224
- { id: "right_b_chain_1", from: "right_b0", to: "right_b1" },
225
- { id: "right_b_chain_2", from: "right_b1", to: "right_b2" },
226
- { id: "right_b_chain_3", from: "right_b2", to: "right_b3" },
227
- { id: "right_b_chain_4", from: "right_b3", to: "right_b4" },
228
- { id: "right_b_chain_5", from: "right_b4", to: "right_b5" },
229
- { id: "right_c_chain_1", from: "right_c0", to: "right_c1" },
230
- { id: "right_c_chain_2", from: "right_c1", to: "right_c2" },
231
- { id: "right_c_chain_3", from: "right_c2", to: "right_c3" },
232
- { id: "right_c_chain_4", from: "right_c3", to: "right_c4" },
233
- { id: "right_c_chain_5", from: "right_c4", to: "right_c5" },
234
- { id: "right_tri_1", from: "right_a0", to: "right_b0" },
235
- { id: "right_tri_2", from: "right_a1", to: "right_b1" },
236
- { id: "right_tri_3", from: "right_a2", to: "right_b2" },
237
- { id: "right_tri_4", from: "right_a3", to: "right_b3" },
238
- { id: "right_tri_5", from: "right_a4", to: "right_b4" },
239
- { id: "right_tri_6", from: "right_a5", to: "right_b5" },
240
- { id: "right_thick_1", from: "right_b0", to: "right_c0" },
241
- { id: "right_thick_2", from: "right_b1", to: "right_c1" },
242
- { id: "right_thick_3", from: "right_b2", to: "right_c2" },
243
- { id: "right_thick_4", from: "right_b3", to: "right_c3" },
244
- { id: "right_thick_5", from: "right_b4", to: "right_c4" },
245
- { id: "right_thick_6", from: "right_b5", to: "right_c5" },
246
- { id: "right_cross_1", from: "right_b0", to: "right_a1" },
247
- { id: "right_cross_2", from: "right_b1", to: "right_a2" },
248
- { id: "right_cross_3", from: "right_b2", to: "right_a3" },
249
- { id: "right_cross_4", from: "right_b3", to: "right_a4" },
250
- { id: "right_cross_5", from: "right_b4", to: "right_a5" },
251
- { id: "right_cross_7", from: "right_c1", to: "right_b2" },
252
- { id: "right_cross_8", from: "right_c2", to: "right_b3" },
253
- { id: "right_cross_9", from: "right_c3", to: "right_b4" },
254
- { id: "right_cross_10", from: "right_c4", to: "right_b5" },
255
- { id: "right_attach_3", from: "right_attach_bottom", to: "right_b0" },
256
- { id: "right_attach_4", from: "right_attach_bottom", to: "right_c0" },
257
- { id: "right_to_outer_12", from: "right_bridge_top", to: "halo_ne" },
258
- { id: "right_to_outer_13", from: "right_bridge_top", to: "halo_e" },
259
- { id: "right_to_outer_14", from: "right_bridge_peak", to: "halo_ne" },
260
- { id: "right_to_outer_15", from: "right_bridge_peak", to: "halo_n" },
261
- { id: "right_bridge_chain_1", from: "right_bridge_top", to: "right_bridge_peak" },
262
-
263
- { id: "corridor_chain_1", from: "corridor_0", to: "corridor_1" },
264
- { id: "corridor_chain_2", from: "corridor_1", to: "corridor_2" },
265
- { id: "corridor_chain_3", from: "corridor_2", to: "corridor_3" },
266
- { id: "corridor_chain_4", from: "corridor_3", to: "corridor_4" },
267
- { id: "corridor_chain_5", from: "corridor_4", to: "corridor_5" },
268
- { id: "corridor_chain_6", from: "corridor_5", to: "corridor_6" },
269
- { id: "corridor_to_arm_1", from: "corridor_0", to: "right_c1" },
270
- { id: "corridor_to_arm_2", from: "corridor_1", to: "right_c2" },
271
- { id: "corridor_to_arm_3", from: "corridor_2", to: "right_c3" },
272
- { id: "corridor_to_arm_4", from: "corridor_3", to: "right_b2" },
273
- { id: "corridor_to_arm_5", from: "corridor_4", to: "right_b4" },
274
- { id: "corridor_to_arm_6", from: "corridor_5", to: "right_a5" },
275
- { id: "corridor_to_arm_7", from: "corridor_5", to: "right_c3" },
276
- { id: "corridor_to_arm_8", from: "corridor_5", to: "right_b2" },
277
- ];
278
-
279
- export const BLOG_DEFENSE_COMPROMISE_PATH: string[] = [
280
- "corridor_6",
281
- "corridor_5",
282
- "corridor_4",
283
- "corridor_3",
284
- "right_b3",
285
- "right_b2",
286
- "right_b1",
287
- "right_b0",
288
- "ring_s",
289
- "mid_s",
290
- "core",
291
- ];
292
-
293
- export type BlogDefenseCompromiseStep =
294
- | { kind: "node"; nodeId: string }
295
- | { kind: "edge"; from: string; to: string; edgeId: string };
296
-
297
- function edgeKey(a: string, b: string): string {
298
- return a < b ? `${a}::${b}` : `${b}::${a}`;
299
- }
300
-
301
- function normalizeAngle(angle: number): number {
302
- while (angle > Math.PI) angle -= Math.PI * 2;
303
- while (angle < -Math.PI) angle += Math.PI * 2;
304
- return angle;
305
- }
306
-
307
- function buildGraphIndex(edges: BlogDefenseEdge[]): {
308
- adjacency: Map<string, string[]>;
309
- edgeByNodes: Map<string, BlogDefenseEdge>;
310
- } {
311
- const adjacency = new Map<string, string[]>();
312
- const edgeByNodes = new Map<string, BlogDefenseEdge>();
313
- for (const edge of edges) {
314
- if (!adjacency.has(edge.from)) adjacency.set(edge.from, []);
315
- if (!adjacency.has(edge.to)) adjacency.set(edge.to, []);
316
- adjacency.get(edge.from)?.push(edge.to);
317
- adjacency.get(edge.to)?.push(edge.from);
318
- edgeByNodes.set(edgeKey(edge.from, edge.to), edge);
319
- }
320
- return { adjacency, edgeByNodes };
321
- }
322
-
323
- export function getBlogDefensePathEdge(from: string, to: string): BlogDefenseEdge | undefined {
324
- return BLOG_DEFENSE_EDGES.find(
325
- (edge) => (edge.from === from && edge.to === to) || (edge.from === to && edge.to === from),
326
- );
327
- }
328
-
329
- export function buildCompromiseSequenceFromPath(nodeIds: string[], edges: BlogDefenseEdge[]): BlogDefenseCompromiseStep[] {
330
- const steps: BlogDefenseCompromiseStep[] = [];
331
- if (!nodeIds.length) return steps;
332
- const { edgeByNodes } = buildGraphIndex(edges);
333
- steps.push({ kind: "node", nodeId: nodeIds[0] });
334
- for (let index = 0; index < nodeIds.length - 1; index += 1) {
335
- const from = nodeIds[index];
336
- const to = nodeIds[index + 1];
337
- const edge = edgeByNodes.get(edgeKey(from, to));
338
- if (!edge) continue;
339
- steps.push({ kind: "edge", from, to, edgeId: edge.id });
340
- steps.push({ kind: "node", nodeId: to });
341
- }
342
- return steps;
343
- }
344
-
345
- function shortestPathWithTieBreak(
346
- startId: string,
347
- targetId: string,
348
- nodeById: Map<string, BlogDefenseNode>,
349
- adjacency: Map<string, string[]>,
350
- blockedNodeIds: Set<string> = new Set(),
351
- ): string[] {
352
- const queue: string[] = [startId];
353
- const visited = new Set<string>([startId]);
354
- const prev = new Map<string, string>();
355
- const targetNode = nodeById.get(targetId);
356
- while (queue.length) {
357
- const current = queue.shift();
358
- if (!current) break;
359
- if (current === targetId) break;
360
- const neighbors = [...(adjacency.get(current) ?? [])];
361
- neighbors.sort((a, b) => {
362
- const aNode = nodeById.get(a);
363
- const bNode = nodeById.get(b);
364
- if (!aNode || !bNode || !targetNode) return 0;
365
- const aDist = Math.hypot(aNode.x - targetNode.x, aNode.y - targetNode.y);
366
- const bDist = Math.hypot(bNode.x - targetNode.x, bNode.y - targetNode.y);
367
- return aDist - bDist;
368
- });
369
- for (const neighbor of neighbors) {
370
- if (neighbor !== targetId && blockedNodeIds.has(neighbor)) continue;
371
- if (visited.has(neighbor)) continue;
372
- visited.add(neighbor);
373
- prev.set(neighbor, current);
374
- queue.push(neighbor);
375
- }
376
- }
377
-
378
- if (!visited.has(targetId)) return [];
379
- const path: string[] = [targetId];
380
- let current = targetId;
381
- while (current !== startId) {
382
- const parent = prev.get(current);
383
- if (!parent) return [];
384
- path.unshift(parent);
385
- current = parent;
386
- }
387
- return path;
388
- }
389
-
390
- function nodeApproachPriority(node: BlogDefenseNode): number {
391
- if (node.id.startsWith("mid_")) return 0;
392
- if (node.id.startsWith("ring_")) return 1;
393
- if (node.id.startsWith("outer_")) return 2;
394
- if (node.id.startsWith("halo_")) return 3;
395
- return 4;
396
- }
397
-
398
- function resolveCoreApproachCandidates(
399
- startId: string,
400
- coreId: string,
401
- nodeById: Map<string, BlogDefenseNode>,
402
- adjacency: Map<string, string[]>,
403
- ): string[] {
404
- const coreNode = nodeById.get(coreId);
405
- const startNode = nodeById.get(startId);
406
- if (!coreNode || !startNode) return [];
407
-
408
- const startAngle = Math.atan2(startNode.y - coreNode.y, startNode.x - coreNode.x);
409
- return [...(adjacency.get(coreId) ?? [])]
410
- .map((id) => nodeById.get(id))
411
- .filter((node): node is BlogDefenseNode => Boolean(node))
412
- .sort((left, right) => {
413
- const leftPriority = nodeApproachPriority(left);
414
- const rightPriority = nodeApproachPriority(right);
415
- if (leftPriority !== rightPriority) return leftPriority - rightPriority;
416
-
417
- const leftAngle = Math.atan2(left.y - coreNode.y, left.x - coreNode.x);
418
- const rightAngle = Math.atan2(right.y - coreNode.y, right.x - coreNode.x);
419
- const leftDelta = Math.abs(normalizeAngle(leftAngle - startAngle));
420
- const rightDelta = Math.abs(normalizeAngle(rightAngle - startAngle));
421
- if (Math.abs(leftDelta - rightDelta) > 0.001) return leftDelta - rightDelta;
422
-
423
- const leftDistance = Math.hypot(startNode.x - left.x, startNode.y - left.y);
424
- const rightDistance = Math.hypot(startNode.x - right.x, startNode.y - right.y);
425
- return leftDistance - rightDistance;
426
- })
427
- .map((node) => node.id);
428
- }
429
-
430
- function buildPathToCoreWithPreferredApproach(
431
- startId: string,
432
- coreId: string,
433
- nodeById: Map<string, BlogDefenseNode>,
434
- adjacency: Map<string, string[]>,
435
- ): string[] {
436
- const approachCandidates = resolveCoreApproachCandidates(startId, coreId, nodeById, adjacency);
437
- for (const approachId of approachCandidates) {
438
- const approachPath = shortestPathWithTieBreak(startId, approachId, nodeById, adjacency, new Set([coreId]));
439
- if (!approachPath.length) continue;
440
- if (!(adjacency.get(approachId) ?? []).includes(coreId)) continue;
441
- return [...approachPath, coreId];
442
- }
443
-
444
- return shortestPathWithTieBreak(startId, coreId, nodeById, adjacency);
445
- }
446
-
447
- function hopDistanceToCore(
448
- coreId: string,
449
- adjacency: Map<string, string[]>,
450
- blockedNodeIds: Set<string> = new Set(),
451
- ): Map<string, number> {
452
- const distances = new Map<string, number>();
453
- const queue: string[] = [coreId];
454
- distances.set(coreId, 0);
455
-
456
- while (queue.length) {
457
- const current = queue.shift();
458
- if (!current) break;
459
- const currentDistance = distances.get(current) ?? 0;
460
- for (const neighbor of adjacency.get(current) ?? []) {
461
- if (neighbor !== coreId && blockedNodeIds.has(neighbor)) continue;
462
- if (distances.has(neighbor)) continue;
463
- distances.set(neighbor, currentDistance + 1);
464
- queue.push(neighbor);
465
- }
466
- }
467
-
468
- return distances;
469
- }
470
-
471
- function chooseWeightedIndex(weights: number[]): number {
472
- const total = weights.reduce((sum, weight) => sum + (weight > 0 ? weight : 0), 0);
473
- if (total <= 0) return 0;
474
- let roll = Math.random() * total;
475
- for (let index = 0; index < weights.length; index += 1) {
476
- roll -= Math.max(0, weights[index]);
477
- if (roll <= 0) return index;
478
- }
479
- return Math.max(0, weights.length - 1);
480
- }
481
-
482
- function buildStochasticInwardPath(
483
- startId: string,
484
- coreId: string,
485
- nodeById: Map<string, BlogDefenseNode>,
486
- adjacency: Map<string, string[]>,
487
- ): string[] {
488
- const approachCandidates = resolveCoreApproachCandidates(startId, coreId, nodeById, adjacency);
489
- const preferredApproachId = approachCandidates[0];
490
- if (!preferredApproachId) {
491
- return shortestPathWithTieBreak(startId, coreId, nodeById, adjacency);
492
- }
493
-
494
- const distances = hopDistanceToCore(preferredApproachId, adjacency, new Set([coreId]));
495
- if (!distances.has(startId)) return [];
496
- if (startId === preferredApproachId) return [preferredApproachId, coreId];
497
-
498
- const maxSteps = Math.max(10, Math.min(40, nodeById.size + 6));
499
- const path = [startId];
500
- const visitCounts = new Map<string, number>([[startId, 1]]);
501
- let current = startId;
502
-
503
- for (let step = 0; step < maxSteps && current !== preferredApproachId; step += 1) {
504
- const neighbors = (adjacency.get(current) ?? []).filter((neighbor) => distances.has(neighbor));
505
- if (!neighbors.length) break;
506
-
507
- const currentHop = distances.get(current) ?? Number.POSITIVE_INFINITY;
508
- const improving = neighbors.filter((neighbor) => (distances.get(neighbor) ?? Number.POSITIVE_INFINITY) < currentHop);
509
- const candidatePool = improving.length && Math.random() < 0.82 ? improving : neighbors;
510
-
511
- const weights = candidatePool.map((neighbor) => {
512
- const neighborHop = distances.get(neighbor) ?? Number.POSITIVE_INFINITY;
513
- const hopDelta = currentHop - neighborHop;
514
- const revisitPenalty = 1 / (1 + (visitCounts.get(neighbor) ?? 0) * 1.8);
515
- const core = nodeById.get(preferredApproachId);
516
- const neighborNode = nodeById.get(neighbor);
517
- const euclideanBias = core && neighborNode
518
- ? 1 / (1 + Math.hypot(neighborNode.x - core.x, neighborNode.y - core.y) * 0.015)
519
- : 1;
520
-
521
- let bias = 1;
522
- if (hopDelta > 0) bias = 5 + hopDelta * 1.8;
523
- else if (hopDelta === 0) bias = 0.9;
524
- else bias = 0.18;
525
- if (neighbor === preferredApproachId) bias *= 4;
526
-
527
- return Math.max(0.01, bias * revisitPenalty * euclideanBias);
528
- });
529
-
530
- const next = candidatePool[chooseWeightedIndex(weights)];
531
- path.push(next);
532
- visitCounts.set(next, (visitCounts.get(next) ?? 0) + 1);
533
- current = next;
534
-
535
- if (current === preferredApproachId) return [...path, coreId];
536
- }
537
-
538
- if (current !== preferredApproachId) {
539
- const completion = shortestPathWithTieBreak(current, preferredApproachId, nodeById, adjacency, new Set([coreId]));
540
- if (completion.length > 1) path.push(...completion.slice(1));
541
- }
542
-
543
- return path[path.length - 1] === preferredApproachId ? [...path, coreId] : [];
544
- }
545
-
546
- export function buildVisibleAttackPath(input: VisibleAttackPathInput): VisibleAttackPath {
547
- const coreId = input.coreId ?? BLOG_DEFENSE_CORE_NODE_ID;
548
- const nodeById = new Map(input.nodes.map((node) => [node.id, node]));
549
- const visibleEdges = input.edges.filter((edge) => nodeById.has(edge.from) && nodeById.has(edge.to));
550
- const { adjacency, edgeByNodes } = buildGraphIndex(visibleEdges);
551
-
552
- const corridorCandidates = input.nodes
553
- .filter((node) => node.zone === "right_corridor")
554
- .sort((a, b) => {
555
- if (b.y !== a.y) return b.y - a.y;
556
- return b.x - a.x;
557
- });
558
-
559
- const starts = corridorCandidates.length
560
- ? corridorCandidates
561
- : input.nodes.slice().sort((a, b) => {
562
- if (b.y !== a.y) return b.y - a.y;
563
- return b.x - a.x;
564
- });
565
-
566
- if (input.stochastic && starts.length) {
567
- const stochasticStartWeights = starts.map((node, index) => {
568
- const yBias = Math.max(1, node.y);
569
- const rankBias = Math.max(0.35, 1 - index * 0.06);
570
- return yBias * rankBias;
571
- });
572
- const stochasticStart = starts[chooseWeightedIndex(stochasticStartWeights)];
573
- const stochasticPath = buildStochasticInwardPath(stochasticStart.id, coreId, nodeById, adjacency);
574
- if (stochasticPath.length) {
575
- const sequence = buildCompromiseSequenceFromPath(stochasticPath, visibleEdges);
576
- return {
577
- nodeIds: sequence.filter((step) => step.kind === "node").map((step) => step.nodeId),
578
- edgeIds: sequence.filter((step) => step.kind === "edge").map((step) => step.edgeId),
579
- };
580
- }
581
- }
582
-
583
- for (const start of starts) {
584
- const path = buildPathToCoreWithPreferredApproach(start.id, coreId, nodeById, adjacency);
585
- if (!path.length) continue;
586
- const sequence = buildCompromiseSequenceFromPath(path, visibleEdges);
587
- return {
588
- nodeIds: sequence.filter((step) => step.kind === "node").map((step) => step.nodeId),
589
- edgeIds: sequence.filter((step) => step.kind === "edge").map((step) => step.edgeId),
590
- };
591
- }
592
-
593
- const fallback = input.nodes
594
- .filter((node) => node.id !== coreId)
595
- .sort((a, b) => {
596
- const core = nodeById.get(coreId);
597
- if (!core) return 0;
598
- const aDist = Math.hypot(a.x - core.x, a.y - core.y);
599
- const bDist = Math.hypot(b.x - core.x, b.y - core.y);
600
- return bDist - aDist;
601
- });
602
- for (const start of fallback) {
603
- const path = buildPathToCoreWithPreferredApproach(start.id, coreId, nodeById, adjacency);
604
- if (!path.length) continue;
605
- const sequence = buildCompromiseSequenceFromPath(path, visibleEdges);
606
- return {
607
- nodeIds: sequence.filter((step) => step.kind === "node").map((step) => step.nodeId),
608
- edgeIds: sequence.filter((step) => step.kind === "edge").map((step) => step.edgeId),
609
- };
610
- }
611
-
612
- return {
613
- nodeIds: nodeById.has(coreId) ? [coreId] : [],
614
- edgeIds: [],
615
- };
616
- }
617
-
618
- export function buildBlogDefenseCompromiseSequence(): BlogDefenseCompromiseStep[] {
619
- return buildCompromiseSequenceFromPath(BLOG_DEFENSE_COMPROMISE_PATH, BLOG_DEFENSE_EDGES);
620
- }
621
-
622
- export function isBlogDefenseAttackEdge(from: string, to: string): boolean {
623
- for (let index = 0; index < BLOG_DEFENSE_COMPROMISE_PATH.length - 1; index += 1) {
624
- const a = BLOG_DEFENSE_COMPROMISE_PATH[index];
625
- const b = BLOG_DEFENSE_COMPROMISE_PATH[index + 1];
626
- if ((from === a && to === b) || (from === b && to === a)) return true;
627
- }
628
- return false;
629
- }
630
-
631
- export function edgeIdForNodes(from: string, to: string, edges: BlogDefenseEdge[]): string | null {
632
- const edge = edges.find((entry) => (
633
- (entry.from === from && entry.to === to) || (entry.from === to && entry.to === from)
634
- ));
635
- return edge ? edge.id : null;
636
- }