create-windy 0.3.6 → 0.3.8

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
@@ -2767,6 +2767,34 @@ function isDirectoryNotRemovable(error) {
2767
2767
  // src/workspace-membership.ts
2768
2768
  import { readdir as readdir2 } from "fs/promises";
2769
2769
  import { join as join5 } from "path";
2770
+ function deriveFinalWorkspaceMembership(current, target, files) {
2771
+ const membersByBase = new Map;
2772
+ for (const expectation of [...current, ...target]) {
2773
+ if (!membersByBase.has(expectation.base)) {
2774
+ membersByBase.set(expectation.base, new Set);
2775
+ }
2776
+ }
2777
+ for (const expectation of current) {
2778
+ const members = membersByBase.get(expectation.base);
2779
+ for (const path of expectation.members)
2780
+ members.add(path);
2781
+ }
2782
+ for (const file of files) {
2783
+ for (const [base, members] of membersByBase) {
2784
+ if (!isDirectWorkspaceManifest(base, file.path))
2785
+ continue;
2786
+ if (file.action === "delete")
2787
+ members.delete(file.path);
2788
+ if (file.action === "create" || file.action === "update") {
2789
+ members.add(file.path);
2790
+ }
2791
+ }
2792
+ }
2793
+ return [...membersByBase].sort(([left], [right]) => left.localeCompare(right)).map(([base, members]) => ({
2794
+ base,
2795
+ members: [...members].sort((left, right) => left.localeCompare(right))
2796
+ }));
2797
+ }
2770
2798
  async function snapshotWorkspaceBase(root, base) {
2771
2799
  assertSafeWorkspaceBase(base);
2772
2800
  await inspectSafePath(root, base);
@@ -2808,6 +2836,14 @@ function assertSafeWorkspaceBase(base) {
2808
2836
  throw new Error(`workspace \u4E0D\u5F97\u904D\u5386 node_modules\uFF1A${base}`);
2809
2837
  }
2810
2838
  }
2839
+ function isDirectWorkspaceManifest(base, path) {
2840
+ const prefix = `${base}/`;
2841
+ if (!path.startsWith(prefix))
2842
+ return false;
2843
+ const relative3 = path.slice(prefix.length);
2844
+ const segments = relative3.split("/");
2845
+ return segments.length === 2 && segments[0]?.length > 0 && segments[1] === "package.json";
2846
+ }
2811
2847
  function isMissing(error) {
2812
2848
  return error instanceof Error && "code" in error && error.code === "ENOENT";
2813
2849
  }
@@ -8687,7 +8723,7 @@ function conflict(path2, reason, expectedProjectContent) {
8687
8723
  }
8688
8724
 
8689
8725
  // src/update/planner-file.ts
8690
- async function planFile(path2, baseRoot, targetRoot, source, target, ours, provenance, targetContentOverride, manageGeneratedProjectOwned = false) {
8726
+ async function planFile(path2, baseRoot, targetRoot, source, target, ours, provenance, targetContentOverride, manageGeneratedProjectOwned = false, forcePlatformBaseline = false) {
8691
8727
  const [base, theirs] = await Promise.all([
8692
8728
  readOptional(join19(baseRoot, path2)),
8693
8729
  targetContentOverride ? Promise.resolve(targetContentOverride) : readOptional(join19(targetRoot, path2))
@@ -8698,7 +8734,7 @@ async function planFile(path2, baseRoot, targetRoot, source, target, ours, prove
8698
8734
  if (manageGeneratedProjectOwned) {
8699
8735
  return planGeneratedProjectOwned(path2, source, target, ours, theirs, expected);
8700
8736
  }
8701
- return !source && !ours && theirs ? readyPlannedFile(path2, ownership, "create", "\u65B0\u589E\u9879\u76EE\u6269\u5C55\u70B9", expected, theirs) : readyPlannedFile(path2, ownership, "preserve", "\u9879\u76EE\u62E5\u6709\uFF0CWindy \u4E0D\u8986\u76D6", expected);
8737
+ return !source && !ours && theirs ? readyPlannedFile(path2, ownership, "create", "\u65B0\u589E\u9879\u76EE\u6269\u5C55\u70B9", expected, theirs) : readyPlannedFile(path2, ownership, "preserve", "\u9ED8\u8BA4\u4FDD\u7559\u9879\u76EE\u7248\u672C", expected);
8702
8738
  }
8703
8739
  const repaired = ours && theirs ? repairKnownDerivedFile({
8704
8740
  path: path2,
@@ -8736,6 +8772,14 @@ async function planFile(path2, baseRoot, targetRoot, source, target, ours, prove
8736
8772
  const oursChanged = hashContent(ours) !== source.hash;
8737
8773
  const sourceArtifactHash = artifactHashOf(source);
8738
8774
  const targetChanged = targetChangedSinceBase(sourceArtifactHash, base, theirs);
8775
+ if (shouldForcePlatformBaseline({
8776
+ forcePlatformBaseline,
8777
+ oursChanged,
8778
+ sourceArtifactHash,
8779
+ base
8780
+ })) {
8781
+ return readyPlannedFile(path2, ownership, "update", "\u5F3A\u5236\u91C7\u7528\u76EE\u6807\u5E73\u53F0\u57FA\u7EBF", expected, theirs);
8782
+ }
8739
8783
  if (!oursChanged && !targetChanged) {
8740
8784
  return readyPlannedFile(path2, ownership, "preserve", "\u53CC\u65B9\u672A\u4FEE\u6539", expected);
8741
8785
  }
@@ -8752,12 +8796,15 @@ async function planFile(path2, baseRoot, targetRoot, source, target, ours, prove
8752
8796
  const merged = mergePackageJson(base, ours, theirs);
8753
8797
  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);
8754
8798
  }
8755
- if (ownership === "shared-scaffold" && base) {
8799
+ if (base) {
8756
8800
  const merged = await mergeTextFile(base, ours, theirs);
8757
8801
  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);
8758
8802
  }
8759
8803
  return conflict2(path2, ownership, "\u5BA2\u6237\u548C Windy \u5747\u5DF2\u4FEE\u6539", expected);
8760
8804
  }
8805
+ function shouldForcePlatformBaseline(input) {
8806
+ return input.forcePlatformBaseline && !input.oursChanged && input.base !== undefined && hashContent(input.base) !== input.sourceArtifactHash;
8807
+ }
8761
8808
  function targetChangedSinceBase(sourceHash, base, target) {
8762
8809
  return base && target ? hashContent(base) !== hashContent(target) : !!target && hashContent(target) !== sourceHash;
8763
8810
  }
@@ -8959,7 +9006,9 @@ var recipes = [
8959
9006
  { id: "starter-0.3.2-to-0.3.3", from: "0.3.2", to: "0.3.3" },
8960
9007
  { id: "starter-0.3.3-to-0.3.4", from: "0.3.3", to: "0.3.4" },
8961
9008
  { id: "starter-0.3.4-to-0.3.5", from: "0.3.4", to: "0.3.5" },
8962
- { id: "starter-0.3.5-to-0.3.6", from: "0.3.5", to: "0.3.6" }
9009
+ { id: "starter-0.3.5-to-0.3.6", from: "0.3.5", to: "0.3.6" },
9010
+ { id: "starter-0.3.6-to-0.3.7", from: "0.3.6", to: "0.3.7" },
9011
+ { id: "starter-0.3.7-to-0.3.8", from: "0.3.7", to: "0.3.8" }
8963
9012
  ];
8964
9013
  function resolveRecipeChain(sourceVersion, targetVersion) {
8965
9014
  if (sourceVersion === targetVersion)
@@ -9054,7 +9103,8 @@ async function createUpdatePlan(request, artifacts) {
9054
9103
  migrations,
9055
9104
  artifactSet,
9056
9105
  provenance,
9057
- manageGeneratedProjectOwned: request.targetModules !== undefined
9106
+ manageGeneratedProjectOwned: request.targetModules !== undefined,
9107
+ forcePlatformBaseline: request.forcePlatformBaseline === true
9058
9108
  });
9059
9109
  deriveWebDockerfilePlan(files, projectSnapshots, new Set([...paths, ...workspaceSnapshot.manifests.keys()]));
9060
9110
  for (const file of files) {
@@ -9105,7 +9155,7 @@ async function createUpdatePlan(request, artifacts) {
9105
9155
  files,
9106
9156
  dependencyExpectations,
9107
9157
  workspaceMembershipExpectations: workspaceSnapshot.membershipExpectations,
9108
- finalWorkspaceMembershipExpectations: targetWorkspaceSnapshot.membershipExpectations,
9158
+ finalWorkspaceMembershipExpectations: deriveFinalWorkspaceMembership(workspaceSnapshot.membershipExpectations, targetWorkspaceSnapshot.membershipExpectations, files),
9109
9159
  conflicts,
9110
9160
  detachedToolchain,
9111
9161
  targetProvenance,
@@ -9141,7 +9191,7 @@ async function planFiles(input) {
9141
9191
  } else if (forcedContent) {
9142
9192
  files.push(readyPlannedFile(path2, input.targetEntries.get(path2)?.ownership ?? input.sourceEntries.get(path2)?.ownership ?? classifyOwnership(path2), "update", "\u5408\u5E76\u5E73\u53F0\u4E0E\u4E0B\u6E38 migration \u5386\u53F2", expected, forcedContent));
9143
9193
  } else {
9144
- files.push(await planFile(path2, input.artifactSet.baseDirectory, input.artifactSet.targetDirectory, input.sourceEntries.get(path2), input.targetEntries.get(path2), ours, input.provenance, input.migrations.contentOverrides.get(path2), input.manageGeneratedProjectOwned));
9194
+ files.push(await planFile(path2, input.artifactSet.baseDirectory, input.artifactSet.targetDirectory, input.sourceEntries.get(path2), input.targetEntries.get(path2), ours, input.provenance, input.migrations.contentOverrides.get(path2), input.manageGeneratedProjectOwned, input.forcePlatformBaseline));
9145
9195
  }
9146
9196
  }
9147
9197
  return files;
@@ -10702,7 +10752,11 @@ async function runUpdate(args, projectDirectory) {
10702
10752
  return;
10703
10753
  }
10704
10754
  const updater = new DefaultStarterUpdater;
10705
- const plan = await updater.plan({ projectDirectory, targetVersion: target });
10755
+ const plan = await updater.plan({
10756
+ projectDirectory,
10757
+ targetVersion: target,
10758
+ forcePlatformBaseline: args.includes("--force")
10759
+ });
10706
10760
  printPlan(plan);
10707
10761
  if (args.includes("--dry-run")) {
10708
10762
  await rm11(plan.temporaryRoot, { recursive: true, force: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-windy",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "创建单组织、单租户、私有部署优先的 Windy 企业 Dashboard Starter",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "templateCommit": "5be88a688b54b87cf58a753535efb875ee3058e4",
4
- "generatorVersion": "0.3.6",
3
+ "templateCommit": "25260c844c95d14d4ac3ccae6e7553dbde2fd9c6",
4
+ "generatorVersion": "0.3.8",
5
5
  "modules": [
6
6
  {
7
7
  "name": "system",
@@ -31,26 +31,30 @@
31
31
  - 这是 Starter 的初始配置;项目所有者可按团队需要保留、替换或扩展。
32
32
  - 实际可用命令和提交检查以当前项目的 `package.json` 与 Git hooks 为准。
33
33
 
34
- ## Windy 升级边界
34
+ ## Windy 升级策略
35
35
 
36
- - 业务需求优先通过自有模块和既有扩展点实现,尽量不要直接修改 Windy 提供的平台核心、
37
- Composition Root、共享脚手架和根工具链配置,以减少后续升级冲突。
38
- - 修改前先查看 `.windy/files.json` 的 ownership;优先扩展 `project-owned` 路径,
39
- 对 `windy-managed` 路径仅在没有可用扩展点时做最小改动。
40
- - 确需修改 Windy 受管代码时,使用独立小提交记录原因和影响;升级前先运行
36
+ - 除 `node_modules` 等依赖产物外,生成到仓库中的源码都归项目所有者所有,可以按需
37
+ 修改、替换或删除。`.windy/files.json` 的 ownership 是兼容字段名,只表示 updater
38
+ 的默认合并策略和冲突风险,不是源码权限控制。
39
+ - 业务需求优先通过自有模块和既有扩展点实现;直接修改平台核心、Composition Root、
40
+ 共享脚手架或根工具链同样受支持,但通常会增加后续三方合并成本。
41
+ - 修改前可查看 ownership 评估升级风险;优先扩展 `project-owned` 路径,对
42
+ `windy-managed` 路径建议保持改动小而清晰。
43
+ - 直接修改 Windy 基线代码时,使用独立小提交记录原因和影响;升级前先运行
41
44
  `bun run windy update --dry-run`,根据三方合并计划处理冲突。
42
45
  - 完整定制登录页时,在 project-owned 的
43
- `apps/web/src/app/auth/login-page.ts` 注册业务组件;不要修改
44
- `apps/web/src/pages/auth/LoginPage.vue` 或 Router。业务页面复用
46
+ `apps/web/src/app/auth/login-page.ts` 注册业务组件可获得最低升级成本。若直接修改
47
+ `apps/web/src/pages/auth/LoginPage.vue` 或 Router,升级器会尝试三方合并。业务页面复用
45
48
  `useLoginFlow`、`DevelopmentAuthWarning` 和平台品牌设置,保留安全
46
49
  `returnTo`、真实认证与开发认证提示。
47
- - 定制 404、500、维护中或网络异常页时,只修改 project-owned 的
50
+ - 定制 404、500、维护中或网络异常页时,优先修改 project-owned 的
48
51
  `apps/web/src/app/error-pages.ts`,并把业务 SFC 放在 `apps/web/src/app/`;
49
- 保留四个类型化注册项,不要为换视觉修改 Router、HTTP 核心或平台默认错误页。
52
+ 直接修改 Router、HTTP 核心或平台默认错误页时会增加三方合并成本。
50
53
  - 新增业务管理页面时,在业务 Module Manifest 注册 Feature、Permission、菜单和
51
54
  Admin Route,再把 Vue 页面绑定到 project-owned 的
52
- `apps/web/src/app/business-routes.ts`;不要修改 Router 或 AppSidebar。导航来自
53
- 服务端 Manifest 快照,接入见 `docs/development/business-web-routes.md`。
55
+ `apps/web/src/app/business-routes.ts` 是推荐接入方式。直接修改 Router 或 AppSidebar
56
+ 时同样按 Base/Ours/Theirs 合并。导航来自服务端 Manifest 快照,接入见
57
+ `docs/development/business-web-routes.md`。
54
58
  - 业务 Agent Operation 开启 Provider 出网时,必须在 Manifest 同时声明最小
55
59
  `providerDataPolicy.inputFields` JSON Pointer allowlist 与
56
60
  `envelopeFields`;raw input 会在进入 `prepare` 前裁剪并执行 DLP。接入与升级
@@ -9,8 +9,9 @@ apps/web/src/app/error-pages.ts
9
9
 
10
10
  该文件在 `.windy/files.json` 中属于 `project-owned`。Windy 升级会给尚无此文件的
11
11
  项目创建默认选择器;文件存在后,后续更新只保留业务选择。平台默认页面
12
- `apps/web/src/pages/errors/PlatformErrorPage.vue` 属于 `windy-managed`,Router 和
13
- 全局错误导航属于 `shared-scaffold`,业务项目不应为换视觉而修改这些文件。
12
+ `apps/web/src/pages/errors/PlatformErrorPage.vue` 标记为 `windy-managed`,Router 和
13
+ 全局错误导航标记为 `shared-scaffold`。优先使用 project-owned 接缝可降低升级合并
14
+ 成本;项目仍可直接修改这些文件,updater 会按 Base/Ours/Theirs 尝试合并。
14
15
 
15
16
  ## 默认行为
16
17
 
@@ -8,8 +8,9 @@ apps/web/src/app/auth/login-page.ts
8
8
 
9
9
  该文件在 `.windy/files.json` 中属于 `project-owned`。Windy 升级会为旧项目新增它,
10
10
  但一旦文件存在,后续 `windy update` 只会保留,不会覆盖业务选择。平台默认登录页
11
- `apps/web/src/pages/auth/LoginPage.vue` 仍是 `windy-managed`,Router 仍是
12
- `shared-scaffold`;业务项目不应直接修改这两处。
11
+ `apps/web/src/pages/auth/LoginPage.vue` 标记为 `windy-managed`,Router 标记为
12
+ `shared-scaffold`。优先使用 project-owned 接缝可降低升级合并成本;业务项目仍可直接
13
+ 修改这两处,updater 会按 Base/Ours/Theirs 尝试合并。
13
14
 
14
15
  ## 默认行为
15
16