koppler 0.0.0-stage → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +101 -2
  3. package/bin/run-logged +16 -0
  4. package/bin/secret-list +7 -0
  5. package/bin/secret-psql +41 -0
  6. package/bin/secret-set +17 -0
  7. package/bin/secret-store +82 -0
  8. package/bin/secret-test +10 -0
  9. package/bin/with-secrets +25 -0
  10. package/dist/.built +1 -0
  11. package/dist/cli/agent.js +144 -0
  12. package/dist/cli/agent.js.map +1 -0
  13. package/dist/cli/doctor.js +173 -0
  14. package/dist/cli/doctor.js.map +1 -0
  15. package/dist/cli/mcp.js +204 -0
  16. package/dist/cli/mcp.js.map +1 -0
  17. package/dist/cli/output.js +37 -0
  18. package/dist/cli/output.js.map +1 -0
  19. package/dist/cli/records.js +121 -0
  20. package/dist/cli/records.js.map +1 -0
  21. package/dist/cli/running.js +270 -0
  22. package/dist/cli/running.js.map +1 -0
  23. package/dist/cli/shell.js +43 -0
  24. package/dist/cli/shell.js.map +1 -0
  25. package/dist/cli/wait.js +59 -0
  26. package/dist/cli/wait.js.map +1 -0
  27. package/dist/core/addresses.js +55 -0
  28. package/dist/core/addresses.js.map +1 -0
  29. package/dist/core/arrange.js +329 -0
  30. package/dist/core/arrange.js.map +1 -0
  31. package/dist/core/cables.js +158 -0
  32. package/dist/core/cables.js.map +1 -0
  33. package/dist/core/expressions.js +72 -0
  34. package/dist/core/expressions.js.map +1 -0
  35. package/dist/core/frameworks.js +13 -0
  36. package/dist/core/frameworks.js.map +1 -0
  37. package/dist/core/kinds.js +244 -0
  38. package/dist/core/kinds.js.map +1 -0
  39. package/dist/core/launch.js +181 -0
  40. package/dist/core/launch.js.map +1 -0
  41. package/dist/core/logfmt.js +144 -0
  42. package/dist/core/logfmt.js.map +1 -0
  43. package/dist/core/mock.js +27 -0
  44. package/dist/core/mock.js.map +1 -0
  45. package/dist/core/plan.js +147 -0
  46. package/dist/core/plan.js.map +1 -0
  47. package/dist/core/ports.js +155 -0
  48. package/dist/core/ports.js.map +1 -0
  49. package/dist/core/presets.js +186 -0
  50. package/dist/core/presets.js.map +1 -0
  51. package/dist/core/secret-types.js +3 -0
  52. package/dist/core/secret-types.js.map +1 -0
  53. package/dist/core/sign-in.js +30 -0
  54. package/dist/core/sign-in.js.map +1 -0
  55. package/dist/core/stack-path.js +64 -0
  56. package/dist/core/stack-path.js.map +1 -0
  57. package/dist/core/stack.js +509 -0
  58. package/dist/core/stack.js.map +1 -0
  59. package/dist/core/terraform.js +393 -0
  60. package/dist/core/terraform.js.map +1 -0
  61. package/dist/data.js +37 -0
  62. package/dist/data.js.map +1 -0
  63. package/dist/engine/actions.js +99 -0
  64. package/dist/engine/actions.js.map +1 -0
  65. package/dist/engine/app-commands.js +106 -0
  66. package/dist/engine/app-commands.js.map +1 -0
  67. package/dist/engine/app-start.js +167 -0
  68. package/dist/engine/app-start.js.map +1 -0
  69. package/dist/engine/app-state.js +43 -0
  70. package/dist/engine/app-state.js.map +1 -0
  71. package/dist/engine/boards.js +219 -0
  72. package/dist/engine/boards.js.map +1 -0
  73. package/dist/engine/branch.js +194 -0
  74. package/dist/engine/branch.js.map +1 -0
  75. package/dist/engine/browser-console.js +45 -0
  76. package/dist/engine/browser-console.js.map +1 -0
  77. package/dist/engine/calls.js +97 -0
  78. package/dist/engine/calls.js.map +1 -0
  79. package/dist/engine/checks.js +270 -0
  80. package/dist/engine/checks.js.map +1 -0
  81. package/dist/engine/clones.js +67 -0
  82. package/dist/engine/clones.js.map +1 -0
  83. package/dist/engine/config.js +26 -0
  84. package/dist/engine/config.js.map +1 -0
  85. package/dist/engine/deployed.js +488 -0
  86. package/dist/engine/deployed.js.map +1 -0
  87. package/dist/engine/devices.js +81 -0
  88. package/dist/engine/devices.js.map +1 -0
  89. package/dist/engine/discover.js +236 -0
  90. package/dist/engine/discover.js.map +1 -0
  91. package/dist/engine/down.js +253 -0
  92. package/dist/engine/down.js.map +1 -0
  93. package/dist/engine/history.js +215 -0
  94. package/dist/engine/history.js.map +1 -0
  95. package/dist/engine/investigate.js +278 -0
  96. package/dist/engine/investigate.js.map +1 -0
  97. package/dist/engine/libraries.js +129 -0
  98. package/dist/engine/libraries.js.map +1 -0
  99. package/dist/engine/linked.js +146 -0
  100. package/dist/engine/linked.js.map +1 -0
  101. package/dist/engine/listen.js +223 -0
  102. package/dist/engine/listen.js.map +1 -0
  103. package/dist/engine/locks.js +38 -0
  104. package/dist/engine/locks.js.map +1 -0
  105. package/dist/engine/messages.js +177 -0
  106. package/dist/engine/messages.js.map +1 -0
  107. package/dist/engine/needs.js +363 -0
  108. package/dist/engine/needs.js.map +1 -0
  109. package/dist/engine/processes.js +350 -0
  110. package/dist/engine/processes.js.map +1 -0
  111. package/dist/engine/query.js +241 -0
  112. package/dist/engine/query.js.map +1 -0
  113. package/dist/engine/queues.js +100 -0
  114. package/dist/engine/queues.js.map +1 -0
  115. package/dist/engine/recipes.js +248 -0
  116. package/dist/engine/recipes.js.map +1 -0
  117. package/dist/engine/reload.js +32 -0
  118. package/dist/engine/reload.js.map +1 -0
  119. package/dist/engine/repos.js +214 -0
  120. package/dist/engine/repos.js.map +1 -0
  121. package/dist/engine/research.js +134 -0
  122. package/dist/engine/research.js.map +1 -0
  123. package/dist/engine/run-logs.js +79 -0
  124. package/dist/engine/run-logs.js.map +1 -0
  125. package/dist/engine/run.js +54 -0
  126. package/dist/engine/run.js.map +1 -0
  127. package/dist/engine/runs.js +223 -0
  128. package/dist/engine/runs.js.map +1 -0
  129. package/dist/engine/session.js +130 -0
  130. package/dist/engine/session.js.map +1 -0
  131. package/dist/engine/share.js +124 -0
  132. package/dist/engine/share.js.map +1 -0
  133. package/dist/engine/stack-file.js +206 -0
  134. package/dist/engine/stack-file.js.map +1 -0
  135. package/dist/engine/stack-share.js +86 -0
  136. package/dist/engine/stack-share.js.map +1 -0
  137. package/dist/engine/stacks.js +143 -0
  138. package/dist/engine/stacks.js.map +1 -0
  139. package/dist/engine/sync.js +214 -0
  140. package/dist/engine/sync.js.map +1 -0
  141. package/dist/engine/traces.js +65 -0
  142. package/dist/engine/traces.js.map +1 -0
  143. package/dist/engine/up.js +621 -0
  144. package/dist/engine/up.js.map +1 -0
  145. package/dist/engine/watch.js +71 -0
  146. package/dist/engine/watch.js.map +1 -0
  147. package/dist/engine/wire.js +120 -0
  148. package/dist/engine/wire.js.map +1 -0
  149. package/dist/failure.js +16 -0
  150. package/dist/failure.js.map +1 -0
  151. package/dist/github.js +95 -0
  152. package/dist/github.js.map +1 -0
  153. package/dist/ids.js +6 -0
  154. package/dist/ids.js.map +1 -0
  155. package/dist/json-files.js +36 -0
  156. package/dist/json-files.js.map +1 -0
  157. package/dist/koppler.mjs +840 -0
  158. package/dist/koppler.mjs.map +1 -0
  159. package/dist/lambda-local.mjs +250 -0
  160. package/dist/lambda-local.mjs.map +1 -0
  161. package/dist/lambda-secrets-proxy.go +31 -0
  162. package/dist/mock-server.mjs +23 -0
  163. package/dist/mock-server.mjs.map +1 -0
  164. package/dist/page/assets/JetBrainsMono-cyrillic-D2jMJ_bk.woff2 +0 -0
  165. package/dist/page/assets/JetBrainsMono-greek-D2nYnAec.woff2 +0 -0
  166. package/dist/page/assets/JetBrainsMono-latin-Db4Uuiha.woff2 +0 -0
  167. package/dist/page/assets/JetBrainsMono-latin-ext-Bnd2h4Br.woff2 +0 -0
  168. package/dist/page/assets/JetBrainsMono-vietnamese-CNlsqbWb.woff2 +0 -0
  169. package/dist/page/assets/SchibstedGrotesk-latin-DxefFeRw.woff2 +0 -0
  170. package/dist/page/assets/SchibstedGrotesk-latin-ext-CnpJE18X.woff2 +0 -0
  171. package/dist/page/assets/index-Bkj_pX35.js +42 -0
  172. package/dist/page/assets/index-Dq45jswH.css +1 -0
  173. package/dist/page/index.html +14 -0
  174. package/dist/page/theme.js +8 -0
  175. package/dist/paths.js +19 -0
  176. package/dist/paths.js.map +1 -0
  177. package/dist/safe-env.js +73 -0
  178. package/dist/safe-env.js.map +1 -0
  179. package/dist/sam-local.mjs +135 -0
  180. package/dist/sam-local.mjs.map +1 -0
  181. package/dist/scrub.js +87 -0
  182. package/dist/scrub.js.map +1 -0
  183. package/dist/secret-check.mjs +262 -0
  184. package/dist/secret-check.mjs.map +1 -0
  185. package/dist/server/browser-console.js +117 -0
  186. package/dist/server/browser-console.js.map +1 -0
  187. package/dist/server/cable-test.js +365 -0
  188. package/dist/server/cable-test.js.map +1 -0
  189. package/dist/server/choices.js +51 -0
  190. package/dist/server/choices.js.map +1 -0
  191. package/dist/server/files.js +107 -0
  192. package/dist/server/files.js.map +1 -0
  193. package/dist/server/gate.js +128 -0
  194. package/dist/server/gate.js.map +1 -0
  195. package/dist/server/logs.js +76 -0
  196. package/dist/server/logs.js.map +1 -0
  197. package/dist/server/mobile.js +113 -0
  198. package/dist/server/mobile.js.map +1 -0
  199. package/dist/server/routes.js +326 -0
  200. package/dist/server/routes.js.map +1 -0
  201. package/dist/server/runs.js +111 -0
  202. package/dist/server/runs.js.map +1 -0
  203. package/dist/server/sanitize.js +242 -0
  204. package/dist/server/sanitize.js.map +1 -0
  205. package/dist/server/secrets.js +111 -0
  206. package/dist/server/secrets.js.map +1 -0
  207. package/dist/server/status.js +321 -0
  208. package/dist/server/status.js.map +1 -0
  209. package/dist/server/token.js +38 -0
  210. package/dist/server/token.js.map +1 -0
  211. package/dist/server/waiting.js +79 -0
  212. package/dist/server/waiting.js.map +1 -0
  213. package/dist/status-watch.mjs +63 -0
  214. package/dist/status-watch.mjs.map +1 -0
  215. package/package.json +73 -4
  216. package/skills/koppler-run/SKILL.md +169 -0
  217. package/skills/koppler-run/VERSION +1 -0
  218. package/skills/koppler-run/references/action-log.md +50 -0
  219. package/skills/koppler-run/references/permissions.md +25 -0
  220. package/skills/koppler-run/references/preparing-nodes.md +62 -0
  221. package/skills/koppler-run/references/reporting-bugs.md +76 -0
  222. package/skills/koppler-run/references/research.md +40 -0
  223. package/skills/koppler-run/references/secrets.md +56 -0
  224. package/skills/koppler-run/references/stack.md +197 -0
  225. package/skills/koppler-run/references/status.md +92 -0
@@ -0,0 +1,329 @@
1
+ const GAP_X = 80;
2
+ const GAP_Y = 90;
3
+ const get = (from, key) => from[key];
4
+ /**
5
+ * Cables crossing each other are counted between neighbouring layers. A cable that skips layers passes a waypoint in
6
+ * each one it crosses, so it is counted where it really runs (the usual layered-graph method).
7
+ */
8
+ function withWaypoints(layers, edges) {
9
+ // Rows are the layers as laid out (empty depths already dropped), so a card's row is its index here.
10
+ const rows = layers.map(layer => [...layer]);
11
+ const links = [];
12
+ const row = {};
13
+ rows.forEach((layer, at) => {
14
+ for (const id of layer)
15
+ row[id] = at;
16
+ });
17
+ for (const edge of edges) {
18
+ const forwards = get(row, edge.from) <= get(row, edge.to);
19
+ const to = forwards ? edge.to : edge.from;
20
+ let from = forwards ? edge.from : edge.to;
21
+ if (row[from] === row[to])
22
+ continue;
23
+ for (let at = get(row, from) + 1; at < get(row, to); at++) {
24
+ const waypoint = `~${edge.from}>${edge.to}:${String(at)}`;
25
+ row[waypoint] = at;
26
+ rows[at]?.push(waypoint);
27
+ links.push([from, waypoint]);
28
+ from = waypoint;
29
+ }
30
+ links.push([from, to]);
31
+ }
32
+ return { rows, links, row };
33
+ }
34
+ const placesIn = (rows) => {
35
+ const place = {};
36
+ for (const layer of rows)
37
+ layer.forEach((id, at) => (place[id] = at));
38
+ return place;
39
+ };
40
+ function crossingsOf(rows, links, row) {
41
+ const place = placesIn(rows);
42
+ let crossings = 0;
43
+ links.forEach(([a1, a2], first) => {
44
+ for (const [b1, b2] of links.slice(first + 1)) {
45
+ if (row[a1] !== row[b1])
46
+ continue;
47
+ if ((get(place, a1) - get(place, b1)) * (get(place, a2) - get(place, b2)) < 0)
48
+ crossings++;
49
+ }
50
+ });
51
+ return crossings;
52
+ }
53
+ /**
54
+ * Orders each layer so cables cross as little as possible: each card (and waypoint) moves to the average place of its
55
+ * neighbours in the layer before it, sweeping down then up; the order with the fewest crossings wins. Returns that
56
+ * number, with `layers` reordered in place.
57
+ */
58
+ function orderLayers(layers, edges) {
59
+ const { rows, links, row } = withWaypoints(layers, edges);
60
+ const near = {};
61
+ for (const [a, b] of links) {
62
+ (near[a] ??= []).push(b);
63
+ (near[b] ??= []).push(a);
64
+ }
65
+ let best = rows.map(layer => [...layer]);
66
+ let fewest = crossingsOf(rows, links, row);
67
+ for (let sweep = 0; sweep < 12 && fewest; sweep++) {
68
+ const down = sweep % 2 === 0;
69
+ const order = down
70
+ ? rows.map((_, at) => at).slice(1)
71
+ : rows.map((_, at) => rows.length - 2 - at).filter(at => at >= 0);
72
+ for (const at of order) {
73
+ const place = placesIn(rows);
74
+ const mean = (id) => {
75
+ const before = (near[id] ?? []).filter(other => row[other] === at + (down ? -1 : 1));
76
+ return before.length
77
+ ? before.reduce((sum, other) => sum + get(place, other), 0) / before.length
78
+ : get(place, id);
79
+ };
80
+ rows[at]?.sort((a, b) => mean(a) - mean(b));
81
+ }
82
+ const crossings = crossingsOf(rows, links, row);
83
+ if (crossings < fewest) {
84
+ fewest = crossings;
85
+ best = rows.map(layer => [...layer]);
86
+ }
87
+ }
88
+ layers.forEach((layer, at) => {
89
+ layer.splice(0, layer.length, ...(best[at] ?? []).filter(id => !id.startsWith('~')));
90
+ });
91
+ return fewest;
92
+ }
93
+ /**
94
+ * Each card's layer, top down. The walk starts at the ways in (cards nothing leads to, the one reaching the most first);
95
+ * a link that closes a loop runs back up and does not decide the layers, so a loop reads in its own order. A card sits
96
+ * one layer under the furthest card leading to it, then moves down to just above the nearest card it leads to: a card
97
+ * that only feeds a queue sits right over it, not up with the way in.
98
+ */
99
+ export function layerDepths(ids, edges) {
100
+ const out = Object.fromEntries(ids.map(id => [id, []]));
101
+ for (const { from, to } of edges)
102
+ if (from in out && to in out && from !== to)
103
+ out[from]?.push(to);
104
+ const reach = (start) => {
105
+ const seen = [start];
106
+ for (let at = 0; at < seen.length; at++)
107
+ for (const next of out[seen[at] ?? ''] ?? [])
108
+ if (!seen.includes(next))
109
+ seen.push(next);
110
+ return seen.length;
111
+ };
112
+ const led = new Set(Object.values(out).flat());
113
+ const starts = [
114
+ ...ids.filter(id => !led.has(id)).sort((a, b) => reach(b) - reach(a)),
115
+ ...[...ids].sort((a, b) => reach(b) - reach(a)),
116
+ ];
117
+ // Depth first from the ways in: a link to a card still on the path closes a loop and is left out.
118
+ const forward = Object.fromEntries(ids.map(id => [id, []]));
119
+ const done = [];
120
+ const state = {};
121
+ const visit = (id) => {
122
+ state[id] = 'open';
123
+ for (const next of out[id] ?? []) {
124
+ if (state[next] === 'open')
125
+ continue;
126
+ forward[id]?.push(next);
127
+ if (!state[next])
128
+ visit(next);
129
+ }
130
+ state[id] = 'done';
131
+ done.push(id);
132
+ };
133
+ for (const id of starts)
134
+ if (!state[id])
135
+ visit(id);
136
+ // `done` ends with the ways in: read backwards, every card comes after all that lead to it.
137
+ const order = [...done].reverse();
138
+ const depth = Object.fromEntries(ids.map(id => [id, 0]));
139
+ for (const id of order)
140
+ for (const next of forward[id] ?? [])
141
+ depth[next] = Math.max(get(depth, next), get(depth, id) + 1);
142
+ for (const id of done) {
143
+ const below = (forward[id] ?? []).map(next => get(depth, next));
144
+ if (below.length)
145
+ depth[id] = Math.max(get(depth, id), Math.min(...below) - 1);
146
+ }
147
+ return depth;
148
+ }
149
+ /**
150
+ * Moves each card along its layer towards the cards it is cabled to, so cables run as straight as they can: a chain
151
+ * lines up, a card called by two sits between them. Each layer in turn takes the places that make its cables shortest
152
+ * while its cards keep their order and gaps (pooled adjacent averages, each card weighed by its cables), until nothing
153
+ * moves.
154
+ */
155
+ function straighten(layers, edges, at, size, gap, anchor) {
156
+ const layerOf = {};
157
+ layers.forEach((layer, n) => {
158
+ for (const id of layer)
159
+ layerOf[id] = n;
160
+ });
161
+ const near = {};
162
+ for (const { from, to } of edges) {
163
+ if (!(from in layerOf) || !(to in layerOf) || layerOf[from] === layerOf[to])
164
+ continue;
165
+ (near[from] ??= []).push(to);
166
+ (near[to] ??= []).push(from);
167
+ }
168
+ // Worked out on where cables meet each card, its anchor, rather than on its corner.
169
+ const mid = Object.fromEntries(Object.keys(layerOf).map(id => [id, get(at, id) + anchor(id)]));
170
+ for (let sweep = 0, moved = Infinity; sweep < 500 && moved > 0.01; sweep++) {
171
+ moved = 0;
172
+ for (const layer of sweep % 2 ? [...layers].reverse() : layers) {
173
+ // How far each card's anchor must at least be from the first one's, for the cards before it to fit.
174
+ const offsets = [];
175
+ layer.forEach((id, n) => {
176
+ const before = layer[n - 1];
177
+ offsets.push(before === undefined ? 0 : (offsets[n - 1] ?? 0) + size(before) - anchor(before) + gap + anchor(id));
178
+ });
179
+ const blocks = [];
180
+ layer.forEach((id, n) => {
181
+ const others = near[id] ?? [];
182
+ // A card with no cables stays where it is, unless others need its room.
183
+ const weight = others.length || 1e-3;
184
+ const want = others.length
185
+ ? others.reduce((sum, other) => sum + get(mid, other), 0) / others.length
186
+ : get(mid, id);
187
+ blocks.push({ sum: (want - (offsets[n] ?? 0)) * weight, weight, count: 1 });
188
+ for (;;) {
189
+ const last = blocks.at(-1);
190
+ const before = blocks.at(-2);
191
+ if (!last || !before || before.sum / before.weight <= last.sum / last.weight)
192
+ break;
193
+ blocks.pop();
194
+ before.sum += last.sum;
195
+ before.weight += last.weight;
196
+ before.count += last.count;
197
+ }
198
+ });
199
+ let n = 0;
200
+ for (const block of blocks)
201
+ for (let k = 0; k < block.count; k++, n++) {
202
+ const id = layer[n] ?? '';
203
+ const next = block.sum / block.weight + (offsets[n] ?? 0);
204
+ moved = Math.max(moved, Math.abs(next - get(mid, id)));
205
+ mid[id] = next;
206
+ }
207
+ }
208
+ }
209
+ for (const id of Object.keys(mid))
210
+ at[id] = get(mid, id) - anchor(id);
211
+ }
212
+ /** Where each card's top left corner goes. `mirror` gives the same layout read the other way round. */
213
+ export function arrange(ids, edges, sizeOf, dir = 'down', mirror = false, { pad, tight = false, anchor } = { pad: 0 }) {
214
+ const depth = layerDepths(ids, edges);
215
+ const layers = [];
216
+ for (const id of ids)
217
+ (layers[get(depth, id)] ??= []).push(id);
218
+ // A depth no card has leaves a hole in the list.
219
+ const filled = layers.filter(Boolean);
220
+ orderLayers(filled, edges.filter(({ from, to }) => from in depth && to in depth && from !== to));
221
+ // The mirrored version keeps the same crossings, read the other way round.
222
+ if (mirror)
223
+ for (const layer of filled)
224
+ layer.reverse();
225
+ const sizes = Object.fromEntries(ids.map(id => [id, sizeOf(id)]));
226
+ // Along a layer cards sit side by side (rows) or stacked (columns); across layers the gap leaves room for cables.
227
+ const scale = tight ? 0.6 : 1;
228
+ const down = dir === 'down';
229
+ const along = down ? 'w' : 'h';
230
+ const across = down ? 'h' : 'w';
231
+ const gapAlong = down ? GAP_X * scale : GAP_Y * 0.6 * scale;
232
+ const gapAcross = down ? GAP_Y * scale : GAP_X * 1.6 * scale;
233
+ const length = (layer) => layer.reduce((sum, id) => sum + get(sizes, id)[along], 0) + gapAlong * (layer.length - 1);
234
+ const longest = Math.max(...filled.map(length));
235
+ const alongAt = {};
236
+ const acrossOf = {};
237
+ let acrossAt = pad + 40;
238
+ for (const layer of filled) {
239
+ let at = pad + 40 + (longest - length(layer)) / 2;
240
+ for (const id of layer) {
241
+ alongAt[id] = at;
242
+ acrossOf[id] = acrossAt;
243
+ at += get(sizes, id)[along] + gapAlong;
244
+ }
245
+ acrossAt += Math.max(...layer.map(id => get(sizes, id)[across])) + gapAcross;
246
+ }
247
+ straighten(filled, edges, alongAt, id => get(sizes, id)[along], gapAlong, id => anchor?.(id) ?? get(sizes, id)[along] / 2);
248
+ // The board starts where it did: the card furthest up (or left) at the margin.
249
+ const first = Math.min(...Object.values(alongAt));
250
+ const pos = {};
251
+ for (const id of ids) {
252
+ const at = Math.round(get(alongAt, id) - first + pad + 40);
253
+ pos[id] = down ? { x: at, y: get(acrossOf, id) } : { x: get(acrossOf, id), y: at };
254
+ }
255
+ return pos;
256
+ }
257
+ /** The room a layout takes. */
258
+ export function layoutBox(pos, sizeOf) {
259
+ const cards = Object.entries(pos).map(([id, at]) => ({ ...at, ...sizeOf(id) }));
260
+ return {
261
+ w: Math.max(...cards.map(card => card.x + card.w)) - Math.min(...cards.map(card => card.x)),
262
+ h: Math.max(...cards.map(card => card.y + card.h)) - Math.min(...cards.map(card => card.y)),
263
+ };
264
+ }
265
+ /**
266
+ * The sides of two cards that face each other, from their boxes (not their corners): a card clearly above or below
267
+ * the other, with more room between them that way, gets a vertical cable; otherwise it runs sideways, towards the
268
+ * other card. A side kept for a card's tunnels and queues (`keep`) is used only when no other side faces.
269
+ */
270
+ export function sidesFor(pos, edge, dir, sizeOf, keep = {}) {
271
+ const box = (id) => {
272
+ const at = get(pos, id);
273
+ const { w, h } = sizeOf(id);
274
+ return { ...at, w, h, cx: at.x + w / 2, cy: at.y + h / 2 };
275
+ };
276
+ const a = box(edge.from);
277
+ const b = box(edge.to);
278
+ const gapX = Math.max(b.x - (a.x + a.w), a.x - (b.x + b.w));
279
+ const gapY = Math.max(b.y - (a.y + a.h), a.y - (b.y + b.h));
280
+ const vertical = b.cy > a.cy ? { fromSide: 'bottom', toSide: 'top' } : { fromSide: 'top', toSide: 'bottom' };
281
+ const sideways = b.cx >= a.cx ? { fromSide: 'right', toSide: 'left' } : { fromSide: 'left', toSide: 'right' };
282
+ const [first, second] = gapY > gapX || (gapY === gapX && dir === 'down') ? [vertical, sideways] : [sideways, vertical];
283
+ const free = (sides) => keep[edge.from] !== sides.fromSide && keep[edge.to] !== sides.toSide;
284
+ return free(first) || !free(second) ? first : second;
285
+ }
286
+ /**
287
+ * Closes up the room the cards take only in full: each band in `needed` that nothing in `taken` covers is cut out.
288
+ * `show` gives where a place is drawn, `back` where a drawn place is kept.
289
+ */
290
+ export function squeeze(taken, needed) {
291
+ const merged = (spans) => [...spans]
292
+ .sort((a, b) => a[0] - b[0])
293
+ .reduce((all, [top, end]) => {
294
+ const last = all.at(-1);
295
+ if (last && top <= last[1])
296
+ last[1] = Math.max(last[1], end);
297
+ else
298
+ all.push([top, end]);
299
+ return all;
300
+ }, []);
301
+ const busy = merged(taken);
302
+ const bands = merged(needed).flatMap(([top, end]) => {
303
+ const free = [];
304
+ let at = top;
305
+ for (const [s, e] of busy) {
306
+ if (e <= at || s >= end)
307
+ continue;
308
+ if (s > at)
309
+ free.push([at, s]);
310
+ at = Math.max(at, e);
311
+ }
312
+ if (at < end)
313
+ free.push([at, end]);
314
+ return free;
315
+ });
316
+ return {
317
+ show: y => y - bands.reduce((cut, [s, e]) => cut + Math.max(0, Math.min(y, e) - s), 0),
318
+ back: d => {
319
+ let cut = 0;
320
+ for (const [s, e] of bands) {
321
+ if (d < s - cut)
322
+ break;
323
+ cut += e - s;
324
+ }
325
+ return d + cut;
326
+ },
327
+ };
328
+ }
329
+ //# sourceMappingURL=arrange.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arrange.js","sourceRoot":"","sources":["../../src/core/arrange.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,158 @@
1
+ /** The direction each side faces, as a step along x and y. */
2
+ export const SIDES = {
3
+ top: [0, -1],
4
+ right: [1, 0],
5
+ bottom: [0, 1],
6
+ left: [-1, 0],
7
+ };
8
+ export const OPPOSITE = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' };
9
+ const clamp = (value, low, high) => Math.min(Math.max(value, low), Math.max(low, high));
10
+ const lead = (from, side, by) => side ? { x: from.x + SIDES[side][0] * by, y: from.y + SIDES[side][1] * by } : from;
11
+ const at = ({ x, y }) => `${String(x)},${String(y)}`;
12
+ /** The middle of a card's side, where a single cable meets it. */
13
+ export const sidePoint = ({ x, y, w, h }, side) => ({
14
+ top: { x: x + w / 2, y },
15
+ right: { x: x + w, y: y + h / 2 },
16
+ bottom: { x: x + w / 2, y: y + h },
17
+ left: { x, y: y + h / 2 },
18
+ })[side];
19
+ /** A cable leaves and enters along each socket's side; a null side means a free end. The curve's four points. */
20
+ export function cableCurve(a, aSide, b, bSide) {
21
+ const by = clamp(Math.hypot(b.x - a.x, b.y - a.y) * 0.4, 30, 140);
22
+ return [a, lead(a, aSide, by), lead(b, bSide, by), b];
23
+ }
24
+ /** A cable as one curve, as an SVG path. */
25
+ export function cablePath(a, aSide, b, bSide) {
26
+ const [, first, second] = cableCurve(a, aSide, b, bSide);
27
+ return `M${at(a)} C${at(first)} ${at(second)} ${at(b)}`;
28
+ }
29
+ /**
30
+ * A cable through the waypoints the user placed: it leaves and enters along the sockets' sides and bends smoothly at
31
+ * each point (Catmull-Rom, as Béziers).
32
+ */
33
+ export function throughPath(a, aSide, b, bSide, points) {
34
+ const out = lead(a, aSide, 40);
35
+ const through = [out, ...points, lead(b, bSide, 40)];
36
+ let path = `M${at(a)} L${at(out)}`;
37
+ for (let step = 0; step < through.length - 1; step++) {
38
+ const before = through[step - 1] ?? a;
39
+ const here = through[step] ?? a;
40
+ const next = through[step + 1] ?? b;
41
+ const after = through[step + 2] ?? b;
42
+ const first = { x: here.x + (next.x - before.x) / 6, y: here.y + (next.y - before.y) / 6 };
43
+ const second = { x: next.x - (after.x - here.x) / 6, y: next.y - (after.y - here.y) / 6 };
44
+ path += ` C${at(first)} ${at(second)} ${at(next)}`;
45
+ }
46
+ return `${path} L${at(b)}`;
47
+ }
48
+ const across = (side) => side === 'left' || side === 'right';
49
+ const endsOf = (cable) => [
50
+ ['from', cable.from, cable.fromSide, cable.to],
51
+ ['to', cable.to, cable.toSide, cable.from],
52
+ ];
53
+ /** The ends that share each side of each card. */
54
+ function bySide(cables) {
55
+ const sides = new Map();
56
+ cables.forEach((cable, index) => {
57
+ for (const [end, id, side, other] of endsOf(cable)) {
58
+ const key = `${id}|${side}`;
59
+ const list = sides.get(key) ?? [];
60
+ list.push({ cable, index, end, side, id, other });
61
+ sides.set(key, list);
62
+ }
63
+ });
64
+ return sides;
65
+ }
66
+ const spread = (length, count) => (count > 1 ? Math.min(26, (length - 48) / (count - 1)) : 0);
67
+ /** The one card every cable on this side goes to, when they all meet it on the side facing this one. */
68
+ const facingCard = (list) => list.every(one => one.other === list[0]?.other) &&
69
+ list.every(({ cable, end, side }) => (end === 'from' ? cable.toSide : cable.fromSide) === OPPOSITE[side])
70
+ ? list[0]?.other
71
+ : undefined;
72
+ /**
73
+ * Arrange's order of the cables along each side of a card, by the way each comes in, and where it centres them.
74
+ * Cables that all go to one card, onto the side facing this one, are centred on the stretch both cards share, so they
75
+ * run straight across. Sets each cable's slots and shifts: they stay as set, whatever moves, until the next Arrange.
76
+ */
77
+ export function orderSlots(cables, boxOf) {
78
+ for (const list of bySide(cables).values()) {
79
+ const [{ id, side } = { id: '', side: 'top' }] = list;
80
+ const along = across(side) ? 'y' : 'x';
81
+ const lengthOf = (box) => (along === 'x' ? box.w : box.h);
82
+ const middle = (box) => box[along] + lengthOf(box) / 2;
83
+ const card = boxOf(id);
84
+ const other = facingCard(list);
85
+ let shift;
86
+ if (other) {
87
+ const low = Math.max(card[along], boxOf(other)[along]);
88
+ const high = Math.min(card[along] + lengthOf(card), boxOf(other)[along] + lengthOf(boxOf(other)));
89
+ const step = Math.min(spread(lengthOf(card), list.length), spread(lengthOf(boxOf(other)), list.length));
90
+ if (high - low >= step * (list.length - 1) + 32)
91
+ shift = Math.round((low + high) / 2 - middle(card));
92
+ }
93
+ // By the way each cable comes in: the angle from this side's middle to where it leaves the other card. Ends in that
94
+ // order cannot cross next to the card, as ordering by the other card's middle can when that card is tall.
95
+ const port = sidePoint(card, side);
96
+ const [nx, ny] = SIDES[side];
97
+ const comesIn = ({ cable, end, other: id }) => {
98
+ const from = sidePoint(boxOf(id), end === 'from' ? cable.toSide : cable.fromSide);
99
+ const [dx, dy] = [from.x - port.x, from.y - port.y];
100
+ return Math.atan2(along === 'x' ? dx : dy, dx * nx + dy * ny);
101
+ };
102
+ list
103
+ .sort((a, b) => comesIn(a) - comesIn(b) || middle(boxOf(a.other)) - middle(boxOf(b.other)))
104
+ .forEach(({ cable, end }, slot) => {
105
+ cable[`${end}Slot`] = slot;
106
+ if (shift)
107
+ cable[`${end}Shift`] = shift;
108
+ else if (end === 'from')
109
+ delete cable.fromShift;
110
+ else
111
+ delete cable.toShift;
112
+ });
113
+ }
114
+ }
115
+ /**
116
+ * Several cables on one side of a card spread along it, in the order Arrange gave them (cables drawn since go after,
117
+ * in the order they were drawn), so they fan out instead of leaving from one point. Nothing here follows the cards as
118
+ * they move: where a fan is centred is Arrange's to set. Gives each end's distance from the
119
+ * middle of its side, keyed `<cable index>|<from or to>`, and the sides whose one cable sits right on the port.
120
+ */
121
+ export function endOffsets(cables, { cardOf, coveredBelow }) {
122
+ const offset = {};
123
+ const onPort = [];
124
+ for (const list of bySide(cables).values()) {
125
+ const [{ id, side } = { id: '', side: 'top' }] = list;
126
+ const card = cardOf(id);
127
+ if (!card)
128
+ continue;
129
+ const along = across(side) ? 'y' : 'x';
130
+ const lengthOf = (box) => (along === 'x' ? box.w : box.h);
131
+ const room = lengthOf(card);
132
+ const count = list.length;
133
+ list.sort((a, b) => (a.cable[`${a.end}Slot`] ?? Infinity) - (b.cable[`${b.end}Slot`] ?? Infinity) || a.index - b.index);
134
+ // Tunnels stacked under a card cover part of its bottom edge: cables leave from what is still free.
135
+ const { covered, atRight } = (side === 'bottom' && coveredBelow?.(id)) || { covered: 0, atRight: false };
136
+ const free = covered && room - covered >= 56 ? room - covered : 0;
137
+ const shift = !free ? 0 : atRight ? free / 2 - room / 2 : covered + free / 2 - room / 2;
138
+ let step = count > 1 ? Math.min(26, ((free || room) - (free ? 20 : 48)) / (count - 1)) : 0;
139
+ let centre = shift;
140
+ if (!shift) {
141
+ // Cables that all go to one card, onto the side facing this one, keep the same spacing on both cards.
142
+ const other = cardOf(facingCard(list) ?? '');
143
+ if (other)
144
+ step = Math.min(step, spread(lengthOf(other), count));
145
+ // Where Arrange centred them, as far as the side still has room for it.
146
+ const placed = list.map(({ cable, end }) => cable[`${end}Shift`]).find(at => at !== undefined) ?? 0;
147
+ const reach = Math.max(0, room / 2 - 16 - ((count - 1) / 2) * step);
148
+ centre = clamp(placed, -reach, reach);
149
+ }
150
+ list.forEach(({ index, end }, place) => {
151
+ offset[`${String(index)}|${end}`] = centre + (place - (count - 1) / 2) * step;
152
+ });
153
+ if (count === 1 && !centre)
154
+ onPort.push({ id, side });
155
+ }
156
+ return { offset, onPort };
157
+ }
158
+ //# sourceMappingURL=cables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cables.js","sourceRoot":"","sources":["../../src/core/cables.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ // `{{<app>.<field>}}` in a stack value: another app's fact, filled in where the stack is used, so a port is written once.
2
+ // stack.json keeps the expression; only what reads the stack to act on it sees the value.
3
+ const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
4
+ const EXPRESSION = /\{\{\s*([a-z0-9-]+)\.([A-Za-z]\w*)\s*\}\}/g;
5
+ const WHOLE = /^\{\{\s*([a-z0-9-]+)\.([A-Za-z]\w*)\s*\}\}$/;
6
+ const isFact = (value) => (typeof value === 'string' && !value.includes('{{')) || typeof value === 'number' || typeof value === 'boolean';
7
+ function resolver(apps) {
8
+ /** What `{{app.field}}` stands for: a field of that app, or its url's port or host; undefined when nothing. */
9
+ const lookup = (app, field, seen) => {
10
+ const key = `${app}.${field}`;
11
+ const facts = Object.hasOwn(apps, app) ? apps[app] : undefined;
12
+ if (!isObject(facts) || seen.includes(key))
13
+ return undefined;
14
+ if (Object.hasOwn(facts, field)) {
15
+ const value = fill(facts[field], [...seen, key]);
16
+ return isFact(value) ? value : undefined;
17
+ }
18
+ if (field !== 'port' && field !== 'host')
19
+ return undefined;
20
+ const url = lookup(app, 'url', [...seen, key]);
21
+ if (typeof url !== 'string' || !URL.canParse(url))
22
+ return undefined;
23
+ const { port, hostname } = new URL(url);
24
+ return field === 'host' ? hostname : Number(port) || undefined;
25
+ };
26
+ const fill = (value, seen) => {
27
+ if (typeof value === 'string') {
28
+ // A value that is only an expression takes the type of what it names, so a port stays a number.
29
+ const whole = WHOLE.exec(value);
30
+ if (whole)
31
+ return lookup(whole[1] ?? '', whole[2] ?? '', seen) ?? value;
32
+ return value.replace(EXPRESSION, (text, app, field) => {
33
+ const found = lookup(app, field, seen);
34
+ return found === undefined ? text : String(found);
35
+ });
36
+ }
37
+ if (Array.isArray(value))
38
+ return value.map(item => fill(item, seen));
39
+ if (isObject(value))
40
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, fill(item, seen)]));
41
+ return value;
42
+ };
43
+ return fill;
44
+ }
45
+ /** The stack with every expression filled in; one that names nothing stays as written. */
46
+ export function resolveExpressions(stack) {
47
+ if (!isObject(stack) || !isObject(stack['apps']))
48
+ return stack;
49
+ return resolver(stack['apps'])(stack, []);
50
+ }
51
+ /** Each expression that names no app's field, or only itself, as `where: what`. */
52
+ export function expressionProblems(stack) {
53
+ if (!isObject(stack) || !isObject(stack['apps']))
54
+ return [];
55
+ const resolved = resolveExpressions(stack);
56
+ const problems = [];
57
+ const walk = (value, after, at) => {
58
+ if (typeof value === 'string' && typeof after === 'string')
59
+ for (const [text] of after.matchAll(EXPRESSION))
60
+ problems.push(`${at}: ${text} names no field of an app (or only itself); use {{<app>.<field>}}, such as {{orders-api.port}}`);
61
+ else if (Array.isArray(value) && Array.isArray(after))
62
+ value.forEach((item, index) => {
63
+ walk(item, after[index], `${at}[${String(index)}]`);
64
+ });
65
+ else if (isObject(value) && isObject(after))
66
+ for (const [key, item] of Object.entries(value))
67
+ walk(item, after[key], at ? `${at}.${key}` : key);
68
+ };
69
+ walk(stack, resolved, '');
70
+ return problems;
71
+ }
72
+ //# sourceMappingURL=expressions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expressions.js","sourceRoot":"","sources":["../../src/core/expressions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ // Browser front-end frameworks, recognised from package.json: the usual dev port, how to open the dev server to the
2
+ // network, and the prefix the framework requires for settings the browser code can read.
3
+ export const FRAMEWORKS = [
4
+ { dep: 'next', name: 'Next.js', port: 3000, lanArgs: ['-H', '0.0.0.0'], envPrefix: 'NEXT_PUBLIC_' },
5
+ { dep: 'vite', name: 'Vite', port: 5173, lanArgs: ['--host', '0.0.0.0'], envPrefix: 'VITE_' },
6
+ { dep: '@angular/cli', name: 'Angular', port: 4200, lanArgs: ['--host', '0.0.0.0'], envPrefix: '' },
7
+ { dep: 'react-scripts', name: 'Create React App', port: 3000, lanEnv: 'HOST=0.0.0.0', envPrefix: 'REACT_APP_' },
8
+ ];
9
+ export function frameworkOf(pkg) {
10
+ const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
11
+ return FRAMEWORKS.find(framework => Object.hasOwn(deps, framework.dep)) ?? null;
12
+ }
13
+ //# sourceMappingURL=frameworks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frameworks.js","sourceRoot":"","sources":["../../src/core/frameworks.ts"],"names":[],"mappings":"AAAA,oHAAoH;AACpH,yFAAyF;AAazF,MAAM,CAAC,MAAM,UAAU,GAAgB;IACrC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE;IACnG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE;IAC7F,EAAE,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;IACnG,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE;CAChH,CAAC;AAQF,MAAM,UAAU,WAAW,CAAC,GAAmC;IAC7D,MAAM,IAAI,GAAG,EAAE,GAAG,GAAG,EAAE,YAAY,EAAE,GAAG,GAAG,EAAE,eAAe,EAAE,CAAC;IAC/D,OAAO,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;AAClF,CAAC"}