create-windy 0.5.0 → 0.5.2

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.
package/dist/cli.js CHANGED
@@ -2563,15 +2563,25 @@ async function materializeSelectedModules(targetDirectory, selectedNames) {
2563
2563
  ]),
2564
2564
  ...moduleDistributionCatalog.filter(({ databaseLineageOwner }) => databaseLineageOwner === "module").flatMap(({ databaseTables }) => databaseTables)
2565
2565
  ], removed.flatMap(({ databaseStatementTokens }) => databaseStatementTokens));
2566
- const removedRoots = removed.flatMap((manifest) => [
2566
+ const removedRoots = collapseRemovalRoots(removed.flatMap((manifest) => [
2567
2567
  ...manifest.packages,
2568
2568
  ...manifest.fileRoots,
2569
2569
  ...manifest.documentation,
2570
2570
  ...manifest.schemaFiles
2571
- ]);
2571
+ ]));
2572
2572
  await Promise.all([...new Set(removedRoots)].map((path) => rm3(join7(targetDirectory, path), { recursive: true, force: true })));
2573
2573
  await pruneWorkspaceReferences(targetDirectory, removedPackageNames, removedRoots);
2574
2574
  }
2575
+ function collapseRemovalRoots(paths) {
2576
+ const roots = [];
2577
+ for (const path of [...new Set(paths)].sort((left, right) => left.length - right.length || left.localeCompare(right))) {
2578
+ if (roots.some((root) => path === root || path.startsWith(`${root}/`))) {
2579
+ continue;
2580
+ }
2581
+ roots.push(path);
2582
+ }
2583
+ return roots.sort();
2584
+ }
2575
2585
  async function stripModuleBlocks(root, selected) {
2576
2586
  for (const path of await collectFiles2(root)) {
2577
2587
  if (!textExtensions.has(extname(path)))
@@ -2661,16 +2671,27 @@ async function pruneDatabaseArtifacts(root, tableNames, statementTokens) {
2661
2671
  for (const path of await collectFiles2(drizzleRoot)) {
2662
2672
  if (path.endsWith(".sql")) {
2663
2673
  const source = await readFile6(path, "utf8");
2664
- const statements = source.split("--> statement-breakpoint").map((statement) => statement.trim()).filter(Boolean).filter((statement) => !tablePatterns.some((pattern) => pattern.test(statement)) && !statementTokens.some((token) => statement.includes(token)));
2665
- await writeFile5(path, statements.length ? `${statements.join(`
2674
+ const statements = source.split("--> statement-breakpoint").map((statement) => statement.trim()).filter(Boolean);
2675
+ const retained = statements.filter((statement) => !tablePatterns.some((pattern) => pattern.test(statement)) && !statementTokens.some((token) => statement.includes(token)));
2676
+ if (retained.length === statements.length)
2677
+ continue;
2678
+ await writeFile5(path, retained.length ? `${retained.join(`
2666
2679
  --> statement-breakpoint
2667
2680
  `)}
2668
2681
  ` : `-- \u6B64 migration \u7684\u6A21\u5757\u672A\u88AB\u5F53\u524D\u9879\u76EE\u9009\u62E9\u3002
2669
2682
  `);
2670
2683
  } else if (path.endsWith("_snapshot.json")) {
2671
2684
  const snapshot = JSON.parse(await readFile6(path, "utf8"));
2672
- for (const name of tableNames)
2673
- delete snapshot.tables?.[`public.${name}`];
2685
+ let changed = false;
2686
+ for (const name of tableNames) {
2687
+ const key = `public.${name}`;
2688
+ if (!Object.hasOwn(snapshot.tables ?? {}, key))
2689
+ continue;
2690
+ delete snapshot.tables?.[key];
2691
+ changed = true;
2692
+ }
2693
+ if (!changed)
2694
+ continue;
2674
2695
  await writeFile5(path, `${JSON.stringify(snapshot, null, 2)}
2675
2696
  `);
2676
2697
  }
@@ -7558,6 +7579,16 @@ var recipes = [
7558
7579
  id: "starter-0.4.1-to-0.5.0",
7559
7580
  from: "0.4.1",
7560
7581
  to: "0.5.0"
7582
+ },
7583
+ {
7584
+ id: "starter-0.5.0-to-0.5.1",
7585
+ from: "0.5.0",
7586
+ to: "0.5.1"
7587
+ },
7588
+ {
7589
+ id: "starter-0.5.1-to-0.5.2",
7590
+ from: "0.5.1",
7591
+ to: "0.5.2"
7561
7592
  }
7562
7593
  ];
7563
7594
  function resolveRecipeChain(sourceVersion, targetVersion) {
@@ -8441,6 +8472,146 @@ import { join as join23 } from "path";
8441
8472
  import { mkdtemp as mkdtemp2, rm as rm6, writeFile as writeFile12 } from "fs/promises";
8442
8473
  import { tmpdir as tmpdir2 } from "os";
8443
8474
  import { join as join22 } from "path";
8475
+
8476
+ // src/update/merge-convergent-additions.ts
8477
+ async function reconcileConvergentAdditions(input) {
8478
+ const [oursDiff, theirsDiff] = await Promise.all([
8479
+ diff(input.root, input.basePath, input.oursPath),
8480
+ diff(input.root, input.basePath, input.theirsPath)
8481
+ ]);
8482
+ if (oursDiff === undefined || theirsDiff === undefined)
8483
+ return;
8484
+ const oursAdditions = pureAdditions(oursDiff);
8485
+ const theirsAdditions = pureAdditions(theirsDiff);
8486
+ const oursByContent = new Map;
8487
+ for (const addition of oursAdditions) {
8488
+ const key = addition.lines.join(`
8489
+ `);
8490
+ oursByContent.set(key, [...oursByContent.get(key) ?? [], addition]);
8491
+ }
8492
+ const decode = (content) => new TextDecoder().decode(content);
8493
+ const baseLines = decode(input.base).split(`
8494
+ `);
8495
+ const oursLines = decode(input.ours).split(`
8496
+ `);
8497
+ const theirsLines = decode(input.theirs).split(`
8498
+ `);
8499
+ const mergedLines = input.merged.split(`
8500
+ `);
8501
+ const convergent = theirsAdditions.flatMap((addition) => {
8502
+ if (!isSubstantial(addition.lines) || !isNamedClassMethod(addition.lines)) {
8503
+ return [];
8504
+ }
8505
+ const key = addition.lines.join(`
8506
+ `);
8507
+ const matchingOurs = oursByContent.get(key);
8508
+ if (matchingOurs?.length !== 1)
8509
+ return [];
8510
+ if (theirsAdditions.filter((candidate2) => candidate2.lines.join(`
8511
+ `) === key).length !== 1) {
8512
+ return [];
8513
+ }
8514
+ const oursContainer = enclosingClass(oursLines, matchingOurs[0]);
8515
+ const theirsContainer = enclosingClass(theirsLines, addition);
8516
+ if (!oursContainer || oursContainer !== theirsContainer)
8517
+ return [];
8518
+ return countSequence(baseLines, addition.lines) === 0 && countSequence(oursLines, addition.lines) === 1 && countSequence(theirsLines, addition.lines) === 1 && countSequence(mergedLines, addition.lines) > 1 ? [{ ours: matchingOurs[0], theirs: addition }] : [];
8519
+ });
8520
+ if (convergent.length !== 1)
8521
+ return;
8522
+ const candidate = convergent[0];
8523
+ if (!candidate)
8524
+ return;
8525
+ const oursWithout = withoutAddition(oursLines, candidate.ours);
8526
+ const theirsWithout = withoutAddition(theirsLines, candidate.theirs);
8527
+ if (!oursWithout || !theirsWithout || !sameLines(oursWithout, theirsWithout)) {
8528
+ return;
8529
+ }
8530
+ return input.ours;
8531
+ }
8532
+ async function diff(root, basePath, branchPath) {
8533
+ const result2 = await captureProcess("git", ["diff", "--no-index", "--unified=0", "--no-color", basePath, branchPath], root);
8534
+ return result2.code === 0 || result2.code === 1 ? result2.stdout : undefined;
8535
+ }
8536
+ function pureAdditions(diff2) {
8537
+ const lines = diff2.split(`
8538
+ `);
8539
+ const additions = [];
8540
+ for (let index = 0;index < lines.length; index += 1) {
8541
+ const match = /^@@ -\d+(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(lines[index] ?? "");
8542
+ if (!match)
8543
+ continue;
8544
+ const oldCount = match[1] === undefined ? 1 : Number(match[1]);
8545
+ const start = Number(match[2]);
8546
+ const newCount = match[3] === undefined ? 1 : Number(match[3]);
8547
+ if (oldCount !== 0 || newCount === 0)
8548
+ continue;
8549
+ const added = [];
8550
+ for (let cursor = index + 1;cursor < lines.length; cursor += 1) {
8551
+ const line = lines[cursor] ?? "";
8552
+ if (line.startsWith("@@ "))
8553
+ break;
8554
+ if (line.startsWith("+"))
8555
+ added.push(line.slice(1));
8556
+ }
8557
+ if (added.length === newCount)
8558
+ additions.push({ start, lines: added });
8559
+ }
8560
+ return additions;
8561
+ }
8562
+ function isSubstantial(lines) {
8563
+ return lines.filter((line) => line.trim()).length >= 3 && lines.join(`
8564
+ `).length >= 32;
8565
+ }
8566
+ function isNamedClassMethod(lines) {
8567
+ const first = lines.find((line) => line.trim())?.trim() ?? "";
8568
+ const last = [...lines].reverse().find((line) => line.trim())?.trim() ?? "";
8569
+ return /^(?:(?:public|protected|private|static|abstract|override|async|declare|get|set)\s+)*(?:\*\s*)?[A-Za-z_$][\w$]*\s*(?:<[^>{}]*>)?\s*\(/.test(first) && lines.some((line) => line.includes("{")) && /^}\s*;?$/.test(last);
8570
+ }
8571
+ function enclosingClass(lines, addition) {
8572
+ const memberIndent = leadingWhitespace(addition.lines.find((line) => line.trim()) ?? "");
8573
+ for (let index = addition.start - 2;index >= 0; index -= 1) {
8574
+ const line = lines[index] ?? "";
8575
+ if (!line.trim() || leadingWhitespace(line) >= memberIndent)
8576
+ continue;
8577
+ const match = /^(\s*)(?:(?:export|default|abstract|declare)\s+)*class\s+([A-Za-z_$][\w$]*)\b/.exec(line);
8578
+ if (!match)
8579
+ return;
8580
+ const name = match[2] ?? "";
8581
+ const ordinal = lines.slice(0, index + 1).filter((candidate) => new RegExp(`^\\s*(?:(?:export|default|abstract|declare)\\s+)*class\\s+${escapeRegExp2(name)}\\b`).test(candidate)).length;
8582
+ return `${name}#${ordinal}`;
8583
+ }
8584
+ }
8585
+ function leadingWhitespace(line) {
8586
+ return /^\s*/.exec(line)?.[0].length ?? 0;
8587
+ }
8588
+ function escapeRegExp2(value) {
8589
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8590
+ }
8591
+ function countSequence(lines, sequence) {
8592
+ let count = 0;
8593
+ for (let index = 0;index <= lines.length - sequence.length; index += 1) {
8594
+ if (sameLines(lines.slice(index, index + sequence.length), sequence)) {
8595
+ count += 1;
8596
+ }
8597
+ }
8598
+ return count;
8599
+ }
8600
+ function withoutAddition(lines, addition) {
8601
+ const index = addition.start - 1;
8602
+ if (!sameLines(lines.slice(index, index + addition.lines.length), addition.lines)) {
8603
+ return;
8604
+ }
8605
+ return [
8606
+ ...lines.slice(0, index),
8607
+ ...lines.slice(index + addition.lines.length)
8608
+ ];
8609
+ }
8610
+ function sameLines(left, right) {
8611
+ return left.length === right.length && left.every((line, index) => line === right[index]);
8612
+ }
8613
+
8614
+ // src/update/merge.ts
8444
8615
  async function mergeTextFile(base, ours, theirs) {
8445
8616
  const root = await mkdtemp2(join22(tmpdir2(), "windy-merge-"));
8446
8617
  try {
@@ -8454,6 +8625,19 @@ async function mergeTextFile(base, ours, theirs) {
8454
8625
  ]);
8455
8626
  const result2 = await captureProcess("git", ["merge-file", "-p", "--diff3", oursPath, basePath, theirsPath], root);
8456
8627
  if (result2.code === 0) {
8628
+ const reconciledContent = await reconcileConvergentAdditions({
8629
+ root,
8630
+ basePath,
8631
+ oursPath,
8632
+ theirsPath,
8633
+ base,
8634
+ ours,
8635
+ theirs,
8636
+ merged: result2.stdout
8637
+ });
8638
+ if (reconciledContent !== undefined) {
8639
+ return { content: reconciledContent };
8640
+ }
8457
8641
  return { content: new TextEncoder().encode(result2.stdout) };
8458
8642
  }
8459
8643
  if (result2.code === 1) {
@@ -8634,6 +8818,11 @@ function mergeValue(base, ours, theirs, path2, conflicts) {
8634
8818
  return theirs;
8635
8819
  if (equal(theirs, base))
8636
8820
  return ours;
8821
+ if (path2 === "package.json.files") {
8822
+ const files = mergeAppendOnlyStringSet(base, ours, theirs);
8823
+ if (files)
8824
+ return files;
8825
+ }
8637
8826
  if (isObject(base) && isObject(ours) && isObject(theirs)) {
8638
8827
  const output = {};
8639
8828
  const keys = new Set([
@@ -8654,6 +8843,15 @@ function mergeValue(base, ours, theirs, path2, conflicts) {
8654
8843
  function equal(left, right) {
8655
8844
  return JSON.stringify(left) === JSON.stringify(right);
8656
8845
  }
8846
+ function mergeAppendOnlyStringSet(base, ours, theirs) {
8847
+ if (!isStringArray(base) || !isStringArray(ours) || !isStringArray(theirs) || !base.every((value) => ours.includes(value) && theirs.includes(value))) {
8848
+ return;
8849
+ }
8850
+ return [...new Set([...ours, ...theirs])].sort();
8851
+ }
8852
+ function isStringArray(value) {
8853
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
8854
+ }
8657
8855
  function isObject(value) {
8658
8856
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8659
8857
  }
@@ -8836,6 +9034,33 @@ function resolveModuleRecipeIds(request) {
8836
9034
  return lifecycle.updatedModules.flatMap((name) => catalog.get(name).updateRecipes);
8837
9035
  }
8838
9036
 
9037
+ // src/update/planner-project-owned-tests.ts
9038
+ var testSuffixes = [
9039
+ ".webtest.ts",
9040
+ ".webtest.tsx",
9041
+ ".test.ts",
9042
+ ".test.tsx"
9043
+ ];
9044
+ var sourceExtensions = [".vue", ".ts", ".tsx", ".js", ".jsx"];
9045
+ function preserveTestsForExistingProjectOwnedSources(files, sourceEntries) {
9046
+ const projectOwnedSourcePaths = new Set([...sourceEntries.values()].filter(({ ownership }) => ownership === "project-owned").map(({ path: path2 }) => path2));
9047
+ for (const file of files) {
9048
+ if (file.ownership !== "project-owned" || file.action !== "create" || !hasExistingSourceForTest(file.path, projectOwnedSourcePaths)) {
9049
+ continue;
9050
+ }
9051
+ file.action = "preserve";
9052
+ file.reason = "\u9879\u76EE\u81EA\u6709\u5B9E\u73B0\u5DF2\u5B58\u5728\uFF0C\u4E0D\u65B0\u589E\u4E0A\u6E38\u884C\u4E3A\u6D4B\u8BD5";
9053
+ delete file.content;
9054
+ }
9055
+ }
9056
+ function hasExistingSourceForTest(path2, sourcePaths) {
9057
+ const suffix = testSuffixes.find((candidate) => path2.endsWith(candidate));
9058
+ if (!suffix)
9059
+ return false;
9060
+ const stem = path2.slice(0, -suffix.length);
9061
+ return sourceExtensions.some((extension) => sourcePaths.has(`${stem}${extension}`));
9062
+ }
9063
+
8839
9064
  // src/update/planner-snapshots.ts
8840
9065
  import { readFile as readFile20, stat as stat2 } from "fs/promises";
8841
9066
  import { join as join24 } from "path";
@@ -8995,6 +9220,7 @@ async function createUpdatePlan(request, artifacts) {
8995
9220
  manageGeneratedProjectOwned: request.targetModules !== undefined,
8996
9221
  forcePlatformBaseline: request.forcePlatformBaseline === true
8997
9222
  });
9223
+ preserveTestsForExistingProjectOwnedSources(files, sourceEntries);
8998
9224
  await deriveDockerfilePlans(files, projectSnapshots, new Set([
8999
9225
  ...paths,
9000
9226
  ...workspaceSnapshot.manifests.keys(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-windy",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "创建单组织、单租户、私有部署优先的 Windy 企业 Dashboard Starter",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "templateCommit": "647dac095fa843d7c6eaa3e734d3fbf0d80d81d8",
4
- "generatorVersion": "0.5.0",
3
+ "templateCommit": "b4fdd1a323b9445461c149ce93191292283bce6b",
4
+ "generatorVersion": "0.5.2",
5
5
  "modules": [
6
6
  {
7
7
  "name": "system",
@@ -1,7 +1,26 @@
1
1
  <script setup lang="ts">
2
- import type { OperationSnapshot } from "@/services/operations-api";
2
+ interface GpuMonitorSnapshot {
3
+ status: "available" | "not-detected" | "unavailable";
4
+ provider: "NVIDIA";
5
+ message: string;
6
+ devices: Array<{
7
+ index: number;
8
+ uuid: string;
9
+ name: string;
10
+ usagePercent: number | null;
11
+ memoryUsedBytes: number | null;
12
+ memoryTotalBytes: number | null;
13
+ memoryUsagePercent: number | null;
14
+ temperatureCelsius: number | null;
15
+ processes: Array<{
16
+ pid: number;
17
+ name: string;
18
+ usedMemoryBytes: number | null;
19
+ }>;
20
+ }>;
21
+ }
3
22
 
4
- defineProps<{ gpu: OperationSnapshot["gpuMonitor"] }>();
23
+ defineProps<{ gpu: GpuMonitorSnapshot }>();
5
24
 
6
25
  function formatBytes(value: number | null) {
7
26
  if (value === null) return "未知";