@stacksjs/ts-cloud 0.7.104 → 0.7.108

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 (83) hide show
  1. package/dist/aws/index.js +2 -14
  2. package/dist/bin/cli.js +784 -785
  3. package/dist/bin/dashboard-server.js +291 -291
  4. package/dist/{chunk-dmzez5f8.js → chunk-33sy72r8.js} +52 -3
  5. package/dist/{chunk-5cz14833.js → chunk-ef2dwfrg.js} +3 -3
  6. package/dist/{chunk-gtz4jar2.js → chunk-eg8ck25v.js} +859 -151
  7. package/dist/{chunk-6hymgy61.js → chunk-gttvakpv.js} +1 -85
  8. package/dist/{chunk-adgz2c49.js → chunk-s9c2x5jv.js} +2 -2
  9. package/dist/deploy/dashboard-bundle-encoding.test.d.ts +1 -0
  10. package/dist/deploy/dashboard-payload.d.ts +20 -0
  11. package/dist/deploy/dashboard-payload.test.d.ts +1 -0
  12. package/dist/deploy/dashboard-topology.d.ts +85 -0
  13. package/dist/deploy/dashboard-topology.test.d.ts +1 -0
  14. package/dist/deploy/egress-collection.d.ts +60 -0
  15. package/dist/deploy/index.d.ts +2 -0
  16. package/dist/deploy/index.js +16 -4
  17. package/dist/dns/index.d.ts +0 -2
  18. package/dist/dns/index.js +2 -14
  19. package/dist/drivers/index.js +1 -1
  20. package/dist/index.d.ts +1 -2
  21. package/dist/index.js +5 -17
  22. package/dist/ui/404.html +1 -0
  23. package/dist/ui/access-denied.html +2 -2
  24. package/dist/ui/account/automation.html +4 -4
  25. package/dist/ui/account/security.html +2 -2
  26. package/dist/ui/applications/compose.html +3 -3
  27. package/dist/ui/applications/new.html +2 -2
  28. package/dist/ui/data/backups.html +22 -9
  29. package/dist/ui/data/services.html +22 -9
  30. package/dist/ui/data/volumes.html +22 -9
  31. package/dist/ui/index.html +22 -9
  32. package/dist/ui/infrastructure/topology.html +2223 -0
  33. package/dist/ui/integrations.html +2 -2
  34. package/dist/ui/operations/alerts.html +22 -9
  35. package/dist/ui/operations/configuration.html +4 -4
  36. package/dist/ui/operations/jobs.html +22 -9
  37. package/dist/ui/operations/maintenance.html +4 -4
  38. package/dist/ui/operations/observability.html +21 -8
  39. package/dist/ui/operations/previews.html +4 -4
  40. package/dist/ui/operations/queue.html +4 -4
  41. package/dist/ui/operations/regions.html +3 -3
  42. package/dist/ui/operations/releases.html +4 -4
  43. package/dist/ui/operations/workloads.html +22 -9
  44. package/dist/ui/security.html +2 -2
  45. package/dist/ui/server/actions.html +4 -4
  46. package/dist/ui/server/activity.html +2 -2
  47. package/dist/ui/server/capacity.html +4 -4
  48. package/dist/ui/server/database.html +4 -4
  49. package/dist/ui/server/deployments.html +4 -4
  50. package/dist/ui/server/diagnostics.html +2 -2
  51. package/dist/ui/server/firewall.html +3 -3
  52. package/dist/ui/server/fleet.html +22 -9
  53. package/dist/ui/server/logs.html +22 -9
  54. package/dist/ui/server/metrics.html +130 -14
  55. package/dist/ui/server/security.html +2 -2
  56. package/dist/ui/server/services.html +2 -2
  57. package/dist/ui/server/sites.html +21 -8
  58. package/dist/ui/server/ssh-keys.html +4 -4
  59. package/dist/ui/server/team.html +4 -4
  60. package/dist/ui/server/terminal.html +1 -1
  61. package/dist/ui/serverless/alarms.html +4 -4
  62. package/dist/ui/serverless/assets.html +2 -2
  63. package/dist/ui/serverless/cost.html +1 -1
  64. package/dist/ui/serverless/data.html +4 -4
  65. package/dist/ui/serverless/deployments.html +2 -2
  66. package/dist/ui/serverless/firewall.html +2 -2
  67. package/dist/ui/serverless/functions.html +4 -4
  68. package/dist/ui/serverless/logs.html +22 -9
  69. package/dist/ui/serverless/metrics.html +2 -2
  70. package/dist/ui/serverless/queues.html +4 -4
  71. package/dist/ui/serverless/secrets.html +4 -4
  72. package/dist/ui/serverless/traces.html +4 -4
  73. package/dist/ui/serverless.html +4 -4
  74. package/dist/ui-src/functions/requestJson.ts +41 -0
  75. package/dist/ui-src/functions/useOpConfirm.ts +132 -0
  76. package/dist/ui-src/functions/usePolling.ts +98 -0
  77. package/dist/ui-src/functions/useTerminal.ts +79 -0
  78. package/dist/ui-src/pages/infrastructure/topology.stx +517 -0
  79. package/dist/ui-src/pages/partials/nav.stx +2 -0
  80. package/dist/ui-src/pages/server/metrics.stx +140 -6
  81. package/dist/ui-src/src/topology.ts +485 -0
  82. package/package.json +3 -3
  83. package/dist/dns/mail-records.d.ts +0 -82
@@ -0,0 +1,485 @@
1
+ // ../../node_modules/@ts-charts/graph/dist/layered.js
2
+ var Mj = { direction: "down", nodeWidth: 160, nodeHeight: 56, layerGap: 80, nodeGap: 28, padding: 24, iterations: 8, straightenPasses: 6 };
3
+ function Nj(Q, W) {
4
+ let J = [], _ = new Map;
5
+ for (let H of Q)
6
+ _.set(H, []);
7
+ for (let H of W)
8
+ _.get(H.source)?.push(H);
9
+ let V = new Set, Z = new Set, G = new Set, T = (H) => {
10
+ V.add(H), Z.add(H);
11
+ for (let I of _.get(H) ?? [])
12
+ if (Z.has(I.target))
13
+ G.add(I);
14
+ else if (!V.has(I.target))
15
+ T(I.target);
16
+ Z.delete(H);
17
+ };
18
+ for (let H of Q)
19
+ if (!V.has(H))
20
+ T(H);
21
+ for (let H of W) {
22
+ let I = H.weight ?? 1;
23
+ if (G.has(H))
24
+ J.push({ source: H.target, target: H.source, weight: I, reversed: true });
25
+ else
26
+ J.push({ source: H.source, target: H.target, weight: I, reversed: false });
27
+ }
28
+ return J;
29
+ }
30
+ function Qj(Q, W) {
31
+ let J = new Map, _ = new Map, V = new Map, Z = new Map;
32
+ for (let H of Q)
33
+ _.set(H.id, []), V.set(H.id, []), Z.set(H.id, 0);
34
+ for (let H of W) {
35
+ if (H.source === H.target)
36
+ continue;
37
+ _.get(H.target)?.push(H.source), V.get(H.source)?.push(H.target), Z.set(H.target, (Z.get(H.target) ?? 0) + 1);
38
+ }
39
+ let G = [];
40
+ for (let H of Q)
41
+ if ((Z.get(H.id) ?? 0) === 0)
42
+ J.set(H.id, 0), G.push(H.id);
43
+ for (let H = 0;H < G.length; H++) {
44
+ let I = G[H];
45
+ for (let U of V.get(I) ?? []) {
46
+ J.set(U, Math.max(J.get(U) ?? 0, (J.get(I) ?? 0) + 1));
47
+ let L = (Z.get(U) ?? 0) - 1;
48
+ if (Z.set(U, L), L === 0)
49
+ G.push(U);
50
+ }
51
+ }
52
+ for (let H of Q)
53
+ if (!J.has(H.id))
54
+ J.set(H.id, 0);
55
+ for (let H of Q)
56
+ if (H.layer != null && Number.isFinite(H.layer))
57
+ J.set(H.id, H.layer);
58
+ let T = Math.min(...J.values());
59
+ if (T !== 0)
60
+ for (let [H, I] of J)
61
+ J.set(H, I - T);
62
+ return J;
63
+ }
64
+ function b(Q, W) {
65
+ let J = 0;
66
+ for (let _ = 0;_ + 1 < Q.length; _++) {
67
+ let V = new Map;
68
+ Q[_ + 1].forEach((G, T) => V.set(G, T));
69
+ let Z = [];
70
+ for (let G of Q[_])
71
+ for (let T of W.get(G) ?? []) {
72
+ let H = V.get(T);
73
+ if (H != null)
74
+ Z.push(H);
75
+ }
76
+ for (let G = 0;G < Z.length; G++)
77
+ for (let T = G + 1;T < Z.length; T++)
78
+ if (Z[G] > Z[T])
79
+ J++;
80
+ }
81
+ return J;
82
+ }
83
+ function s(Q) {
84
+ if (!Q.length)
85
+ return -1;
86
+ let W = [...Q].sort((Z, G) => Z - G), J = W.length >> 1;
87
+ if (W.length % 2 === 1)
88
+ return W[J];
89
+ if (W.length === 2)
90
+ return (W[0] + W[1]) / 2;
91
+ let _ = W[J - 1] - W[0], V = W[W.length - 1] - W[J];
92
+ return (W[J - 1] * V + W[J] * _) / (_ + V || 1);
93
+ }
94
+ function Tj(Q, W = {}) {
95
+ let J = { ...Mj, ...W }, _ = J.direction === "right" || J.direction === "left", V = new Map;
96
+ for (let j of Q.nodes)
97
+ V.set(j.id, j);
98
+ let Z = [...V.keys()], G = Q.edges.filter((j) => V.has(j.source) && V.has(j.target));
99
+ if (!Z.length)
100
+ return { nodes: [], edges: [], layers: [], width: J.padding * 2, height: J.padding * 2, crossings: 0 };
101
+ let T = Nj(Z, G), H = Qj(Q.nodes, T), I = new Map, U = (j) => _ ? j.width ?? J.nodeWidth : j.height ?? J.nodeHeight, L = (j) => _ ? j.height ?? J.nodeHeight : j.width ?? J.nodeWidth;
102
+ for (let j of Q.nodes)
103
+ I.set(j.id, { id: j.id, layer: H.get(j.id) ?? 0, order: 0, depth: U(j), breadth: L(j), pos: 0, dummy: false });
104
+ let A = [], P = [], z = "__bend";
105
+ while (Z.some((j) => j.startsWith(z)))
106
+ z = `_${z}`;
107
+ let d = 0;
108
+ for (let j of T) {
109
+ if (j.source === j.target)
110
+ continue;
111
+ let E = I.get(j.source), F = I.get(j.target), N = { source: j.source, target: j.target, weight: j.weight, reversed: j.reversed, bends: [] };
112
+ if (A.push(N), F.layer <= E.layer) {
113
+ P.push({ from: j.source, to: j.target, weight: j.weight });
114
+ continue;
115
+ }
116
+ let R = j.source;
117
+ for (let K = E.layer + 1;K < F.layer; K++) {
118
+ let X = `${z}${d++}`;
119
+ I.set(X, { id: X, layer: K, order: 0, depth: 0, breadth: 0, pos: 0, dummy: true }), N.bends.push(X), P.push({ from: R, to: X, weight: j.weight }), R = X;
120
+ }
121
+ P.push({ from: R, to: j.target, weight: j.weight });
122
+ }
123
+ let n = Math.max(...[...I.values()].map((j) => j.layer)) + 1, B = Array.from({ length: n }, () => []);
124
+ for (let j of I.values())
125
+ B[j.layer].push(j.id);
126
+ let q = new Map, O = new Map;
127
+ for (let j of I.keys())
128
+ q.set(j, []), O.set(j, []);
129
+ for (let j of P) {
130
+ let E = I.get(j.from);
131
+ if (I.get(j.to).layer === E.layer + 1)
132
+ q.get(j.from).push(j.to), O.get(j.to).push(j.from);
133
+ }
134
+ let t = (j) => {
135
+ let E = new Map;
136
+ return j.forEach((F, N) => E.set(F, N)), E;
137
+ }, o = (j, E) => {
138
+ let F = j.map((R) => [...R]), N = E ? [...F.keys()].slice(1) : [...F.keys()].slice(0, -1).reverse();
139
+ for (let R of N) {
140
+ let K = t(F[E ? R - 1 : R + 1]), X = E ? O : q, Y = new Map;
141
+ F[R].forEach(($, C) => {
142
+ let Jj = (X.get($) ?? []).map((x) => K.get(x)).filter((x) => x != null), a = s(Jj);
143
+ Y.set($, a < 0 ? C : a);
144
+ }), F[R].sort(($, C) => (Y.get($) ?? 0) - (Y.get(C) ?? 0));
145
+ }
146
+ return F;
147
+ }, r = (j) => {
148
+ let E = j.map((R) => [...R]), F = true, N = 0;
149
+ while (F && N++ < 8) {
150
+ F = false;
151
+ for (let R = 0;R < E.length; R++)
152
+ for (let K = 0;K + 1 < E[R].length; K++) {
153
+ let X = b(E, q), Y = E[R], $ = () => {
154
+ let C = Y[K];
155
+ Y[K] = Y[K + 1], Y[K + 1] = C;
156
+ };
157
+ if ($(), b(E, q) < X)
158
+ F = true;
159
+ else
160
+ $();
161
+ }
162
+ }
163
+ return E;
164
+ }, v = B.map((j) => [...j]), f = b(v, q);
165
+ for (let j = 0;j < J.iterations; j++) {
166
+ B = r(o(B, j % 2 === 0));
167
+ let E = b(B, q);
168
+ if (E <= f)
169
+ f = E, v = B.map((F) => [...F]);
170
+ }
171
+ B = v, B.forEach((j, E) => {
172
+ j.forEach((F, N) => {
173
+ let R = I.get(F);
174
+ R.layer = E, R.order = N;
175
+ });
176
+ });
177
+ let e = (j) => {
178
+ let E = 0;
179
+ for (let F of j) {
180
+ let N = I.get(F);
181
+ N.pos = E + N.breadth / 2, E += N.breadth + J.nodeGap;
182
+ }
183
+ };
184
+ for (let j of B)
185
+ e(j);
186
+ let jj = (j) => {
187
+ for (let E = 1;E < j.length; E++) {
188
+ let F = I.get(j[E - 1]), N = I.get(j[E]), R = F.pos + F.breadth / 2 + J.nodeGap + N.breadth / 2;
189
+ if (N.pos < R)
190
+ N.pos = R;
191
+ }
192
+ for (let E = j.length - 2;E >= 0; E--) {
193
+ let F = I.get(j[E + 1]), N = I.get(j[E]), R = F.pos - F.breadth / 2 - J.nodeGap - N.breadth / 2;
194
+ if (N.pos > R)
195
+ N.pos = R;
196
+ }
197
+ };
198
+ for (let j = 0;j < J.straightenPasses; j++) {
199
+ let E = j % 2 === 0, F = E ? [...B.keys()] : [...B.keys()].reverse();
200
+ for (let N of F) {
201
+ let R = E ? O : q;
202
+ for (let K of B[N]) {
203
+ let X = I.get(K), Y = (R.get(K) ?? []).map(($) => I.get($).pos);
204
+ if (Y.length)
205
+ X.pos = X.dummy ? Rj(Y) : s(Y);
206
+ }
207
+ jj(B[N]);
208
+ }
209
+ }
210
+ let Ej = (j) => {
211
+ let E = 1 / 0, F = -1 / 0;
212
+ for (let N of j) {
213
+ let R = I.get(N);
214
+ E = Math.min(E, R.pos - R.breadth / 2), F = Math.max(F, R.pos + R.breadth / 2);
215
+ }
216
+ return j.length ? { min: E, max: F } : { min: 0, max: 0 };
217
+ }, k = B.map(Ej), m = Math.max(0, ...k.map((j) => j.max - j.min));
218
+ B.forEach((j, E) => {
219
+ let F = (m - (k[E].max - k[E].min)) / 2 - k[E].min;
220
+ for (let N of j)
221
+ I.get(N).pos += F;
222
+ });
223
+ let S = B.map((j) => Math.max(0, ...j.map((E) => I.get(E).depth))), g = [], h = J.padding;
224
+ for (let j = 0;j < B.length; j++)
225
+ g.push(h), h += S[j] + J.layerGap;
226
+ let y = h - J.layerGap + J.padding, u = m + J.padding * 2, p = J.direction === "up" || J.direction === "left", c = (j, E) => {
227
+ let F = g[j] + (S[j] - E) / 2;
228
+ return p ? y - F - E : F;
229
+ }, D = (j) => {
230
+ let E = c(j.layer, j.depth), F = j.pos + J.padding - j.breadth / 2;
231
+ return _ ? { x: E, y: F, width: j.depth, height: j.breadth } : { x: F, y: E, width: j.breadth, height: j.depth };
232
+ }, l = [];
233
+ for (let j of Q.nodes) {
234
+ let E = I.get(j.id), F = D(E);
235
+ l.push({ id: j.id, layer: E.layer, order: E.order, ...F });
236
+ }
237
+ let Fj = (j) => {
238
+ let E = D(j);
239
+ return { x: E.x + E.width / 2, y: E.y + E.height / 2 };
240
+ }, w = (j, E) => {
241
+ let F = D(j), N = E !== p;
242
+ if (_)
243
+ return { x: N ? F.x + F.width : F.x, y: F.y + F.height / 2 };
244
+ return { x: F.x + F.width / 2, y: N ? F.y + F.height : F.y };
245
+ }, i = (j, E) => {
246
+ let F = D(j), N = E.pos >= j.pos;
247
+ if (_)
248
+ return { x: F.x + F.width / 2, y: N ? F.y + F.height : F.y };
249
+ return { x: N ? F.x + F.width : F.x, y: F.y + F.height / 2 };
250
+ }, Hj = A.map((j) => {
251
+ let E = I.get(j.source), F = I.get(j.target), N = F.layer === E.layer, R = F.layer < E.layer, K = N ? [i(E, F), i(F, E)] : R ? [w(E, false), w(F, true)] : [w(E, true), ...j.bends.map((X) => Fj(I.get(X))), w(F, false)];
252
+ return j.reversed ? { source: j.target, target: j.source, points: [...K].reverse(), reversed: true, flat: N } : { source: j.source, target: j.target, points: K, reversed: false, flat: N };
253
+ }), Ij = B.map((j, E) => {
254
+ let F = j.filter(($) => !I.get($).dummy).map(($) => D(I.get($))), N = c(E, S[E]);
255
+ if (!F.length)
256
+ return _ ? { index: E, nodes: [], x: N, y: J.padding, width: S[E], height: 0 } : { index: E, nodes: [], x: J.padding, y: N, width: 0, height: S[E] };
257
+ let R = Math.min(...F.map(($) => $.x)), K = Math.min(...F.map(($) => $.y)), X = Math.max(...F.map(($) => $.x + $.width)), Y = Math.max(...F.map(($) => $.y + $.height));
258
+ return { index: E, nodes: j.filter(($) => !I.get($).dummy), x: R, y: K, width: X - R, height: Y - K };
259
+ });
260
+ return { nodes: l, edges: Hj, layers: Ij, width: _ ? y : u, height: _ ? u : y, crossings: f };
261
+ }
262
+ function Rj(Q) {
263
+ let W = 0;
264
+ for (let J of Q)
265
+ W += J;
266
+ return W / Q.length;
267
+ }
268
+ function Uj(Q, W = {}) {
269
+ if (Q.length < 2)
270
+ return "";
271
+ let { curve: J = "smooth", direction: _ = "down", radius: V = 10 } = W, Z = _ === "right" || _ === "left";
272
+ if (J === "linear")
273
+ return `M${M(Q[0].x)},${M(Q[0].y)}${Q.slice(1).map((T) => `L${M(T.x)},${M(T.y)}`).join("")}`;
274
+ if (J === "orthogonal") {
275
+ let T = `M${M(Q[0].x)},${M(Q[0].y)}`;
276
+ for (let H = 1;H < Q.length; H++) {
277
+ let I = Q[H - 1], U = Q[H], L = Z ? (I.x + U.x) / 2 : (I.y + U.y) / 2, A = Math.min(V, Math.abs(Z ? U.y - I.y : U.x - I.x) / 2);
278
+ if (A < 0.5) {
279
+ T += `L${M(U.x)},${M(U.y)}`;
280
+ continue;
281
+ }
282
+ if (Z) {
283
+ let P = U.y > I.y ? 1 : -1;
284
+ T += `L${M(L - A)},${M(I.y)}Q${M(L)},${M(I.y)} ${M(L)},${M(I.y + A * P)}`, T += `L${M(L)},${M(U.y - A * P)}Q${M(L)},${M(U.y)} ${M(L + A)},${M(U.y)}`, T += `L${M(U.x)},${M(U.y)}`;
285
+ } else {
286
+ let P = U.x > I.x ? 1 : -1;
287
+ T += `L${M(I.x)},${M(L - A)}Q${M(I.x)},${M(L)} ${M(I.x + A * P)},${M(L)}`, T += `L${M(U.x - A * P)},${M(L)}Q${M(U.x)},${M(L)} ${M(U.x)},${M(L + A)}`, T += `L${M(U.x)},${M(U.y)}`;
288
+ }
289
+ }
290
+ return T;
291
+ }
292
+ let G = `M${M(Q[0].x)},${M(Q[0].y)}`;
293
+ for (let T = 1;T < Q.length; T++) {
294
+ let H = Q[T - 1], I = Q[T];
295
+ if (Z) {
296
+ let U = (H.x + I.x) / 2;
297
+ G += `C${M(U)},${M(H.y)} ${M(U)},${M(I.y)} ${M(I.x)},${M(I.y)}`;
298
+ } else {
299
+ let U = (H.y + I.y) / 2;
300
+ G += `C${M(H.x)},${M(U)} ${M(I.x)},${M(U)} ${M(I.x)},${M(I.y)}`;
301
+ }
302
+ }
303
+ return G;
304
+ }
305
+ function M(Q) {
306
+ return Math.round(Q * 100) / 100;
307
+ }
308
+
309
+ // ../ui/src/topology.ts
310
+ var NODE_HEIGHT = 64;
311
+ var LANE_GUTTER = 78;
312
+ var GROUP_PADDING = 22;
313
+ function midpoint(a, b2) {
314
+ return { x: (a.x + b2.x) / 2, y: (a.y + b2.y) / 2 };
315
+ }
316
+ var ICONS = {
317
+ internet: "M12 3a9 9 0 100 18 9 9 0 000-18zM3 12h18M12 3c2.5 2.4 3.8 5.5 3.8 9S14.5 21.6 12 21c-2.5-2.4-3.8-5.5-3.8-9S9.5 3 12 3z",
318
+ dns: "M4 7h16M4 12h16M4 17h10M7 4v16",
319
+ waf: "M12 3l7 3v5c0 4.4-2.9 8.2-7 10-4.1-1.8-7-5.6-7-10V6l7-3z",
320
+ firewall: "M3 6h18M3 12h18M3 18h18M8 6v6M16 6v6M12 12v6",
321
+ cdn: "M6 17a4 4 0 010-8 5.5 5.5 0 0110.6-1.3A4.2 4.2 0 0118 17H6z",
322
+ gateway: "M4 8h16M4 16h16M8 4v16M16 4v16",
323
+ proxy: "M4 6h7l3 3h6M4 18h7l3-3h6M17 6l3 3-3 3M17 12l3 3-3 3",
324
+ site: "M4 5h16v14H4zM4 9h16M7 7h.01M10 7h.01",
325
+ function: "M8 5c-2 0-2 3-2 5s-1 2-2 2 1 0 2 2 0 5 2 5M16 5c2 0 2 3 2 5s1 2 2 2-1 0-2 2 0 5-2 5",
326
+ worker: "M12 9a3 3 0 100 6 3 3 0 000-6zM12 3v3M12 18v3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M3 12h3M18 12h3M4.9 19.1L7 17M17 7l2.1-2.1",
327
+ scheduler: "M12 3a9 9 0 100 18 9 9 0 000-18zM12 7v5l3 2",
328
+ queue: "M4 7h16v4H4zM4 13h16v4H4z",
329
+ cache: "M12 4c4.4 0 8 1.3 8 3s-3.6 3-8 3-8-1.3-8-3 3.6-3 8-3zM4 7v10c0 1.7 3.6 3 8 3s8-1.3 8-3V7",
330
+ database: "M12 3c4.4 0 8 1.3 8 3s-3.6 3-8 3-8-1.3-8-3 3.6-3 8-3zM4 6v12c0 1.7 3.6 3 8 3s8-1.3 8-3V6M4 12c0 1.7 3.6 3 8 3s8-1.3 8-3",
331
+ storage: "M4 6h16v12H4zM4 10h16M8 14h4",
332
+ filesystem: "M4 7a2 2 0 012-2h4l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H6a2 2 0 01-2-2V7z",
333
+ backup: "M12 5a7 7 0 107 7M12 5V2M12 5l3 3M5 12a7 7 0 002 5"
334
+ };
335
+ var FALLBACK_ICON = "M5 5h14v14H5z";
336
+ function iconFor(kind) {
337
+ return ICONS[kind] ?? FALLBACK_ICON;
338
+ }
339
+ function widthFor(node) {
340
+ const label = (node.label ?? "").length;
341
+ const sub = (node.sub ?? "").length;
342
+ const longest = Math.max(label * 8.4, sub * 6.4);
343
+ return Math.round(Math.min(288, Math.max(168, longest + 78)));
344
+ }
345
+ function topologyGeometry(model, options = {}) {
346
+ const laneGutter = options.laneGutter ?? LANE_GUTTER;
347
+ const nodeHeight = options.nodeHeight ?? NODE_HEIGHT;
348
+ const nodes = Array.isArray(model?.nodes) ? model.nodes : [];
349
+ const links = Array.isArray(model?.links) ? model.links : [];
350
+ if (!nodes.length)
351
+ return { width: 400, height: laneGutter + 200, nodes: [], links: [], lanes: [], groups: [], crossings: 0 };
352
+ const used = [...new Set(nodes.map((node) => node.layer))].sort((a, b2) => a - b2);
353
+ const rankOf = new Map(used.map((layer, rank) => [layer, rank]));
354
+ const layout = Tj({
355
+ nodes: nodes.map((node) => ({
356
+ id: node.id,
357
+ layer: rankOf.get(node.layer) ?? 0,
358
+ width: widthFor(node),
359
+ height: nodeHeight
360
+ })),
361
+ edges: links.map((link) => ({ source: link.source, target: link.target }))
362
+ }, {
363
+ direction: "right",
364
+ layerGap: options.layerGap ?? 96,
365
+ nodeGap: options.nodeGap ?? 30,
366
+ padding: 28
367
+ });
368
+ const placed = new Map(layout.nodes.map((node) => [node.id, node]));
369
+ const neighbours = new Map;
370
+ for (const node of nodes)
371
+ neighbours.set(node.id, new Set);
372
+ for (const link of links) {
373
+ neighbours.get(link.source)?.add(link.target);
374
+ neighbours.get(link.target)?.add(link.source);
375
+ }
376
+ const shift = laneGutter;
377
+ const geometryNodes = nodes.flatMap((node) => {
378
+ const box = placed.get(node.id);
379
+ if (!box)
380
+ return [];
381
+ const y = box.y + shift;
382
+ return [{
383
+ ...node,
384
+ x: box.x,
385
+ y,
386
+ width: box.width,
387
+ height: box.height,
388
+ cx: box.x + box.width / 2,
389
+ cy: y + box.height / 2,
390
+ icon: iconFor(node.kind),
391
+ neighbours: [...neighbours.get(node.id) ?? []]
392
+ }];
393
+ });
394
+ const pending = new Map;
395
+ for (const link of links) {
396
+ const key = `${link.source}->${link.target}`;
397
+ if (!pending.has(key))
398
+ pending.set(key, []);
399
+ pending.get(key).push(link);
400
+ }
401
+ const geometryLinks = layout.edges.map((edge) => {
402
+ const source = pending.get(`${edge.source}->${edge.target}`)?.shift();
403
+ const points = edge.points.map((point) => ({ x: point.x, y: point.y + shift }));
404
+ const middle = points.length % 2 ? points[Math.floor(points.length / 2)] : midpoint(points[points.length / 2 - 1], points[points.length / 2]);
405
+ return {
406
+ source: edge.source,
407
+ target: edge.target,
408
+ flow: source?.flow ?? "request",
409
+ label: source?.label,
410
+ status: source?.status,
411
+ id: `${edge.source}->${edge.target}`,
412
+ d: Uj(points, { curve: "smooth", direction: "right" }),
413
+ labelX: middle.x,
414
+ labelY: middle.y - 7
415
+ };
416
+ });
417
+ const width = layout.width;
418
+ const height = layout.height + shift;
419
+ const labelOf = new Map((model.layers ?? []).map((layer) => [layer.index, layer.label]));
420
+ const lanes = used.flatMap((layer) => {
421
+ const members = geometryNodes.filter((node) => node.layer === layer);
422
+ if (!members.length)
423
+ return [];
424
+ const left = Math.min(...members.map((node) => node.x));
425
+ const right = Math.max(...members.map((node) => node.x + node.width));
426
+ return [{
427
+ id: `lane-${layer}`,
428
+ label: labelOf.get(layer) ?? `Layer ${layer}`,
429
+ x: left - 16,
430
+ y: shift - 34,
431
+ width: right - left + 32,
432
+ height: height - shift + 46,
433
+ count: members.length
434
+ }];
435
+ });
436
+ const groups = (model.groups ?? []).flatMap((group) => {
437
+ const members = geometryNodes.filter((node) => node.group === group.id);
438
+ if (!members.length)
439
+ return [];
440
+ const x = Math.min(...members.map((node) => node.x)) - GROUP_PADDING;
441
+ const y = Math.min(...members.map((node) => node.y)) - GROUP_PADDING - 12;
442
+ const right = Math.max(...members.map((node) => node.x + node.width)) + GROUP_PADDING;
443
+ const bottom = Math.max(...members.map((node) => node.y + node.height)) + GROUP_PADDING;
444
+ return [{ ...group, x, y, width: right - x, height: bottom - y }];
445
+ });
446
+ return { width, height, nodes: geometryNodes, links: geometryLinks, lanes, groups, crossings: layout.crossings };
447
+ }
448
+ function matchTopology(nodes, query) {
449
+ const needle = query.trim().toLowerCase();
450
+ if (!needle)
451
+ return new Set(nodes.map((node) => node.id));
452
+ return new Set(nodes.filter((node) => `${node.label} ${node.kind} ${node.sub ?? ""} ${node.facts.map((fact) => fact.value).join(" ")}`.toLowerCase().includes(needle)).map((node) => node.id));
453
+ }
454
+ function highlightFor(geometry, id) {
455
+ if (!id)
456
+ return null;
457
+ const keep = new Set([id]);
458
+ const incoming = new Map;
459
+ for (const link of geometry.links) {
460
+ if (!incoming.has(link.target))
461
+ incoming.set(link.target, []);
462
+ incoming.get(link.target).push(link.source);
463
+ }
464
+ const node = geometry.nodes.find((candidate) => candidate.id === id);
465
+ for (const neighbour of node?.neighbours ?? [])
466
+ keep.add(neighbour);
467
+ const seen = new Set([id]);
468
+ const queue = [id];
469
+ while (queue.length) {
470
+ const current = queue.shift();
471
+ for (const parent of incoming.get(current) ?? []) {
472
+ keep.add(parent);
473
+ if (seen.has(parent))
474
+ continue;
475
+ seen.add(parent);
476
+ queue.push(parent);
477
+ }
478
+ }
479
+ return keep;
480
+ }
481
+ export {
482
+ topologyGeometry,
483
+ matchTopology,
484
+ highlightFor
485
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/ts-cloud",
3
3
  "type": "module",
4
- "version": "0.7.104",
4
+ "version": "0.7.108",
5
5
  "description": "A lightweight, performant infrastructure-as-code library and CLI for deploying both server-based (EC2) and serverless applications.",
6
6
  "author": "Chris Breuer <chris@stacksjs.com>",
7
7
  "license": "MIT",
@@ -90,8 +90,8 @@
90
90
  },
91
91
  "dependencies": {
92
92
  "@stacksjs/ts-xml": "^0.1.0",
93
- "@ts-cloud/aws-types": "0.7.104",
94
- "@ts-cloud/core": "0.7.104"
93
+ "@ts-cloud/aws-types": "0.7.108",
94
+ "@ts-cloud/core": "0.7.108"
95
95
  },
96
96
  "devDependencies": {
97
97
  "better-dx": "^0.2.16",
@@ -1,82 +0,0 @@
1
- /**
2
- * Mail DNS provisioning.
3
- *
4
- * A domain that sends mail needs four records, and getting any one of them
5
- * wrong is not a visible error: mail simply arrives in spam, or is silently
6
- * rejected by the receiving side. That failure mode is why this belongs in the
7
- * deploy rather than in a runbook — a deploy that stands up a mail-sending app
8
- * and leaves its DNS to be typed by hand has not finished the job.
9
- *
10
- * The record set is deliberately a pure function of its inputs, so what a
11
- * deploy is about to publish can be inspected, diffed and tested without an
12
- * API call or a live zone.
13
- *
14
- * @module ts-cloud/dns/mail-records
15
- */
16
- import type { DnsProvider, DnsRecord } from './types';
17
- export interface MailDnsOptions {
18
- /** The domain that sends mail, e.g. `example.com`. */
19
- domain: string;
20
- /** Hostname of the mail server that receives for it. */
21
- mailHost: string;
22
- /** Public IPv4 of the sending server, authorised in SPF. */
23
- ip?: string;
24
- /** Additional SPF mechanisms, e.g. `include:amazonses.com`. */
25
- spfIncludes?: string[];
26
- /** DKIM selector. Convention is `default`. */
27
- dkimSelector?: string;
28
- /**
29
- * DKIM public key, base64, without PEM armour or newlines. Omit to skip the
30
- * DKIM record: publishing an empty or malformed key is worse than publishing
31
- * none, because receivers treat a broken signature as a stronger negative
32
- * signal than an unsigned message.
33
- */
34
- dkimPublicKey?: string;
35
- /** MX preference. */
36
- mxPriority?: number;
37
- /**
38
- * DMARC policy. `none` by default: it asks receivers to report rather than
39
- * to reject, which is the only safe setting to switch on automatically for a
40
- * domain whose mail history nobody has checked yet.
41
- */
42
- dmarcPolicy?: 'none' | 'quarantine' | 'reject';
43
- /** Where aggregate DMARC reports go. Omitted from the record when absent. */
44
- dmarcReportTo?: string;
45
- ttl?: number;
46
- }
47
- /** SPF is a single TXT record; more than one is a permanent error. */
48
- export declare function buildSpfValue(options: Pick<MailDnsOptions, 'ip' | 'spfIncludes'>): string;
49
- /** Strip PEM armour and whitespace, which a DKIM TXT record must not contain. */
50
- export declare function normalizeDkimKey(key: string): string;
51
- export declare function buildDkimValue(publicKey: string): string;
52
- export declare function buildDmarcValue(options: Pick<MailDnsOptions, 'dmarcPolicy' | 'dmarcReportTo'>): string;
53
- /**
54
- * The full record set a mail-sending domain needs.
55
- *
56
- * Names are returned as the labels providers expect (`@` for the apex), since
57
- * every provider in this package normalises from that form.
58
- */
59
- export declare function buildMailDnsRecords(options: MailDnsOptions): DnsRecord[];
60
- export interface MailDnsSyncResult {
61
- domain: string;
62
- applied: Array<{
63
- name: string;
64
- type: string;
65
- ok: boolean;
66
- message?: string;
67
- }>;
68
- /** True when every record was written. */
69
- ok: boolean;
70
- }
71
- /**
72
- * Publish the record set, idempotently.
73
- *
74
- * Uses `upsertRecord` rather than create, so a redeploy converges instead of
75
- * accumulating duplicate SPF records — two SPF TXT records on one domain is a
76
- * permanent error under RFC 7208 and breaks mail that previously worked.
77
- *
78
- * Never throws. Mail DNS is one part of a deploy, and a zone that is
79
- * momentarily unreachable should surface as a reported failure rather than
80
- * roll back an otherwise healthy release.
81
- */
82
- export declare function syncMailDns(provider: DnsProvider, options: MailDnsOptions): Promise<MailDnsSyncResult>;