trace-viz 0.3.0

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 (110) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/LICENSE +21 -0
  3. package/README.md +385 -0
  4. package/dist/adapters/index.d.ts +32 -0
  5. package/dist/adapters/index.d.ts.map +1 -0
  6. package/dist/adapters/index.js +83 -0
  7. package/dist/adapters/index.js.map +1 -0
  8. package/dist/agent.d.ts +66 -0
  9. package/dist/agent.d.ts.map +1 -0
  10. package/dist/agent.js +329 -0
  11. package/dist/agent.js.map +1 -0
  12. package/dist/atlas/atlas.d.ts +203 -0
  13. package/dist/atlas/atlas.d.ts.map +1 -0
  14. package/dist/atlas/atlas.js +1466 -0
  15. package/dist/atlas/atlas.js.map +1 -0
  16. package/dist/atlas/index.d.ts +7 -0
  17. package/dist/atlas/index.d.ts.map +1 -0
  18. package/dist/atlas/index.js +4 -0
  19. package/dist/atlas/index.js.map +1 -0
  20. package/dist/atlas/layout.d.ts +44 -0
  21. package/dist/atlas/layout.d.ts.map +1 -0
  22. package/dist/atlas/layout.js +103 -0
  23. package/dist/atlas/layout.js.map +1 -0
  24. package/dist/atlas/model.d.ts +164 -0
  25. package/dist/atlas/model.d.ts.map +1 -0
  26. package/dist/atlas/model.js +489 -0
  27. package/dist/atlas/model.js.map +1 -0
  28. package/dist/channels.d.ts +105 -0
  29. package/dist/channels.d.ts.map +1 -0
  30. package/dist/channels.js +411 -0
  31. package/dist/channels.js.map +1 -0
  32. package/dist/components.d.ts +76 -0
  33. package/dist/components.d.ts.map +1 -0
  34. package/dist/components.js +432 -0
  35. package/dist/components.js.map +1 -0
  36. package/dist/estate.d.ts +25 -0
  37. package/dist/estate.d.ts.map +1 -0
  38. package/dist/estate.js +285 -0
  39. package/dist/estate.js.map +1 -0
  40. package/dist/failure.d.ts +36 -0
  41. package/dist/failure.d.ts.map +1 -0
  42. package/dist/failure.js +82 -0
  43. package/dist/failure.js.map +1 -0
  44. package/dist/geometry.d.ts +24 -0
  45. package/dist/geometry.d.ts.map +1 -0
  46. package/dist/geometry.js +92 -0
  47. package/dist/geometry.js.map +1 -0
  48. package/dist/index.d.ts +51 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +34 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/layout.d.ts +66 -0
  53. package/dist/layout.d.ts.map +1 -0
  54. package/dist/layout.js +152 -0
  55. package/dist/layout.js.map +1 -0
  56. package/dist/layout3d.d.ts +98 -0
  57. package/dist/layout3d.d.ts.map +1 -0
  58. package/dist/layout3d.js +350 -0
  59. package/dist/layout3d.js.map +1 -0
  60. package/dist/mesh.d.ts +23 -0
  61. package/dist/mesh.d.ts.map +1 -0
  62. package/dist/mesh.js +263 -0
  63. package/dist/mesh.js.map +1 -0
  64. package/dist/model.d.ts +94 -0
  65. package/dist/model.d.ts.map +1 -0
  66. package/dist/model.js +84 -0
  67. package/dist/model.js.map +1 -0
  68. package/dist/particles.d.ts +80 -0
  69. package/dist/particles.d.ts.map +1 -0
  70. package/dist/particles.js +118 -0
  71. package/dist/particles.js.map +1 -0
  72. package/dist/project.d.ts +113 -0
  73. package/dist/project.d.ts.map +1 -0
  74. package/dist/project.js +288 -0
  75. package/dist/project.js.map +1 -0
  76. package/dist/react/index.d.ts +22 -0
  77. package/dist/react/index.d.ts.map +1 -0
  78. package/dist/react/index.js +69 -0
  79. package/dist/react/index.js.map +1 -0
  80. package/dist/renderer.d.ts +252 -0
  81. package/dist/renderer.d.ts.map +1 -0
  82. package/dist/renderer.js +1710 -0
  83. package/dist/renderer.js.map +1 -0
  84. package/dist/scales.d.ts +99 -0
  85. package/dist/scales.d.ts.map +1 -0
  86. package/dist/scales.js +126 -0
  87. package/dist/scales.js.map +1 -0
  88. package/dist/scene.d.ts +53 -0
  89. package/dist/scene.d.ts.map +1 -0
  90. package/dist/scene.js +44 -0
  91. package/dist/scene.js.map +1 -0
  92. package/dist/schema.d.ts +24 -0
  93. package/dist/schema.d.ts.map +1 -0
  94. package/dist/schema.js +131 -0
  95. package/dist/schema.js.map +1 -0
  96. package/dist/seed.d.ts +33 -0
  97. package/dist/seed.d.ts.map +1 -0
  98. package/dist/seed.js +173 -0
  99. package/dist/seed.js.map +1 -0
  100. package/dist/theme.d.ts +35 -0
  101. package/dist/theme.d.ts.map +1 -0
  102. package/dist/theme.js +83 -0
  103. package/dist/theme.js.map +1 -0
  104. package/dist/trace-viz.esm.js +15 -0
  105. package/dist/trace-viz.min.js +15 -0
  106. package/dist/types.d.ts +92 -0
  107. package/dist/types.d.ts.map +1 -0
  108. package/dist/types.js +9 -0
  109. package/dist/types.js.map +1 -0
  110. package/package.json +105 -0
@@ -0,0 +1,350 @@
1
+ import { defaultTier } from './components.js';
2
+ import { mulberry32 } from './particles.js';
3
+ export function project3(p, cam) {
4
+ const cos = Math.cos(cam.yaw);
5
+ const sin = Math.sin(cam.yaw);
6
+ const rx = p.x * cos - p.z * sin;
7
+ const depth = p.x * sin + p.z * cos;
8
+ return {
9
+ x: cam.cx + rx * cam.scale,
10
+ y: cam.cy - p.y * cam.scaleY + depth * cam.scaleY * cam.tilt,
11
+ depth,
12
+ };
13
+ }
14
+ export const defaultWorldLayout = {
15
+ tierOf: defaultTier,
16
+ attractionOf: () => 0.5,
17
+ tierGap: 1.4,
18
+ nodeWidth: 62,
19
+ nodeHeight: 52,
20
+ iterations: 320,
21
+ seed: 7,
22
+ tilt: 0.3,
23
+ gap: 1.2,
24
+ labelSpace: 26,
25
+ };
26
+ /**
27
+ * Place nodes in the world.
28
+ *
29
+ * A short deterministic relaxation, not a live force simulation: same scene in,
30
+ * same world out, so two screenshots are comparable and a re-render does not
31
+ * shuffle everything the user had just learned to read.
32
+ */
33
+ export function layoutWorld(scene, options = {}) {
34
+ const opt = { ...defaultWorldLayout, ...options };
35
+ const rand = mulberry32(opt.seed);
36
+ const nodes = scene.nodes;
37
+ if (!nodes.length)
38
+ return new Map();
39
+ const tiers = nodes.map((n) => opt.tierOf(n));
40
+ const maxTier = Math.max(...tiers, 0);
41
+ // Seed each plane on a ring, which starts things spread out and keeps the
42
+ // relaxation from having to undo a pile in the middle.
43
+ const perTier = new Map();
44
+ for (const t of tiers)
45
+ perTier.set(t, (perTier.get(t) ?? 0) + 1);
46
+ const seenInTier = new Map();
47
+ const px = [];
48
+ const pz = [];
49
+ for (const [i, node] of nodes.entries()) {
50
+ const tier = tiers[i];
51
+ const idx = seenInTier.get(tier) ?? 0;
52
+ seenInTier.set(tier, idx + 1);
53
+ const count = perTier.get(tier) ?? 1;
54
+ const angle = (idx / count) * Math.PI * 2 + tier * 0.7;
55
+ const radius = 0.55 + (rand() - 0.5) * 0.12;
56
+ px.push(Math.cos(angle) * radius);
57
+ pz.push(Math.sin(angle) * radius);
58
+ void node;
59
+ }
60
+ const index = new Map(nodes.map((n, i) => [n.id, i]));
61
+ const links = [];
62
+ for (const e of scene.edges) {
63
+ const a = index.get(e.from);
64
+ const b = index.get(e.to);
65
+ if (a === undefined || b === undefined || a === b)
66
+ continue;
67
+ links.push({ a, b, w: opt.attractionOf(e.id) });
68
+ }
69
+ // Categorical grouping is a second kind of link: same value, fixed pull.
70
+ if (opt.groupOf) {
71
+ const buckets = new Map();
72
+ for (const [i, node] of nodes.entries()) {
73
+ const g = opt.groupOf(node);
74
+ if (!g)
75
+ continue;
76
+ (buckets.get(g) ?? buckets.set(g, []).get(g)).push(i);
77
+ }
78
+ for (const members of buckets.values()) {
79
+ for (let i = 0; i < members.length; i++) {
80
+ for (let j = i + 1; j < members.length; j++) {
81
+ links.push({ a: members[i], b: members[j], w: 0.55 });
82
+ }
83
+ }
84
+ }
85
+ }
86
+ const n = nodes.length;
87
+ const fx = new Float64Array(n);
88
+ const fz = new Float64Array(n);
89
+ for (let step = 0; step < opt.iterations; step++) {
90
+ const cool = 1 - step / opt.iterations;
91
+ fx.fill(0);
92
+ fz.fill(0);
93
+ // Repulsion. Cross-tier pairs still repel, because they overlap on screen
94
+ // even though they never touch in world space.
95
+ for (let i = 0; i < n; i++) {
96
+ for (let j = i + 1; j < n; j++) {
97
+ let dx = px[i] - px[j];
98
+ let dz = pz[i] - pz[j];
99
+ let d2 = dx * dx + dz * dz;
100
+ if (d2 < 1e-6) {
101
+ dx = (rand() - 0.5) * 0.01;
102
+ dz = (rand() - 0.5) * 0.01;
103
+ d2 = 1e-4;
104
+ }
105
+ const sameTier = tiers[i] === tiers[j];
106
+ const strength = (sameTier ? 0.02 : 0.006) / d2;
107
+ const d = Math.sqrt(d2);
108
+ fx[i] += (dx / d) * strength;
109
+ fz[i] += (dz / d) * strength;
110
+ fx[j] -= (dx / d) * strength;
111
+ fz[j] -= (dz / d) * strength;
112
+ }
113
+ }
114
+ // Attraction along links, weighted by whatever is bound to distance.
115
+ for (const link of links) {
116
+ const dx = px[link.b] - px[link.a];
117
+ const dz = pz[link.b] - pz[link.a];
118
+ const pull = 0.02 * (0.15 + link.w);
119
+ fx[link.a] += dx * pull;
120
+ fz[link.a] += dz * pull;
121
+ fx[link.b] -= dx * pull;
122
+ fz[link.b] -= dz * pull;
123
+ }
124
+ // Weak gravity, so disconnected nodes do not drift to infinity.
125
+ for (let i = 0; i < n; i++) {
126
+ fx[i] -= px[i] * 0.006;
127
+ fz[i] -= pz[i] * 0.006;
128
+ px[i] += fx[i] * cool * 2.2;
129
+ pz[i] += fz[i] * cool * 2.2;
130
+ }
131
+ }
132
+ // Flatten the planes before spacing them out. A square plane spends the
133
+ // canvas's vertical budget on depth, and vertical budget is exactly what the
134
+ // tiers need: services have to sit visibly *above* the data they read, and
135
+ // deep planes bleed into each other until that reading is gone.
136
+ for (let i = 0; i < n; i++)
137
+ pz[i] *= 0.42;
138
+ // Force relaxation gets the *arrangement* right - who is near whom - but on
139
+ // its own it happily stacks two boxes on the same spot, and an overlapping
140
+ // pile is worse than a wrong arrangement. So the neighbourhood comes from the
141
+ // forces and the spacing is then enforced outright.
142
+ separate(px, pz, tiers, rand, opt.tilt, opt.gap);
143
+ // No normalisation. Rescaling to a unit box here would undo the spacing that
144
+ // was just enforced - the more crowded the plane, the harder it would squeeze.
145
+ // The camera does the fitting instead, in pixels, where it belongs.
146
+ const centreX = px.reduce((a, b) => a + b, 0) / n;
147
+ const centreZ = pz.reduce((a, b) => a + b, 0) / n;
148
+ /**
149
+ * How far apart the planes have to be so they do not overlap on screen.
150
+ *
151
+ * A plane is not a line: its own depth projects to `2 * zExtent * tilt` of
152
+ * vertical space. Any tier gap smaller than that guarantees the services
153
+ * plane bleeds into the data plane, which destroys the one reading the world
154
+ * exists to give - what runs above what. So the gap is derived from the
155
+ * depth actually used, not configured and hoped for.
156
+ */
157
+ const planeGap = () => {
158
+ let zExtent = 0.2;
159
+ for (let i = 0; i < n; i++)
160
+ zExtent = Math.max(zExtent, Math.abs(pz[i]));
161
+ return Math.max(opt.tierGap, 2 * zExtent * opt.tilt + 0.62);
162
+ };
163
+ const build = () => {
164
+ const gap = planeGap();
165
+ const out = new Map();
166
+ for (const [i, node] of nodes.entries()) {
167
+ const size = opt.sizeOf?.(node) ?? { w: opt.nodeWidth, h: opt.nodeHeight };
168
+ out.set(node.id, {
169
+ id: node.id,
170
+ x: px[i] - centreX,
171
+ z: pz[i] - centreZ,
172
+ y: (maxTier - tiers[i]) * gap,
173
+ tier: tiers[i],
174
+ w: size.w,
175
+ h: size.h,
176
+ });
177
+ }
178
+ return out;
179
+ };
180
+ // Final pass, and the one that actually decides whether the world is legible:
181
+ // separate the bodies in *pixels*, against the camera that will draw them.
182
+ // World-space spacing cannot do this on its own, because the camera rescales
183
+ // to fit - push things further apart in the world and the camera simply zooms
184
+ // out, leaving the overlap exactly where it was.
185
+ if (opt.viewport) {
186
+ for (let round = 0; round < 5; round++) {
187
+ const current = build();
188
+ const cam = fitCamera(current, opt.viewport.width, opt.viewport.height, opt.viewport.yaw, opt.tilt, undefined, worldExtent(current));
189
+ if (!separateOnScreen(px, pz, tiers, nodes, current, cam, opt))
190
+ break;
191
+ }
192
+ }
193
+ return build();
194
+ }
195
+ /**
196
+ * Resolve overlapping bodies in screen space, then convert the push back into
197
+ * world coordinates. Returns whether anything moved.
198
+ */
199
+ function separateOnScreen(px, pz, tiers, nodes, world, cam, opt) {
200
+ const n = nodes.length;
201
+ const boxes = nodes.map((node, i) => {
202
+ const w = world.get(node.id);
203
+ const p = project3(w, cam);
204
+ return { i, x: p.x, y: p.y, w: w.w, h: w.h + opt.labelSpace, tier: tiers[i] };
205
+ });
206
+ const cos = Math.cos(cam.yaw);
207
+ const sin = Math.sin(cam.yaw);
208
+ const unproject = (dxScreen, dyScreen) => {
209
+ const a = dxScreen / cam.scale;
210
+ const b = dyScreen / (cam.scaleY * cam.tilt);
211
+ return { dx: a * cos + b * sin, dz: -a * sin + b * cos };
212
+ };
213
+ let moved = false;
214
+ for (let pass = 0; pass < 24; pass++) {
215
+ let touched = false;
216
+ for (let i = 0; i < n; i++) {
217
+ for (let j = i + 1; j < n; j++) {
218
+ const a = boxes[i];
219
+ const b = boxes[j];
220
+ // Different planes are already separated vertically by the tier gap;
221
+ // only their in-plane positions need to stop colliding.
222
+ const padX = 12;
223
+ const padY = a.tier === b.tier ? 8 : 2;
224
+ const needX = (a.w + b.w) / 2 + padX;
225
+ const needY = (a.h + b.h) / 2 + padY;
226
+ const dx = b.x - a.x;
227
+ const dy = b.y - a.y;
228
+ const overlapX = needX - Math.abs(dx);
229
+ const overlapY = needY - Math.abs(dy);
230
+ if (overlapX <= 0 || overlapY <= 0)
231
+ continue;
232
+ // Prefer to solve a collision sideways. Moving a body up or down means
233
+ // moving it deeper into the plane, which eats the vertical separation
234
+ // between tiers; there is almost always width to spare instead.
235
+ let pushX = 0;
236
+ let pushY = 0;
237
+ if (overlapX / needX < (overlapY / needY) * 2.2)
238
+ pushX = (dx >= 0 ? 1 : -1) * overlapX * 0.5;
239
+ else
240
+ pushY = (dy >= 0 ? 1 : -1) * overlapY * 0.5;
241
+ const { dx: wx, dz: wz } = unproject(pushX, pushY);
242
+ px[i] -= wx * 0.55;
243
+ pz[i] -= wz * 0.55;
244
+ px[j] += wx * 0.55;
245
+ pz[j] += wz * 0.55;
246
+ a.x -= pushX * 0.55;
247
+ a.y -= pushY * 0.55;
248
+ b.x += pushX * 0.55;
249
+ b.y += pushY * 0.55;
250
+ touched = true;
251
+ moved = true;
252
+ }
253
+ }
254
+ if (!touched)
255
+ break;
256
+ }
257
+ return moved;
258
+ }
259
+ /** How far the planes reach, so the ground can be drawn under everything on it. */
260
+ export function worldExtent(world, margin = 0.35) {
261
+ let extent = 0.6;
262
+ for (const n of world.values())
263
+ extent = Math.max(extent, Math.abs(n.x), Math.abs(n.z));
264
+ return extent + margin;
265
+ }
266
+ /**
267
+ * Push apart anything closer than the room a body needs.
268
+ *
269
+ * The target gap comes from how many things share the plane: a dozen services
270
+ * on one plane need to be further apart in world units than three do, because
271
+ * they are competing for the same projected area.
272
+ */
273
+ function separate(px, pz, tiers, rand, tilt, gap) {
274
+ const n = px.length;
275
+ // Separation has to be measured in the space the viewer sees, not the space
276
+ // the nodes live in. The projection squashes the depth axis by `tilt`, so two
277
+ // bodies a comfortable distance apart in z land almost on top of each other
278
+ // on screen. Measuring with z pre-squashed is what makes the plane read as a
279
+ // plane instead of a pile.
280
+ const squash = Math.max(tilt, 0.15);
281
+ for (let pass = 0; pass < 90; pass++) {
282
+ let worst = 0;
283
+ for (let i = 0; i < n; i++) {
284
+ for (let j = i + 1; j < n; j++) {
285
+ const sameTier = tiers[i] === tiers[j];
286
+ // Different planes still collide on screen, just less severely.
287
+ const want = sameTier ? gap : gap * 0.3;
288
+ let dx = px[j] - px[i];
289
+ let dz = (pz[j] - pz[i]) * squash;
290
+ let d = Math.hypot(dx, dz);
291
+ if (d < 1e-5) {
292
+ dx = rand() - 0.5;
293
+ dz = rand() - 0.5;
294
+ d = Math.hypot(dx, dz) || 1;
295
+ }
296
+ if (d >= want)
297
+ continue;
298
+ const push = ((want - d) / d) * 0.5;
299
+ px[i] -= dx * push;
300
+ pz[i] -= (dz / squash) * push;
301
+ px[j] += dx * push;
302
+ pz[j] += (dz / squash) * push;
303
+ worst = Math.max(worst, want - d);
304
+ }
305
+ }
306
+ if (worst < 0.004)
307
+ break;
308
+ }
309
+ }
310
+ /** Fit the projected world into a box, leaving room for labels under each body. */
311
+ export function fitCamera(world, width, height, yaw, tilt = 0.3, padding = 44,
312
+ /** Include the tier planes in the fit, so the ground is not clipped. */
313
+ extent) {
314
+ const probe = { yaw, tilt, scale: 1, scaleY: 1, cx: 0, cy: 0 };
315
+ let minX = Infinity;
316
+ let maxX = -Infinity;
317
+ let minY = Infinity;
318
+ let maxY = -Infinity;
319
+ const ys = [...world.values()].map((n) => n.y);
320
+ const probes = [...world.values()].map((n) => ({ x: n.x, y: n.y, z: n.z }));
321
+ if (extent && ys.length) {
322
+ for (const y of [Math.min(...ys), Math.max(...ys)]) {
323
+ for (const sx of [-extent, extent])
324
+ for (const sz of [-extent, extent])
325
+ probes.push({ x: sx, y, z: sz });
326
+ }
327
+ }
328
+ for (const node of probes) {
329
+ const p = project3(node, probe);
330
+ minX = Math.min(minX, p.x);
331
+ maxX = Math.max(maxX, p.x);
332
+ minY = Math.min(minY, p.y);
333
+ maxY = Math.max(maxY, p.y);
334
+ }
335
+ if (!Number.isFinite(minX)) {
336
+ return { yaw, tilt, scale: 100, scaleY: 100, cx: width / 2, cy: height / 2 };
337
+ }
338
+ const boxW = Math.max(width - padding * 2, 120);
339
+ const boxH = Math.max(height - padding * 2 - 44, 120);
340
+ const fitX = boxW / Math.max(maxX - minX, 0.001);
341
+ const fitY = boxH / Math.max(maxY - minY, 0.001);
342
+ const base = Math.min(fitX, fitY);
343
+ const MAX_STRETCH = 1.6;
344
+ const scale = Math.min(fitX, base * MAX_STRETCH);
345
+ const scaleY = Math.min(fitY, base * MAX_STRETCH);
346
+ const midX = (minX + maxX) / 2;
347
+ const midY = (minY + maxY) / 2;
348
+ return { yaw, tilt, scale, scaleY, cx: width / 2 - midX * scale, cy: height / 2 - midY * scaleY };
349
+ }
350
+ //# sourceMappingURL=layout3d.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout3d.js","sourceRoot":"","sources":["../src/layout3d.ts"],"names":[],"mappings":"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"}
package/dist/mesh.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ import type { FlowTable } from './model.js';
2
+ export interface MeshOptions {
3
+ seed?: number;
4
+ services?: number;
5
+ /** Environments, in the order you would promote through them. */
6
+ envs?: string[];
7
+ regions?: string[];
8
+ /** Baseline requests per second entering prod in the primary region. */
9
+ baseRps?: number;
10
+ title?: string;
11
+ }
12
+ /**
13
+ * A deliberately messy but realistic mesh.
14
+ *
15
+ * Every knob the framework exposes is present in the data: services own several
16
+ * APIs, the same API runs in several environments and regions, dependencies
17
+ * declare whether the caller fails open or closed on them, and every thing
18
+ * carries an engine, a team, an instance count and live CPU and memory. That is
19
+ * the point - a demo where each dimension has exactly one plausible use teaches
20
+ * you nothing about choosing between them.
21
+ */
22
+ export declare function generateMesh(options?: MeshOptions): FlowTable;
23
+ //# sourceMappingURL=mesh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh.d.ts","sourceRoot":"","sources":["../src/mesh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,SAAS,EAAY,MAAM,YAAY,CAAC;AAGlE,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4DD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,WAAgB,GAAG,SAAS,CAqNjE"}
package/dist/mesh.js ADDED
@@ -0,0 +1,263 @@
1
+ import { mulberry32 } from './particles.js';
2
+ // Three teams. Each service, and the store it owns, belongs to exactly one, so
3
+ // "group by team" draws three neighbourhoods rather than a scatter.
4
+ const CATALOG = [
5
+ ['orders', 'Payments', ['POST /orders', 'GET /orders/{id}', 'POST /orders/{id}/cancel']],
6
+ ['pricing', 'Payments', ['GET /quote', 'POST /quote/bulk']],
7
+ ['catalog', 'Identity', ['GET /items/{sku}', 'GET /items:search', 'POST /items']],
8
+ ['identity', 'Identity', ['POST /token', 'GET /me', 'POST /introspect']],
9
+ ['payments', 'Payments', ['POST /charges', 'POST /refunds', 'GET /charges/{id}']],
10
+ ['shipping', 'Payments', ['POST /shipments', 'GET /rates']],
11
+ ['inventory', 'Risk', ['GET /stock/{sku}', 'POST /reserve']],
12
+ ['fraud', 'Risk', ['POST /score']],
13
+ ['notify', 'Identity', ['POST /send', 'GET /prefs']],
14
+ ['reviews', 'Risk', ['GET /reviews/{sku}', 'POST /reviews']],
15
+ ['ledger', 'Payments', ['POST /entries', 'GET /balance']],
16
+ ['search', 'Identity', ['GET /search', 'POST /index']],
17
+ ];
18
+ /**
19
+ * A store has no APIs, but every record needs one on both ends. Name the
20
+ * operation the engine serves rather than a placeholder, so a box expanded
21
+ * into its APIs reads "orders-db / query", not "orders-db / primary".
22
+ */
23
+ function storeOp(engine) {
24
+ switch (engine) {
25
+ case 'redis':
26
+ case 'dynamo':
27
+ return 'get';
28
+ case 'kafka':
29
+ return 'produce';
30
+ case 's3':
31
+ return 'put';
32
+ default:
33
+ return 'query';
34
+ }
35
+ }
36
+ const STORES = [
37
+ ['orders-db', 'postgres'],
38
+ ['pricing-cache', 'redis'],
39
+ ['catalog-db', 'postgres'],
40
+ ['identity-db', 'postgres'],
41
+ ['payments-db', 'postgres'],
42
+ ['shipping-db', 'postgres'],
43
+ ['inventory-kv', 'dynamo'],
44
+ ['fraud-features', 'cassandra'],
45
+ ['notify-queue', 'kafka'],
46
+ ['reviews-blob', 's3'],
47
+ ['ledger-db', 'postgres'],
48
+ ['search-index', 'clickhouse'],
49
+ ];
50
+ /**
51
+ * A deliberately messy but realistic mesh.
52
+ *
53
+ * Every knob the framework exposes is present in the data: services own several
54
+ * APIs, the same API runs in several environments and regions, dependencies
55
+ * declare whether the caller fails open or closed on them, and every thing
56
+ * carries an engine, a team, an instance count and live CPU and memory. That is
57
+ * the point - a demo where each dimension has exactly one plausible use teaches
58
+ * you nothing about choosing between them.
59
+ */
60
+ export function generateMesh(options = {}) {
61
+ const { seed = 21, services = 12, envs = ['prod', 'staging', 'dev'], regions = ['eu-west', 'us-east'], baseRps = 6400, title = 'Messy mesh: 12 services, ~40 APIs, 3 environments, 2 regions', } = options;
62
+ const rand = mulberry32(seed);
63
+ const between = (lo, hi) => lo + rand() * (hi - lo);
64
+ const specs = CATALOG.slice(0, services).map(([name, team, apis], i) => ({
65
+ name,
66
+ team,
67
+ apis,
68
+ store: { name: STORES[i % STORES.length][0], engine: STORES[i % STORES.length][1] },
69
+ }));
70
+ // Only prod runs everywhere. Lower environments live in the primary region,
71
+ // which is exactly the asymmetry that makes region worth being a dimension.
72
+ const deployments = [];
73
+ for (const env of envs) {
74
+ for (const region of regions) {
75
+ if (env !== 'prod' && region !== regions[0])
76
+ continue;
77
+ const share = env === 'prod' ? (region === regions[0] ? 1 : 0.62) : env === 'staging' ? 0.06 : 0.015;
78
+ deployments.push({ env, region, share });
79
+ }
80
+ }
81
+ const records = [];
82
+ const nodes = [];
83
+ // Errors are not uniform. A healthy fleet sits well under 0.5%; one service
84
+ // is having a bad day (a few percent) and one busy API is on fire (double
85
+ // digits). The halo channel reads error rate on an absolute scale so those
86
+ // two stand out - which means the seed has to contain them. Picked once, so
87
+ // the same things are broken in both regions, as a real incident would be.
88
+ const errPick = specs.filter((s) => !['identity', 'orders', 'payments'].includes(s.name));
89
+ const sick = errPick[Math.floor(rand() * errPick.length)].name;
90
+ let burning = errPick[Math.floor(rand() * errPick.length)];
91
+ if (burning.name === sick)
92
+ burning = errPick[(errPick.indexOf(burning) + 1) % errPick.length];
93
+ const burningApi = burning.apis[0];
94
+ const push = (from, to, dims, rps, latencyMs, errorRate, bytes) => {
95
+ records.push({
96
+ from,
97
+ to,
98
+ dims,
99
+ metrics: {
100
+ rps: Math.round(rps * 100) / 100,
101
+ latencyMs: Math.round(latencyMs * 10) / 10,
102
+ errorRate: Math.round(errorRate * 10000) / 10000,
103
+ bytes: Math.round(bytes),
104
+ },
105
+ });
106
+ };
107
+ for (const { env, region, share } of deployments) {
108
+ const scale = baseRps * share;
109
+ // Edge: clients -> gateway -> each public API.
110
+ push({ service: 'clients', api: 'browser', kind: 'client' }, { service: 'gateway', api: 'ingress', kind: 'gateway' }, { env, region, failure: 'fail-closed', protocol: 'https' }, scale, between(1.5, 3), between(0, 0.002), 820);
111
+ const publicApis = specs.flatMap((s) => s.apis.slice(0, 2).map((api) => ({ service: s.name, api })));
112
+ // Log-normal, not uniform: a handful of APIs carry most of the traffic and
113
+ // a long tail carries almost none, spanning two to three orders of
114
+ // magnitude. That is what production looks like, and it is the contrast
115
+ // the density channel exists to show.
116
+ const weights = publicApis.map(() => Math.exp(between(-2.6, 2.6)));
117
+ const total = weights.reduce((a, b) => a + b, 0);
118
+ // The gateway is hard-wired to the services a request cannot complete
119
+ // without, and deliberately soft on the rest. Getting this wrong - failing
120
+ // closed on everything - is what turns one dead service into a dead site,
121
+ // and the blast-radius view exists to show exactly that.
122
+ const CRITICAL = new Set(['identity', 'orders', 'payments']);
123
+ // Errors are not uniform either. A healthy fleet sits well under 0.5%;
124
+ // one service is having a bad day (a few percent), and one API is on
125
+ // fire (double digits). The halo channel is built to make those two
126
+ // stand out at an absolute scale, so the seed has to contain them.
127
+ const errorFor = (service, api, healthy) => {
128
+ if (env !== 'prod')
129
+ return healthy;
130
+ if (service === burning.name && api === burningApi)
131
+ return between(0.12, 0.3);
132
+ if (service === sick)
133
+ return between(0.02, 0.05);
134
+ return healthy;
135
+ };
136
+ publicApis.forEach((target, i) => {
137
+ push({ service: 'gateway', api: 'ingress', kind: 'gateway' }, { ...target, kind: 'service' }, {
138
+ env,
139
+ region,
140
+ failure: CRITICAL.has(target.service) ? 'fail-closed' : 'fail-open',
141
+ protocol: 'https',
142
+ }, (scale * weights[i]) / total, between(4, 40), errorFor(target.service, target.api, between(0, 0.004)), 900);
143
+ });
144
+ for (const spec of specs) {
145
+ for (const api of spec.apis) {
146
+ const inbound = records.find((r) => r.to.service === spec.name && r.to.api === api && r.dims?.env === env && r.dims?.region === region)
147
+ ?.metrics.rps ?? scale * Math.exp(between(-6, -2.5));
148
+ // Every API touches its own store.
149
+ push({ service: spec.name, api, kind: 'service' }, { service: spec.store.name, api: storeOp(spec.store.engine), kind: storeKind(spec.store.engine), engine: spec.store.engine }, { env, region, failure: 'fail-closed', protocol: engineProtocol(spec.store.engine) }, inbound * between(0.9, 2.4), storeLatency(spec.store.engine, between(0.6, 1.4)), between(0, 0.003), between(300, 5200));
150
+ // Cross-service calls, each with its own failure contract. Most
151
+ // enrichment is genuinely optional, so fail-open is the common case -
152
+ // and those links are the firebreaks that keep one dead service from
153
+ // taking the estate with it.
154
+ const fanout = rand() < 0.55 ? 1 : rand() < 0.8 ? 2 : 0;
155
+ for (let k = 0; k < fanout; k++) {
156
+ const other = specs[Math.floor(rand() * specs.length)];
157
+ if (other.name === spec.name)
158
+ continue;
159
+ const otherApi = other.apis[Math.floor(rand() * other.apis.length)];
160
+ const failOpen = rand() < 0.66;
161
+ push({ service: spec.name, api, kind: 'service' }, { service: other.name, api: otherApi, kind: 'service' }, {
162
+ env,
163
+ region,
164
+ failure: failOpen ? 'fail-open' : 'fail-closed',
165
+ protocol: rand() < 0.7 ? 'grpc' : 'https',
166
+ }, inbound * between(0.1, 0.9), between(3, 70), errorFor(other.name, otherApi, failOpen ? between(0, 0.008) : between(0, 0.004)), between(200, 2600));
167
+ }
168
+ }
169
+ }
170
+ // Per-thing facts: instances, and how hot they are running.
171
+ for (const spec of specs) {
172
+ const instances = env === 'prod' ? Math.round(between(4, 48)) : env === 'staging' ? 2 : 1;
173
+ nodes.push({
174
+ key: { service: spec.name },
175
+ dims: { env, region, team: spec.team, kind: 'service' },
176
+ attrs: {
177
+ instances,
178
+ cpu: clamp01(between(0.18, env === 'prod' ? 0.94 : 0.4)),
179
+ mem: clamp01(between(0.3, env === 'prod' ? 0.88 : 0.5)),
180
+ },
181
+ });
182
+ nodes.push({
183
+ key: { service: spec.store.name },
184
+ dims: { env, region, engine: spec.store.engine, team: spec.team, kind: storeKind(spec.store.engine) },
185
+ component: componentFor(spec.store.engine),
186
+ attrs: {
187
+ instances: env === 'prod' ? Math.round(between(3, 12)) : 1,
188
+ cpu: clamp01(between(0.25, 0.9)),
189
+ mem: clamp01(between(0.4, 0.95)),
190
+ },
191
+ });
192
+ }
193
+ nodes.push({
194
+ key: { service: 'gateway' },
195
+ dims: { env, region, team: 'Identity', kind: 'gateway' },
196
+ attrs: { instances: env === 'prod' ? 24 : 2, cpu: clamp01(between(0.3, 0.8)), mem: clamp01(between(0.3, 0.7)) },
197
+ });
198
+ nodes.push({
199
+ key: { service: 'clients' },
200
+ dims: { env, region, team: 'Outside', kind: 'client' },
201
+ attrs: { instances: 1, cpu: 0, mem: 0 },
202
+ });
203
+ }
204
+ return {
205
+ version: 1,
206
+ records,
207
+ nodes,
208
+ meta: {
209
+ title,
210
+ window: 'synthetic',
211
+ note: 'Every dimension here is a knob: service, api, env, region, team, engine, protocol, failure.',
212
+ },
213
+ };
214
+ }
215
+ function storeKind(engine) {
216
+ if (/redis|memcache/.test(engine))
217
+ return 'cache';
218
+ if (/kafka|sqs|rabbit/.test(engine))
219
+ return 'queue';
220
+ return 'datastore';
221
+ }
222
+ function componentFor(engine) {
223
+ if (/postgres|mysql|aurora/.test(engine))
224
+ return 'db-cylinder';
225
+ if (/cassandra|clickhouse|bigtable/.test(engine))
226
+ return 'db-discs';
227
+ if (/dynamo|rocks|kv/.test(engine))
228
+ return 'db-cube';
229
+ if (/kafka|pulsar|kinesis/.test(engine))
230
+ return 'db-drum';
231
+ if (/redis|memcache/.test(engine))
232
+ return 'cache-chip';
233
+ if (/s3|gcs|blob/.test(engine))
234
+ return 'blob-bucket';
235
+ return 'db-cylinder';
236
+ }
237
+ function engineProtocol(engine) {
238
+ if (/redis/.test(engine))
239
+ return 'resp';
240
+ if (/kafka/.test(engine))
241
+ return 'kafka';
242
+ if (/s3/.test(engine))
243
+ return 'https';
244
+ return 'sql';
245
+ }
246
+ function storeLatency(engine, jitter) {
247
+ const base = /redis|memcache/.test(engine)
248
+ ? 0.7
249
+ : /dynamo|kv/.test(engine)
250
+ ? 4
251
+ : /kafka/.test(engine)
252
+ ? 2.5
253
+ : /s3|blob/.test(engine)
254
+ ? 38
255
+ : /clickhouse|cassandra/.test(engine)
256
+ ? 22
257
+ : 9;
258
+ return base * jitter;
259
+ }
260
+ function clamp01(v) {
261
+ return Math.round(Math.max(0, Math.min(1, v)) * 100) / 100;
262
+ }
263
+ //# sourceMappingURL=mesh.js.map