stitchkit 0.107.0 → 0.108.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 (85) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/README.md +2 -0
  3. package/darwin-native.cjs +1 -0
  4. package/dist/browser/stream.d.ts +38 -12
  5. package/dist/browser/stream.d.ts.map +1 -1
  6. package/dist/entrypoints/application/diagnostic-journal.js +2 -2
  7. package/dist/entrypoints/application/directory-inbox.js +4 -4
  8. package/dist/entrypoints/bin/upgrade-cli.d.ts +32 -0
  9. package/dist/entrypoints/bin/upgrade-cli.d.ts.map +1 -1
  10. package/dist/entrypoints/bin/upgrade-cli.js +451 -8
  11. package/dist/entrypoints/cli/publish.js +3 -3
  12. package/dist/entrypoints/files/packaging.d.ts +3 -1
  13. package/dist/entrypoints/files/packaging.d.ts.map +1 -1
  14. package/dist/entrypoints/files/packaging.js +105 -41
  15. package/dist/entrypoints/files.js +3 -3
  16. package/dist/entrypoints/index.d.ts +1 -1
  17. package/dist/entrypoints/index.d.ts.map +1 -1
  18. package/dist/entrypoints/index.js +7 -1
  19. package/dist/entrypoints/process.js +1 -1
  20. package/dist/entrypoints/remote.js +1 -1
  21. package/dist/entrypoints/server.js +4 -4
  22. package/dist/entrypoints/telegram/screens.js +1 -1
  23. package/dist/entrypoints/telegram.d.ts +2 -0
  24. package/dist/entrypoints/telegram.d.ts.map +1 -1
  25. package/dist/entrypoints/telegram.js +337 -3
  26. package/dist/entrypoints/testing.d.ts +1 -0
  27. package/dist/entrypoints/testing.d.ts.map +1 -1
  28. package/dist/entrypoints/testing.js +131 -1
  29. package/dist/files/native-artifact.d.ts +14 -0
  30. package/dist/files/native-artifact.d.ts.map +1 -0
  31. package/dist/files/native-packaging-layout.d.ts +5 -0
  32. package/dist/files/native-packaging-layout.d.ts.map +1 -1
  33. package/dist/files/native-packaging-plugin.d.ts +53 -0
  34. package/dist/files/native-packaging-plugin.d.ts.map +1 -0
  35. package/dist/files/native-packaging.d.ts +1 -35
  36. package/dist/files/native-packaging.d.ts.map +1 -1
  37. package/dist/index-a5qr2rj6.js +26 -0
  38. package/dist/{index-7r74re56.js → index-bh2ht9y5.js} +12 -6
  39. package/dist/{index-mbf4h8kp.js → index-nx49x0tw.js} +1 -1
  40. package/dist/{index-zsqt5q98.js → index-p5kx5vqd.js} +209 -54
  41. package/dist/{index-bnf8s6v3.js → index-t2bsd0hm.js} +1 -1
  42. package/dist/{index-4f37d3bg.js → index-w9mqgnz9.js} +1 -1
  43. package/dist/{index-e05cjgtf.js → index-xvj9h8yh.js} +28 -1
  44. package/dist/internal/bounded-lines.d.ts +46 -2
  45. package/dist/internal/bounded-lines.d.ts.map +1 -1
  46. package/dist/internal/process-identity.d.ts.map +1 -1
  47. package/dist/internal/until-aborted.d.ts +9 -0
  48. package/dist/internal/until-aborted.d.ts.map +1 -0
  49. package/dist/internal/upgrade-affects.d.ts +58 -0
  50. package/dist/internal/upgrade-affects.d.ts.map +1 -0
  51. package/dist/internal/upgrade-plan.d.ts +20 -0
  52. package/dist/internal/upgrade-plan.d.ts.map +1 -1
  53. package/dist/internal/upgrade-usage.d.ts +61 -0
  54. package/dist/internal/upgrade-usage.d.ts.map +1 -0
  55. package/dist/telegram/bot-api.d.ts +10 -4
  56. package/dist/telegram/bot-api.d.ts.map +1 -1
  57. package/dist/telegram/broadcast.d.ts +2 -1
  58. package/dist/telegram/broadcast.d.ts.map +1 -1
  59. package/dist/telegram/not-dispatched.d.ts +26 -0
  60. package/dist/telegram/not-dispatched.d.ts.map +1 -0
  61. package/dist/telegram/operator-channel.d.ts +2 -1
  62. package/dist/telegram/operator-channel.d.ts.map +1 -1
  63. package/dist/telegram/send-failure.d.ts +4 -3
  64. package/dist/telegram/send-failure.d.ts.map +1 -1
  65. package/dist/telegram/transport-connect.d.ts +38 -0
  66. package/dist/telegram/transport-connect.d.ts.map +1 -0
  67. package/dist/telegram/transport.d.ts +43 -0
  68. package/dist/telegram/transport.d.ts.map +1 -0
  69. package/dist/telegram/webhook.d.ts +2 -1
  70. package/dist/telegram/webhook.d.ts.map +1 -1
  71. package/dist/testing/manual-clock.d.ts +82 -0
  72. package/dist/testing/manual-clock.d.ts.map +1 -0
  73. package/llms/files-packaging.txt +4 -1
  74. package/llms/process.txt +19 -5
  75. package/llms/stitchkit.2.txt +211 -628
  76. package/llms/stitchkit.txt +635 -10
  77. package/llms/telegram.txt +36 -3
  78. package/llms/testing.txt +47 -0
  79. package/llms/{upgrading-0.98.0-0.107.0.txt → upgrading-0.100.0-0.108.0.txt} +59 -64
  80. package/llms/{upgrading-0.72.0-0.97.0.txt → upgrading-0.72.0-0.99.0.txt} +64 -3
  81. package/llms/upgrading.txt +34 -2
  82. package/llms-full.txt +246 -17
  83. package/llms.txt +8 -8
  84. package/package.json +1 -1
  85. package/llms/stitchkit.3.txt +0 -177
@@ -1,11 +1,175 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/entrypoints/bin/upgrade-cli.ts
4
- import { readFileSync, realpathSync } from "node:fs";
5
- import { dirname, join, resolve } from "node:path";
4
+ import { readdirSync, readFileSync, realpathSync } from "node:fs";
5
+ import { dirname, join, relative, resolve } from "node:path";
6
6
  import { fileURLToPath, pathToFileURL } from "node:url";
7
7
 
8
+ // src/internal/upgrade-affects.ts
9
+ var AFFECTS_LINE = /^\s*\*\*Affects:\*\*\s*(.*)$/;
10
+ var FENCE = /^\s*(`{3,}|~{3,})/;
11
+ var ENTRYPOINT = /^`(stitchkit(?:\/[a-z0-9][a-z0-9-]*)*)`/;
12
+ var SYMBOL = /^([A-Za-z_$][\w$]*)(?:\(([^()]+)\))?$/;
13
+
14
+ class UpgradeAffectsError extends Error {
15
+ name = "UpgradeAffectsError";
16
+ }
17
+ function breakingItems(section) {
18
+ const items = [];
19
+ let fenced = false;
20
+ let current;
21
+ const flush = () => {
22
+ if (current)
23
+ items.push({ markdown: current.join(`
24
+ `).trimEnd() });
25
+ current = undefined;
26
+ };
27
+ for (const line of section.split(`
28
+ `)) {
29
+ if (FENCE.test(line)) {
30
+ fenced = !fenced;
31
+ current?.push(line);
32
+ continue;
33
+ }
34
+ if (fenced) {
35
+ current?.push(line);
36
+ continue;
37
+ }
38
+ if (/^[-*+] /.test(line)) {
39
+ flush();
40
+ current = [line];
41
+ } else if (current && /^\S/.test(line)) {
42
+ flush();
43
+ } else {
44
+ current?.push(line);
45
+ }
46
+ }
47
+ flush();
48
+ return items;
49
+ }
50
+ function affectsLineOf(itemMarkdown) {
51
+ let fenced = false;
52
+ const lines = [];
53
+ for (const line of itemMarkdown.split(`
54
+ `)) {
55
+ if (FENCE.test(line)) {
56
+ fenced = !fenced;
57
+ continue;
58
+ }
59
+ if (fenced)
60
+ continue;
61
+ const match = AFFECTS_LINE.exec(line);
62
+ if (match)
63
+ lines.push((match[1] ?? "").trim());
64
+ }
65
+ if (lines.length > 1)
66
+ throw new UpgradeAffectsError(`the item carries ${lines.length} "**Affects:**" lines; join their targets with "; " on one line`);
67
+ return lines[0];
68
+ }
69
+ function splitTopLevel(text, separator) {
70
+ const parts = [];
71
+ let depth = 0;
72
+ let start = 0;
73
+ for (let index = 0;index < text.length; index++) {
74
+ const char = text[index];
75
+ if (char === "(")
76
+ depth++;
77
+ else if (char === ")")
78
+ depth--;
79
+ else if (depth === 0 && text.startsWith(separator, index)) {
80
+ parts.push(text.slice(start, index));
81
+ start = index + separator.length;
82
+ index += separator.length - 1;
83
+ }
84
+ }
85
+ parts.push(text.slice(start));
86
+ return parts;
87
+ }
88
+ function parseTarget(text) {
89
+ let rest = text.trim();
90
+ const entrypoints = [];
91
+ for (;; ) {
92
+ const match = ENTRYPOINT.exec(rest);
93
+ const name = match?.[1];
94
+ if (!match || name === undefined)
95
+ break;
96
+ entrypoints.push(name);
97
+ rest = rest.slice(match[0].length);
98
+ if (!rest.startsWith(", `"))
99
+ break;
100
+ rest = rest.slice(2);
101
+ }
102
+ if (entrypoints.length === 0)
103
+ throw new UpgradeAffectsError(`"${text.trim()}" does not start with a backticked stitchkit entrypoint`);
104
+ if (!rest.startsWith(" "))
105
+ throw new UpgradeAffectsError(`"${text.trim()}" names no export, \`behaviour\` or \`*\``);
106
+ const what = rest.trim();
107
+ if (what === "behaviour")
108
+ return entrypoints.map((entrypoint) => ({ entrypoint, kind: "behaviour" }));
109
+ if (what === "*")
110
+ return entrypoints.map((entrypoint) => ({ entrypoint, kind: "any-import" }));
111
+ const symbols = splitTopLevel(what, ", ").map((part) => {
112
+ const match = SYMBOL.exec(part.trim());
113
+ const name = match?.[1];
114
+ if (!match || name === undefined)
115
+ throw new UpgradeAffectsError(`"${part.trim()}" is not an export name with an optional (qualifier)`);
116
+ const qualifier = match[2]?.trim();
117
+ return qualifier ? { name, qualifier } : { name };
118
+ });
119
+ return entrypoints.map((entrypoint) => ({ entrypoint, kind: "symbols", symbols }));
120
+ }
121
+ function parseAffectsLine(line) {
122
+ if (line.trim() === "")
123
+ throw new UpgradeAffectsError("the line names nothing");
124
+ return splitTopLevel(line, "; ").flatMap(parseTarget);
125
+ }
126
+
8
127
  // src/internal/upgrade-plan.ts
128
+ var BREAKING_HEADING = /^### \s*⚠️?\s*Breaking changes[^\n]*$/m;
129
+ var NOT_DECLARED = "Not declared in this legacy changelog section.";
130
+ function leadingEntrypoints(item) {
131
+ let rest = item.replace(/^[-*+] /, "").replace(/^\*\*/, "");
132
+ const names = [];
133
+ for (;; ) {
134
+ const match = /^`(stitchkit(?:\/[a-z0-9][a-z0-9-]*)*)`/.exec(rest);
135
+ const name = match?.[1];
136
+ if (!match || name === undefined)
137
+ break;
138
+ names.push(name);
139
+ rest = rest.slice(match[0].length);
140
+ const separator = /^(?:, and |, | and | \+ )/.exec(rest);
141
+ if (!separator)
142
+ break;
143
+ rest = rest.slice(separator[0].length);
144
+ }
145
+ return names;
146
+ }
147
+ function itemTitle(item) {
148
+ const flat = item.replace(/\s+/g, " ").trim();
149
+ const bold = /\*\*(.+?)\*\*/.exec(flat)?.[1];
150
+ if (bold !== undefined && !bold.startsWith("Who must act") && !bold.startsWith("Affects"))
151
+ return bold.replace(/\.$/, "");
152
+ const first = flat.replace(/^[-*+] /, "");
153
+ return first.length > 100 ? `${first.slice(0, 99)}…` : first;
154
+ }
155
+ function itemAudience(item) {
156
+ const flat = item.replace(/\s+/g, " ");
157
+ const who = /\*\*Who must act:\*\*\s*(.*?)(?=\s+See \[|\s+→|\s+\*\*Affects:\*\*|$)/.exec(flat)?.[1];
158
+ return who?.trim() || undefined;
159
+ }
160
+ function sectionItems(version, markdown, sectionAudience) {
161
+ return breakingItems(markdown).map(({ markdown: item }) => {
162
+ const line = affectsLineOf(item);
163
+ return {
164
+ version,
165
+ entrypoints: leadingEntrypoints(item),
166
+ title: itemTitle(item),
167
+ affects: line === undefined ? undefined : parseAffectsLine(line),
168
+ whoMustAct: itemAudience(item) ?? sectionAudience,
169
+ markdown: item
170
+ };
171
+ });
172
+ }
9
173
  function semverParts(version) {
10
174
  const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
11
175
  if (!match)
@@ -43,7 +207,7 @@ function planUpgrade(changelog, from, to) {
43
207
  const bodyStart = (release.index ?? 0) + release[0].length;
44
208
  const bodyEnd = releases[index + 1]?.index ?? changelog.length;
45
209
  const body = changelog.slice(bodyStart, bodyEnd);
46
- const breakingHeader = /^### ⚠️ Breaking changes\s*$/m.exec(body);
210
+ const breakingHeader = BREAKING_HEADING.exec(body);
47
211
  if (!breakingHeader)
48
212
  continue;
49
213
  const breakingStart = (breakingHeader.index ?? 0) + breakingHeader[0].length;
@@ -51,10 +215,12 @@ function planUpgrade(changelog, from, to) {
51
215
  const nextSection = /^### /m.exec(afterHeader);
52
216
  const markdown = afterHeader.slice(0, nextSection?.index ?? afterHeader.length).trim();
53
217
  const who = /^\*\*Who must act:\*\*\s*([\s\S]*?)(?=\n\s*\n|\n[-*] )/m.exec(markdown);
218
+ const whoMustAct = who?.[1]?.replace(/\s+/g, " ").trim() ?? NOT_DECLARED;
54
219
  changes.push({
55
220
  version,
56
- whoMustAct: who?.[1]?.replace(/\s+/g, " ").trim() ?? "Not declared in this legacy changelog section.",
57
- markdown
221
+ whoMustAct,
222
+ markdown,
223
+ items: sectionItems(version, markdown, whoMustAct)
58
224
  });
59
225
  }
60
226
  return changes.sort((left, right) => compareSemver(left.version, right.version));
@@ -78,6 +244,206 @@ ${change.markdown.replace(/^\*\*Who must act:\*\*[\s\S]*?(?=\n\s*\n|\n[-*] )/m,
78
244
  `;
79
245
  }
80
246
 
247
+ // src/internal/upgrade-usage.ts
248
+ var SPECIFIER = /^stitchkit(?:\/[A-Za-z0-9._-]+)*$/;
249
+ function masked(text) {
250
+ const strings = new Map;
251
+ const blank = (part) => part.replace(/[^\n]/g, " ");
252
+ let code = "";
253
+ let index = 0;
254
+ while (index < text.length) {
255
+ const char = text[index] ?? "";
256
+ const next = text[index + 1];
257
+ if (char === '"' || char === "'" || char === "`") {
258
+ let end = index + 1;
259
+ let value = "";
260
+ while (end < text.length && text[end] !== char) {
261
+ if (text[end] === "\\") {
262
+ value += text.slice(end, end + 2);
263
+ end += 2;
264
+ continue;
265
+ }
266
+ if (char !== "`" && text[end] === `
267
+ `)
268
+ break;
269
+ value += text[end];
270
+ end++;
271
+ }
272
+ if (char !== "`")
273
+ strings.set(index, value);
274
+ code += `${char}${blank(text.slice(index + 1, end))}${text[end] === char ? char : ""}`;
275
+ index = text[end] === char ? end + 1 : end;
276
+ continue;
277
+ }
278
+ if (char === "/" && next === "/") {
279
+ const end = text.indexOf(`
280
+ `, index);
281
+ const stop = end === -1 ? text.length : end;
282
+ code += blank(text.slice(index, stop));
283
+ index = stop;
284
+ continue;
285
+ }
286
+ if (char === "/" && next === "*") {
287
+ const end = text.indexOf("*/", index + 2);
288
+ const stop = end === -1 ? text.length : end + 2;
289
+ code += blank(text.slice(index, stop));
290
+ index = stop;
291
+ continue;
292
+ }
293
+ code += char;
294
+ index++;
295
+ }
296
+ return { code, strings };
297
+ }
298
+ function scriptBlocks(text) {
299
+ let out = "";
300
+ let at = 0;
301
+ for (const match of text.matchAll(/(<script\b[^>]*>)([\s\S]*?)<\/script\s*>/gi)) {
302
+ const start = (match.index ?? 0) + (match[1]?.length ?? 0);
303
+ out += text.slice(at, start).replace(/[^\n]/g, " ") + (match[2] ?? "");
304
+ at = start + (match[2]?.length ?? 0);
305
+ }
306
+ return out + text.slice(at).replace(/[^\n]/g, " ");
307
+ }
308
+ function lineLocator(text) {
309
+ const starts = [0];
310
+ for (let index = text.indexOf(`
311
+ `);index !== -1; index = text.indexOf(`
312
+ `, index + 1))
313
+ starts.push(index + 1);
314
+ return (offset) => {
315
+ let low = 0;
316
+ let high = starts.length - 1;
317
+ while (low < high) {
318
+ const middle = low + high + 1 >> 1;
319
+ if ((starts[middle] ?? 0) <= offset)
320
+ low = middle;
321
+ else
322
+ high = middle - 1;
323
+ }
324
+ return low + 1;
325
+ };
326
+ }
327
+ function clauseNames(clause) {
328
+ const names = [];
329
+ const braces = /\{([^}]*)\}/.exec(clause);
330
+ if (braces?.[1] !== undefined) {
331
+ let at = (braces.index ?? 0) + 1;
332
+ for (const part of braces[1].split(",")) {
333
+ const match = /^(\s*)(?:type\s+)?([A-Za-z_$][\w$]*)/.exec(part);
334
+ if (match?.[2])
335
+ names.push({ name: match[2], at: at + (match[1]?.length ?? 0) });
336
+ at += part.length + 1;
337
+ }
338
+ }
339
+ const star = /\*/.exec(clause.replace(/\{[^}]*\}/, (block) => " ".repeat(block.length)));
340
+ if (star)
341
+ names.push({ name: "*", at: star.index ?? 0 });
342
+ const head = /^\s*(?:type\s+)?([A-Za-z_$][\w$]*)\s*(?:,|$)/.exec(clause);
343
+ if (head?.[1] && head[1] !== "type")
344
+ names.push({ name: "*", at: head.index ?? 0 });
345
+ return names;
346
+ }
347
+ var IDENTIFIER = "[A-Za-z_$][\\w$]*";
348
+ var CLAUSE = `(?:${IDENTIFIER}\\s*,\\s*)?(?:\\{[^{}'"\`;]*\\}|\\*(?:\\s+as\\s+${IDENTIFIER})?)|${IDENTIFIER}`;
349
+ var DECLARATION = new RegExp(`(?:^|[;}])[ \\t]*(import|export)(\\s+type)?(\\s+(?:${CLAUSE})\\s*|\\s*\\{[^{}'"\`;]*\\}\\s*|\\s*\\*(?:\\s+as\\s+${IDENTIFIER})?\\s*)from\\s*(['"])`, "gm");
350
+ var BARE = /(?:^|[;}])[ \t]*import\s*(['"])/gm;
351
+ var DYNAMIC = /\b(?:import|require)\s*\(\s*(['"])/g;
352
+ var quoteAt = (match) => (match.index ?? 0) + match[0].length - 1;
353
+ function scanStitchkitImports(sources) {
354
+ const found = [];
355
+ for (const { path, text: raw } of sources) {
356
+ const text = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
357
+ const source = /\.(?:vue|svelte)$/.test(path) ? scriptBlocks(text) : text;
358
+ const { code, strings } = masked(source);
359
+ const lineAt = lineLocator(code);
360
+ const specifierAt = (match) => {
361
+ const specifier = strings.get(quoteAt(match));
362
+ return specifier !== undefined && SPECIFIER.test(specifier) ? specifier : undefined;
363
+ };
364
+ for (const match of code.matchAll(DECLARATION)) {
365
+ const entrypoint = specifierAt(match);
366
+ if (entrypoint === undefined)
367
+ continue;
368
+ const clause = match[3] ?? "";
369
+ const clauseStart = (match.index ?? 0) + match[0].lastIndexOf("from") - clause.length;
370
+ for (const { name, at } of clauseNames(clause))
371
+ found.push({ path, line: lineAt(clauseStart + at), entrypoint, name });
372
+ }
373
+ for (const pattern of [BARE, DYNAMIC])
374
+ for (const match of code.matchAll(pattern)) {
375
+ const entrypoint = specifierAt(match);
376
+ if (entrypoint !== undefined)
377
+ found.push({ path, line: lineAt(quoteAt(match)), entrypoint, name: "*" });
378
+ }
379
+ }
380
+ return found.sort((left, right) => left.path === right.path ? left.line - right.line : left.path < right.path ? -1 : 1);
381
+ }
382
+ var filesImporting = (imports, entrypoints) => [
383
+ ...new Set(imports.filter((use) => entrypoints.includes(use.entrypoint)).map((use) => use.path))
384
+ ].sort();
385
+ function usesOf(target, imports) {
386
+ if (target.kind === "behaviour")
387
+ return [];
388
+ const names = target.kind === "symbols" ? new Set(target.symbols.map((symbol) => symbol.name)) : undefined;
389
+ return imports.filter((use) => use.entrypoint === target.entrypoint && (names === undefined || use.name === "*" || names.has(use.name)));
390
+ }
391
+ function upgradeVerdicts(items, imports) {
392
+ return items.map((item) => {
393
+ if (item.affects === undefined)
394
+ return {
395
+ item,
396
+ verdict: { kind: "not-declared", files: filesImporting(imports, item.entrypoints) }
397
+ };
398
+ const uses = item.affects.flatMap((target) => usesOf(target, imports));
399
+ if (uses.length > 0)
400
+ return { item, verdict: { kind: "affects", uses } };
401
+ const behavioural = item.affects.filter((target) => target.kind === "behaviour");
402
+ const files = filesImporting(imports, behavioural.map((target) => target.entrypoint));
403
+ if (files.length > 0)
404
+ return { item, verdict: { kind: "behavioural", files } };
405
+ return { item, verdict: { kind: "not-used" } };
406
+ });
407
+ }
408
+ var VERDICT_TEXT = {
409
+ affects: "affects this project",
410
+ "not-used": "not used here",
411
+ behavioural: "behavioural — check by hand",
412
+ "not-declared": "not declared — check by hand"
413
+ };
414
+ function renderUpgradeVerdicts(verdicts) {
415
+ if (verdicts.length === 0)
416
+ return "";
417
+ const lines = ["## Does it touch this project?", ""];
418
+ for (const { item, verdict } of verdicts) {
419
+ const where = item.entrypoints.length > 0 ? ` ${item.entrypoints.map((name) => `\`${name}\``).join(", ")}` : "";
420
+ lines.push(`- **${item.version}**${where} — ${item.title}: **${VERDICT_TEXT[verdict.kind]}**`);
421
+ if (verdict.kind === "affects")
422
+ for (const use of verdict.uses)
423
+ lines.push(` - ${use.path}:${use.line} ${use.name === "*" ? `every import of \`${use.entrypoint}\`` : `\`${use.name}\``}`);
424
+ if (verdict.kind === "behavioural" || verdict.kind === "not-declared")
425
+ for (const file of verdict.files)
426
+ lines.push(` - ${file}`);
427
+ }
428
+ return `${lines.join(`
429
+ `)}
430
+ `;
431
+ }
432
+ function upgradeVerdictsJson(verdicts) {
433
+ return verdicts.map(({ item, verdict }) => ({
434
+ version: item.version,
435
+ entrypoints: item.entrypoints,
436
+ title: item.title,
437
+ whoMustAct: item.whoMustAct,
438
+ affects: item.affects ?? null,
439
+ verdict: verdict.kind,
440
+ ...verdict.kind === "affects" && { uses: verdict.uses },
441
+ ...(verdict.kind === "behavioural" || verdict.kind === "not-declared") && {
442
+ files: verdict.files
443
+ }
444
+ }));
445
+ }
446
+
81
447
  // src/entrypoints/bin/upgrade-cli.ts
82
448
  var USAGE = `stitchkit upgrade — print every breaking change between the installed version and this one
83
449
 
@@ -87,9 +453,59 @@ var USAGE = `stitchkit upgrade — print every breaking change between the insta
87
453
  Options
88
454
  --from <version> installed version (default: the stitchkit in ./node_modules)
89
455
  --to <version> target version (default: the version of this package)
90
- --cwd <dir> project to read the installed version from (default: .)
456
+ --cwd <dir> project to read the installed version and imports from (default: .)
91
457
  --changelog <path> changelog to plan from (default: the one in this package)
458
+ --json print the breaking items and their verdicts as JSON
459
+
460
+ Each breaking item is matched against what the project imports from stitchkit:
461
+ "affects this project" (with file:line), "not used here", "behavioural — check
462
+ by hand" or, for an item written before machine lines, "not declared — check by hand".
92
463
  `;
464
+ var SKIPPED_DIRECTORIES = new Set(["node_modules", ".git", ".next", ".turbo", ".cache"]);
465
+ var OUTPUT_DIRECTORIES = new Set(["dist", "build", "out", "coverage"]);
466
+ var SOURCE_FILE = /\.(?:[cm]?[jt]sx?|vue|svelte)$/;
467
+ var DENIED = new Set(["EACCES", "EPERM"]);
468
+ function deniedCode(error) {
469
+ const code = typeof error === "object" && error !== null ? Reflect.get(error, "code") : undefined;
470
+ return typeof code === "string" && DENIED.has(code) ? code : undefined;
471
+ }
472
+ function projectSources(root, io = {
473
+ readdir: (path) => readdirSync(path, { withFileTypes: true }),
474
+ readFile: (path) => readFileSync(path, "utf8")
475
+ }) {
476
+ const sources = [];
477
+ const unscanned = [];
478
+ const attempt = (path, read) => {
479
+ try {
480
+ return read();
481
+ } catch (error) {
482
+ const code = deniedCode(error);
483
+ if (code === undefined)
484
+ throw error;
485
+ unscanned.push({ path: relative(root, path) || ".", code });
486
+ return;
487
+ }
488
+ };
489
+ const walk = (directory) => {
490
+ const entries = attempt(directory, () => io.readdir(directory));
491
+ const packageRoot = directory === root || (entries ?? []).some((entry) => entry.name === "package.json");
492
+ for (const entry of entries ?? []) {
493
+ const path = join(directory, entry.name);
494
+ if (entry.isDirectory()) {
495
+ const skipped = SKIPPED_DIRECTORIES.has(entry.name) || packageRoot && OUTPUT_DIRECTORIES.has(entry.name);
496
+ if (!skipped)
497
+ walk(path);
498
+ } else if (entry.isFile() && SOURCE_FILE.test(entry.name)) {
499
+ const text = attempt(path, () => io.readFile(path));
500
+ if (text !== undefined)
501
+ sources.push({ path: relative(root, path), text });
502
+ }
503
+ }
504
+ };
505
+ walk(root);
506
+ unscanned.sort((left, right) => left.path < right.path ? -1 : 1);
507
+ return { sources, unscanned };
508
+ }
93
509
  function option(argv, name) {
94
510
  const index = argv.indexOf(`--${name}`);
95
511
  if (index < 0)
@@ -136,8 +552,13 @@ ${USAGE}`, code: 1 };
136
552
  if (from === undefined) {
137
553
  throw new Error(`No stitchkit found in ${join(cwd, "node_modules")} — run this in the project that depends on it, or pass --from <installed version>`);
138
554
  }
555
+ const json = argv.includes("--json");
139
556
  if (from === to) {
140
- return { output: `Already on ${to}. Nothing to upgrade.
557
+ return json ? {
558
+ output: `${JSON.stringify({ from, to, items: [], unscanned: [] }, null, 2)}
559
+ `,
560
+ code: 0
561
+ } : { output: `Already on ${to}. Nothing to upgrade.
141
562
  `, code: 0 };
142
563
  }
143
564
  const changelogPath = option(argv, "changelog") ?? join(root, "CHANGELOG.md");
@@ -147,7 +568,28 @@ ${USAGE}`, code: 1 };
147
568
  } catch {
148
569
  throw new Error(`Cannot read the changelog at ${changelogPath}`);
149
570
  }
150
- return { output: renderUpgradePlan(planUpgrade(changelog, from, to), from, to), code: 0 };
571
+ const plan = planUpgrade(changelog, from, to);
572
+ const { sources, unscanned } = projectSources(cwd);
573
+ const verdicts = upgradeVerdicts(plan.flatMap((change) => change.items), scanStitchkitImports(sources));
574
+ if (json) {
575
+ const document = { from, to, items: upgradeVerdictsJson(verdicts), unscanned };
576
+ return { output: `${JSON.stringify(document, null, 2)}
577
+ `, code: 0 };
578
+ }
579
+ const usage = renderUpgradeVerdicts(verdicts);
580
+ const skipped = unscanned.length === 0 ? "" : `
581
+ ## Not scanned
582
+
583
+ The verdicts above do not include these paths (no permission to read them):
584
+
585
+ ${unscanned.map((entry) => `- ${entry.path} (${entry.code})`).join(`
586
+ `)}
587
+ `;
588
+ return {
589
+ output: `${renderUpgradePlan(plan, from, to)}${usage ? `
590
+ ${usage}` : ""}${skipped}`,
591
+ code: 0
592
+ };
151
593
  }
152
594
  function isDirectInvocation(invoked, self) {
153
595
  if (invoked === undefined)
@@ -173,5 +615,6 @@ if (isDirectInvocation(process.argv[1], fileURLToPath(import.meta.url))) {
173
615
  }
174
616
  export {
175
617
  isDirectInvocation,
618
+ projectSources,
176
619
  runUpgradeCli
177
620
  };
@@ -1,7 +1,7 @@
1
1
  import"../../index-xqhede6x.js";
2
2
  import"../../index-2kbysk9s.js";
3
- import"../../index-7r74re56.js";
4
- import"../../index-bnf8s6v3.js";
3
+ import"../../index-bh2ht9y5.js";
4
+ import"../../index-t2bsd0hm.js";
5
5
  import {
6
6
  signCliManifest2,
7
7
  verifyCliManifest2,
@@ -18,7 +18,7 @@ import {
18
18
  } from "../../index-ktjmgq9p.js";
19
19
  import {
20
20
  withExclusiveLock2
21
- } from "../../index-4f37d3bg.js";
21
+ } from "../../index-w9mqgnz9.js";
22
22
  import {
23
23
  ManagedFileError2,
24
24
  createManagedFileBoundary2
@@ -1,3 +1,5 @@
1
- export type { NativePackagingAsset, NativePackagingEmbeddedAsset, NativePackagingEmbeddedOptions, NativePackagingEmbeddedResult, NativePackagingOptions, NativePackagingPlugin, NativePackagingResult, } from '../../files/native-packaging.js';
1
+ export { inspectNativeArtifact, type NativeArtifactLoader } from '../../files/native-artifact.js';
2
+ export type { NativePackagingAsset, NativePackagingEmbeddedAsset, NativePackagingEmbeddedOptions, NativePackagingEmbeddedResult, NativePackagingOptions, NativePackagingResult, } from '../../files/native-packaging.js';
2
3
  export { createNativePackaging } from '../../files/native-packaging.js';
4
+ export type { NativePackagingPlugin } from '../../files/native-packaging-plugin.js';
3
5
  //# sourceMappingURL=packaging.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"packaging.d.ts","sourceRoot":"","sources":["../../../src/entrypoints/files/packaging.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,oBAAoB,EACpB,4BAA4B,EAC5B,8BAA8B,EAC9B,6BAA6B,EAC7B,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"packaging.d.ts","sourceRoot":"","sources":["../../../src/entrypoints/files/packaging.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC/F,YAAY,EACV,oBAAoB,EACpB,4BAA4B,EAC5B,8BAA8B,EAC9B,6BAA6B,EAC7B,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,YAAY,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC"}