create-windy 0.3.16 → 0.3.17

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
@@ -1915,7 +1915,7 @@ function isManagedFilesManifest(value, trustedProofs) {
1915
1915
  if (typeof manifest.generatorVersion !== "string")
1916
1916
  return false;
1917
1917
  const generatorVersion = manifest.generatorVersion;
1918
- return (manifest.schemaVersion === 1 || manifest.schemaVersion === 2 || manifest.schemaVersion === 3) && manifest.algorithm === "sha256" && Array.isArray(manifest.files) && manifest.files.every((entry) => isManagedFileEntry(entry, manifest.schemaVersion, generatorVersion, trustedProofs));
1918
+ return (manifest.schemaVersion === 1 || manifest.schemaVersion === 2 || manifest.schemaVersion === 3 || manifest.schemaVersion === 4) && manifest.algorithm === "sha256" && Array.isArray(manifest.files) && manifest.files.every((entry) => isManagedFileEntry(entry, manifest.schemaVersion, generatorVersion, trustedProofs));
1919
1919
  }
1920
1920
  function isManagedFileEntry(entry, schemaVersion, generatorVersion, trustedProofs) {
1921
1921
  if (!entry || typeof entry !== "object")
@@ -1924,9 +1924,33 @@ function isManagedFileEntry(entry, schemaVersion, generatorVersion, trustedProof
1924
1924
  const hasArtifactHash = file.artifactHash !== undefined;
1925
1925
  const hasReconciliation = file.reconciliation !== undefined;
1926
1926
  if (file.state === "missing") {
1927
- return schemaVersion === 3 && typeof file.path === "string" && file.ownership === "project-owned" && file.hash === undefined && !hasArtifactHash && !hasReconciliation && Object.keys(file).every((key) => ["path", "ownership", "state"].includes(key));
1927
+ return (schemaVersion === 3 || schemaVersion === 4) && typeof file.path === "string" && file.ownership === "project-owned" && file.hash === undefined && !hasArtifactHash && !hasReconciliation && Object.keys(file).every((key) => ["path", "ownership", "state"].includes(key));
1928
1928
  }
1929
- return typeof file.path === "string" && (file.ownership === "windy-managed" || file.ownership === "shared-scaffold" || file.ownership === "project-owned") && file.state === undefined && isSha256(file.hash) && (schemaVersion === 1 ? !hasArtifactHash && !hasReconciliation : hasArtifactHash === hasReconciliation && (!hasReconciliation || file.ownership !== "project-owned" && isSha256(file.artifactHash) && isReconciliation(file.reconciliation) && isReleasedReconciliation(file, file.reconciliation, generatorVersion, trustedProofs)));
1929
+ return typeof file.path === "string" && (file.ownership === "windy-managed" || file.ownership === "shared-scaffold" || file.ownership === "project-owned") && file.state === undefined && isSha256(file.hash) && isValidLineage(file, schemaVersion, generatorVersion, trustedProofs, hasArtifactHash, hasReconciliation);
1930
+ }
1931
+ function isValidLineage(file, schemaVersion, generatorVersion, trustedProofs, hasArtifactHash, hasReconciliation) {
1932
+ if (schemaVersion === 1) {
1933
+ return !hasArtifactHash && !hasReconciliation;
1934
+ }
1935
+ if (schemaVersion === 2 || schemaVersion === 3) {
1936
+ return hasArtifactHash === hasReconciliation && (!hasReconciliation || isTrustedReconciliation(file, generatorVersion, trustedProofs));
1937
+ }
1938
+ if (schemaVersion !== 4)
1939
+ return false;
1940
+ if (file.ownership === "project-owned") {
1941
+ return !hasArtifactHash && !hasReconciliation;
1942
+ }
1943
+ if (!hasArtifactHash)
1944
+ return !hasReconciliation;
1945
+ if (!isSha256(file.artifactHash))
1946
+ return false;
1947
+ if (hasReconciliation) {
1948
+ return isTrustedReconciliation(file, generatorVersion, trustedProofs);
1949
+ }
1950
+ return file.artifactHash !== file.hash;
1951
+ }
1952
+ function isTrustedReconciliation(file, generatorVersion, trustedProofs) {
1953
+ return file.ownership !== "project-owned" && isSha256(file.artifactHash) && isReconciliation(file.reconciliation) && isReleasedReconciliation(file, file.reconciliation, generatorVersion, trustedProofs);
1930
1954
  }
1931
1955
  function isSha256(value) {
1932
1956
  return typeof value === "string" && /^[0-9a-f]{64}$/.test(value);
@@ -1999,7 +2023,7 @@ async function writeManifest(targetDirectory, generatorVersion, previous) {
1999
2023
  }
2000
2024
  files.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
2001
2025
  const manifest = {
2002
- schemaVersion: 3,
2026
+ schemaVersion: 4,
2003
2027
  generatorVersion,
2004
2028
  algorithm: "sha256",
2005
2029
  files
@@ -2852,6 +2876,7 @@ var serverDockerfilePath = "apps/server/Dockerfile";
2852
2876
  var serverEntryDirectory = "apps/server";
2853
2877
  var depsCopyAnchor = "windy-copy:deps";
2854
2878
  var buildCopyAnchor = "windy-copy:build";
2879
+ var dockerBunInstallInstruction = `RUN --mount=type=cache,id=windy-bun-1.3.14,target=/root/.bun/install/cache,sharing=shared bun -e 'const child=Bun.spawn(["bun","install","--frozen-lockfile","--no-progress"],{stdin:"inherit",stdout:"inherit",stderr:"inherit"});const heartbeat=setInterval(()=>console.log("[windy] Bun \u4F9D\u8D56\u5B89\u88C5\u4ECD\u5728\u8FDB\u884C"),15000);const exitCode=await child.exited;clearInterval(heartbeat);process.exit(exitCode)'`;
2855
2880
  var dockerfileCopyTargets = [
2856
2881
  {
2857
2882
  dockerfilePath: serverDockerfilePath,
@@ -3804,7 +3829,9 @@ create-windy \u5DF2\u521B\u5EFA\u4EC5\u4F9B\u672C\u673A\u4F7F\u7528\u4E14\u88AB
3804
3829
  Drizzle migration\uFF0C\u518D\u542F\u52A8 Server \u4E0E Web\uFF1B\u4E0D\u8981\u8DF3\u8FC7 migration \u540E\u76F4\u63A5\u628A Server \u8FDE\u63A5\u5230
3805
3830
  \u7A7A\u6570\u636E\u5E93\u3002\u9996\u6B21\u542F\u52A8\u9700\u8981\u672C\u673A\u5DF2\u7ECF\u5B89\u88C5\u5E76\u542F\u52A8 Docker \u4E0E Docker Compose\uFF0C\u5E76\u53EF\u80FD\u9700\u8981\u4E0B\u8F7D
3806
3831
  \u57FA\u7840\u955C\u50CF\u3002\u540E\u7EED \`bun run dev\` \u4F1A\u590D\u7528\u5DF2\u6709\u955C\u50CF\uFF0C\u4E0D\u4F1A\u91CD\u590D\u6784\u5EFA\uFF1B\u4F9D\u8D56\u3001Dockerfile \u6216
3807
- \`bun.lock\` \u53D8\u5316\u540E\u8FD0\u884C \`bun run dev:build\`\u3002
3832
+ \`bun.lock\` \u53D8\u5316\u540E\u8FD0\u884C \`bun run dev:build\`\u3002\u91CD\u5EFA\u4F1A\u8BA9 Web \u4E0E Server \u5E76\u53D1\u590D\u7528
3833
+ BuildKit \u4E2D\u7684 Bun \u5305\u4E0B\u8F7D\u7F13\u5B58\uFF0C\u53EA\u4E0B\u8F7D\u65B0\u589E\u6216\u53D1\u751F\u53D8\u5316\u7684\u4F9D\u8D56\uFF1B\u51B7\u5B89\u88C5\u6BCF 15 \u79D2\u8F93\u51FA\u4E00\u6B21
3834
+ \u4E0D\u542B registry \u5730\u5740\u6216\u51ED\u636E\u7684\u8FDB\u5EA6\u5FC3\u8DF3\u3002
3808
3835
 
3809
3836
  | \u670D\u52A1 | \u5730\u5740 |
3810
3837
  | ---- | ---- |
@@ -4011,7 +4038,8 @@ create-windy \u9ED8\u8BA4\u5EFA\u7ACB \`main\` \u5206\u652F\u5E76\u521B\u5EFA \`
4011
4038
 
4012
4039
  // src/project-package.ts
4013
4040
  var defaultLintStaged = {
4014
- "*.{cjs,css,cts,html,js,json,jsonc,jsx,less,md,mdx,mjs,mts,scss,ts,tsx,vue,yaml,yml}": ["oxfmt --write", "oxlint"]
4041
+ "*.{css,html,json,jsonc,less,md,mdx,scss,yaml,yml}": "oxfmt --write",
4042
+ "*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}": ["oxfmt --write", "oxlint"]
4015
4043
  };
4016
4044
  function createStarterPackage(source, projectName, includeOxc = true, projectLicense = "UNLICENSED", selectedModules = [], generatorVersion) {
4017
4045
  const { "lint-staged": sourceLintStaged, ...sourceWithoutLintStaged } = source;
@@ -6307,7 +6335,7 @@ import { resolve as resolve8 } from "path";
6307
6335
 
6308
6336
  // src/reconcile/managed-reconciler.ts
6309
6337
  import { rm as rm6 } from "fs/promises";
6310
- import { join as join25 } from "path";
6338
+ import { join as join26 } from "path";
6311
6339
 
6312
6340
  // src/update/transaction-cas.ts
6313
6341
  import { basename as basename6, dirname as dirname5 } from "path";
@@ -7372,7 +7400,8 @@ async function unresolvedPreservePaths(root, backupPath, state) {
7372
7400
  const mutation = state.mutationEvidence?.find((evidence) => evidence.path === path2);
7373
7401
  const matchesBackup = matchesExpected(actualHash, actualMode, backup, entry.mode);
7374
7402
  const matchesOutcome = Boolean(mutation?.linearized && mutation.outcome && matchesExpected(actualHash, actualMode, mutation.outcome, mutation.outcomeMode));
7375
- if (!matchesBackup && !matchesOutcome) {
7403
+ const hasConflictArtifact = state.conflictArtifacts?.some((artifact) => artifact.path === path2) ?? false;
7404
+ if (!matchesBackup && (!matchesOutcome || hasConflictArtifact)) {
7376
7405
  unresolved.push(path2);
7377
7406
  }
7378
7407
  }
@@ -7935,355 +7964,250 @@ async function applyTransaction(plan) {
7935
7964
 
7936
7965
  // src/update/planner.ts
7937
7966
  import { readFile as readFile17 } from "fs/promises";
7938
- import { dirname as dirname6, join as join22 } from "path";
7939
-
7940
- // src/update/docker-copy-update-plan.ts
7941
- function deriveDockerfilePlans(files, snapshots, paths, targetWorkspaceManifests) {
7942
- const rootPackage = virtualPathContent("package.json", files, snapshots, targetWorkspaceManifests);
7943
- if (!rootPackage)
7944
- return;
7945
- const rootManifest = parsePackage(rootPackage, "package.json");
7946
- const workspacePatterns = rootManifest.workspaces ?? [];
7947
- const packages = [];
7948
- for (const path2 of [
7949
- ...new Set([...paths, ...targetWorkspaceManifests.keys()])
7950
- ].filter((path3) => path3.endsWith("package.json"))) {
7951
- const directory = path2 === "package.json" ? "." : path2.slice(0, -13);
7952
- if (directory !== "." && !workspacePatterns.some((pattern) => matchesWorkspace(pattern, directory))) {
7953
- continue;
7954
- }
7955
- const content = virtualPathContent(path2, files, snapshots, targetWorkspaceManifests);
7956
- if (!content)
7957
- continue;
7958
- const manifest = parsePackage(content, path2);
7959
- packages.push({
7960
- name: manifest.name,
7961
- directory,
7962
- dependencies: [
7963
- ...Object.keys(manifest.dependencies ?? {}),
7964
- ...Object.keys(manifest.devDependencies ?? {})
7965
- ],
7966
- workspaceDependencies: Object.entries({
7967
- ...manifest.dependencies,
7968
- ...manifest.devDependencies
7969
- }).filter(([, range]) => range.startsWith("workspace:")).map(([name]) => name)
7970
- });
7971
- }
7972
- for (const target of dockerfileCopyTargets) {
7973
- const dockerPlan = files.find(({ path: path2 }) => path2 === target.dockerfilePath);
7974
- if (!dockerPlan || dockerPlan.conflict)
7975
- continue;
7976
- const dockerSource = virtualContent(dockerPlan, snapshots);
7977
- if (!dockerSource)
7978
- continue;
7979
- const source = new TextDecoder().decode(dockerSource);
7980
- const next = syncDockerfileSource(source, target, packages);
7981
- if (next === source)
7982
- continue;
7983
- dockerPlan.action = "update";
7984
- dockerPlan.reason = "\u6309\u4E8B\u52A1\u5B89\u5168 workspace \u89C6\u56FE\u540C\u6B65 Docker COPY \u6E05\u5355";
7985
- dockerPlan.content = new TextEncoder().encode(next);
7986
- }
7987
- }
7988
- function virtualPathContent(path2, files, snapshots, targetWorkspaceManifests) {
7989
- const plan = files.find((item) => item.path === path2);
7990
- return plan ? virtualContent(plan, snapshots) : snapshots.get(path2) ?? targetWorkspaceManifests.get(path2);
7991
- }
7992
- function virtualContent(plan, snapshots) {
7993
- if (plan.action === "delete")
7994
- return;
7995
- if (plan.action === "create" || plan.action === "update")
7996
- return plan.content;
7997
- return snapshots.get(plan.path);
7998
- }
7999
- function parsePackage(content, path2) {
8000
- try {
8001
- return JSON.parse(new TextDecoder().decode(content));
8002
- } catch (error) {
8003
- throw new Error(`\u65E0\u6CD5\u89E3\u6790 Docker workspace \u8F93\u5165\uFF1A${path2}`, {
8004
- cause: error
8005
- });
8006
- }
8007
- }
8008
- function matchesWorkspace(pattern, directory) {
8009
- return pattern.endsWith("/*") && directory.startsWith(pattern.slice(0, -1));
8010
- }
7967
+ import { dirname as dirname6, join as join23 } from "path";
8011
7968
 
8012
- // src/update/migration-reconciler.ts
8013
- import { readFile as readFile13 } from "fs/promises";
8014
- import { join as join17 } from "path";
7969
+ // src/update/docker-copy-legacy-migration.ts
7970
+ import { join as join19 } from "path";
8015
7971
 
8016
- // src/update/migration-project-file.ts
8017
- import { readFile as readFile12 } from "fs/promises";
7972
+ // src/update/merge.ts
7973
+ import { mkdtemp, rm as rm5, writeFile as writeFile9 } from "fs/promises";
7974
+ import { tmpdir } from "os";
8018
7975
  import { join as join16 } from "path";
8019
- async function readProjectJson(input, path2) {
8020
- const content = await readProjectFile(input, path2);
8021
- if (!content) {
8022
- const error = new Error(`\u5F53\u524D\u9879\u76EE\u6587\u4EF6\u7F3A\u5931\uFF1A${path2}`);
8023
- error.code = "ENOENT";
8024
- throw error;
8025
- }
8026
- return JSON.parse(new TextDecoder().decode(content));
7976
+
7977
+ // src/update/process.ts
7978
+ import { spawn as spawn2 } from "child_process";
7979
+ var LOCAL_GIT_ENVIRONMENT_VARIABLES = [
7980
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
7981
+ "GIT_CONFIG",
7982
+ "GIT_CONFIG_PARAMETERS",
7983
+ "GIT_CONFIG_COUNT",
7984
+ "GIT_OBJECT_DIRECTORY",
7985
+ "GIT_DIR",
7986
+ "GIT_WORK_TREE",
7987
+ "GIT_IMPLICIT_WORK_TREE",
7988
+ "GIT_GRAFT_FILE",
7989
+ "GIT_INDEX_FILE",
7990
+ "GIT_NO_REPLACE_OBJECTS",
7991
+ "GIT_REPLACE_REF_BASE",
7992
+ "GIT_PREFIX",
7993
+ "GIT_SHALLOW_FILE",
7994
+ "GIT_COMMON_DIR"
7995
+ ];
7996
+ async function captureProcess(command, args, cwd, inherit = false) {
7997
+ return await new Promise((resolve6, reject) => {
7998
+ const child = spawn2(command, args, {
7999
+ cwd,
8000
+ env: withoutParentGitRepository(process.env),
8001
+ stdio: inherit ? "inherit" : ["ignore", "pipe", "pipe"]
8002
+ });
8003
+ let stdout2 = "";
8004
+ let stderr = "";
8005
+ child.stdout?.on("data", (chunk) => stdout2 += chunk.toString());
8006
+ child.stderr?.on("data", (chunk) => stderr += chunk.toString());
8007
+ child.once("error", reject);
8008
+ child.once("close", (code) => resolve6({ code: code ?? 1, stdout: stdout2, stderr }));
8009
+ });
8027
8010
  }
8028
- async function readProjectFile(input, path2) {
8029
- if (input.projectSnapshots?.has(path2)) {
8030
- return input.projectSnapshots.get(path2);
8031
- }
8032
- let content;
8033
- try {
8034
- content = await readFile12(join16(input.projectDirectory, path2));
8035
- } catch (error) {
8036
- if (!isMissingFileError(error))
8037
- throw error;
8038
- }
8039
- if (input.projectSnapshots) {
8040
- input.projectSnapshots.set(path2, content);
8011
+ async function assertProcess(command, args, cwd, inherit = false) {
8012
+ const result = await captureProcess(command, args, cwd, inherit);
8013
+ if (result.code !== 0) {
8014
+ const detail = result.stderr.trim() || result.stdout.trim();
8015
+ throw new Error(`${command} ${args.join(" ")} \u6267\u884C\u5931\u8D25${detail ? `\uFF1A${detail}` : ""}`);
8041
8016
  }
8042
- return content;
8017
+ return result;
8043
8018
  }
8044
- function isMissingFileError(error) {
8045
- return error instanceof Error && "code" in error && error.code === "ENOENT";
8019
+ function withoutParentGitRepository(source) {
8020
+ const environment = { ...source };
8021
+ for (const key of LOCAL_GIT_ENVIRONMENT_VARIABLES) {
8022
+ delete environment[key];
8023
+ }
8024
+ return environment;
8046
8025
  }
8047
8026
 
8048
- // src/update/snapshot-delta.ts
8049
- function applySnapshotDelta(before, after, current, path2, conflicts, conflictPath) {
8050
- if (deepEqual(before, after))
8051
- return structuredClone(current);
8052
- if (!isRecord(before) || !isRecord(after) || !isRecord(current)) {
8053
- if (deepEqual(current, before) || deepEqual(current, after)) {
8054
- return structuredClone(after);
8027
+ // src/update/merge.ts
8028
+ async function mergeTextFile(base, ours, theirs) {
8029
+ const root = await mkdtemp(join16(tmpdir(), "windy-merge-"));
8030
+ try {
8031
+ const oursPath = join16(root, "ours");
8032
+ const basePath = join16(root, "base");
8033
+ const theirsPath = join16(root, "theirs");
8034
+ await Promise.all([
8035
+ writeFile9(oursPath, ours),
8036
+ writeFile9(basePath, base),
8037
+ writeFile9(theirsPath, theirs)
8038
+ ]);
8039
+ const result = await captureProcess("git", ["merge-file", "-p", "--diff3", oursPath, basePath, theirsPath], root);
8040
+ if (result.code === 0) {
8041
+ return { content: new TextEncoder().encode(result.stdout) };
8055
8042
  }
8056
- conflicts.push(`${conflictPath}\uFF1A\u5E73\u53F0\u4E0E\u4E1A\u52A1\u540C\u65F6\u4FEE\u6539 snapshot \u8282\u70B9 ${path2.join(".") || "<root>"}`);
8057
- return structuredClone(current);
8058
- }
8059
- const merged = structuredClone(current);
8060
- const keys = new Set([...Object.keys(before), ...Object.keys(after)]);
8061
- for (const key of keys) {
8062
- if (path2.length === 0 && (key === "id" || key === "prevId"))
8063
- continue;
8064
- const nextPath = [...path2, key];
8065
- if (!(key in after)) {
8066
- if (!(key in current))
8067
- continue;
8068
- if (deepEqual(current[key], before[key]))
8069
- delete merged[key];
8070
- else
8071
- conflicts.push(`${conflictPath}\uFF1A\u5E73\u53F0\u5220\u9664\u4E86\u4E1A\u52A1\u5DF2\u4FEE\u6539\u7684 snapshot \u8282\u70B9 ${nextPath.join(".")}`);
8072
- continue;
8043
+ if (result.code === 1) {
8044
+ const resolved = resolveConservativeConflicts(result.stdout);
8045
+ return resolved ? { content: new TextEncoder().encode(resolved) } : { conflict: "\u4E09\u65B9\u5408\u5E76\u5B58\u5728\u91CD\u53E0\u4FEE\u6539" };
8073
8046
  }
8074
- if (!(key in before)) {
8075
- if (!(key in current) || deepEqual(current[key], after[key])) {
8076
- merged[key] = structuredClone(after[key]);
8077
- } else {
8078
- conflicts.push(`${conflictPath}\uFF1A\u5E73\u53F0\u65B0\u589E\u7684 snapshot \u8282\u70B9\u5DF2\u88AB\u4E1A\u52A1\u5360\u7528 ${nextPath.join(".")}`);
8079
- }
8047
+ return { conflict: result.stderr.trim() || "git merge-file \u6267\u884C\u5931\u8D25" };
8048
+ } finally {
8049
+ await rm5(root, { recursive: true, force: true });
8050
+ }
8051
+ }
8052
+ function resolveConservativeConflicts(content) {
8053
+ const lines = content.split(`
8054
+ `);
8055
+ const output = [];
8056
+ for (let index = 0;index < lines.length; index += 1) {
8057
+ if (!lines[index]?.startsWith("<<<<<<< ")) {
8058
+ output.push(lines[index] ?? "");
8080
8059
  continue;
8081
8060
  }
8082
- merged[key] = applySnapshotDelta(before[key], after[key], current[key], nextPath, conflicts, conflictPath);
8061
+ const baseMarker = lines.findIndex((line, candidate) => candidate > index && line.startsWith("||||||| "));
8062
+ const separator = lines.indexOf("=======", baseMarker + 1);
8063
+ const endMarker = lines.findIndex((line, candidate) => candidate > separator && line.startsWith(">>>>>>> "));
8064
+ if (baseMarker < 0 || separator < 0 || endMarker < 0)
8065
+ return;
8066
+ const ours = lines.slice(index + 1, baseMarker);
8067
+ const base = lines.slice(baseMarker + 1, separator);
8068
+ const theirs = lines.slice(separator + 1, endMarker);
8069
+ const resolved = resolveRegion(ours, base, theirs);
8070
+ if (!resolved)
8071
+ return;
8072
+ output.push(...resolved);
8073
+ index = endMarker;
8083
8074
  }
8084
- return merged;
8085
- }
8086
- function isRecord(value) {
8087
- return !!value && typeof value === "object" && !Array.isArray(value);
8075
+ return output.join(`
8076
+ `);
8088
8077
  }
8089
- function deepEqual(left, right) {
8090
- return JSON.stringify(left) === JSON.stringify(right);
8078
+ function resolveRegion(ours, base, theirs) {
8079
+ const oursRebased = replaceSubsequence(ours, base, theirs);
8080
+ if (oursRebased)
8081
+ return oursRebased;
8082
+ const theirsRebased = replaceSubsequence(theirs, base, ours);
8083
+ if (theirsRebased)
8084
+ return theirsRebased;
8085
+ if (normalizeWhitespace(ours) === normalizeWhitespace(base))
8086
+ return theirs;
8087
+ if (normalizeWhitespace(theirs) === normalizeWhitespace(base))
8088
+ return ours;
8089
+ return applySemanticTokenEdits(ours, base, theirs) ?? mergeMarkdownTable(ours, base, theirs);
8091
8090
  }
8092
-
8093
- // src/update/migration-reconciler.ts
8094
- var drizzleRoot = "packages/database/drizzle";
8095
- var journalPath = `${drizzleRoot}/meta/_journal.json`;
8096
- var migrationPattern = /^(\d{4})_(.+)$/;
8097
- async function reconcileDrizzleMigrations(input) {
8098
- const journals = await readJournals(input);
8099
- if (!journals) {
8100
- return unchanged(input.targetManifest);
8091
+ function replaceSubsequence(content, baseline, replacement) {
8092
+ if (!baseline.length || baseline.length > content.length)
8093
+ return;
8094
+ const starts = [];
8095
+ for (let index = 0;index <= content.length - baseline.length; index += 1) {
8096
+ if (baseline.every((line, offset) => content[index + offset] === line)) {
8097
+ starts.push(index);
8098
+ }
8101
8099
  }
8102
- const { base, project, target } = journals;
8103
- const conflicts = [];
8104
- validateUniqueNames(base, "\u6765\u6E90\u6A21\u677F", conflicts);
8105
- validateUniqueNames(project, "\u5F53\u524D\u9879\u76EE", conflicts);
8106
- validateUniqueNames(target, "\u76EE\u6807\u6A21\u677F", conflicts);
8107
- if (conflicts.length) {
8108
- return { ...unchanged(input.targetManifest), conflicts };
8109
- }
8110
- const baseNames = new Set(base.entries.map(({ tag }) => stableName(tag)));
8111
- const projectNames = new Set(project.entries.map(({ tag }) => stableName(tag)));
8112
- const managedNames = new Set(input.sourceManifest.files.filter(({ path: path2 }) => isSqlMigration(path2)).map(({ path: path2 }) => stableName(path2.slice(drizzleRoot.length + 1, -4))));
8113
- const candidates = target.entries.filter(({ tag }) => !baseNames.has(stableName(tag)));
8114
- for (const { tag } of candidates) {
8115
- const name = stableName(tag);
8116
- if (projectNames.has(name) && !managedNames.has(name)) {
8117
- conflicts.push(`${journalPath}\uFF1A\u5E73\u53F0 migration \u540D\u79F0\u4E0E\u4E0B\u6E38\u4E1A\u52A1\u5386\u53F2\u51B2\u7A81 ${name}`);
8118
- }
8119
- }
8120
- const additions = candidates.filter(({ tag }) => !projectNames.has(stableName(tag)));
8121
- const contentOverrides = new Map;
8122
- const files = await preservePublishedHistory(input.sourceManifest, input, contentOverrides);
8123
- const publishedPaths = new Set(files.map(({ path: path2 }) => path2));
8124
- files.push(...input.targetManifest.files.filter(({ path: path2 }) => !isDrizzleHistoryPath(path2)));
8125
- if (!additions.length) {
8126
- const journalContent2 = encodeJson(project);
8127
- contentOverrides.set(journalPath, journalContent2);
8128
- files.push(managedEntry(journalPath, journalContent2));
8129
- return result(input.targetManifest, files, contentOverrides, publishedPaths, conflicts);
8130
- }
8131
- let nextPrefix = highestPrefix(project) + 1;
8132
- let nextIndex = highestIndex(project) + 1;
8133
- let previousWhen = project.entries.at(-1)?.when ?? 0;
8134
- let currentSnapshot = await readLatestSnapshot(input, project);
8135
- const mergedJournal = structuredClone(project);
8136
- for (const addition of additions) {
8137
- const targetPosition = target.entries.indexOf(addition);
8138
- const previousTarget = target.entries[targetPosition - 1];
8139
- if (!previousTarget) {
8140
- conflicts.push(`${journalPath}\uFF1A\u76EE\u6807 migration \u7F3A\u5C11\u524D\u7F6E\u5FEB\u7167`);
8141
- break;
8142
- }
8143
- const targetBefore = await readSnapshot(input.targetDirectory, previousTarget.tag);
8144
- const targetAfter = await readSnapshot(input.targetDirectory, addition.tag);
8145
- const merged = applySnapshotDelta(targetBefore, targetAfter, currentSnapshot, [], conflicts, journalPath);
8146
- if (conflicts.length)
8147
- break;
8148
- const prefix = String(nextPrefix).padStart(4, "0");
8149
- const name = stableName(addition.tag);
8150
- const tag = `${prefix}_${name}`;
8151
- const sqlPath = `${drizzleRoot}/${tag}.sql`;
8152
- const snapshotPath = `${drizzleRoot}/meta/${prefix}_snapshot.json`;
8153
- const sql = await readFile13(join17(input.targetDirectory, `${drizzleRoot}/${addition.tag}.sql`));
8154
- merged.id = targetAfter.id;
8155
- merged.prevId = currentSnapshot.id;
8156
- const snapshot = encodeJson(merged);
8157
- contentOverrides.set(sqlPath, sql);
8158
- contentOverrides.set(snapshotPath, snapshot);
8159
- files.push(managedEntry(sqlPath, sql), managedEntry(snapshotPath, snapshot));
8160
- previousWhen = Math.max(previousWhen + 1, addition.when);
8161
- mergedJournal.entries.push({
8162
- ...addition,
8163
- idx: nextIndex,
8164
- when: previousWhen,
8165
- tag
8166
- });
8167
- currentSnapshot = merged;
8168
- nextPrefix += 1;
8169
- nextIndex += 1;
8170
- }
8171
- const journalContent = encodeJson(mergedJournal);
8172
- contentOverrides.set(journalPath, journalContent);
8173
- files.push(managedEntry(journalPath, journalContent));
8174
- return result(input.targetManifest, files, contentOverrides, publishedPaths, conflicts);
8175
- }
8176
- function result(targetManifest, files, contentOverrides, forcePreserves, conflicts) {
8177
- return {
8178
- targetManifest: {
8179
- ...targetManifest,
8180
- files: deduplicate(files).sort((left, right) => left.path.localeCompare(right.path))
8181
- },
8182
- contentOverrides,
8183
- forceUpdates: new Set([journalPath]),
8184
- forcePreserves,
8185
- conflicts
8186
- };
8100
+ if (starts.length !== 1)
8101
+ return;
8102
+ const start = starts[0] ?? 0;
8103
+ return [
8104
+ ...content.slice(0, start),
8105
+ ...replacement,
8106
+ ...content.slice(start + baseline.length)
8107
+ ];
8187
8108
  }
8188
- function unchanged(targetManifest) {
8189
- return {
8190
- targetManifest,
8191
- contentOverrides: new Map,
8192
- forceUpdates: new Set,
8193
- forcePreserves: new Set,
8194
- conflicts: []
8195
- };
8109
+ function normalizeWhitespace(lines) {
8110
+ return lines.join(" ").replaceAll(/\s+/g, " ").trim();
8196
8111
  }
8197
- async function readJournals(input) {
8198
- try {
8199
- const [base, project, target] = await Promise.all([
8200
- readJson(join17(input.baseDirectory, journalPath)),
8201
- readProjectJson(input, journalPath),
8202
- readJson(join17(input.targetDirectory, journalPath))
8203
- ]);
8204
- return { base, project, target };
8205
- } catch (error) {
8206
- if (isMissingFileError(error))
8207
- return;
8208
- throw error;
8112
+ function applySemanticTokenEdits(oursLines, baseLines, theirsLines) {
8113
+ const oursText = oursLines.join(`
8114
+ `);
8115
+ const baseText = baseLines.join(`
8116
+ `);
8117
+ const theirsText = theirsLines.join(`
8118
+ `);
8119
+ const ours = semanticTokens(oursText);
8120
+ const base = semanticTokens(baseText);
8121
+ const theirs = semanticTokens(theirsText);
8122
+ let prefix = 0;
8123
+ while (prefix < base.length && prefix < theirs.length && base[prefix]?.value === theirs[prefix]?.value) {
8124
+ prefix += 1;
8209
8125
  }
8126
+ let suffix = 0;
8127
+ while (suffix < base.length - prefix && suffix < theirs.length - prefix && base[base.length - 1 - suffix]?.value === theirs[theirs.length - 1 - suffix]?.value) {
8128
+ suffix += 1;
8129
+ }
8130
+ const baseEnd = base.length - suffix;
8131
+ const targetEnd = theirs.length - suffix;
8132
+ const needle = base.slice(prefix, baseEnd).map(({ value }) => value);
8133
+ if (!needle.length)
8134
+ return;
8135
+ const starts = findTokenSequence(ours, needle);
8136
+ if (starts.length !== 1)
8137
+ return;
8138
+ const oursStart = starts[0] ?? 0;
8139
+ const oursEnd = oursStart + needle.length - 1;
8140
+ const targetFirst = theirs[prefix];
8141
+ const targetLast = theirs[targetEnd - 1];
8142
+ const replacement = targetFirst && targetLast ? theirsText.slice(targetFirst.start, targetLast.end) : "";
8143
+ const merged = oursText.slice(0, ours[oursStart]?.start ?? 0) + replacement + oursText.slice(ours[oursEnd]?.end ?? 0);
8144
+ return merged.split(`
8145
+ `);
8210
8146
  }
8211
- async function preservePublishedHistory(manifest, input, overrides) {
8212
- const entries = manifest.files.filter(({ path: path2 }) => isMigrationArtifact(path2) && path2 !== journalPath);
8213
- await Promise.all(entries.map(async ({ path: path2 }) => {
8214
- const content = await readProjectFile(input, path2);
8215
- if (!content)
8216
- throw new Error(`\u5F53\u524D\u9879\u76EE migration \u6587\u4EF6\u7F3A\u5931\uFF1A${path2}`);
8217
- overrides.set(path2, content);
8147
+ function semanticTokens(content) {
8148
+ return [...content.matchAll(/[A-Za-z0-9_@./:-]+|[^\s]/g)].map((match) => ({
8149
+ value: match[0],
8150
+ start: match.index,
8151
+ end: match.index + match[0].length
8218
8152
  }));
8219
- return entries;
8220
8153
  }
8221
- async function readLatestSnapshot(input, journal) {
8222
- const latest = journal.entries.at(-1);
8223
- if (!latest)
8224
- throw new Error(`${journalPath}\uFF1A\u5F53\u524D\u9879\u76EE\u6CA1\u6709 migration`);
8225
- const match = migrationPattern.exec(latest.tag);
8226
- if (!match) {
8227
- throw new Error(`${journalPath}\uFF1Amigration tag \u683C\u5F0F\u65E0\u6548\uFF1A${latest.tag}`);
8228
- }
8229
- const snapshotPath = `${drizzleRoot}/meta/${match[1]}_snapshot.json`;
8230
- const content = await readProjectFile(input, snapshotPath);
8231
- if (!content) {
8232
- throw new Error(`\u5F53\u524D\u9879\u76EE migration \u6587\u4EF6\u7F3A\u5931\uFF1A${snapshotPath}`);
8154
+ function findTokenSequence(tokens, needle) {
8155
+ const starts = [];
8156
+ for (let index = 0;index <= tokens.length - needle.length; index += 1) {
8157
+ if (needle.every((value, offset) => tokens[index + offset]?.value === value)) {
8158
+ starts.push(index);
8159
+ }
8233
8160
  }
8234
- return JSON.parse(new TextDecoder().decode(content));
8235
- }
8236
- async function readSnapshot(root, tag) {
8237
- const match = migrationPattern.exec(tag);
8238
- if (!match)
8239
- throw new Error(`${journalPath}\uFF1Amigration tag \u683C\u5F0F\u65E0\u6548\uFF1A${tag}`);
8240
- return await readJson(join17(root, `${drizzleRoot}/meta/${match[1]}_snapshot.json`));
8161
+ return starts;
8241
8162
  }
8242
- function validateUniqueNames(journal, label, conflicts) {
8243
- const seen = new Set;
8244
- for (const { tag } of journal.entries) {
8245
- const name = stableName(tag);
8246
- if (seen.has(name)) {
8247
- conflicts.push(`${journalPath}\uFF1A${label}\u5B58\u5728\u91CD\u590D migration \u540D\u79F0 ${name}`);
8163
+ function mergeMarkdownTable(ours, base, theirs) {
8164
+ if (![ours, base, theirs].every((rows) => rows.length === base.length && rows.every(isMarkdownTableRow))) {
8165
+ return;
8166
+ }
8167
+ const parsed = [ours, base, theirs].map((rows) => rows.map(parseTableRow));
8168
+ const [oursRows, baseRows, theirsRows] = parsed;
8169
+ if (!oursRows || !baseRows || !theirsRows || !oursRows.every((cells, index) => cells.length === baseRows[index]?.length && cells.length === theirsRows[index]?.length)) {
8170
+ return;
8171
+ }
8172
+ const merged = [];
8173
+ for (let row = 0;row < baseRows.length; row += 1) {
8174
+ const cells = [];
8175
+ for (let column = 0;column < (baseRows[row]?.length ?? 0); column += 1) {
8176
+ const baseCell = baseRows[row]?.[column] ?? "";
8177
+ const oursCell = oursRows[row]?.[column] ?? "";
8178
+ const theirsCell = theirsRows[row]?.[column] ?? "";
8179
+ if (isTableSeparator(baseCell)) {
8180
+ cells.push("---");
8181
+ } else if (oursCell === baseCell || oursCell === theirsCell) {
8182
+ cells.push(theirsCell);
8183
+ } else if (theirsCell === baseCell) {
8184
+ cells.push(oursCell);
8185
+ } else {
8186
+ return;
8187
+ }
8248
8188
  }
8249
- seen.add(name);
8189
+ merged.push(`| ${cells.join(" | ")} |`);
8250
8190
  }
8191
+ return merged;
8251
8192
  }
8252
- function stableName(tag) {
8253
- return migrationPattern.exec(tag)?.[2] ?? tag;
8254
- }
8255
- function highestPrefix(journal) {
8256
- return Math.max(-1, ...journal.entries.map(({ tag }) => Number(migrationPattern.exec(tag)?.[1] ?? -1)));
8257
- }
8258
- function highestIndex(journal) {
8259
- return Math.max(-1, ...journal.entries.map(({ idx }) => idx));
8260
- }
8261
- function isDrizzleHistoryPath(path2) {
8262
- return path2 === journalPath || isMigrationArtifact(path2);
8263
- }
8264
- function isMigrationArtifact(path2) {
8265
- return isSqlMigration(path2) || new RegExp(`^${drizzleRoot}/meta/\\d{4}_snapshot\\.json$`).test(path2);
8266
- }
8267
- function isSqlMigration(path2) {
8268
- return new RegExp(`^${drizzleRoot}/\\d{4}_.+\\.sql$`).test(path2);
8269
- }
8270
- function managedEntry(path2, content) {
8271
- return { path: path2, ownership: "windy-managed", hash: hashContent(content) };
8272
- }
8273
- function deduplicate(files) {
8274
- return [...new Map(files.map((entry) => [entry.path, entry])).values()];
8193
+ function isMarkdownTableRow(line) {
8194
+ const trimmed = line.trim();
8195
+ return trimmed.startsWith("|") && trimmed.endsWith("|");
8275
8196
  }
8276
- function encodeJson(value) {
8277
- return new TextEncoder().encode(`${JSON.stringify(value, null, 2)}
8278
- `);
8197
+ function parseTableRow(line) {
8198
+ return line.trim().slice(1, -1).split("|").map((cell) => cell.trim());
8279
8199
  }
8280
- async function readJson(path2) {
8281
- return JSON.parse(await readFile13(path2, "utf8"));
8200
+ function isTableSeparator(cell) {
8201
+ return /^:?-{3,}:?$/.test(cell);
8282
8202
  }
8283
8203
 
8204
+ // src/update/planner-snapshots.ts
8205
+ import { readFile as readFile13, stat as stat2 } from "fs/promises";
8206
+ import { join as join18 } from "path";
8207
+
8284
8208
  // src/update/planner-file.ts
8285
- import { readFile as readFile14 } from "fs/promises";
8286
- import { join as join19 } from "path";
8209
+ import { readFile as readFile12 } from "fs/promises";
8210
+ import { join as join17 } from "path";
8287
8211
 
8288
8212
  // src/update/known-repairs.ts
8289
8213
  import { createHash as createHash5 } from "crypto";
@@ -8487,238 +8411,6 @@ function normalizeTableCell(cell) {
8487
8411
  return separator ? `${separator[1]}---${separator[2]}` : cell;
8488
8412
  }
8489
8413
 
8490
- // src/update/merge.ts
8491
- import { mkdtemp, rm as rm5, writeFile as writeFile9 } from "fs/promises";
8492
- import { tmpdir } from "os";
8493
- import { join as join18 } from "path";
8494
-
8495
- // src/update/process.ts
8496
- import { spawn as spawn2 } from "child_process";
8497
- var LOCAL_GIT_ENVIRONMENT_VARIABLES = [
8498
- "GIT_ALTERNATE_OBJECT_DIRECTORIES",
8499
- "GIT_CONFIG",
8500
- "GIT_CONFIG_PARAMETERS",
8501
- "GIT_CONFIG_COUNT",
8502
- "GIT_OBJECT_DIRECTORY",
8503
- "GIT_DIR",
8504
- "GIT_WORK_TREE",
8505
- "GIT_IMPLICIT_WORK_TREE",
8506
- "GIT_GRAFT_FILE",
8507
- "GIT_INDEX_FILE",
8508
- "GIT_NO_REPLACE_OBJECTS",
8509
- "GIT_REPLACE_REF_BASE",
8510
- "GIT_PREFIX",
8511
- "GIT_SHALLOW_FILE",
8512
- "GIT_COMMON_DIR"
8513
- ];
8514
- async function captureProcess(command, args, cwd, inherit = false) {
8515
- return await new Promise((resolve6, reject) => {
8516
- const child = spawn2(command, args, {
8517
- cwd,
8518
- env: withoutParentGitRepository(process.env),
8519
- stdio: inherit ? "inherit" : ["ignore", "pipe", "pipe"]
8520
- });
8521
- let stdout2 = "";
8522
- let stderr = "";
8523
- child.stdout?.on("data", (chunk) => stdout2 += chunk.toString());
8524
- child.stderr?.on("data", (chunk) => stderr += chunk.toString());
8525
- child.once("error", reject);
8526
- child.once("close", (code) => resolve6({ code: code ?? 1, stdout: stdout2, stderr }));
8527
- });
8528
- }
8529
- async function assertProcess(command, args, cwd, inherit = false) {
8530
- const result2 = await captureProcess(command, args, cwd, inherit);
8531
- if (result2.code !== 0) {
8532
- const detail = result2.stderr.trim() || result2.stdout.trim();
8533
- throw new Error(`${command} ${args.join(" ")} \u6267\u884C\u5931\u8D25${detail ? `\uFF1A${detail}` : ""}`);
8534
- }
8535
- return result2;
8536
- }
8537
- function withoutParentGitRepository(source) {
8538
- const environment = { ...source };
8539
- for (const key of LOCAL_GIT_ENVIRONMENT_VARIABLES) {
8540
- delete environment[key];
8541
- }
8542
- return environment;
8543
- }
8544
-
8545
- // src/update/merge.ts
8546
- async function mergeTextFile(base, ours, theirs) {
8547
- const root = await mkdtemp(join18(tmpdir(), "windy-merge-"));
8548
- try {
8549
- const oursPath = join18(root, "ours");
8550
- const basePath = join18(root, "base");
8551
- const theirsPath = join18(root, "theirs");
8552
- await Promise.all([
8553
- writeFile9(oursPath, ours),
8554
- writeFile9(basePath, base),
8555
- writeFile9(theirsPath, theirs)
8556
- ]);
8557
- const result2 = await captureProcess("git", ["merge-file", "-p", "--diff3", oursPath, basePath, theirsPath], root);
8558
- if (result2.code === 0) {
8559
- return { content: new TextEncoder().encode(result2.stdout) };
8560
- }
8561
- if (result2.code === 1) {
8562
- const resolved = resolveConservativeConflicts(result2.stdout);
8563
- return resolved ? { content: new TextEncoder().encode(resolved) } : { conflict: "\u4E09\u65B9\u5408\u5E76\u5B58\u5728\u91CD\u53E0\u4FEE\u6539" };
8564
- }
8565
- return { conflict: result2.stderr.trim() || "git merge-file \u6267\u884C\u5931\u8D25" };
8566
- } finally {
8567
- await rm5(root, { recursive: true, force: true });
8568
- }
8569
- }
8570
- function resolveConservativeConflicts(content) {
8571
- const lines = content.split(`
8572
- `);
8573
- const output = [];
8574
- for (let index = 0;index < lines.length; index += 1) {
8575
- if (!lines[index]?.startsWith("<<<<<<< ")) {
8576
- output.push(lines[index] ?? "");
8577
- continue;
8578
- }
8579
- const baseMarker = lines.findIndex((line, candidate) => candidate > index && line.startsWith("||||||| "));
8580
- const separator = lines.indexOf("=======", baseMarker + 1);
8581
- const endMarker = lines.findIndex((line, candidate) => candidate > separator && line.startsWith(">>>>>>> "));
8582
- if (baseMarker < 0 || separator < 0 || endMarker < 0)
8583
- return;
8584
- const ours = lines.slice(index + 1, baseMarker);
8585
- const base = lines.slice(baseMarker + 1, separator);
8586
- const theirs = lines.slice(separator + 1, endMarker);
8587
- const resolved = resolveRegion(ours, base, theirs);
8588
- if (!resolved)
8589
- return;
8590
- output.push(...resolved);
8591
- index = endMarker;
8592
- }
8593
- return output.join(`
8594
- `);
8595
- }
8596
- function resolveRegion(ours, base, theirs) {
8597
- const oursRebased = replaceSubsequence(ours, base, theirs);
8598
- if (oursRebased)
8599
- return oursRebased;
8600
- const theirsRebased = replaceSubsequence(theirs, base, ours);
8601
- if (theirsRebased)
8602
- return theirsRebased;
8603
- if (normalizeWhitespace(ours) === normalizeWhitespace(base))
8604
- return theirs;
8605
- if (normalizeWhitespace(theirs) === normalizeWhitespace(base))
8606
- return ours;
8607
- return applySemanticTokenEdits(ours, base, theirs) ?? mergeMarkdownTable(ours, base, theirs);
8608
- }
8609
- function replaceSubsequence(content, baseline, replacement) {
8610
- if (!baseline.length || baseline.length > content.length)
8611
- return;
8612
- const starts = [];
8613
- for (let index = 0;index <= content.length - baseline.length; index += 1) {
8614
- if (baseline.every((line, offset) => content[index + offset] === line)) {
8615
- starts.push(index);
8616
- }
8617
- }
8618
- if (starts.length !== 1)
8619
- return;
8620
- const start = starts[0] ?? 0;
8621
- return [
8622
- ...content.slice(0, start),
8623
- ...replacement,
8624
- ...content.slice(start + baseline.length)
8625
- ];
8626
- }
8627
- function normalizeWhitespace(lines) {
8628
- return lines.join(" ").replaceAll(/\s+/g, " ").trim();
8629
- }
8630
- function applySemanticTokenEdits(oursLines, baseLines, theirsLines) {
8631
- const oursText = oursLines.join(`
8632
- `);
8633
- const baseText = baseLines.join(`
8634
- `);
8635
- const theirsText = theirsLines.join(`
8636
- `);
8637
- const ours = semanticTokens(oursText);
8638
- const base = semanticTokens(baseText);
8639
- const theirs = semanticTokens(theirsText);
8640
- let prefix = 0;
8641
- while (prefix < base.length && prefix < theirs.length && base[prefix]?.value === theirs[prefix]?.value) {
8642
- prefix += 1;
8643
- }
8644
- let suffix = 0;
8645
- while (suffix < base.length - prefix && suffix < theirs.length - prefix && base[base.length - 1 - suffix]?.value === theirs[theirs.length - 1 - suffix]?.value) {
8646
- suffix += 1;
8647
- }
8648
- const baseEnd = base.length - suffix;
8649
- const targetEnd = theirs.length - suffix;
8650
- const needle = base.slice(prefix, baseEnd).map(({ value }) => value);
8651
- if (!needle.length)
8652
- return;
8653
- const starts = findTokenSequence(ours, needle);
8654
- if (starts.length !== 1)
8655
- return;
8656
- const oursStart = starts[0] ?? 0;
8657
- const oursEnd = oursStart + needle.length - 1;
8658
- const targetFirst = theirs[prefix];
8659
- const targetLast = theirs[targetEnd - 1];
8660
- const replacement = targetFirst && targetLast ? theirsText.slice(targetFirst.start, targetLast.end) : "";
8661
- const merged = oursText.slice(0, ours[oursStart]?.start ?? 0) + replacement + oursText.slice(ours[oursEnd]?.end ?? 0);
8662
- return merged.split(`
8663
- `);
8664
- }
8665
- function semanticTokens(content) {
8666
- return [...content.matchAll(/[A-Za-z0-9_@./:-]+|[^\s]/g)].map((match) => ({
8667
- value: match[0],
8668
- start: match.index,
8669
- end: match.index + match[0].length
8670
- }));
8671
- }
8672
- function findTokenSequence(tokens, needle) {
8673
- const starts = [];
8674
- for (let index = 0;index <= tokens.length - needle.length; index += 1) {
8675
- if (needle.every((value, offset) => tokens[index + offset]?.value === value)) {
8676
- starts.push(index);
8677
- }
8678
- }
8679
- return starts;
8680
- }
8681
- function mergeMarkdownTable(ours, base, theirs) {
8682
- if (![ours, base, theirs].every((rows) => rows.length === base.length && rows.every(isMarkdownTableRow))) {
8683
- return;
8684
- }
8685
- const parsed = [ours, base, theirs].map((rows) => rows.map(parseTableRow));
8686
- const [oursRows, baseRows, theirsRows] = parsed;
8687
- if (!oursRows || !baseRows || !theirsRows || !oursRows.every((cells, index) => cells.length === baseRows[index]?.length && cells.length === theirsRows[index]?.length)) {
8688
- return;
8689
- }
8690
- const merged = [];
8691
- for (let row = 0;row < baseRows.length; row += 1) {
8692
- const cells = [];
8693
- for (let column = 0;column < (baseRows[row]?.length ?? 0); column += 1) {
8694
- const baseCell = baseRows[row]?.[column] ?? "";
8695
- const oursCell = oursRows[row]?.[column] ?? "";
8696
- const theirsCell = theirsRows[row]?.[column] ?? "";
8697
- if (isTableSeparator(baseCell)) {
8698
- cells.push("---");
8699
- } else if (oursCell === baseCell || oursCell === theirsCell) {
8700
- cells.push(theirsCell);
8701
- } else if (theirsCell === baseCell) {
8702
- cells.push(oursCell);
8703
- } else {
8704
- return;
8705
- }
8706
- }
8707
- merged.push(`| ${cells.join(" | ")} |`);
8708
- }
8709
- return merged;
8710
- }
8711
- function isMarkdownTableRow(line) {
8712
- const trimmed = line.trim();
8713
- return trimmed.startsWith("|") && trimmed.endsWith("|");
8714
- }
8715
- function parseTableRow(line) {
8716
- return line.trim().slice(1, -1).split("|").map((cell) => cell.trim());
8717
- }
8718
- function isTableSeparator(cell) {
8719
- return /^:?-{3,}:?$/.test(cell);
8720
- }
8721
-
8722
8414
  // src/update/package-merge.ts
8723
8415
  function mergePackageJson(base, ours, theirs) {
8724
8416
  const conflicts = [];
@@ -8806,8 +8498,8 @@ function conflict(path2, reason, expectedProjectContent) {
8806
8498
  // src/update/planner-file.ts
8807
8499
  async function planFile(path2, baseRoot, targetRoot, source, target, ours, provenance, targetContentOverride, manageGeneratedProjectOwned = false, forcePlatformBaseline = false) {
8808
8500
  const [base, theirs] = await Promise.all([
8809
- readOptional(join19(baseRoot, path2)),
8810
- targetContentOverride ? Promise.resolve(targetContentOverride) : readOptional(join19(targetRoot, path2))
8501
+ readOptional(join17(baseRoot, path2)),
8502
+ targetContentOverride ? Promise.resolve(targetContentOverride) : readOptional(join17(targetRoot, path2))
8811
8503
  ]);
8812
8504
  const ownership = source?.ownership === "project-owned" ? source.ownership : target?.ownership || source?.ownership || classifyOwnership(path2);
8813
8505
  const expected = expectationForSnapshot(ours);
@@ -8853,7 +8545,8 @@ async function planFile(path2, baseRoot, targetRoot, source, target, ours, prove
8853
8545
  const oursChanged = hashContent(ours) !== source.hash;
8854
8546
  const sourceArtifactHash = artifactHashOf(source);
8855
8547
  const targetChanged = targetChangedSinceBase(sourceArtifactHash, base, theirs);
8856
- const forceTargetBaseline = shouldForcePlatformBaseline({
8548
+ const adoptedProjectDelta = source.artifactHash !== undefined && source.artifactHash !== source.hash && source.reconciliation === undefined;
8549
+ const forceTargetBaseline = !adoptedProjectDelta && shouldForcePlatformBaseline({
8857
8550
  forcePlatformBaseline,
8858
8551
  oursChanged,
8859
8552
  sourceArtifactHash,
@@ -8873,123 +8566,630 @@ async function planFile(path2, baseRoot, targetRoot, source, target, ours, prove
8873
8566
  const merged = mergePackageJson(base, ours, theirs);
8874
8567
  return merged.content ? readyPlannedFile(path2, ownership, "update", "package.json \u5B57\u6BB5\u7EA7\u5408\u5E76", expected, merged.content) : conflict2(path2, ownership, `\u5B57\u6BB5\u51B2\u7A81\uFF1A${merged.conflicts.join("\u3001")}`, expected);
8875
8568
  }
8876
- if (!oursChanged) {
8877
- return readyPlannedFile(path2, ownership, "update", "\u4EC5 Windy \u4FEE\u6539", expected, theirs);
8569
+ if (!oursChanged && targetChanged && adoptedProjectDelta && base) {
8570
+ const merged = await mergeTextFile(base, ours, theirs);
8571
+ return merged.content ? readyPlannedFile(path2, ownership, "update", "\u4E09\u65B9\u5408\u5E76\u5DF2\u91C7\u7EB3\u9879\u76EE\u589E\u91CF", expected, merged.content) : conflict2(path2, ownership, merged.conflict || "\u4E09\u65B9\u5408\u5E76\u5931\u8D25", expected);
8572
+ }
8573
+ if (!oursChanged) {
8574
+ return readyPlannedFile(path2, ownership, "update", "\u4EC5 Windy \u4FEE\u6539", expected, theirs);
8575
+ }
8576
+ if (!targetChanged) {
8577
+ return readyPlannedFile(path2, ownership, "preserve", "\u5BA2\u6237\u5DF2\u4FEE\u6539", expected);
8578
+ }
8579
+ if (hashContent(ours) === target.hash) {
8580
+ return readyPlannedFile(path2, ownership, "preserve", "\u5DF2\u5305\u542B\u76EE\u6807\u5185\u5BB9", expected);
8581
+ }
8582
+ if (base) {
8583
+ const merged = await mergeTextFile(base, ours, theirs);
8584
+ return merged.content ? readyPlannedFile(path2, ownership, "update", "\u4E09\u65B9\u5408\u5E76", expected, merged.content) : conflict2(path2, ownership, merged.conflict || "\u4E09\u65B9\u5408\u5E76\u5931\u8D25", expected);
8585
+ }
8586
+ return conflict2(path2, ownership, "\u5BA2\u6237\u548C Windy \u5747\u5DF2\u4FEE\u6539", expected);
8587
+ }
8588
+ function shouldForcePlatformBaseline(input) {
8589
+ return input.forcePlatformBaseline && !input.oursChanged && input.base !== undefined && hashContent(input.base) !== input.sourceArtifactHash;
8590
+ }
8591
+ function targetChangedSinceBase(sourceHash, base, target) {
8592
+ return base && target ? hashContent(base) !== hashContent(target) : !!target && hashContent(target) !== sourceHash;
8593
+ }
8594
+ function readyPlannedFile(path2, ownership, action, reason, expectedProjectContent, content) {
8595
+ return {
8596
+ path: path2,
8597
+ ownership,
8598
+ action,
8599
+ reason,
8600
+ expectedProjectContent,
8601
+ content
8602
+ };
8603
+ }
8604
+ function expectationForSnapshot(content) {
8605
+ return content ? { kind: "hash", value: hashContent(content) } : { kind: "missing" };
8606
+ }
8607
+ function conflict2(path2, ownership, reason, expectedProjectContent) {
8608
+ return {
8609
+ path: path2,
8610
+ ownership,
8611
+ action: "preserve",
8612
+ reason: "\u5BA2\u6237\u5DF2\u4FEE\u6539",
8613
+ expectedProjectContent,
8614
+ conflict: reason
8615
+ };
8616
+ }
8617
+ async function readOptional(path2) {
8618
+ try {
8619
+ return await readFile12(path2);
8620
+ } catch (error) {
8621
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
8622
+ return;
8623
+ }
8624
+ throw error;
8625
+ }
8626
+ }
8627
+
8628
+ // src/update/planner-snapshots.ts
8629
+ async function snapshotProjectFiles(project, paths, seed) {
8630
+ const snapshots = new Map(seed);
8631
+ await Promise.all([...paths].filter((path2) => !snapshots.has(path2)).map(async (path2) => {
8632
+ snapshots.set(path2, await readOptional2(join18(project, path2)));
8633
+ }));
8634
+ return snapshots;
8635
+ }
8636
+ async function snapshotProjectModes(project, paths) {
8637
+ return new Map(await Promise.all([...paths].map(async (path2) => [path2, await readModeOptional(join18(project, path2))])));
8638
+ }
8639
+ async function assertPlanSnapshots(project, expectations) {
8640
+ const changed = [];
8641
+ for (const expectation2 of expectations) {
8642
+ const actual = expectationForSnapshot(await readOptional2(join18(project, expectation2.path)));
8643
+ if (JSON.stringify(actual) !== JSON.stringify(expectation2.expected)) {
8644
+ changed.push(expectation2.path);
8645
+ continue;
8646
+ }
8647
+ if ("mode" in expectation2 && await readModeOptional(join18(project, expectation2.path)) !== expectation2.mode) {
8648
+ changed.push(expectation2.path);
8649
+ }
8650
+ }
8651
+ if (changed.length) {
8652
+ throw new Error(`\u751F\u6210\u66F4\u65B0\u8BA1\u5212\u671F\u95F4\u5019\u9009\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${changed.join("\u3001")}`);
8653
+ }
8654
+ }
8655
+ async function readModeOptional(path2) {
8656
+ try {
8657
+ return (await stat2(path2)).mode & 511;
8658
+ } catch (error) {
8659
+ if (isMissing5(error))
8660
+ return;
8661
+ throw error;
8662
+ }
8663
+ }
8664
+ async function readSourceParentMode(path2) {
8665
+ try {
8666
+ const info = await stat2(path2);
8667
+ if (!info.isDirectory()) {
8668
+ throw new Error(`artifact \u6E90\u7801\u7236\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${path2}`);
8669
+ }
8670
+ return info.mode & 493 | 493;
8671
+ } catch (error) {
8672
+ if (isMissing5(error))
8673
+ return 493;
8674
+ throw error;
8675
+ }
8676
+ }
8677
+ async function readOptional2(path2) {
8678
+ try {
8679
+ return await readFile13(path2);
8680
+ } catch (error) {
8681
+ if (isMissing5(error))
8682
+ return;
8683
+ throw error;
8684
+ }
8685
+ }
8686
+ function isMissing5(error) {
8687
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
8688
+ }
8689
+
8690
+ // src/update/docker-copy-legacy-migration.ts
8691
+ async function migrateDockerfileCopySource(source, target, conflict3, locations) {
8692
+ const decoded = new TextDecoder().decode(source);
8693
+ const legacy = !hasDockerfileCopyAnchors(decoded, target);
8694
+ if (!hasSinglePublishedBunInstallInstruction(decoded)) {
8695
+ return {
8696
+ legacy,
8697
+ conflict: "Bun \u5B89\u88C5\u547D\u4EE4\u4E0D\u662F\u53EF\u8BC1\u660E\u7684 Windy \u5DF2\u53D1\u5E03\u6807\u51C6"
8698
+ };
8699
+ }
8700
+ if (!legacy)
8701
+ return { content: decoded, legacy };
8702
+ if (!conflict3) {
8703
+ return {
8704
+ content: adoptLegacyCopyAnchors(decoded, target),
8705
+ legacy
8706
+ };
8707
+ }
8708
+ const [base, theirs] = await Promise.all([
8709
+ readOptional2(join19(locations.baseDirectory, locations.path)),
8710
+ readOptional2(join19(locations.targetDirectory, locations.path))
8711
+ ]);
8712
+ if (!base || !theirs)
8713
+ return { legacy };
8714
+ const merged = await mergeLegacyDockerfile(base, source, theirs, target);
8715
+ return {
8716
+ content: merged.content,
8717
+ conflict: merged.conflict,
8718
+ legacy
8719
+ };
8720
+ }
8721
+ async function mergeLegacyDockerfile(base, ours, theirs, target) {
8722
+ const normalized = [base, ours, theirs].map((content2) => normalizeDockerfileForMerge(new TextDecoder().decode(content2), target));
8723
+ if (normalized.some((content2) => content2 === undefined))
8724
+ return {};
8725
+ const encode = (content2) => new TextEncoder().encode(content2);
8726
+ const merged = await mergeTextFile(encode(normalized[0]), encode(normalized[1]), encode(normalized[2]));
8727
+ if (!merged.content)
8728
+ return { conflict: merged.conflict };
8729
+ const content = restoreNormalizedDockerfile(new TextDecoder().decode(merged.content), target);
8730
+ return content === undefined ? {} : { content };
8731
+ }
8732
+ var installPlaceholder = "# windy-merge:bun-install";
8733
+ function normalizeDockerfileForMerge(source, target) {
8734
+ let normalized = hasDockerfileCopyAnchors(source, target) ? source : addLegacyCopyAnchors(source, target);
8735
+ if (normalized === undefined)
8736
+ return;
8737
+ for (const anchor of dockerAnchors(target)) {
8738
+ const next = replaceAnchoredSection(normalized, anchor, [
8739
+ `# windy-merge:${anchor}`
8740
+ ]);
8741
+ if (next === undefined)
8742
+ return;
8743
+ normalized = next;
8744
+ }
8745
+ return replaceUniqueLine(normalized, isPublishedBunInstallInstruction, installPlaceholder);
8746
+ }
8747
+ function restoreNormalizedDockerfile(source, target) {
8748
+ let restored = source;
8749
+ for (const anchor of dockerAnchors(target)) {
8750
+ const next = replaceAnchoredSection(restored, anchor, []);
8751
+ if (next === undefined)
8752
+ return;
8753
+ restored = next;
8754
+ }
8755
+ return replaceUniqueLine(restored, (line) => line === installPlaceholder, dockerBunInstallInstruction);
8756
+ }
8757
+ function dockerAnchors(target) {
8758
+ return target.includeBuildClosure ? [depsCopyAnchor, buildCopyAnchor] : [depsCopyAnchor];
8759
+ }
8760
+ function adoptLegacyCopyAnchors(source, target) {
8761
+ const anchored = addLegacyCopyAnchors(source, target);
8762
+ return anchored === undefined ? undefined : replaceUniqueLine(anchored, isPublishedBunInstallInstruction, dockerBunInstallInstruction);
8763
+ }
8764
+ function addLegacyCopyAnchors(source, target) {
8765
+ const deps = wrapLegacyCopySection(source, "COPY package.json bun.lock ./", isPublishedBunInstallInstruction, depsCopyAnchor, (line) => /^COPY ((?:apps|packages)\/[^/\s]+\/package\.json) \1$/.test(line), `COPY ${target.entryDirectory}/package.json ${target.entryDirectory}/package.json`);
8766
+ if (deps === undefined)
8767
+ return;
8768
+ if (!target.includeBuildClosure)
8769
+ return deps;
8770
+ return wrapLegacyCopySection(deps, "FROM deps AS build", (line) => line === "RUN bun run --cwd apps/web build", buildCopyAnchor, (line) => /^COPY ((?:apps|packages)\/[^/\s]+) \1$/.test(line), `COPY ${target.entryDirectory} ${target.entryDirectory}`);
8771
+ }
8772
+ var publishedBunInstallInstructions = new Set([
8773
+ "RUN bun install --frozen-lockfile",
8774
+ "RUN --mount=type=cache,target=/root/.bun/install/cache,sharing=locked bun install --frozen-lockfile",
8775
+ dockerBunInstallInstruction
8776
+ ]);
8777
+ function hasSinglePublishedBunInstallInstruction(source) {
8778
+ const candidates = source.split(`
8779
+ `).filter((line) => line.startsWith("RUN ") && (/\bbun\s+install\b/.test(line) || line.includes('Bun.spawn(["bun","install"')));
8780
+ return candidates.length === 1 && isPublishedBunInstallInstruction(candidates[0]);
8781
+ }
8782
+ function isPublishedBunInstallInstruction(line) {
8783
+ return publishedBunInstallInstructions.has(line);
8784
+ }
8785
+ function wrapLegacyCopySection(source, startLine, isEnd, anchor, isCopy, requiredCopy) {
8786
+ const lines = source.split(`
8787
+ `);
8788
+ const starts = indexesOf(lines, (line) => line === startLine);
8789
+ const ends = indexesOf(lines, isEnd);
8790
+ if (starts.length !== 1 || ends.length !== 1 || source.includes(`# ${anchor} begin`) || source.includes(`# ${anchor} end`)) {
8791
+ return;
8792
+ }
8793
+ const start = starts[0];
8794
+ const end = ends[0];
8795
+ if (end <= start)
8796
+ return;
8797
+ const section = lines.slice(start + 1, end);
8798
+ const copyLines = section.filter((line) => line.length > 0);
8799
+ if (copyLines.length === 0 || !copyLines.every(isCopy) || copyLines.filter((line) => line === requiredCopy).length !== 1) {
8800
+ return;
8801
+ }
8802
+ return [
8803
+ ...lines.slice(0, start + 1),
8804
+ `# ${anchor} begin`,
8805
+ ...section,
8806
+ `# ${anchor} end`,
8807
+ ...lines.slice(end)
8808
+ ].join(`
8809
+ `);
8810
+ }
8811
+ function replaceUniqueLine(source, predicate, replacement) {
8812
+ const lines = source.split(`
8813
+ `);
8814
+ const matches2 = indexesOf(lines, predicate);
8815
+ if (matches2.length !== 1)
8816
+ return;
8817
+ lines[matches2[0]] = replacement;
8818
+ return lines.join(`
8819
+ `);
8820
+ }
8821
+ function replaceAnchoredSection(source, anchor, content) {
8822
+ const lines = source.split(`
8823
+ `);
8824
+ const begins = indexesOf(lines, (line) => line === `# ${anchor} begin`);
8825
+ const ends = indexesOf(lines, (line) => line === `# ${anchor} end`);
8826
+ if (begins.length !== 1 || ends.length !== 1 || ends[0] <= begins[0]) {
8827
+ return;
8828
+ }
8829
+ return [
8830
+ ...lines.slice(0, begins[0] + 1),
8831
+ ...content,
8832
+ ...lines.slice(ends[0])
8833
+ ].join(`
8834
+ `);
8835
+ }
8836
+ function indexesOf(lines, predicate) {
8837
+ return lines.flatMap((line, index) => predicate(line) ? [index] : []);
8838
+ }
8839
+
8840
+ // src/update/docker-copy-update-plan.ts
8841
+ async function deriveDockerfilePlans(files, snapshots, paths, targetWorkspaceManifests, baseDirectory, targetDirectory) {
8842
+ const rootPackage = virtualPathContent("package.json", files, snapshots, targetWorkspaceManifests);
8843
+ if (!rootPackage)
8844
+ return;
8845
+ const rootManifest = parsePackage(rootPackage, "package.json");
8846
+ const workspacePatterns = rootManifest.workspaces ?? [];
8847
+ const packages = [];
8848
+ for (const path2 of [
8849
+ ...new Set([...paths, ...targetWorkspaceManifests.keys()])
8850
+ ].filter((path3) => path3.endsWith("package.json"))) {
8851
+ const directory = path2 === "package.json" ? "." : path2.slice(0, -13);
8852
+ if (directory !== "." && !workspacePatterns.some((pattern) => matchesWorkspace(pattern, directory))) {
8853
+ continue;
8854
+ }
8855
+ const content = virtualPathContent(path2, files, snapshots, targetWorkspaceManifests);
8856
+ if (!content)
8857
+ continue;
8858
+ const manifest = parsePackage(content, path2);
8859
+ packages.push({
8860
+ name: manifest.name,
8861
+ directory,
8862
+ dependencies: [
8863
+ ...Object.keys(manifest.dependencies ?? {}),
8864
+ ...Object.keys(manifest.devDependencies ?? {})
8865
+ ],
8866
+ workspaceDependencies: Object.entries({
8867
+ ...manifest.dependencies,
8868
+ ...manifest.devDependencies
8869
+ }).filter(([, range]) => range.startsWith("workspace:")).map(([name]) => name)
8870
+ });
8871
+ }
8872
+ for (const target of dockerfileCopyTargets) {
8873
+ const dockerPlan = files.find(({ path: path2 }) => path2 === target.dockerfilePath);
8874
+ if (!dockerPlan)
8875
+ continue;
8876
+ const dockerSource = dockerPlan.conflict ? snapshots.get(dockerPlan.path) : virtualContent(dockerPlan, snapshots);
8877
+ if (!dockerSource)
8878
+ continue;
8879
+ const source = new TextDecoder().decode(dockerSource);
8880
+ const migration = await migrateDockerfileCopySource(dockerSource, target, Boolean(dockerPlan.conflict), {
8881
+ path: dockerPlan.path,
8882
+ baseDirectory,
8883
+ targetDirectory
8884
+ });
8885
+ if (migration.conflict)
8886
+ dockerPlan.conflict = migration.conflict;
8887
+ if (migration.content === undefined || dockerPlan.conflict && !migration.legacy) {
8888
+ continue;
8889
+ }
8890
+ const next = syncDockerfileSource(migration.content, target, packages);
8891
+ if (next === source && !dockerPlan.conflict)
8892
+ continue;
8893
+ dockerPlan.action = "update";
8894
+ dockerPlan.reason = migration.legacy ? "\u7ED3\u6784\u5316\u91C7\u7EB3\u65E7 Dockerfile \u5E76\u540C\u6B65\u5BA2\u6237 workspace COPY \u6E05\u5355" : "\u6309\u4E8B\u52A1\u5B89\u5168 workspace \u89C6\u56FE\u540C\u6B65 Docker COPY \u6E05\u5355";
8895
+ dockerPlan.content = new TextEncoder().encode(next);
8896
+ delete dockerPlan.conflict;
8897
+ }
8898
+ }
8899
+ function virtualPathContent(path2, files, snapshots, targetWorkspaceManifests) {
8900
+ const plan = files.find((item) => item.path === path2);
8901
+ return plan ? virtualContent(plan, snapshots) : snapshots.get(path2) ?? targetWorkspaceManifests.get(path2);
8902
+ }
8903
+ function virtualContent(plan, snapshots) {
8904
+ if (plan.action === "delete")
8905
+ return;
8906
+ if (plan.action === "create" || plan.action === "update")
8907
+ return plan.content;
8908
+ return snapshots.get(plan.path);
8909
+ }
8910
+ function parsePackage(content, path2) {
8911
+ try {
8912
+ return JSON.parse(new TextDecoder().decode(content));
8913
+ } catch (error) {
8914
+ throw new Error(`\u65E0\u6CD5\u89E3\u6790 Docker workspace \u8F93\u5165\uFF1A${path2}`, {
8915
+ cause: error
8916
+ });
8917
+ }
8918
+ }
8919
+ function matchesWorkspace(pattern, directory) {
8920
+ return pattern.endsWith("/*") && directory.startsWith(pattern.slice(0, -1));
8921
+ }
8922
+
8923
+ // src/update/migration-reconciler.ts
8924
+ import { readFile as readFile15 } from "fs/promises";
8925
+ import { join as join21 } from "path";
8926
+
8927
+ // src/update/migration-project-file.ts
8928
+ import { readFile as readFile14 } from "fs/promises";
8929
+ import { join as join20 } from "path";
8930
+ async function readProjectJson(input, path2) {
8931
+ const content = await readProjectFile(input, path2);
8932
+ if (!content) {
8933
+ const error = new Error(`\u5F53\u524D\u9879\u76EE\u6587\u4EF6\u7F3A\u5931\uFF1A${path2}`);
8934
+ error.code = "ENOENT";
8935
+ throw error;
8936
+ }
8937
+ return JSON.parse(new TextDecoder().decode(content));
8938
+ }
8939
+ async function readProjectFile(input, path2) {
8940
+ if (input.projectSnapshots?.has(path2)) {
8941
+ return input.projectSnapshots.get(path2);
8942
+ }
8943
+ let content;
8944
+ try {
8945
+ content = await readFile14(join20(input.projectDirectory, path2));
8946
+ } catch (error) {
8947
+ if (!isMissingFileError(error))
8948
+ throw error;
8949
+ }
8950
+ if (input.projectSnapshots) {
8951
+ input.projectSnapshots.set(path2, content);
8952
+ }
8953
+ return content;
8954
+ }
8955
+ function isMissingFileError(error) {
8956
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
8957
+ }
8958
+
8959
+ // src/update/snapshot-delta.ts
8960
+ function applySnapshotDelta(before, after, current, path2, conflicts, conflictPath) {
8961
+ if (deepEqual(before, after))
8962
+ return structuredClone(current);
8963
+ if (!isRecord(before) || !isRecord(after) || !isRecord(current)) {
8964
+ if (deepEqual(current, before) || deepEqual(current, after)) {
8965
+ return structuredClone(after);
8966
+ }
8967
+ conflicts.push(`${conflictPath}\uFF1A\u5E73\u53F0\u4E0E\u4E1A\u52A1\u540C\u65F6\u4FEE\u6539 snapshot \u8282\u70B9 ${path2.join(".") || "<root>"}`);
8968
+ return structuredClone(current);
8969
+ }
8970
+ const merged = structuredClone(current);
8971
+ const keys = new Set([...Object.keys(before), ...Object.keys(after)]);
8972
+ for (const key of keys) {
8973
+ if (path2.length === 0 && (key === "id" || key === "prevId"))
8974
+ continue;
8975
+ const nextPath = [...path2, key];
8976
+ if (!(key in after)) {
8977
+ if (!(key in current))
8978
+ continue;
8979
+ if (deepEqual(current[key], before[key]))
8980
+ delete merged[key];
8981
+ else
8982
+ conflicts.push(`${conflictPath}\uFF1A\u5E73\u53F0\u5220\u9664\u4E86\u4E1A\u52A1\u5DF2\u4FEE\u6539\u7684 snapshot \u8282\u70B9 ${nextPath.join(".")}`);
8983
+ continue;
8984
+ }
8985
+ if (!(key in before)) {
8986
+ if (!(key in current) || deepEqual(current[key], after[key])) {
8987
+ merged[key] = structuredClone(after[key]);
8988
+ } else {
8989
+ conflicts.push(`${conflictPath}\uFF1A\u5E73\u53F0\u65B0\u589E\u7684 snapshot \u8282\u70B9\u5DF2\u88AB\u4E1A\u52A1\u5360\u7528 ${nextPath.join(".")}`);
8990
+ }
8991
+ continue;
8992
+ }
8993
+ merged[key] = applySnapshotDelta(before[key], after[key], current[key], nextPath, conflicts, conflictPath);
8994
+ }
8995
+ return merged;
8996
+ }
8997
+ function isRecord(value) {
8998
+ return !!value && typeof value === "object" && !Array.isArray(value);
8999
+ }
9000
+ function deepEqual(left, right) {
9001
+ return JSON.stringify(left) === JSON.stringify(right);
9002
+ }
9003
+
9004
+ // src/update/migration-reconciler.ts
9005
+ var drizzleRoot = "packages/database/drizzle";
9006
+ var journalPath = `${drizzleRoot}/meta/_journal.json`;
9007
+ var migrationPattern = /^(\d{4})_(.+)$/;
9008
+ async function reconcileDrizzleMigrations(input) {
9009
+ const journals = await readJournals(input);
9010
+ if (!journals) {
9011
+ return unchanged(input.targetManifest);
8878
9012
  }
8879
- if (!targetChanged) {
8880
- return readyPlannedFile(path2, ownership, "preserve", "\u5BA2\u6237\u5DF2\u4FEE\u6539", expected);
9013
+ const { base, project, target } = journals;
9014
+ const conflicts = [];
9015
+ validateUniqueNames(base, "\u6765\u6E90\u6A21\u677F", conflicts);
9016
+ validateUniqueNames(project, "\u5F53\u524D\u9879\u76EE", conflicts);
9017
+ validateUniqueNames(target, "\u76EE\u6807\u6A21\u677F", conflicts);
9018
+ if (conflicts.length) {
9019
+ return { ...unchanged(input.targetManifest), conflicts };
8881
9020
  }
8882
- if (hashContent(ours) === target.hash) {
8883
- return readyPlannedFile(path2, ownership, "preserve", "\u5DF2\u5305\u542B\u76EE\u6807\u5185\u5BB9", expected);
9021
+ const baseNames = new Set(base.entries.map(({ tag }) => stableName(tag)));
9022
+ const projectNames = new Set(project.entries.map(({ tag }) => stableName(tag)));
9023
+ const managedNames = new Set(input.sourceManifest.files.filter(({ path: path2 }) => isSqlMigration(path2)).map(({ path: path2 }) => stableName(path2.slice(drizzleRoot.length + 1, -4))));
9024
+ const candidates = target.entries.filter(({ tag }) => !baseNames.has(stableName(tag)));
9025
+ for (const { tag } of candidates) {
9026
+ const name = stableName(tag);
9027
+ if (projectNames.has(name) && !managedNames.has(name)) {
9028
+ conflicts.push(`${journalPath}\uFF1A\u5E73\u53F0 migration \u540D\u79F0\u4E0E\u4E0B\u6E38\u4E1A\u52A1\u5386\u53F2\u51B2\u7A81 ${name}`);
9029
+ }
8884
9030
  }
8885
- if (base) {
8886
- const merged = await mergeTextFile(base, ours, theirs);
8887
- return merged.content ? readyPlannedFile(path2, ownership, "update", "\u4E09\u65B9\u5408\u5E76", expected, merged.content) : conflict2(path2, ownership, merged.conflict || "\u4E09\u65B9\u5408\u5E76\u5931\u8D25", expected);
9031
+ const additions = candidates.filter(({ tag }) => !projectNames.has(stableName(tag)));
9032
+ const contentOverrides = new Map;
9033
+ const files = await preservePublishedHistory(input.sourceManifest, input, contentOverrides);
9034
+ const publishedPaths = new Set(files.map(({ path: path2 }) => path2));
9035
+ files.push(...input.targetManifest.files.filter(({ path: path2 }) => !isDrizzleHistoryPath(path2)));
9036
+ if (!additions.length) {
9037
+ const journalContent2 = encodeJson(project);
9038
+ contentOverrides.set(journalPath, journalContent2);
9039
+ files.push(managedEntry(journalPath, journalContent2));
9040
+ return result(input.targetManifest, files, contentOverrides, publishedPaths, conflicts);
8888
9041
  }
8889
- return conflict2(path2, ownership, "\u5BA2\u6237\u548C Windy \u5747\u5DF2\u4FEE\u6539", expected);
8890
- }
8891
- function shouldForcePlatformBaseline(input) {
8892
- return input.forcePlatformBaseline && !input.oursChanged && input.base !== undefined && hashContent(input.base) !== input.sourceArtifactHash;
8893
- }
8894
- function targetChangedSinceBase(sourceHash, base, target) {
8895
- return base && target ? hashContent(base) !== hashContent(target) : !!target && hashContent(target) !== sourceHash;
9042
+ let nextPrefix = highestPrefix(project) + 1;
9043
+ let nextIndex = highestIndex(project) + 1;
9044
+ let previousWhen = project.entries.at(-1)?.when ?? 0;
9045
+ let currentSnapshot = await readLatestSnapshot(input, project);
9046
+ const mergedJournal = structuredClone(project);
9047
+ for (const addition of additions) {
9048
+ const targetPosition = target.entries.indexOf(addition);
9049
+ const previousTarget = target.entries[targetPosition - 1];
9050
+ if (!previousTarget) {
9051
+ conflicts.push(`${journalPath}\uFF1A\u76EE\u6807 migration \u7F3A\u5C11\u524D\u7F6E\u5FEB\u7167`);
9052
+ break;
9053
+ }
9054
+ const targetBefore = await readSnapshot(input.targetDirectory, previousTarget.tag);
9055
+ const targetAfter = await readSnapshot(input.targetDirectory, addition.tag);
9056
+ const merged = applySnapshotDelta(targetBefore, targetAfter, currentSnapshot, [], conflicts, journalPath);
9057
+ if (conflicts.length)
9058
+ break;
9059
+ const prefix = String(nextPrefix).padStart(4, "0");
9060
+ const name = stableName(addition.tag);
9061
+ const tag = `${prefix}_${name}`;
9062
+ const sqlPath = `${drizzleRoot}/${tag}.sql`;
9063
+ const snapshotPath = `${drizzleRoot}/meta/${prefix}_snapshot.json`;
9064
+ const sql = await readFile15(join21(input.targetDirectory, `${drizzleRoot}/${addition.tag}.sql`));
9065
+ merged.id = targetAfter.id;
9066
+ merged.prevId = currentSnapshot.id;
9067
+ const snapshot = encodeJson(merged);
9068
+ contentOverrides.set(sqlPath, sql);
9069
+ contentOverrides.set(snapshotPath, snapshot);
9070
+ files.push(managedEntry(sqlPath, sql), managedEntry(snapshotPath, snapshot));
9071
+ previousWhen = Math.max(previousWhen + 1, addition.when);
9072
+ mergedJournal.entries.push({
9073
+ ...addition,
9074
+ idx: nextIndex,
9075
+ when: previousWhen,
9076
+ tag
9077
+ });
9078
+ currentSnapshot = merged;
9079
+ nextPrefix += 1;
9080
+ nextIndex += 1;
9081
+ }
9082
+ const journalContent = encodeJson(mergedJournal);
9083
+ contentOverrides.set(journalPath, journalContent);
9084
+ files.push(managedEntry(journalPath, journalContent));
9085
+ return result(input.targetManifest, files, contentOverrides, publishedPaths, conflicts);
8896
9086
  }
8897
- function readyPlannedFile(path2, ownership, action, reason, expectedProjectContent, content) {
9087
+ function result(targetManifest, files, contentOverrides, forcePreserves, conflicts) {
8898
9088
  return {
8899
- path: path2,
8900
- ownership,
8901
- action,
8902
- reason,
8903
- expectedProjectContent,
8904
- content
9089
+ targetManifest: {
9090
+ ...targetManifest,
9091
+ files: deduplicate(files).sort((left, right) => left.path.localeCompare(right.path))
9092
+ },
9093
+ contentOverrides,
9094
+ forceUpdates: new Set([journalPath]),
9095
+ forcePreserves,
9096
+ conflicts
8905
9097
  };
8906
9098
  }
8907
- function expectationForSnapshot(content) {
8908
- return content ? { kind: "hash", value: hashContent(content) } : { kind: "missing" };
8909
- }
8910
- function conflict2(path2, ownership, reason, expectedProjectContent) {
9099
+ function unchanged(targetManifest) {
8911
9100
  return {
8912
- path: path2,
8913
- ownership,
8914
- action: "preserve",
8915
- reason: "\u5BA2\u6237\u5DF2\u4FEE\u6539",
8916
- expectedProjectContent,
8917
- conflict: reason
9101
+ targetManifest,
9102
+ contentOverrides: new Map,
9103
+ forceUpdates: new Set,
9104
+ forcePreserves: new Set,
9105
+ conflicts: []
8918
9106
  };
8919
9107
  }
8920
- async function readOptional(path2) {
9108
+ async function readJournals(input) {
8921
9109
  try {
8922
- return await readFile14(path2);
9110
+ const [base, project, target] = await Promise.all([
9111
+ readJson(join21(input.baseDirectory, journalPath)),
9112
+ readProjectJson(input, journalPath),
9113
+ readJson(join21(input.targetDirectory, journalPath))
9114
+ ]);
9115
+ return { base, project, target };
8923
9116
  } catch (error) {
8924
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
9117
+ if (isMissingFileError(error))
8925
9118
  return;
8926
- }
8927
9119
  throw error;
8928
9120
  }
8929
9121
  }
8930
-
8931
- // src/update/planner-snapshots.ts
8932
- import { readFile as readFile15, stat as stat2 } from "fs/promises";
8933
- import { join as join20 } from "path";
8934
- async function snapshotProjectFiles(project, paths, seed) {
8935
- const snapshots = new Map(seed);
8936
- await Promise.all([...paths].filter((path2) => !snapshots.has(path2)).map(async (path2) => {
8937
- snapshots.set(path2, await readOptional2(join20(project, path2)));
9122
+ async function preservePublishedHistory(manifest, input, overrides) {
9123
+ const entries = manifest.files.filter(({ path: path2 }) => isMigrationArtifact(path2) && path2 !== journalPath);
9124
+ await Promise.all(entries.map(async ({ path: path2 }) => {
9125
+ const content = await readProjectFile(input, path2);
9126
+ if (!content)
9127
+ throw new Error(`\u5F53\u524D\u9879\u76EE migration \u6587\u4EF6\u7F3A\u5931\uFF1A${path2}`);
9128
+ overrides.set(path2, content);
8938
9129
  }));
8939
- return snapshots;
8940
- }
8941
- async function snapshotProjectModes(project, paths) {
8942
- return new Map(await Promise.all([...paths].map(async (path2) => [path2, await readModeOptional(join20(project, path2))])));
9130
+ return entries;
8943
9131
  }
8944
- async function assertPlanSnapshots(project, expectations) {
8945
- const changed = [];
8946
- for (const expectation2 of expectations) {
8947
- const actual = expectationForSnapshot(await readOptional2(join20(project, expectation2.path)));
8948
- if (JSON.stringify(actual) !== JSON.stringify(expectation2.expected)) {
8949
- changed.push(expectation2.path);
8950
- continue;
8951
- }
8952
- if ("mode" in expectation2 && await readModeOptional(join20(project, expectation2.path)) !== expectation2.mode) {
8953
- changed.push(expectation2.path);
8954
- }
9132
+ async function readLatestSnapshot(input, journal) {
9133
+ const latest = journal.entries.at(-1);
9134
+ if (!latest)
9135
+ throw new Error(`${journalPath}\uFF1A\u5F53\u524D\u9879\u76EE\u6CA1\u6709 migration`);
9136
+ const match = migrationPattern.exec(latest.tag);
9137
+ if (!match) {
9138
+ throw new Error(`${journalPath}\uFF1Amigration tag \u683C\u5F0F\u65E0\u6548\uFF1A${latest.tag}`);
8955
9139
  }
8956
- if (changed.length) {
8957
- throw new Error(`\u751F\u6210\u66F4\u65B0\u8BA1\u5212\u671F\u95F4\u5019\u9009\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${changed.join("\u3001")}`);
9140
+ const snapshotPath = `${drizzleRoot}/meta/${match[1]}_snapshot.json`;
9141
+ const content = await readProjectFile(input, snapshotPath);
9142
+ if (!content) {
9143
+ throw new Error(`\u5F53\u524D\u9879\u76EE migration \u6587\u4EF6\u7F3A\u5931\uFF1A${snapshotPath}`);
8958
9144
  }
9145
+ return JSON.parse(new TextDecoder().decode(content));
8959
9146
  }
8960
- async function readModeOptional(path2) {
8961
- try {
8962
- return (await stat2(path2)).mode & 511;
8963
- } catch (error) {
8964
- if (isMissing5(error))
8965
- return;
8966
- throw error;
8967
- }
9147
+ async function readSnapshot(root, tag) {
9148
+ const match = migrationPattern.exec(tag);
9149
+ if (!match)
9150
+ throw new Error(`${journalPath}\uFF1Amigration tag \u683C\u5F0F\u65E0\u6548\uFF1A${tag}`);
9151
+ return await readJson(join21(root, `${drizzleRoot}/meta/${match[1]}_snapshot.json`));
8968
9152
  }
8969
- async function readSourceParentMode(path2) {
8970
- try {
8971
- const info = await stat2(path2);
8972
- if (!info.isDirectory()) {
8973
- throw new Error(`artifact \u6E90\u7801\u7236\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${path2}`);
9153
+ function validateUniqueNames(journal, label, conflicts) {
9154
+ const seen = new Set;
9155
+ for (const { tag } of journal.entries) {
9156
+ const name = stableName(tag);
9157
+ if (seen.has(name)) {
9158
+ conflicts.push(`${journalPath}\uFF1A${label}\u5B58\u5728\u91CD\u590D migration \u540D\u79F0 ${name}`);
8974
9159
  }
8975
- return info.mode & 493 | 493;
8976
- } catch (error) {
8977
- if (isMissing5(error))
8978
- return 493;
8979
- throw error;
9160
+ seen.add(name);
8980
9161
  }
8981
9162
  }
8982
- async function readOptional2(path2) {
8983
- try {
8984
- return await readFile15(path2);
8985
- } catch (error) {
8986
- if (isMissing5(error))
8987
- return;
8988
- throw error;
8989
- }
9163
+ function stableName(tag) {
9164
+ return migrationPattern.exec(tag)?.[2] ?? tag;
8990
9165
  }
8991
- function isMissing5(error) {
8992
- return error instanceof Error && "code" in error && error.code === "ENOENT";
9166
+ function highestPrefix(journal) {
9167
+ return Math.max(-1, ...journal.entries.map(({ tag }) => Number(migrationPattern.exec(tag)?.[1] ?? -1)));
9168
+ }
9169
+ function highestIndex(journal) {
9170
+ return Math.max(-1, ...journal.entries.map(({ idx }) => idx));
9171
+ }
9172
+ function isDrizzleHistoryPath(path2) {
9173
+ return path2 === journalPath || isMigrationArtifact(path2);
9174
+ }
9175
+ function isMigrationArtifact(path2) {
9176
+ return isSqlMigration(path2) || new RegExp(`^${drizzleRoot}/meta/\\d{4}_snapshot\\.json$`).test(path2);
9177
+ }
9178
+ function isSqlMigration(path2) {
9179
+ return new RegExp(`^${drizzleRoot}/\\d{4}_.+\\.sql$`).test(path2);
9180
+ }
9181
+ function managedEntry(path2, content) {
9182
+ return { path: path2, ownership: "windy-managed", hash: hashContent(content) };
9183
+ }
9184
+ function deduplicate(files) {
9185
+ return [...new Map(files.map((entry) => [entry.path, entry])).values()];
9186
+ }
9187
+ function encodeJson(value) {
9188
+ return new TextEncoder().encode(`${JSON.stringify(value, null, 2)}
9189
+ `);
9190
+ }
9191
+ async function readJson(path2) {
9192
+ return JSON.parse(await readFile15(path2, "utf8"));
8993
9193
  }
8994
9194
 
8995
9195
  // src/update/preflight.ts
@@ -9006,7 +9206,7 @@ async function assertNoPendingUpdate(projectDirectory) {
9006
9206
 
9007
9207
  // src/update/reconciled-baselines.ts
9008
9208
  import { readFile as readFile16 } from "fs/promises";
9009
- import { join as join21 } from "path";
9209
+ import { join as join22 } from "path";
9010
9210
  async function carryReconciledBaselines(source, target, projectSnapshots, targetDirectory) {
9011
9211
  const sourceByPath = new Map(source.files.map((entry) => [entry.path, entry]));
9012
9212
  return {
@@ -9014,7 +9214,7 @@ async function carryReconciledBaselines(source, target, projectSnapshots, target
9014
9214
  files: await Promise.all(target.files.map(async (entry) => {
9015
9215
  const previous = sourceByPath.get(entry.path);
9016
9216
  const project = projectSnapshots.get(entry.path);
9017
- const targetContent = await readOptional3(join21(targetDirectory, entry.path));
9217
+ const targetContent = await readOptional3(join22(targetDirectory, entry.path));
9018
9218
  if (isMissingManagedFile(entry) || !previous || isMissingManagedFile(previous)) {
9019
9219
  return entry;
9020
9220
  }
@@ -9093,7 +9293,8 @@ var recipes = [
9093
9293
  { id: "starter-0.3.12-to-0.3.13", from: "0.3.12", to: "0.3.13" },
9094
9294
  { id: "starter-0.3.13-to-0.3.14", from: "0.3.13", to: "0.3.14" },
9095
9295
  { id: "starter-0.3.14-to-0.3.15", from: "0.3.14", to: "0.3.15" },
9096
- { id: "starter-0.3.15-to-0.3.16", from: "0.3.15", to: "0.3.16" }
9296
+ { id: "starter-0.3.15-to-0.3.16", from: "0.3.15", to: "0.3.16" },
9297
+ { id: "starter-0.3.16-to-0.3.17", from: "0.3.16", to: "0.3.17" }
9097
9298
  ];
9098
9299
  function resolveRecipeChain(sourceVersion, targetVersion) {
9099
9300
  if (sourceVersion === targetVersion)
@@ -9130,7 +9331,7 @@ var oxcManagedPaths = new Set([
9130
9331
  ]);
9131
9332
  async function createUpdatePlan(request, artifacts) {
9132
9333
  await assertProjectReadyForUpdate(request.projectDirectory);
9133
- const manifestPath = join22(request.projectDirectory, ".windy/files.json");
9334
+ const manifestPath = join23(request.projectDirectory, ".windy/files.json");
9134
9335
  const expectedManifestHash = hashContent(await readFile17(manifestPath));
9135
9336
  const generationSnapshot = await readGenerationProvenanceSnapshot(request.projectDirectory);
9136
9337
  const provenance = generationSnapshot.provenance;
@@ -9176,8 +9377,8 @@ async function createUpdatePlan(request, artifacts) {
9176
9377
  const paths = new Set([...sourceEntries.keys(), ...targetEntries.keys()]);
9177
9378
  for (const path2 of paths) {
9178
9379
  if (!projectSnapshots.has(path2)) {
9179
- projectSnapshots.set(path2, await readOptional2(join22(request.projectDirectory, path2)));
9180
- projectModes.set(path2, await readModeOptional(join22(request.projectDirectory, path2)));
9380
+ projectSnapshots.set(path2, await readOptional2(join23(request.projectDirectory, path2)));
9381
+ projectModes.set(path2, await readModeOptional(join23(request.projectDirectory, path2)));
9181
9382
  }
9182
9383
  }
9183
9384
  const files = await planFiles({
@@ -9191,18 +9392,18 @@ async function createUpdatePlan(request, artifacts) {
9191
9392
  manageGeneratedProjectOwned: request.targetModules !== undefined,
9192
9393
  forcePlatformBaseline: request.forcePlatformBaseline === true
9193
9394
  });
9194
- deriveDockerfilePlans(files, projectSnapshots, new Set([
9395
+ await deriveDockerfilePlans(files, projectSnapshots, new Set([
9195
9396
  ...paths,
9196
9397
  ...workspaceSnapshot.manifests.keys(),
9197
9398
  ...targetWorkspaceSnapshot.manifests.keys()
9198
- ]), targetWorkspaceSnapshot.manifests);
9399
+ ]), targetWorkspaceSnapshot.manifests, artifactSet.baseDirectory, artifactSet.targetDirectory);
9199
9400
  for (const file of files) {
9200
9401
  file.expectedProjectMode = projectModes.get(file.path);
9201
9402
  if (file.action === "create" || file.action === "update") {
9202
- file.mode = file.expectedProjectMode ?? await readModeOptional(join22(artifactSet.targetDirectory, file.path));
9403
+ file.mode = file.expectedProjectMode ?? await readModeOptional(join23(artifactSet.targetDirectory, file.path));
9203
9404
  }
9204
9405
  if (file.action === "create") {
9205
- file.parentMode = await readSourceParentMode(join22(artifactSet.targetDirectory, dirname6(file.path)));
9406
+ file.parentMode = await readSourceParentMode(join23(artifactSet.targetDirectory, dirname6(file.path)));
9206
9407
  }
9207
9408
  }
9208
9409
  const plannedPaths = new Set(files.map(({ path: path2 }) => path2));
@@ -9308,7 +9509,7 @@ function assertArtifactIdentity(identity, expectedVersion) {
9308
9509
  }
9309
9510
 
9310
9511
  // src/reconcile/proof-profile.ts
9311
- import { join as join23 } from "path";
9512
+ import { join as join24 } from "path";
9312
9513
 
9313
9514
  // src/reconcile/reconcile-utils.ts
9314
9515
  import { readFile as readFile18 } from "fs/promises";
@@ -9352,8 +9553,8 @@ async function matchesAnchors(profile, input) {
9352
9553
  return false;
9353
9554
  }
9354
9555
  const [current, source] = await Promise.all([
9355
- readOptionalFile(join23(input.projectDirectory, anchor.path)),
9356
- readOptionalFile(join23(input.sourceDirectory, anchor.path))
9556
+ readOptionalFile(join24(input.projectDirectory, anchor.path)),
9557
+ readOptionalFile(join24(input.sourceDirectory, anchor.path))
9357
9558
  ]);
9358
9559
  if (!current || !source || hashContent(current) !== anchor.currentHash || hashContent(source) !== anchor.sourceArtifactHash) {
9359
9560
  return false;
@@ -9382,7 +9583,7 @@ async function resolveReconcileProofContext(input) {
9382
9583
  }
9383
9584
 
9384
9585
  // src/reconcile/reconcile-entry.ts
9385
- import { join as join24 } from "path";
9586
+ import { join as join25 } from "path";
9386
9587
  async function reconcileManagedEntry(input) {
9387
9588
  if (!input.currentHash)
9388
9589
  return { reason: "\u5F53\u524D\u6587\u4EF6\u7F3A\u5931" };
@@ -9396,11 +9597,11 @@ async function reconcileManagedEntry(input) {
9396
9597
  if (!proof2 || !input.sourceEntry) {
9397
9598
  return { reason: "\u6CA1\u6709\u5339\u914D\u7684\u4E0D\u53EF\u53D8 proof" };
9398
9599
  }
9399
- const source = await readOptionalFile(join24(input.sourceDirectory, input.entry.path));
9600
+ const source = await readOptionalFile(join25(input.sourceDirectory, input.entry.path));
9400
9601
  if (!source || hashContent(source) !== proof2.artifactHash || artifactHashOf(input.sourceEntry) !== proof2.artifactHash) {
9401
9602
  return { reason: "\u516C\u5F00\u6765\u6E90 artifact \u4E0E proof \u4E0D\u4E00\u81F4" };
9402
9603
  }
9403
- const target = await readOptionalFile(join24(input.targetDirectory, input.entry.path));
9604
+ const target = await readOptionalFile(join25(input.targetDirectory, input.entry.path));
9404
9605
  const targetHash = target ? hashContent(target) : undefined;
9405
9606
  if (input.targetEntry && targetHash !== artifactHashOf(input.targetEntry)) {
9406
9607
  return { reason: "\u516C\u5F00\u76EE\u6807 artifact \u4E0E\u6E05\u5355\u4E0D\u4E00\u81F4" };
@@ -9549,7 +9750,7 @@ class DefaultManagedReconciler {
9549
9750
  const nextEntries = [];
9550
9751
  const id = createReconcileId(sourceVersion, input.targetVersion);
9551
9752
  const reportPath = `.windy/reports/${id}.md`;
9552
- const reportContent = await readOptionalFile(join25(input.projectDirectory, reportPath));
9753
+ const reportContent = await readOptionalFile(join26(input.projectDirectory, reportPath));
9553
9754
  const inspectEveryDrift = sourceVersion === input.targetVersion;
9554
9755
  const provenPaths = provenPathsForIdentity(effectiveProofs, {
9555
9756
  sourceVersion,
@@ -9577,7 +9778,7 @@ class DefaultManagedReconciler {
9577
9778
  nextEntries.push(entry);
9578
9779
  continue;
9579
9780
  }
9580
- const current = await readOptionalFile(join25(input.projectDirectory, entry.path));
9781
+ const current = await readOptionalFile(join26(input.projectDirectory, entry.path));
9581
9782
  const currentHash = current ? hashContent(current) : undefined;
9582
9783
  if (!guardedPaths.has(entry.path)) {
9583
9784
  guards.push({ path: entry.path, hash: currentHash });
@@ -9622,7 +9823,7 @@ class DefaultManagedReconciler {
9622
9823
  unresolved,
9623
9824
  nextManifest: {
9624
9825
  ...manifest,
9625
- schemaVersion: manifest.schemaVersion === 3 ? 3 : 2,
9826
+ schemaVersion: manifest.schemaVersion === 4 ? 4 : manifest.schemaVersion === 3 ? 3 : 2,
9626
9827
  files: nextEntries
9627
9828
  },
9628
9829
  guards,
@@ -9683,7 +9884,7 @@ class DefaultManagedReconciler {
9683
9884
  import { createHash as createHash6 } from "crypto";
9684
9885
  import { mkdir as mkdir6, mkdtemp as mkdtemp2, readFile as readFile19, rm as rm7, writeFile as writeFile10 } from "fs/promises";
9685
9886
  import { tmpdir as tmpdir2 } from "os";
9686
- import { basename as basename7, join as join26 } from "path";
9887
+ import { basename as basename7, join as join27 } from "path";
9687
9888
 
9688
9889
  // src/reconcile/safe-archive.ts
9689
9890
  import { mkdir as mkdir5 } from "fs/promises";
@@ -9731,7 +9932,7 @@ class PublicReconcileArtifactProvider {
9731
9932
  this.#fetch = fetcher;
9732
9933
  }
9733
9934
  async prepare(input) {
9734
- const temporaryRoot = await mkdtemp2(join26(tmpdir2(), "windy-reconcile-"));
9935
+ const temporaryRoot = await mkdtemp2(join27(tmpdir2(), "windy-reconcile-"));
9735
9936
  try {
9736
9937
  const provenance = await readGenerationProvenance(input.projectDirectory);
9737
9938
  const sourceArtifact = await downloadPublicArtifact(temporaryRoot, "source", input.sourceVersion, input.sourceIntegrity, this.#fetch);
@@ -9766,17 +9967,17 @@ async function downloadPublicArtifact(temporaryRoot, label, version, expectedInt
9766
9967
  }
9767
9968
  const archive = await readBoundedResponse(response, `create-windy@${version} artifact `);
9768
9969
  verifyArchive(metadata, archive);
9769
- const destination = join26(temporaryRoot, `${label}-artifact`);
9770
- const archivePath = join26(temporaryRoot, `${label}.tgz`);
9970
+ const destination = join27(temporaryRoot, `${label}-artifact`);
9971
+ const archivePath = join27(temporaryRoot, `${label}.tgz`);
9771
9972
  await writeFile10(archivePath, archive);
9772
9973
  await extractSafeTarArchive({
9773
9974
  archivePath,
9774
9975
  destination,
9775
9976
  cwd: temporaryRoot
9776
9977
  });
9777
- const packageRoot2 = join26(destination, "package");
9778
- const packageJson = JSON.parse(await readFile19(join26(packageRoot2, "package.json"), "utf8"));
9779
- const template = await readTemplateMetadata(join26(packageRoot2, "template"));
9978
+ const packageRoot2 = join27(destination, "package");
9979
+ const packageJson = JSON.parse(await readFile19(join27(packageRoot2, "package.json"), "utf8"));
9980
+ const template = await readTemplateMetadata(join27(packageRoot2, "template"));
9780
9981
  if (packageJson.name !== "create-windy" || packageJson.version !== version || template.generatorVersion !== version) {
9781
9982
  throw new Error(`create-windy@${version} \u5305\u8EAB\u4EFD\u6216\u6A21\u677F\u5143\u6570\u636E\u4E0D\u4E00\u81F4`);
9782
9983
  }
@@ -9908,10 +10109,10 @@ function verifyArchive(metadata, archive) {
9908
10109
  }
9909
10110
  }
9910
10111
  async function generatePublicArtifactProject(temporaryRoot, label, projectName, packageRoot2, provenance) {
9911
- const parent = join26(temporaryRoot, label);
10112
+ const parent = join27(temporaryRoot, label);
9912
10113
  await mkdir6(parent, { recursive: true });
9913
10114
  const args = [
9914
- join26(packageRoot2, "dist/cli.js"),
10115
+ join27(packageRoot2, "dist/cli.js"),
9915
10116
  projectName,
9916
10117
  "--yes",
9917
10118
  "--skip-install",
@@ -9931,17 +10132,17 @@ async function generatePublicArtifactProject(temporaryRoot, label, projectName,
9931
10132
  }
9932
10133
  }
9933
10134
  await assertProcess("bun", args, parent);
9934
- return join26(parent, projectName);
10135
+ return join27(parent, projectName);
9935
10136
  }
9936
10137
  async function readArtifactProjectName(projectDirectory) {
9937
- const value = JSON.parse(await readFile19(join26(projectDirectory, "package.json"), "utf8"));
10138
+ const value = JSON.parse(await readFile19(join27(projectDirectory, "package.json"), "utf8"));
9938
10139
  const candidate = value.name ?? basename7(projectDirectory);
9939
10140
  return /^[a-z0-9][a-z0-9._-]*$/.test(candidate) && candidate !== "." && candidate !== ".." ? candidate : basename7(projectDirectory);
9940
10141
  }
9941
10142
 
9942
10143
  // src/update/doctor.ts
9943
10144
  import { readFile as readFile20 } from "fs/promises";
9944
- import { join as join27 } from "path";
10145
+ import { join as join28 } from "path";
9945
10146
  var oxcPaths = new Set([
9946
10147
  ".oxfmtrc.json",
9947
10148
  ".oxlintrc.json",
@@ -9968,7 +10169,7 @@ async function diagnoseProject(projectDirectory, repair = false) {
9968
10169
  if (file.ownership === "project-owned")
9969
10170
  continue;
9970
10171
  try {
9971
- const changed = hashContent(await readFile20(join27(projectDirectory, file.path))) !== file.hash;
10172
+ const changed = hashContent(await readFile20(join28(projectDirectory, file.path))) !== file.hash;
9972
10173
  if (!changed)
9973
10174
  continue;
9974
10175
  modifiedManagedFiles.push(file.path);
@@ -9998,12 +10199,12 @@ async function diagnoseProject(projectDirectory, repair = false) {
9998
10199
 
9999
10200
  // src/update/updater.ts
10000
10201
  import { readFile as readFile24, rm as rm9 } from "fs/promises";
10001
- import { join as join31 } from "path";
10202
+ import { join as join32 } from "path";
10002
10203
 
10003
10204
  // src/update/artifacts.ts
10004
10205
  import { mkdtemp as mkdtemp3, readFile as readFile22 } from "fs/promises";
10005
10206
  import { tmpdir as tmpdir3 } from "os";
10006
- import { basename as basename8, join as join29 } from "path";
10207
+ import { basename as basename8, join as join30 } from "path";
10007
10208
 
10008
10209
  // src/module-history.ts
10009
10210
  import {
@@ -10013,15 +10214,15 @@ import {
10013
10214
  readFile as readFile21,
10014
10215
  writeFile as writeFile11
10015
10216
  } from "fs/promises";
10016
- import { dirname as dirname7, join as join28, relative as relative4, sep as sep4 } from "path";
10217
+ import { dirname as dirname7, join as join29, relative as relative4, sep as sep4 } from "path";
10017
10218
  async function archiveRemovedModuleMigrations(input) {
10018
10219
  await assertExistingHistoryIntegrity(input.projectDirectory);
10019
- let changed = await copyMigrationTree(input.projectDirectory, ".windy/module-history", join28(input.targetDirectory, ".windy/module-history"));
10220
+ let changed = await copyMigrationTree(input.projectDirectory, ".windy/module-history", join29(input.targetDirectory, ".windy/module-history"));
10020
10221
  const target = new Set(input.targetModules);
10021
10222
  const removed = moduleDistributionCatalog.filter(({ name }) => input.sourceModules.includes(name) && !target.has(name));
10022
10223
  for (const manifest of removed) {
10023
10224
  for (const root of manifest.migrationRoots) {
10024
- changed = await copyMigrationTree(input.projectDirectory, root, join28(input.targetDirectory, ".windy/module-history", manifest.name, root)) || changed;
10225
+ changed = await copyMigrationTree(input.projectDirectory, root, join29(input.targetDirectory, ".windy/module-history", manifest.name, root)) || changed;
10025
10226
  }
10026
10227
  }
10027
10228
  if (changed) {
@@ -10029,7 +10230,7 @@ async function archiveRemovedModuleMigrations(input) {
10029
10230
  }
10030
10231
  }
10031
10232
  async function assertExistingHistoryIntegrity(projectDirectory) {
10032
- const historyRoot = join28(projectDirectory, ".windy/module-history");
10233
+ const historyRoot = join29(projectDirectory, ".windy/module-history");
10033
10234
  const metadata = await readOptionalMetadata(historyRoot);
10034
10235
  if (!metadata)
10035
10236
  return;
@@ -10050,7 +10251,7 @@ async function assertExistingHistoryIntegrity(projectDirectory) {
10050
10251
  }
10051
10252
  }
10052
10253
  async function copyMigrationTree(projectRoot, relativeRoot, targetRoot) {
10053
- const sourceRoot = join28(projectRoot, relativeRoot);
10254
+ const sourceRoot = join29(projectRoot, relativeRoot);
10054
10255
  const rootMetadata = await readOptionalMetadata(sourceRoot);
10055
10256
  if (!rootMetadata)
10056
10257
  return false;
@@ -10058,7 +10259,7 @@ async function copyMigrationTree(projectRoot, relativeRoot, targetRoot) {
10058
10259
  throw new Error(`\u6A21\u5757 migration \u6839\u76EE\u5F55\u65E0\u6548\uFF1A${relativeRoot}`);
10059
10260
  }
10060
10261
  for (const source of await collectRegularFiles(sourceRoot)) {
10061
- const target = join28(targetRoot, relative4(sourceRoot, source).split(sep4).join("/"));
10262
+ const target = join29(targetRoot, relative4(sourceRoot, source).split(sep4).join("/"));
10062
10263
  await mkdir7(dirname7(target), { recursive: true });
10063
10264
  await writeFile11(target, await readFile21(source));
10064
10265
  }
@@ -10070,7 +10271,7 @@ async function collectRegularFiles(root) {
10070
10271
  return files.sort();
10071
10272
  async function visit(directory) {
10072
10273
  for (const name of (await readdir8(directory)).sort()) {
10073
- const path2 = join28(directory, name);
10274
+ const path2 = join29(directory, name);
10074
10275
  const metadata = await lstat5(path2);
10075
10276
  if (metadata.isSymbolicLink()) {
10076
10277
  throw new Error(`\u6A21\u5757 migration \u4E0D\u5141\u8BB8\u7B26\u53F7\u94FE\u63A5\uFF1A${path2}`);
@@ -10100,10 +10301,10 @@ class PublishedArtifactProvider {
10100
10301
  this.#fetch = fetcher;
10101
10302
  }
10102
10303
  async prepare(projectDirectory, provenance, targetModules) {
10103
- const temporaryRoot = await mkdtemp3(join29(tmpdir3(), "windy-update-"));
10304
+ const temporaryRoot = await mkdtemp3(join30(tmpdir3(), "windy-update-"));
10104
10305
  const projectName = await readProjectName3(projectDirectory);
10105
- const baseDirectory = join29(temporaryRoot, "base", projectName);
10106
- const targetDirectory = join29(temporaryRoot, "target", projectName);
10306
+ const baseDirectory = join30(temporaryRoot, "base", projectName);
10307
+ const targetDirectory = join30(temporaryRoot, "target", projectName);
10107
10308
  const baseArtifact = await downloadPublicArtifact(temporaryRoot, "base", provenance.generatorVersion, undefined, this.#fetch);
10108
10309
  if (baseArtifact.identity.templateCommit !== provenance.templateCommit) {
10109
10310
  throw new Error("Project Lock \u4E0E\u6765\u6E90 registry artifact \u7684 template commit \u4E0D\u4E00\u81F4");
@@ -10131,7 +10332,7 @@ class PublishedArtifactProvider {
10131
10332
  }
10132
10333
  }
10133
10334
  async function readProjectName3(projectDirectory) {
10134
- const source = JSON.parse(await readFile22(join29(projectDirectory, "package.json"), "utf8"));
10335
+ const source = JSON.parse(await readFile22(join30(projectDirectory, "package.json"), "utf8"));
10135
10336
  return source.name || basename8(projectDirectory);
10136
10337
  }
10137
10338
 
@@ -10148,9 +10349,7 @@ function buildFinalManagedManifest(plan, generationContent, finalizedHashes = ne
10148
10349
  const basis2 = target.get(path2) ?? source.get(path2);
10149
10350
  if (!basis2)
10150
10351
  throw new Error("\u6700\u7EC8\u53D7\u7BA1\u6E05\u5355\u7F3A\u5C11 Project Lock \u6761\u76EE");
10151
- const clean2 = withoutReconciliation(basis2);
10152
- clean2.hash = hashContent(generationContent);
10153
- files.push(clean2);
10352
+ files.push(freezePresentEntry(basis2, hashContent(generationContent)));
10154
10353
  continue;
10155
10354
  }
10156
10355
  const file = filePlans.get(path2);
@@ -10176,14 +10375,18 @@ function buildFinalManagedManifest(plan, generationContent, finalizedHashes = ne
10176
10375
  });
10177
10376
  continue;
10178
10377
  }
10179
- const clean = withoutReconciliation(basis);
10180
- if (isMissingManagedFile(clean)) {
10181
- throw new Error(`\u6700\u7EC8\u53D7\u7BA1\u6E05\u5355\u4E0D\u80FD\u4E3A\u7F3A\u5931\u8DEF\u5F84\u5199\u5165 hash\uFF1A${path2}`);
10182
- }
10183
- clean.hash = frozenManifestHash(file, finalizedHashes);
10184
- files.push(clean);
10378
+ files.push(freezePresentEntry(basis, frozenManifestHash(file, finalizedHashes)));
10379
+ }
10380
+ return { ...plan.targetManifest, schemaVersion: 4, files };
10381
+ }
10382
+ function freezePresentEntry(basis, currentHash) {
10383
+ const artifactHash = artifactHashOf(basis);
10384
+ const clean = withoutReconciliation(basis);
10385
+ if (isMissingManagedFile(clean)) {
10386
+ throw new Error(`\u6700\u7EC8\u53D7\u7BA1\u6E05\u5355\u4E0D\u80FD\u4E3A\u7F3A\u5931\u8DEF\u5F84\u5199\u5165 hash\uFF1A${basis.path}`);
10185
10387
  }
10186
- return { ...plan.targetManifest, schemaVersion: 3, files };
10388
+ clean.hash = currentHash;
10389
+ return clean.ownership !== "project-owned" && artifactHash !== currentHash ? { ...clean, artifactHash } : clean;
10187
10390
  }
10188
10391
  function frozenManifestHash(file, finalizedHashes) {
10189
10392
  if (file.action === "create" || file.action === "update") {
@@ -10200,7 +10403,7 @@ function frozenManifestHash(file, finalizedHashes) {
10200
10403
  // src/update/format-stabilizer.ts
10201
10404
  import { mkdir as mkdir8, mkdtemp as mkdtemp4, readFile as readFile23, rm as rm8, writeFile as writeFile12 } from "fs/promises";
10202
10405
  import { tmpdir as tmpdir4 } from "os";
10203
- import { dirname as dirname8, extname as extname4, join as join30, resolve as resolve6, sep as sep5 } from "path";
10406
+ import { dirname as dirname8, extname as extname4, join as join31, resolve as resolve6, sep as sep5 } from "path";
10204
10407
  var formattedExtensions = new Set([
10205
10408
  ".cjs",
10206
10409
  ".css",
@@ -10239,7 +10442,7 @@ async function stabilizeManagedContent(result2, execute, writeProjectFile) {
10239
10442
  }
10240
10443
  }
10241
10444
  inputs.set(".windy/generation.json", new TextEncoder().encode(generationContent));
10242
- const temporaryRoot = await mkdtemp4(join30(tmpdir4(), "windy-update-format-"));
10445
+ const temporaryRoot = await mkdtemp4(join31(tmpdir4(), "windy-update-format-"));
10243
10446
  try {
10244
10447
  const stagedPaths = await stageInputs(temporaryRoot, inputs);
10245
10448
  for (let offset = 0;offset < stagedPaths.length; offset += 80) {
@@ -10247,7 +10450,7 @@ async function stabilizeManagedContent(result2, execute, writeProjectFile) {
10247
10450
  "x",
10248
10451
  "oxfmt",
10249
10452
  "--write",
10250
- `--config=${join30(result2.plan.projectDirectory, ".oxfmtrc.json")}`,
10453
+ `--config=${join31(result2.plan.projectDirectory, ".oxfmtrc.json")}`,
10251
10454
  ...stagedPaths.slice(offset, offset + 80)
10252
10455
  ], result2.plan.projectDirectory);
10253
10456
  }
@@ -10349,10 +10552,10 @@ class DefaultStarterUpdater {
10349
10552
  status: dockerSync.every(({ sync }) => sync.synced) ? "passed" : "skipped"
10350
10553
  });
10351
10554
  if (this.#skipInstall) {
10352
- steps.push({ name: "bun install --force", status: "skipped" });
10555
+ steps.push({ name: "bun install", status: "skipped" });
10353
10556
  } else {
10354
- await this.#execute("bun", ["install", "--force"], result2.plan.projectDirectory);
10355
- steps.push({ name: "bun install --force", status: "passed" });
10557
+ await this.#execute("bun", ["install"], result2.plan.projectDirectory);
10558
+ steps.push({ name: "bun install", status: "passed" });
10356
10559
  }
10357
10560
  const stabilized = await stabilizeManagedContent(result2, this.#execute, async (path2, content) => {
10358
10561
  await writeOwnedTransactionFile(result2.plan.projectDirectory, path2, content, expectedForPath(result2, path2));
@@ -10371,7 +10574,7 @@ class DefaultStarterUpdater {
10371
10574
  const plannedPaths = new Set(result2.plan.files.map(({ path: path2 }) => path2));
10372
10575
  replaceExpectations(result2, verificationSnapshot.filter(({ path: path2 }) => !metadataPaths2.has(path2) && !plannedPaths.has(path2)));
10373
10576
  verificationExpectationsFrozen = true;
10374
- const packageJson = JSON.parse(await readFile24(join31(result2.plan.projectDirectory, "package.json"), "utf8"));
10577
+ const packageJson = JSON.parse(await readFile24(join32(result2.plan.projectDirectory, "package.json"), "utf8"));
10375
10578
  for (const name of ["lint", "typecheck", "test", "build"]) {
10376
10579
  if (!packageJson.scripts?.[name]) {
10377
10580
  steps.push({ name: `bun run ${name}`, status: "skipped" });
@@ -10982,7 +11185,7 @@ import {
10982
11185
  rm as rm12,
10983
11186
  writeFile as writeFile13
10984
11187
  } from "fs/promises";
10985
- import { dirname as dirname9, join as join32, relative as relative6 } from "path";
11188
+ import { dirname as dirname9, join as join33, relative as relative6 } from "path";
10986
11189
 
10987
11190
  // src/module-schema/config.ts
10988
11191
  import { readFile as readFile25 } from "fs/promises";
@@ -11123,17 +11326,17 @@ async function handoffModuleSchemaLineages(options) {
11123
11326
  async function stageModuleLineages(project, temporaryRoot, config, execute) {
11124
11327
  const outputs = [];
11125
11328
  for (const [index, module3] of config.modules.entries()) {
11126
- const output = join32(temporaryRoot, `module-${index}`);
11329
+ const output = join33(temporaryRoot, `module-${index}`);
11127
11330
  await mkdir9(output, { recursive: true });
11128
- const generatedConfig = join32(temporaryRoot, `module-${index}.config.ts`);
11331
+ const generatedConfig = join33(temporaryRoot, `module-${index}.config.ts`);
11129
11332
  await writeDrizzleConfig(generatedConfig, project, module3.schema, relative6(temporaryRoot, output));
11130
11333
  await execute(generatedConfig, temporaryRoot);
11131
11334
  const sqlFiles = await listSqlFiles(output);
11132
11335
  if (sqlFiles.length !== 1) {
11133
11336
  throw new Error(`${module3.key} baseline \u5FC5\u987B\u6070\u597D\u751F\u6210\u4E00\u4E2A migration`);
11134
11337
  }
11135
- await writeFile13(join32(output, sqlFiles[0]), metadataOnlySql("module"));
11136
- await writeFile13(join32(output, "lineage.json"), `${JSON.stringify({
11338
+ await writeFile13(join33(output, sqlFiles[0]), metadataOnlySql("module"));
11339
+ await writeFile13(join33(output, "lineage.json"), `${JSON.stringify({
11137
11340
  schemaVersion: 1,
11138
11341
  moduleKey: module3.key,
11139
11342
  schema: module3.schema,
@@ -11147,17 +11350,17 @@ async function stageModuleLineages(project, temporaryRoot, config, execute) {
11147
11350
  }
11148
11351
  async function stagePlatformLineage(project, temporaryRoot, config, execute) {
11149
11352
  const source = resolveProjectPath(project, config.platform.out);
11150
- const output = join32(temporaryRoot, "platform");
11353
+ const output = join33(temporaryRoot, "platform");
11151
11354
  await cp(source, output, { recursive: true });
11152
11355
  const before = new Set(await listSqlFiles(output));
11153
- const generatedConfig = join32(temporaryRoot, "platform.config.ts");
11356
+ const generatedConfig = join33(temporaryRoot, "platform.config.ts");
11154
11357
  await writeDrizzleConfig(generatedConfig, project, config.platform.schema, relative6(temporaryRoot, output));
11155
11358
  await execute(generatedConfig, temporaryRoot);
11156
11359
  const created = (await listSqlFiles(output)).filter((path2) => !before.has(path2));
11157
11360
  if (created.length !== 1) {
11158
11361
  throw new Error("\u5E73\u53F0 lineage handoff \u5FC5\u987B\u6070\u597D\u751F\u6210\u4E00\u4E2A migration");
11159
11362
  }
11160
- const sqlPath = join32(output, created[0]);
11363
+ const sqlPath = join33(output, created[0]);
11161
11364
  const allowed = config.modules.flatMap(({ legacyPlatformTables }) => legacyPlatformTables);
11162
11365
  validatePlatformHandoffSql(await readFile26(sqlPath, "utf8"), allowed);
11163
11366
  await writeFile13(sqlPath, metadataOnlySql("platform"));
@@ -11165,7 +11368,7 @@ async function stagePlatformLineage(project, temporaryRoot, config, execute) {
11165
11368
  }
11166
11369
  async function installStagedLineages(project, temporaryRoot, config, stagedModules, stagedPlatform) {
11167
11370
  const platform = resolveProjectPath(project, config.platform.out);
11168
- const backup = join32(temporaryRoot, "platform-backup");
11371
+ const backup = join33(temporaryRoot, "platform-backup");
11169
11372
  const installed = [];
11170
11373
  await rename(platform, backup);
11171
11374
  try {
@@ -11212,9 +11415,9 @@ async function validateSchemaSources(project, config) {
11212
11415
  }
11213
11416
  }
11214
11417
  async function createTemporaryRoot(project) {
11215
- const windy = join32(project, ".windy");
11418
+ const windy = join33(project, ".windy");
11216
11419
  await mkdir9(windy, { recursive: true });
11217
- return await mkdtemp5(join32(windy, "module-schema-handoff-"));
11420
+ return await mkdtemp5(join33(windy, "module-schema-handoff-"));
11218
11421
  }
11219
11422
  async function listSqlFiles(root) {
11220
11423
  return (await readdir9(root)).filter((path2) => path2.endsWith(".sql")).sort();