everything-dev 1.12.2 → 1.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/cli/framework-version.cjs +35 -0
  2. package/dist/cli/framework-version.cjs.map +1 -0
  3. package/dist/cli/framework-version.mjs +34 -0
  4. package/dist/cli/framework-version.mjs.map +1 -0
  5. package/dist/cli/init.cjs +14 -3
  6. package/dist/cli/init.cjs.map +1 -1
  7. package/dist/cli/init.d.cts.map +1 -1
  8. package/dist/cli/init.d.mts.map +1 -1
  9. package/dist/cli/init.mjs +14 -3
  10. package/dist/cli/init.mjs.map +1 -1
  11. package/dist/cli/status.cjs +2 -9
  12. package/dist/cli/status.cjs.map +1 -1
  13. package/dist/cli/status.mjs +2 -9
  14. package/dist/cli/status.mjs.map +1 -1
  15. package/dist/cli/sync.cjs +88 -16
  16. package/dist/cli/sync.cjs.map +1 -1
  17. package/dist/cli/sync.mjs +88 -16
  18. package/dist/cli/sync.mjs.map +1 -1
  19. package/dist/cli/upgrade.cjs +85 -48
  20. package/dist/cli/upgrade.cjs.map +1 -1
  21. package/dist/cli/upgrade.mjs +85 -48
  22. package/dist/cli/upgrade.mjs.map +1 -1
  23. package/dist/contract.d.cts +2 -2
  24. package/dist/contract.d.mts +2 -2
  25. package/dist/host.cjs +1 -0
  26. package/dist/host.cjs.map +1 -1
  27. package/dist/host.d.cts.map +1 -1
  28. package/dist/host.d.mts.map +1 -1
  29. package/dist/host.mjs +1 -0
  30. package/dist/host.mjs.map +1 -1
  31. package/dist/internal/manifest-normalizer.cjs +7 -0
  32. package/dist/internal/manifest-normalizer.cjs.map +1 -1
  33. package/dist/internal/manifest-normalizer.mjs +7 -0
  34. package/dist/internal/manifest-normalizer.mjs.map +1 -1
  35. package/dist/orchestrator.d.cts +1 -1
  36. package/dist/orchestrator.d.mts +1 -1
  37. package/dist/plugin.d.cts +1 -1
  38. package/dist/plugin.d.mts +1 -1
  39. package/dist/ui/index.cjs +0 -9
  40. package/dist/ui/index.d.cts +2 -2
  41. package/dist/ui/index.d.mts +2 -2
  42. package/dist/ui/index.mjs +2 -2
  43. package/dist/ui/runtime.cjs +1 -43
  44. package/dist/ui/runtime.cjs.map +1 -1
  45. package/dist/ui/runtime.d.cts +1 -16
  46. package/dist/ui/runtime.d.cts.map +1 -1
  47. package/dist/ui/runtime.d.mts +1 -16
  48. package/dist/ui/runtime.d.mts.map +1 -1
  49. package/dist/ui/runtime.mjs +2 -36
  50. package/dist/ui/runtime.mjs.map +1 -1
  51. package/package.json +1 -1
  52. package/src/cli/framework-version.ts +61 -0
  53. package/src/cli/init.ts +16 -4
  54. package/src/cli/status.ts +2 -15
  55. package/src/cli/sync.ts +145 -24
  56. package/src/cli/upgrade.ts +94 -72
  57. package/src/host.ts +1 -0
  58. package/src/internal/manifest-normalizer.ts +13 -0
  59. package/src/ui/runtime.ts +1 -48
@@ -1,6 +1,7 @@
1
1
  const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
2
2
  const require_fastkv = require('../fastkv.cjs');
3
3
  const require_snapshot = require('./snapshot.cjs');
4
+ const require_framework_version = require('./framework-version.cjs');
4
5
  let node_fs = require("node:fs");
5
6
  let node_path = require("node:path");
6
7
 
@@ -19,15 +20,7 @@ async function fetchLatestNpmVersion(packageName) {
19
20
  }
20
21
  }
21
22
  function readInstalledVersion(projectDir, packageName) {
22
- const pkgPath = (0, node_path.join)(projectDir, "package.json");
23
- if (!(0, node_fs.existsSync)(pkgPath)) return void 0;
24
- const pkg = JSON.parse((0, node_fs.readFileSync)(pkgPath, "utf-8"));
25
- const deps = pkg.dependencies ?? {};
26
- const devDeps = pkg.devDependencies ?? {};
27
- const version = deps[packageName] || devDeps[packageName];
28
- if (!version) return void 0;
29
- if (version.startsWith("workspace:") || version.startsWith("catalog:") || version.startsWith("file:")) return;
30
- return version.replace(/^[\^~>=]+/, "");
23
+ return require_framework_version.readInstalledFrameworkVersion(projectDir, packageName);
31
24
  }
32
25
  function checkEnvFile(projectDir) {
33
26
  if ((0, node_fs.existsSync)((0, node_path.join)(projectDir, ".env"))) return "found";
@@ -1 +1 @@
1
- {"version":3,"file":"status.cjs","names":["fetchBosConfigFromFastKv","readSnapshot"],"sources":["../../src/cli/status.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { StatusResult } from \"../contract\";\nimport { fetchBosConfigFromFastKv } from \"../fastkv\";\nimport { readSnapshot } from \"./snapshot\";\n\nconst FRAMEWORK_PACKAGES = [\"everything-dev\", \"every-plugin\"];\n\nasync function fetchLatestNpmVersion(packageName: string): Promise<string | null> {\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) return null;\n const data = (await response.json()) as { version: string };\n return data.version;\n } catch {\n return null;\n }\n}\n\nfunction readInstalledVersion(projectDir: string, packageName: string): string | undefined {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) return undefined;\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n const deps = (pkg.dependencies ?? {}) as Record<string, string>;\n const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;\n const version = deps[packageName] || devDeps[packageName];\n if (!version) return undefined;\n if (\n version.startsWith(\"workspace:\") ||\n version.startsWith(\"catalog:\") ||\n version.startsWith(\"file:\")\n ) {\n return undefined;\n }\n return version.replace(/^[\\^~>=]+/, \"\");\n}\n\nfunction checkEnvFile(projectDir: string): \"found\" | \"missing\" | \"example-only\" {\n if (existsSync(join(projectDir, \".env\"))) return \"found\";\n if (existsSync(join(projectDir, \".env.example\"))) return \"example-only\";\n return \"missing\";\n}\n\nasync function checkParentReachable(extendsRef: string | undefined): Promise<boolean | undefined> {\n if (!extendsRef?.startsWith(\"bos://\")) return undefined;\n try {\n const config = await fetchBosConfigFromFastKv(extendsRef);\n return config !== null;\n } catch {\n return false;\n }\n}\n\nexport async function getStatus(projectDir: string): Promise<StatusResult> {\n const configPath = join(projectDir, \"bos.config.json\");\n if (!existsSync(configPath)) {\n return {\n status: \"error\",\n error: \"No bos.config.json found in current directory\",\n packages: [],\n envFile: \"missing\",\n };\n }\n\n const config = JSON.parse(readFileSync(configPath, \"utf-8\")) as Record<string, unknown>;\n\n const packages = [];\n for (const name of FRAMEWORK_PACKAGES) {\n const installed = readInstalledVersion(projectDir, name);\n const latest = await fetchLatestNpmVersion(name);\n packages.push({ name, installed, latest: latest ?? undefined });\n }\n\n const snapshot = await readSnapshot(projectDir);\n\n const extendsRef = config.extends as string | undefined;\n const parentReachable = await checkParentReachable(extendsRef);\n\n return {\n status: \"ok\",\n extends: extendsRef,\n account: config.account as string | undefined,\n domain: config.domain as string | undefined,\n packages,\n lastSync: snapshot?.timestamp,\n envFile: checkEnvFile(projectDir),\n parentReachable,\n };\n}\n"],"mappings":";;;;;;;AAMA,MAAM,qBAAqB,CAAC,kBAAkB,eAAe;AAE7D,eAAe,sBAAsB,aAA6C;AAChF,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,8BAA8B,YAAY,UAAU;GAC/E,SAAS,EAAE,QAAQ,oBAAoB;GACvC,QAAQ,YAAY,QAAQ,IAAO;GACpC,CAAC;AACF,MAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,UADc,MAAM,SAAS,MAAM,EACvB;SACN;AACN,SAAO;;;AAIX,SAAS,qBAAqB,YAAoB,aAAyC;CACzF,MAAM,8BAAe,YAAY,eAAe;AAChD,KAAI,yBAAY,QAAQ,CAAE,QAAO;CACjC,MAAM,MAAM,KAAK,gCAAmB,SAAS,QAAQ,CAAC;CACtD,MAAM,OAAQ,IAAI,gBAAgB,EAAE;CACpC,MAAM,UAAW,IAAI,mBAAmB,EAAE;CAC1C,MAAM,UAAU,KAAK,gBAAgB,QAAQ;AAC7C,KAAI,CAAC,QAAS,QAAO;AACrB,KACE,QAAQ,WAAW,aAAa,IAChC,QAAQ,WAAW,WAAW,IAC9B,QAAQ,WAAW,QAAQ,CAE3B;AAEF,QAAO,QAAQ,QAAQ,aAAa,GAAG;;AAGzC,SAAS,aAAa,YAA0D;AAC9E,iDAAoB,YAAY,OAAO,CAAC,CAAE,QAAO;AACjD,iDAAoB,YAAY,eAAe,CAAC,CAAE,QAAO;AACzD,QAAO;;AAGT,eAAe,qBAAqB,YAA8D;AAChG,KAAI,CAAC,YAAY,WAAW,SAAS,CAAE,QAAO;AAC9C,KAAI;AAEF,SADe,MAAMA,wCAAyB,WAAW,KACvC;SACZ;AACN,SAAO;;;AAIX,eAAsB,UAAU,YAA2C;CACzE,MAAM,iCAAkB,YAAY,kBAAkB;AACtD,KAAI,yBAAY,WAAW,CACzB,QAAO;EACL,QAAQ;EACR,OAAO;EACP,UAAU,EAAE;EACZ,SAAS;EACV;CAGH,MAAM,SAAS,KAAK,gCAAmB,YAAY,QAAQ,CAAC;CAE5D,MAAM,WAAW,EAAE;AACnB,MAAK,MAAM,QAAQ,oBAAoB;EACrC,MAAM,YAAY,qBAAqB,YAAY,KAAK;EACxD,MAAM,SAAS,MAAM,sBAAsB,KAAK;AAChD,WAAS,KAAK;GAAE;GAAM;GAAW,QAAQ,UAAU;GAAW,CAAC;;CAGjE,MAAM,WAAW,MAAMC,8BAAa,WAAW;CAE/C,MAAM,aAAa,OAAO;CAC1B,MAAM,kBAAkB,MAAM,qBAAqB,WAAW;AAE9D,QAAO;EACL,QAAQ;EACR,SAAS;EACT,SAAS,OAAO;EAChB,QAAQ,OAAO;EACf;EACA,UAAU,UAAU;EACpB,SAAS,aAAa,WAAW;EACjC;EACD"}
1
+ {"version":3,"file":"status.cjs","names":["readInstalledFrameworkVersion","fetchBosConfigFromFastKv","readSnapshot"],"sources":["../../src/cli/status.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { StatusResult } from \"../contract\";\nimport { fetchBosConfigFromFastKv } from \"../fastkv\";\nimport { readInstalledFrameworkVersion } from \"./framework-version\";\nimport { readSnapshot } from \"./snapshot\";\n\nconst FRAMEWORK_PACKAGES = [\"everything-dev\", \"every-plugin\"];\n\nasync function fetchLatestNpmVersion(packageName: string): Promise<string | null> {\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) return null;\n const data = (await response.json()) as { version: string };\n return data.version;\n } catch {\n return null;\n }\n}\n\nfunction readInstalledVersion(projectDir: string, packageName: string): string | undefined {\n return readInstalledFrameworkVersion(projectDir, packageName);\n}\n\nfunction checkEnvFile(projectDir: string): \"found\" | \"missing\" | \"example-only\" {\n if (existsSync(join(projectDir, \".env\"))) return \"found\";\n if (existsSync(join(projectDir, \".env.example\"))) return \"example-only\";\n return \"missing\";\n}\n\nasync function checkParentReachable(extendsRef: string | undefined): Promise<boolean | undefined> {\n if (!extendsRef?.startsWith(\"bos://\")) return undefined;\n try {\n const config = await fetchBosConfigFromFastKv(extendsRef);\n return config !== null;\n } catch {\n return false;\n }\n}\n\nexport async function getStatus(projectDir: string): Promise<StatusResult> {\n const configPath = join(projectDir, \"bos.config.json\");\n if (!existsSync(configPath)) {\n return {\n status: \"error\",\n error: \"No bos.config.json found in current directory\",\n packages: [],\n envFile: \"missing\",\n };\n }\n\n const config = JSON.parse(readFileSync(configPath, \"utf-8\")) as Record<string, unknown>;\n\n const packages = [];\n for (const name of FRAMEWORK_PACKAGES) {\n const installed = readInstalledVersion(projectDir, name);\n const latest = await fetchLatestNpmVersion(name);\n packages.push({ name, installed, latest: latest ?? undefined });\n }\n\n const snapshot = await readSnapshot(projectDir);\n\n const extendsRef = config.extends as string | undefined;\n const parentReachable = await checkParentReachable(extendsRef);\n\n return {\n status: \"ok\",\n extends: extendsRef,\n account: config.account as string | undefined,\n domain: config.domain as string | undefined,\n packages,\n lastSync: snapshot?.timestamp,\n envFile: checkEnvFile(projectDir),\n parentReachable,\n };\n}\n"],"mappings":";;;;;;;;AAOA,MAAM,qBAAqB,CAAC,kBAAkB,eAAe;AAE7D,eAAe,sBAAsB,aAA6C;AAChF,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,8BAA8B,YAAY,UAAU;GAC/E,SAAS,EAAE,QAAQ,oBAAoB;GACvC,QAAQ,YAAY,QAAQ,IAAO;GACpC,CAAC;AACF,MAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,UADc,MAAM,SAAS,MAAM,EACvB;SACN;AACN,SAAO;;;AAIX,SAAS,qBAAqB,YAAoB,aAAyC;AACzF,QAAOA,wDAA8B,YAAY,YAAY;;AAG/D,SAAS,aAAa,YAA0D;AAC9E,iDAAoB,YAAY,OAAO,CAAC,CAAE,QAAO;AACjD,iDAAoB,YAAY,eAAe,CAAC,CAAE,QAAO;AACzD,QAAO;;AAGT,eAAe,qBAAqB,YAA8D;AAChG,KAAI,CAAC,YAAY,WAAW,SAAS,CAAE,QAAO;AAC9C,KAAI;AAEF,SADe,MAAMC,wCAAyB,WAAW,KACvC;SACZ;AACN,SAAO;;;AAIX,eAAsB,UAAU,YAA2C;CACzE,MAAM,iCAAkB,YAAY,kBAAkB;AACtD,KAAI,yBAAY,WAAW,CACzB,QAAO;EACL,QAAQ;EACR,OAAO;EACP,UAAU,EAAE;EACZ,SAAS;EACV;CAGH,MAAM,SAAS,KAAK,gCAAmB,YAAY,QAAQ,CAAC;CAE5D,MAAM,WAAW,EAAE;AACnB,MAAK,MAAM,QAAQ,oBAAoB;EACrC,MAAM,YAAY,qBAAqB,YAAY,KAAK;EACxD,MAAM,SAAS,MAAM,sBAAsB,KAAK;AAChD,WAAS,KAAK;GAAE;GAAM;GAAW,QAAQ,UAAU;GAAW,CAAC;;CAGjE,MAAM,WAAW,MAAMC,8BAAa,WAAW;CAE/C,MAAM,aAAa,OAAO;CAC1B,MAAM,kBAAkB,MAAM,qBAAqB,WAAW;AAE9D,QAAO;EACL,QAAQ;EACR,SAAS;EACT,SAAS,OAAO;EAChB,QAAQ,OAAO;EACf;EACA,UAAU,UAAU;EACpB,SAAS,aAAa,WAAW;EACjC;EACD"}
@@ -1,5 +1,6 @@
1
1
  import { fetchBosConfigFromFastKv } from "../fastkv.mjs";
2
2
  import { readSnapshot } from "./snapshot.mjs";
3
+ import { readInstalledFrameworkVersion } from "./framework-version.mjs";
3
4
  import { existsSync, readFileSync } from "node:fs";
4
5
  import { join } from "node:path";
5
6
 
@@ -18,15 +19,7 @@ async function fetchLatestNpmVersion(packageName) {
18
19
  }
19
20
  }
20
21
  function readInstalledVersion(projectDir, packageName) {
21
- const pkgPath = join(projectDir, "package.json");
22
- if (!existsSync(pkgPath)) return void 0;
23
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
24
- const deps = pkg.dependencies ?? {};
25
- const devDeps = pkg.devDependencies ?? {};
26
- const version = deps[packageName] || devDeps[packageName];
27
- if (!version) return void 0;
28
- if (version.startsWith("workspace:") || version.startsWith("catalog:") || version.startsWith("file:")) return;
29
- return version.replace(/^[\^~>=]+/, "");
22
+ return readInstalledFrameworkVersion(projectDir, packageName);
30
23
  }
31
24
  function checkEnvFile(projectDir) {
32
25
  if (existsSync(join(projectDir, ".env"))) return "found";
@@ -1 +1 @@
1
- {"version":3,"file":"status.mjs","names":[],"sources":["../../src/cli/status.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { StatusResult } from \"../contract\";\nimport { fetchBosConfigFromFastKv } from \"../fastkv\";\nimport { readSnapshot } from \"./snapshot\";\n\nconst FRAMEWORK_PACKAGES = [\"everything-dev\", \"every-plugin\"];\n\nasync function fetchLatestNpmVersion(packageName: string): Promise<string | null> {\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) return null;\n const data = (await response.json()) as { version: string };\n return data.version;\n } catch {\n return null;\n }\n}\n\nfunction readInstalledVersion(projectDir: string, packageName: string): string | undefined {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) return undefined;\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n const deps = (pkg.dependencies ?? {}) as Record<string, string>;\n const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;\n const version = deps[packageName] || devDeps[packageName];\n if (!version) return undefined;\n if (\n version.startsWith(\"workspace:\") ||\n version.startsWith(\"catalog:\") ||\n version.startsWith(\"file:\")\n ) {\n return undefined;\n }\n return version.replace(/^[\\^~>=]+/, \"\");\n}\n\nfunction checkEnvFile(projectDir: string): \"found\" | \"missing\" | \"example-only\" {\n if (existsSync(join(projectDir, \".env\"))) return \"found\";\n if (existsSync(join(projectDir, \".env.example\"))) return \"example-only\";\n return \"missing\";\n}\n\nasync function checkParentReachable(extendsRef: string | undefined): Promise<boolean | undefined> {\n if (!extendsRef?.startsWith(\"bos://\")) return undefined;\n try {\n const config = await fetchBosConfigFromFastKv(extendsRef);\n return config !== null;\n } catch {\n return false;\n }\n}\n\nexport async function getStatus(projectDir: string): Promise<StatusResult> {\n const configPath = join(projectDir, \"bos.config.json\");\n if (!existsSync(configPath)) {\n return {\n status: \"error\",\n error: \"No bos.config.json found in current directory\",\n packages: [],\n envFile: \"missing\",\n };\n }\n\n const config = JSON.parse(readFileSync(configPath, \"utf-8\")) as Record<string, unknown>;\n\n const packages = [];\n for (const name of FRAMEWORK_PACKAGES) {\n const installed = readInstalledVersion(projectDir, name);\n const latest = await fetchLatestNpmVersion(name);\n packages.push({ name, installed, latest: latest ?? undefined });\n }\n\n const snapshot = await readSnapshot(projectDir);\n\n const extendsRef = config.extends as string | undefined;\n const parentReachable = await checkParentReachable(extendsRef);\n\n return {\n status: \"ok\",\n extends: extendsRef,\n account: config.account as string | undefined,\n domain: config.domain as string | undefined,\n packages,\n lastSync: snapshot?.timestamp,\n envFile: checkEnvFile(projectDir),\n parentReachable,\n };\n}\n"],"mappings":";;;;;;AAMA,MAAM,qBAAqB,CAAC,kBAAkB,eAAe;AAE7D,eAAe,sBAAsB,aAA6C;AAChF,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,8BAA8B,YAAY,UAAU;GAC/E,SAAS,EAAE,QAAQ,oBAAoB;GACvC,QAAQ,YAAY,QAAQ,IAAO;GACpC,CAAC;AACF,MAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,UADc,MAAM,SAAS,MAAM,EACvB;SACN;AACN,SAAO;;;AAIX,SAAS,qBAAqB,YAAoB,aAAyC;CACzF,MAAM,UAAU,KAAK,YAAY,eAAe;AAChD,KAAI,CAAC,WAAW,QAAQ,CAAE,QAAO;CACjC,MAAM,MAAM,KAAK,MAAM,aAAa,SAAS,QAAQ,CAAC;CACtD,MAAM,OAAQ,IAAI,gBAAgB,EAAE;CACpC,MAAM,UAAW,IAAI,mBAAmB,EAAE;CAC1C,MAAM,UAAU,KAAK,gBAAgB,QAAQ;AAC7C,KAAI,CAAC,QAAS,QAAO;AACrB,KACE,QAAQ,WAAW,aAAa,IAChC,QAAQ,WAAW,WAAW,IAC9B,QAAQ,WAAW,QAAQ,CAE3B;AAEF,QAAO,QAAQ,QAAQ,aAAa,GAAG;;AAGzC,SAAS,aAAa,YAA0D;AAC9E,KAAI,WAAW,KAAK,YAAY,OAAO,CAAC,CAAE,QAAO;AACjD,KAAI,WAAW,KAAK,YAAY,eAAe,CAAC,CAAE,QAAO;AACzD,QAAO;;AAGT,eAAe,qBAAqB,YAA8D;AAChG,KAAI,CAAC,YAAY,WAAW,SAAS,CAAE,QAAO;AAC9C,KAAI;AAEF,SADe,MAAM,yBAAyB,WAAW,KACvC;SACZ;AACN,SAAO;;;AAIX,eAAsB,UAAU,YAA2C;CACzE,MAAM,aAAa,KAAK,YAAY,kBAAkB;AACtD,KAAI,CAAC,WAAW,WAAW,CACzB,QAAO;EACL,QAAQ;EACR,OAAO;EACP,UAAU,EAAE;EACZ,SAAS;EACV;CAGH,MAAM,SAAS,KAAK,MAAM,aAAa,YAAY,QAAQ,CAAC;CAE5D,MAAM,WAAW,EAAE;AACnB,MAAK,MAAM,QAAQ,oBAAoB;EACrC,MAAM,YAAY,qBAAqB,YAAY,KAAK;EACxD,MAAM,SAAS,MAAM,sBAAsB,KAAK;AAChD,WAAS,KAAK;GAAE;GAAM;GAAW,QAAQ,UAAU;GAAW,CAAC;;CAGjE,MAAM,WAAW,MAAM,aAAa,WAAW;CAE/C,MAAM,aAAa,OAAO;CAC1B,MAAM,kBAAkB,MAAM,qBAAqB,WAAW;AAE9D,QAAO;EACL,QAAQ;EACR,SAAS;EACT,SAAS,OAAO;EAChB,QAAQ,OAAO;EACf;EACA,UAAU,UAAU;EACpB,SAAS,aAAa,WAAW;EACjC;EACD"}
1
+ {"version":3,"file":"status.mjs","names":[],"sources":["../../src/cli/status.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { StatusResult } from \"../contract\";\nimport { fetchBosConfigFromFastKv } from \"../fastkv\";\nimport { readInstalledFrameworkVersion } from \"./framework-version\";\nimport { readSnapshot } from \"./snapshot\";\n\nconst FRAMEWORK_PACKAGES = [\"everything-dev\", \"every-plugin\"];\n\nasync function fetchLatestNpmVersion(packageName: string): Promise<string | null> {\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) return null;\n const data = (await response.json()) as { version: string };\n return data.version;\n } catch {\n return null;\n }\n}\n\nfunction readInstalledVersion(projectDir: string, packageName: string): string | undefined {\n return readInstalledFrameworkVersion(projectDir, packageName);\n}\n\nfunction checkEnvFile(projectDir: string): \"found\" | \"missing\" | \"example-only\" {\n if (existsSync(join(projectDir, \".env\"))) return \"found\";\n if (existsSync(join(projectDir, \".env.example\"))) return \"example-only\";\n return \"missing\";\n}\n\nasync function checkParentReachable(extendsRef: string | undefined): Promise<boolean | undefined> {\n if (!extendsRef?.startsWith(\"bos://\")) return undefined;\n try {\n const config = await fetchBosConfigFromFastKv(extendsRef);\n return config !== null;\n } catch {\n return false;\n }\n}\n\nexport async function getStatus(projectDir: string): Promise<StatusResult> {\n const configPath = join(projectDir, \"bos.config.json\");\n if (!existsSync(configPath)) {\n return {\n status: \"error\",\n error: \"No bos.config.json found in current directory\",\n packages: [],\n envFile: \"missing\",\n };\n }\n\n const config = JSON.parse(readFileSync(configPath, \"utf-8\")) as Record<string, unknown>;\n\n const packages = [];\n for (const name of FRAMEWORK_PACKAGES) {\n const installed = readInstalledVersion(projectDir, name);\n const latest = await fetchLatestNpmVersion(name);\n packages.push({ name, installed, latest: latest ?? undefined });\n }\n\n const snapshot = await readSnapshot(projectDir);\n\n const extendsRef = config.extends as string | undefined;\n const parentReachable = await checkParentReachable(extendsRef);\n\n return {\n status: \"ok\",\n extends: extendsRef,\n account: config.account as string | undefined,\n domain: config.domain as string | undefined,\n packages,\n lastSync: snapshot?.timestamp,\n envFile: checkEnvFile(projectDir),\n parentReachable,\n };\n}\n"],"mappings":";;;;;;;AAOA,MAAM,qBAAqB,CAAC,kBAAkB,eAAe;AAE7D,eAAe,sBAAsB,aAA6C;AAChF,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,8BAA8B,YAAY,UAAU;GAC/E,SAAS,EAAE,QAAQ,oBAAoB;GACvC,QAAQ,YAAY,QAAQ,IAAO;GACpC,CAAC;AACF,MAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,UADc,MAAM,SAAS,MAAM,EACvB;SACN;AACN,SAAO;;;AAIX,SAAS,qBAAqB,YAAoB,aAAyC;AACzF,QAAO,8BAA8B,YAAY,YAAY;;AAG/D,SAAS,aAAa,YAA0D;AAC9E,KAAI,WAAW,KAAK,YAAY,OAAO,CAAC,CAAE,QAAO;AACjD,KAAI,WAAW,KAAK,YAAY,eAAe,CAAC,CAAE,QAAO;AACzD,QAAO;;AAGT,eAAe,qBAAqB,YAA8D;AAChG,KAAI,CAAC,YAAY,WAAW,SAAS,CAAE,QAAO;AAC9C,KAAI;AAEF,SADe,MAAM,yBAAyB,WAAW,KACvC;SACZ;AACN,SAAO;;;AAIX,eAAsB,UAAU,YAA2C;CACzE,MAAM,aAAa,KAAK,YAAY,kBAAkB;AACtD,KAAI,CAAC,WAAW,WAAW,CACzB,QAAO;EACL,QAAQ;EACR,OAAO;EACP,UAAU,EAAE;EACZ,SAAS;EACV;CAGH,MAAM,SAAS,KAAK,MAAM,aAAa,YAAY,QAAQ,CAAC;CAE5D,MAAM,WAAW,EAAE;AACnB,MAAK,MAAM,QAAQ,oBAAoB;EACrC,MAAM,YAAY,qBAAqB,YAAY,KAAK;EACxD,MAAM,SAAS,MAAM,sBAAsB,KAAK;AAChD,WAAS,KAAK;GAAE;GAAM;GAAW,QAAQ,UAAU;GAAW,CAAC;;CAGjE,MAAM,WAAW,MAAM,aAAa,WAAW;CAE/C,MAAM,aAAa,OAAO;CAC1B,MAAM,kBAAkB,MAAM,qBAAqB,WAAW;AAE9D,QAAO;EACL,QAAQ;EACR,SAAS;EACT,SAAS,OAAO;EAChB,QAAQ,OAAO;EACf;EACA,UAAU,UAAU;EACpB,SAAS,aAAa,WAAW;EACjC;EACD"}
package/dist/cli/sync.cjs CHANGED
@@ -7,6 +7,33 @@ let node_crypto = require("node:crypto");
7
7
  let glob = require("glob");
8
8
 
9
9
  //#region src/cli/sync.ts
10
+ const FRAMEWORK_OWNED_SYNC_FILES = new Set([
11
+ ".gitignore",
12
+ "biome.json",
13
+ "bos.config.json",
14
+ "package.json",
15
+ ".github/workflows/release-sync.yml",
16
+ "ui/package.json",
17
+ "ui/postcss.config.mjs",
18
+ "ui/rsbuild.config.ts",
19
+ "ui/tsconfig.json",
20
+ "ui/src/app.ts",
21
+ "ui/src/hydrate.tsx",
22
+ "ui/src/lib/api.ts",
23
+ "ui/src/lib/auth.ts",
24
+ "ui/src/router.server.tsx",
25
+ "ui/src/router.tsx",
26
+ "ui/src/routes/__root.tsx",
27
+ "api/package.json",
28
+ "api/plugin.dev.ts",
29
+ "api/rspack.config.js",
30
+ "api/tsconfig.contract.json",
31
+ "api/tsconfig.json",
32
+ "api/src/lib/auth.ts"
33
+ ]);
34
+ function isFrameworkOwnedSyncFile(filePath) {
35
+ return FRAMEWORK_OWNED_SYNC_FILES.has(filePath);
36
+ }
10
37
  function readExcludeFile(filePath) {
11
38
  if (!(0, node_fs.existsSync)(filePath)) return [];
12
39
  return (0, node_fs.readFileSync)(filePath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
@@ -87,8 +114,33 @@ function mergeBosConfig(local, template) {
87
114
  for (const key of orderedKeys) merged[key] = mergeJsonValues(local[key], template[key]);
88
115
  return merged;
89
116
  }
90
- function mergePackageJson(local, template) {
91
- const merged = { ...template };
117
+ function mergeStringMaps(local, template) {
118
+ if (!local && !template) return void 0;
119
+ const merged = { ...local ?? {} };
120
+ for (const [name, value] of Object.entries(template ?? {})) merged[name] = value;
121
+ return Object.keys(merged).length > 0 ? merged : void 0;
122
+ }
123
+ function mergeWorkspacePackages(local, template) {
124
+ const localPackages = Array.isArray(local) ? local : [];
125
+ const templatePackages = Array.isArray(template) ? template : [];
126
+ if (localPackages.length === 0 && templatePackages.length === 0) return void 0;
127
+ const ordered = /* @__PURE__ */ new Set();
128
+ for (const entry of templatePackages) if (typeof entry === "string" && entry.length > 0) ordered.add(entry);
129
+ for (const entry of localPackages) if (typeof entry === "string" && entry.length > 0) ordered.add(entry);
130
+ return ordered.size > 0 ? [...ordered] : void 0;
131
+ }
132
+ function mergePackageJson(filePath, local, template) {
133
+ const merged = {
134
+ ...local,
135
+ ...template
136
+ };
137
+ if (filePath === "package.json") {
138
+ for (const key of [
139
+ "name",
140
+ "private",
141
+ "version"
142
+ ]) if (key in local) merged[key] = local[key];
143
+ } else if ("version" in local) merged.version = local.version;
92
144
  for (const depField of [
93
145
  "dependencies",
94
146
  "devDependencies",
@@ -97,17 +149,31 @@ function mergePackageJson(local, template) {
97
149
  ]) {
98
150
  const localDeps = local[depField];
99
151
  const templateDeps = template[depField];
100
- if (!localDeps && !templateDeps) continue;
101
- const mergedDeps = { ...templateDeps ?? {} };
102
- if (localDeps) {
103
- for (const [name, version] of Object.entries(localDeps)) if (!(name in mergedDeps)) mergedDeps[name] = version;
104
- }
105
- if (Object.keys(mergedDeps).length > 0) merged[depField] = mergedDeps;
152
+ const mergedDeps = mergeStringMaps(localDeps, templateDeps);
153
+ if (mergedDeps) merged[depField] = mergedDeps;
154
+ else delete merged[depField];
155
+ }
156
+ if (local.scripts && typeof local.scripts === "object" || template.scripts && typeof template.scripts === "object") {
157
+ const mergedScripts = mergeStringMaps(local.scripts, template.scripts);
158
+ if (mergedScripts) merged.scripts = mergedScripts;
159
+ else delete merged.scripts;
160
+ }
161
+ if (local.workspaces && typeof local.workspaces === "object" || template.workspaces && typeof template.workspaces === "object") {
162
+ const localWorkspaces = local.workspaces ?? {};
163
+ const templateWorkspaces = template.workspaces ?? {};
164
+ const mergedWorkspaces = {
165
+ ...localWorkspaces,
166
+ ...templateWorkspaces
167
+ };
168
+ const mergedPackages = mergeWorkspacePackages(localWorkspaces.packages, templateWorkspaces.packages);
169
+ if (mergedPackages) mergedWorkspaces.packages = mergedPackages;
170
+ else delete mergedWorkspaces.packages;
171
+ const mergedCatalog = mergeStringMaps(localWorkspaces.catalog, templateWorkspaces.catalog);
172
+ if (mergedCatalog) mergedWorkspaces.catalog = mergedCatalog;
173
+ else delete mergedWorkspaces.catalog;
174
+ if (Object.keys(mergedWorkspaces).length > 0) merged.workspaces = mergedWorkspaces;
175
+ else delete merged.workspaces;
106
176
  }
107
- if (local.scripts && typeof local.scripts === "object") merged.scripts = {
108
- ...template.scripts ?? {},
109
- ...local.scripts
110
- };
111
177
  return merged;
112
178
  }
113
179
  function toDestPath(filePath) {
@@ -115,7 +181,8 @@ function toDestPath(filePath) {
115
181
  }
116
182
  function writeSyncedFile(sourceDir, projectDir, filePath) {
117
183
  const src = (0, node_path.join)(sourceDir, filePath);
118
- const dest = (0, node_path.join)(projectDir, filePath.startsWith(".github/templates/") ? filePath.replace(/^\.github\/templates\//, ".github/") : filePath);
184
+ const destPath = filePath.startsWith(".github/templates/") ? filePath.replace(/^\.github\/templates\//, ".github/") : filePath;
185
+ const dest = (0, node_path.join)(projectDir, destPath);
119
186
  (0, node_fs.mkdirSync)((0, node_path.dirname)(dest), { recursive: true });
120
187
  if (filePath.endsWith("bos.config.json")) {
121
188
  const localContent = (0, node_fs.existsSync)(dest) ? (0, node_fs.readFileSync)(dest, "utf-8") : null;
@@ -130,7 +197,7 @@ function writeSyncedFile(sourceDir, projectDir, filePath) {
130
197
  const localContent = (0, node_fs.existsSync)(dest) ? (0, node_fs.readFileSync)(dest, "utf-8") : null;
131
198
  const templateContent = (0, node_fs.readFileSync)(src, "utf-8");
132
199
  if (localContent) {
133
- const merged = mergePackageJson(JSON.parse(localContent), JSON.parse(templateContent));
200
+ const merged = mergePackageJson(destPath, JSON.parse(localContent), JSON.parse(templateContent));
134
201
  (0, node_fs.writeFileSync)(dest, `${JSON.stringify(merged, null, 2)}\n`);
135
202
  return;
136
203
  }
@@ -215,7 +282,8 @@ async function syncTemplate(projectDir, options) {
215
282
  const added = [];
216
283
  for (const filePath of filteredFiles) {
217
284
  const destPath = toDestPath(filePath);
218
- if (isExcluded(destPath, excludePatterns)) continue;
285
+ const frameworkOwned = isFrameworkOwnedSyncFile(destPath);
286
+ if (isExcluded(destPath, excludePatterns) && !frameworkOwned) continue;
219
287
  const localHash = computeLocalHash(projectDir, destPath);
220
288
  const sourceContent = (0, node_fs.readFileSync)((0, node_path.join)(sourceDir, filePath));
221
289
  const sourceHash = (0, node_crypto.createHash)("sha256").update(sourceContent).digest("hex").substring(0, 16);
@@ -224,6 +292,10 @@ async function syncTemplate(projectDir, options) {
224
292
  continue;
225
293
  }
226
294
  if (localHash === sourceHash) continue;
295
+ if (frameworkOwned) {
296
+ updated.push(destPath);
297
+ continue;
298
+ }
227
299
  const snapshotHash = snapshot?.files[destPath];
228
300
  if (snapshotHash === void 0) {
229
301
  updated.push(destPath);
@@ -239,7 +311,7 @@ async function syncTemplate(projectDir, options) {
239
311
  skipped,
240
312
  added
241
313
  };
242
- const filesToWrite = [...updated, ...added].filter((f) => !isExcluded(f, excludePatterns));
314
+ const filesToWrite = [...updated, ...added].filter((f) => isFrameworkOwnedSyncFile(f) || !isExcluded(f, excludePatterns));
243
315
  const destToSource = /* @__PURE__ */ new Map();
244
316
  for (const filePath of filteredFiles) destToSource.set(toDestPath(filePath), filePath);
245
317
  if (filesToWrite.length > 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"sync.cjs","names":["resolveSourceDir","readTemplatekeep","readSnapshot","writeSnapshot","personalizeConfig","runBunInstall","runTypesGen"],"sources":["../../src/cli/sync.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n copyFileSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { SyncOptions, SyncResult } from \"../contract\";\nimport {\n personalizeConfig,\n readTemplatekeep,\n resolveSourceDir,\n runBunInstall,\n runTypesGen,\n} from \"./init\";\nimport { readSnapshot, writeSnapshot } from \"./snapshot\";\n\nfunction readExcludeFile(filePath: string): string[] {\n if (!existsSync(filePath)) return [];\n const content = readFileSync(filePath, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n}\n\nexport async function readTemplatesyncExclude(sourceDir: string): Promise<string[]> {\n return readExcludeFile(join(sourceDir, \".templatesync-exclude\"));\n}\n\nexport function readLocalSyncExcludes(projectDir: string): string[] {\n return readExcludeFile(join(projectDir, \".bos\", \"sync-local-exclude\"));\n}\n\nfunction isExcluded(filePath: string, excludePatterns: string[]): boolean {\n for (const pattern of excludePatterns) {\n if (pattern.endsWith(\"/**\")) {\n const prefix = pattern.slice(0, -3);\n if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;\n } else if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -2);\n const slashIdx = filePath.indexOf(\"/\", prefix.length + 1);\n if (filePath.startsWith(`${prefix}/`) && slashIdx === -1) return true;\n } else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n return false;\n}\n\nfunction computeLocalHash(projectDir: string, filePath: string): string | null {\n const fullPath = join(projectDir, filePath);\n if (!existsSync(fullPath)) return null;\n try {\n const content = readFileSync(fullPath);\n return createHash(\"sha256\").update(content).digest(\"hex\").substring(0, 16);\n } catch {\n return null;\n }\n}\n\nfunction backupFiles(projectDir: string, filePaths: string[]): string | null {\n const filesToBackup = filePaths.filter((f) => existsSync(join(projectDir, f)));\n if (filesToBackup.length === 0) return null;\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupDir = join(projectDir, \".bos\", \"sync-backup\", timestamp);\n\n for (const filePath of filesToBackup) {\n const src = join(projectDir, filePath);\n const dest = join(backupDir, filePath);\n mkdirSync(dirname(dest), { recursive: true });\n copyFileSync(src, dest);\n }\n\n return backupDir;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction mergeJsonValues(local: unknown, template: unknown): unknown {\n if (isPlainObject(local) && isPlainObject(template)) {\n const merged: Record<string, unknown> = {};\n // Preserve local key order first\n for (const key of Object.keys(local)) {\n merged[key] = mergeJsonValues(local[key], template[key]);\n }\n // Append any new template keys at the end\n for (const key of Object.keys(template)) {\n if (!(key in merged)) {\n merged[key] = template[key];\n }\n }\n return merged;\n }\n // For arrays and primitives, local always wins\n return local ?? template;\n}\n\nfunction mergeBosConfig(\n local: Record<string, unknown>,\n template: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = {};\n\n // 1. extends always first\n if (local.extends !== undefined) merged.extends = local.extends;\n else if (template.extends !== undefined) merged.extends = template.extends;\n\n // 2. Fixed trailing group: app, plugins, shared\n const TRAIL_GROUP = [\"app\", \"plugins\", \"shared\"];\n\n const localKeys = Object.keys(local).filter((k) => k !== \"extends\");\n const templateKeys = Object.keys(template).filter(\n (k) => k !== \"extends\" && !localKeys.includes(k),\n );\n\n // Find the first trailing-group key present locally (\"app\" comes first in the group)\n const firstTrailIndex = localKeys.findIndex((k) => TRAIL_GROUP.includes(k));\n\n const orderedKeys: string[] = [];\n\n if (firstTrailIndex >= 0) {\n // Keys before the trail group stay in local order\n orderedKeys.push(...localKeys.slice(0, firstTrailIndex));\n // New template keys inserted right before the trail group\n orderedKeys.push(...templateKeys);\n // Trail group keys (app, plugins, shared) in canonical order, preserving local if present\n for (const trailKey of TRAIL_GROUP) {\n if (localKeys.includes(trailKey)) orderedKeys.push(trailKey);\n }\n } else {\n // No trail group found locally — keep local order then append new keys and trail group\n orderedKeys.push(...localKeys);\n orderedKeys.push(...templateKeys);\n for (const trailKey of TRAIL_GROUP) {\n if (templateKeys.includes(trailKey)) orderedKeys.push(trailKey);\n }\n }\n\n // 3. Merge values for each key\n for (const key of orderedKeys) {\n merged[key] = mergeJsonValues(local[key], template[key]);\n }\n\n return merged;\n}\n\nfunction mergePackageJson(\n local: Record<string, unknown>,\n template: Record<string, unknown>,\n): Record<string, unknown> {\n const merged = { ...template };\n\n for (const depField of [\n \"dependencies\",\n \"devDependencies\",\n \"peerDependencies\",\n \"overrides\",\n ] as const) {\n const localDeps = local[depField] as Record<string, string> | undefined;\n const templateDeps = template[depField] as Record<string, string> | undefined;\n\n if (!localDeps && !templateDeps) continue;\n\n const mergedDeps: Record<string, string> = { ...(templateDeps ?? {}) };\n\n if (localDeps) {\n for (const [name, version] of Object.entries(localDeps)) {\n if (!(name in mergedDeps)) {\n mergedDeps[name] = version;\n }\n }\n }\n\n if (Object.keys(mergedDeps).length > 0) {\n merged[depField] = mergedDeps;\n }\n }\n\n if (local.scripts && typeof local.scripts === \"object\") {\n merged.scripts = {\n ...((template.scripts as Record<string, string>) ?? {}),\n ...(local.scripts as Record<string, string>),\n };\n }\n\n return merged;\n}\n\nfunction toDestPath(filePath: string): string {\n return filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n}\n\nfunction writeSyncedFile(sourceDir: string, projectDir: string, filePath: string): void {\n const src = join(sourceDir, filePath);\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n const dest = join(projectDir, destPath);\n mkdirSync(dirname(dest), { recursive: true });\n\n if (filePath.endsWith(\"bos.config.json\")) {\n const localContent = existsSync(dest) ? readFileSync(dest, \"utf-8\") : null;\n const templateContent = readFileSync(src, \"utf-8\");\n\n if (localContent) {\n const local = JSON.parse(localContent) as Record<string, unknown>;\n const template = JSON.parse(templateContent) as Record<string, unknown>;\n const merged = mergeBosConfig(local, template);\n writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\\n`);\n return;\n }\n }\n\n if (filePath.endsWith(\"package.json\")) {\n const localContent = existsSync(dest) ? readFileSync(dest, \"utf-8\") : null;\n const templateContent = readFileSync(src, \"utf-8\");\n\n if (localContent) {\n const local = JSON.parse(localContent) as Record<string, unknown>;\n const template = JSON.parse(templateContent) as Record<string, unknown>;\n const merged = mergePackageJson(local, template);\n writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\\n`);\n return;\n }\n }\n\n writeFileSync(dest, readFileSync(src));\n}\n\nexport async function syncTemplate(projectDir: string, options: SyncOptions): Promise<SyncResult> {\n const localConfig = JSON.parse(\n readFileSync(join(projectDir, \"bos.config.json\"), \"utf-8\"),\n ) as Record<string, unknown>;\n\n const extendsRef = localConfig.extends as string | undefined;\n if (!extendsRef?.startsWith(\"bos://\")) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No extends field found in bos.config.json — cannot determine parent\",\n };\n }\n\n const extendsMatch = extendsRef.match(/^bos:\\/\\/([^/]+)\\/(.+)$/);\n if (!extendsMatch) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: `Invalid extends reference: ${extendsRef}`,\n };\n }\n\n const extendsAccount = extendsMatch[1];\n const extendsGateway = extendsMatch[2];\n\n const { sourceDir, parentConfig, cleanup } = await resolveSourceDir({\n extendsAccount,\n extendsGateway,\n });\n\n try {\n const patterns = await readTemplatekeep(sourceDir);\n if (patterns.length === 0) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No .templatekeep found in template source\",\n };\n }\n\n const parentExcludes = await readTemplatesyncExclude(sourceDir);\n const localExcludes = readLocalSyncExcludes(projectDir);\n const excludePatterns = [...parentExcludes, ...localExcludes];\n\n const allTemplateFiles = new Set<string>();\n for (const pattern of patterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n allTemplateFiles.add(match);\n }\n }\n\n const childPlugins =\n localConfig.plugins && typeof localConfig.plugins === \"object\"\n ? Object.keys(localConfig.plugins as Record<string, unknown>)\n : [];\n\n const pluginRoutes: Record<string, string[]> = {};\n if (parentConfig.plugins) {\n for (const [key, ref] of Object.entries(parentConfig.plugins)) {\n if (ref.routes && ref.routes.length > 0) {\n pluginRoutes[key] = ref.routes;\n }\n }\n }\n\n const excludedRoutePatterns: string[] = [];\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n\n const filteredFiles = new Set<string>();\n for (const filePath of allTemplateFiles) {\n const pluginMatch = filePath.match(/^plugins\\/([^/]+)/);\n if (pluginMatch && !childPlugins.includes(pluginMatch[1])) continue;\n if (isExcluded(filePath, excludedRoutePatterns)) continue;\n filteredFiles.add(filePath);\n }\n\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isExcluded(match, excludedRoutePatterns)) {\n filteredFiles.add(match);\n }\n }\n }\n }\n\n const snapshot = await readSnapshot(projectDir);\n\n const updated: string[] = [];\n const skipped: string[] = [];\n const added: string[] = [];\n\n for (const filePath of filteredFiles) {\n const destPath = toDestPath(filePath);\n if (isExcluded(destPath, excludePatterns)) continue;\n\n const localHash = computeLocalHash(projectDir, destPath);\n const sourceContent = readFileSync(join(sourceDir, filePath));\n const sourceHash = createHash(\"sha256\").update(sourceContent).digest(\"hex\").substring(0, 16);\n\n if (localHash === null) {\n added.push(destPath);\n continue;\n }\n\n if (localHash === sourceHash) continue;\n\n const snapshotHash = snapshot?.files[destPath];\n\n if (snapshotHash === undefined) {\n updated.push(destPath);\n continue;\n }\n\n if (localHash === snapshotHash) {\n updated.push(destPath);\n } else {\n if (options.force) {\n updated.push(destPath);\n } else {\n skipped.push(destPath);\n }\n }\n }\n\n if (options.dryRun) {\n return {\n status: \"dry-run\",\n updated,\n skipped,\n added,\n };\n }\n\n const filesToWrite = [...updated, ...added].filter((f) => !isExcluded(f, excludePatterns));\n\n const destToSource = new Map<string, string>();\n for (const filePath of filteredFiles) {\n destToSource.set(toDestPath(filePath), filePath);\n }\n\n if (filesToWrite.length > 0) {\n backupFiles(projectDir, filesToWrite);\n\n for (const destPath of filesToWrite) {\n const sourcePath = destToSource.get(destPath) ?? destPath;\n writeSyncedFile(sourceDir, projectDir, sourcePath);\n }\n }\n\n const newSnapshotFiles: Record<string, string> = {};\n for (const filePath of filteredFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n const content = readFileSync(src);\n newSnapshotFiles[toDestPath(filePath)] = createHash(\"sha256\")\n .update(content)\n .digest(\"hex\")\n .substring(0, 16);\n }\n\n await writeSnapshot(projectDir, {\n parentRef: `bos://${extendsAccount}/${extendsGateway}`,\n files: newSnapshotFiles,\n });\n\n const account = (localConfig.account as string) || extendsAccount;\n const domain = (localConfig.domain as string) || extendsGateway;\n\n await personalizeConfig(projectDir, {\n extendsAccount,\n extendsGateway,\n account,\n domain,\n plugins: childPlugins,\n pluginRoutes,\n workspaceOpts: { sourceDir },\n mode: \"sync\",\n });\n\n if (!options.noInstall) {\n await runBunInstall(projectDir);\n await runTypesGen(projectDir);\n }\n\n return {\n status: \"synced\",\n updated,\n skipped,\n added,\n };\n } finally {\n await cleanup();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sync.cjs","names":["resolveSourceDir","readTemplatekeep","readSnapshot","writeSnapshot","personalizeConfig","runBunInstall","runTypesGen"],"sources":["../../src/cli/sync.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n copyFileSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { SyncOptions, SyncResult } from \"../contract\";\nimport {\n personalizeConfig,\n readTemplatekeep,\n resolveSourceDir,\n runBunInstall,\n runTypesGen,\n} from \"./init\";\nimport { readSnapshot, writeSnapshot } from \"./snapshot\";\n\nconst FRAMEWORK_OWNED_SYNC_FILES = new Set([\n \".gitignore\",\n \"biome.json\",\n \"bos.config.json\",\n \"package.json\",\n \".github/workflows/release-sync.yml\",\n \"ui/package.json\",\n \"ui/postcss.config.mjs\",\n \"ui/rsbuild.config.ts\",\n \"ui/tsconfig.json\",\n \"ui/src/app.ts\",\n \"ui/src/hydrate.tsx\",\n \"ui/src/lib/api.ts\",\n \"ui/src/lib/auth.ts\",\n \"ui/src/router.server.tsx\",\n \"ui/src/router.tsx\",\n \"ui/src/routes/__root.tsx\",\n \"api/package.json\",\n \"api/plugin.dev.ts\",\n \"api/rspack.config.js\",\n \"api/tsconfig.contract.json\",\n \"api/tsconfig.json\",\n \"api/src/lib/auth.ts\",\n]);\n\ntype PackageJson = Record<string, unknown>;\n\nexport function isFrameworkOwnedSyncFile(filePath: string): boolean {\n return FRAMEWORK_OWNED_SYNC_FILES.has(filePath);\n}\n\nfunction readExcludeFile(filePath: string): string[] {\n if (!existsSync(filePath)) return [];\n const content = readFileSync(filePath, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n}\n\nexport async function readTemplatesyncExclude(sourceDir: string): Promise<string[]> {\n return readExcludeFile(join(sourceDir, \".templatesync-exclude\"));\n}\n\nexport function readLocalSyncExcludes(projectDir: string): string[] {\n return readExcludeFile(join(projectDir, \".bos\", \"sync-local-exclude\"));\n}\n\nfunction isExcluded(filePath: string, excludePatterns: string[]): boolean {\n for (const pattern of excludePatterns) {\n if (pattern.endsWith(\"/**\")) {\n const prefix = pattern.slice(0, -3);\n if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;\n } else if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -2);\n const slashIdx = filePath.indexOf(\"/\", prefix.length + 1);\n if (filePath.startsWith(`${prefix}/`) && slashIdx === -1) return true;\n } else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n return false;\n}\n\nfunction computeLocalHash(projectDir: string, filePath: string): string | null {\n const fullPath = join(projectDir, filePath);\n if (!existsSync(fullPath)) return null;\n try {\n const content = readFileSync(fullPath);\n return createHash(\"sha256\").update(content).digest(\"hex\").substring(0, 16);\n } catch {\n return null;\n }\n}\n\nfunction backupFiles(projectDir: string, filePaths: string[]): string | null {\n const filesToBackup = filePaths.filter((f) => existsSync(join(projectDir, f)));\n if (filesToBackup.length === 0) return null;\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupDir = join(projectDir, \".bos\", \"sync-backup\", timestamp);\n\n for (const filePath of filesToBackup) {\n const src = join(projectDir, filePath);\n const dest = join(backupDir, filePath);\n mkdirSync(dirname(dest), { recursive: true });\n copyFileSync(src, dest);\n }\n\n return backupDir;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction mergeJsonValues(local: unknown, template: unknown): unknown {\n if (isPlainObject(local) && isPlainObject(template)) {\n const merged: Record<string, unknown> = {};\n // Preserve local key order first\n for (const key of Object.keys(local)) {\n merged[key] = mergeJsonValues(local[key], template[key]);\n }\n // Append any new template keys at the end\n for (const key of Object.keys(template)) {\n if (!(key in merged)) {\n merged[key] = template[key];\n }\n }\n return merged;\n }\n // For arrays and primitives, local always wins\n return local ?? template;\n}\n\nfunction mergeBosConfig(\n local: Record<string, unknown>,\n template: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = {};\n\n // 1. extends always first\n if (local.extends !== undefined) merged.extends = local.extends;\n else if (template.extends !== undefined) merged.extends = template.extends;\n\n // 2. Fixed trailing group: app, plugins, shared\n const TRAIL_GROUP = [\"app\", \"plugins\", \"shared\"];\n\n const localKeys = Object.keys(local).filter((k) => k !== \"extends\");\n const templateKeys = Object.keys(template).filter(\n (k) => k !== \"extends\" && !localKeys.includes(k),\n );\n\n // Find the first trailing-group key present locally (\"app\" comes first in the group)\n const firstTrailIndex = localKeys.findIndex((k) => TRAIL_GROUP.includes(k));\n\n const orderedKeys: string[] = [];\n\n if (firstTrailIndex >= 0) {\n // Keys before the trail group stay in local order\n orderedKeys.push(...localKeys.slice(0, firstTrailIndex));\n // New template keys inserted right before the trail group\n orderedKeys.push(...templateKeys);\n // Trail group keys (app, plugins, shared) in canonical order, preserving local if present\n for (const trailKey of TRAIL_GROUP) {\n if (localKeys.includes(trailKey)) orderedKeys.push(trailKey);\n }\n } else {\n // No trail group found locally — keep local order then append new keys and trail group\n orderedKeys.push(...localKeys);\n orderedKeys.push(...templateKeys);\n for (const trailKey of TRAIL_GROUP) {\n if (templateKeys.includes(trailKey)) orderedKeys.push(trailKey);\n }\n }\n\n // 3. Merge values for each key\n for (const key of orderedKeys) {\n merged[key] = mergeJsonValues(local[key], template[key]);\n }\n\n return merged;\n}\n\nfunction mergeStringMaps(\n local: Record<string, string> | undefined,\n template: Record<string, string> | undefined,\n): Record<string, string> | undefined {\n if (!local && !template) return undefined;\n\n const merged: Record<string, string> = { ...(local ?? {}) };\n for (const [name, value] of Object.entries(template ?? {})) {\n merged[name] = value;\n }\n\n return Object.keys(merged).length > 0 ? merged : undefined;\n}\n\nfunction mergeWorkspacePackages(local: unknown, template: unknown): string[] | undefined {\n const localPackages = Array.isArray(local) ? local : [];\n const templatePackages = Array.isArray(template) ? template : [];\n if (localPackages.length === 0 && templatePackages.length === 0) return undefined;\n\n const ordered = new Set<string>();\n for (const entry of templatePackages) {\n if (typeof entry === \"string\" && entry.length > 0) ordered.add(entry);\n }\n for (const entry of localPackages) {\n if (typeof entry === \"string\" && entry.length > 0) ordered.add(entry);\n }\n\n return ordered.size > 0 ? [...ordered] : undefined;\n}\n\nexport function mergePackageJson(\n filePath: string,\n local: PackageJson,\n template: PackageJson,\n): PackageJson {\n const merged: PackageJson = { ...local, ...template };\n\n if (filePath === \"package.json\") {\n for (const key of [\"name\", \"private\", \"version\"] as const) {\n if (key in local) {\n merged[key] = local[key];\n }\n }\n } else if (\"version\" in local) {\n merged.version = local.version;\n }\n\n for (const depField of [\n \"dependencies\",\n \"devDependencies\",\n \"peerDependencies\",\n \"overrides\",\n ] as const) {\n const localDeps = local[depField] as Record<string, string> | undefined;\n const templateDeps = template[depField] as Record<string, string> | undefined;\n\n const mergedDeps = mergeStringMaps(localDeps, templateDeps);\n if (mergedDeps) {\n merged[depField] = mergedDeps;\n } else {\n delete merged[depField];\n }\n }\n\n if (\n (local.scripts && typeof local.scripts === \"object\") ||\n (template.scripts && typeof template.scripts === \"object\")\n ) {\n const mergedScripts = mergeStringMaps(\n local.scripts as Record<string, string> | undefined,\n template.scripts as Record<string, string> | undefined,\n );\n if (mergedScripts) {\n merged.scripts = mergedScripts;\n } else {\n delete merged.scripts;\n }\n }\n\n if (\n (local.workspaces && typeof local.workspaces === \"object\") ||\n (template.workspaces && typeof template.workspaces === \"object\")\n ) {\n const localWorkspaces = (local.workspaces ?? {}) as {\n packages?: string[];\n catalog?: Record<string, string>;\n };\n const templateWorkspaces = (template.workspaces ?? {}) as {\n packages?: string[];\n catalog?: Record<string, string>;\n };\n\n const mergedWorkspaces: { packages?: string[]; catalog?: Record<string, string> } = {\n ...localWorkspaces,\n ...templateWorkspaces,\n };\n\n const mergedPackages = mergeWorkspacePackages(\n localWorkspaces.packages,\n templateWorkspaces.packages,\n );\n if (mergedPackages) {\n mergedWorkspaces.packages = mergedPackages;\n } else {\n delete mergedWorkspaces.packages;\n }\n\n const mergedCatalog = mergeStringMaps(localWorkspaces.catalog, templateWorkspaces.catalog);\n if (mergedCatalog) {\n mergedWorkspaces.catalog = mergedCatalog;\n } else {\n delete mergedWorkspaces.catalog;\n }\n\n if (Object.keys(mergedWorkspaces).length > 0) {\n merged.workspaces = mergedWorkspaces;\n } else {\n delete merged.workspaces;\n }\n }\n\n return merged;\n}\n\nfunction toDestPath(filePath: string): string {\n return filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n}\n\nfunction writeSyncedFile(sourceDir: string, projectDir: string, filePath: string): void {\n const src = join(sourceDir, filePath);\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n const dest = join(projectDir, destPath);\n mkdirSync(dirname(dest), { recursive: true });\n\n if (filePath.endsWith(\"bos.config.json\")) {\n const localContent = existsSync(dest) ? readFileSync(dest, \"utf-8\") : null;\n const templateContent = readFileSync(src, \"utf-8\");\n\n if (localContent) {\n const local = JSON.parse(localContent) as Record<string, unknown>;\n const template = JSON.parse(templateContent) as Record<string, unknown>;\n const merged = mergeBosConfig(local, template);\n writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\\n`);\n return;\n }\n }\n\n if (filePath.endsWith(\"package.json\")) {\n const localContent = existsSync(dest) ? readFileSync(dest, \"utf-8\") : null;\n const templateContent = readFileSync(src, \"utf-8\");\n\n if (localContent) {\n const local = JSON.parse(localContent) as Record<string, unknown>;\n const template = JSON.parse(templateContent) as Record<string, unknown>;\n const merged = mergePackageJson(destPath, local, template);\n writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\\n`);\n return;\n }\n }\n\n writeFileSync(dest, readFileSync(src));\n}\n\nexport async function syncTemplate(projectDir: string, options: SyncOptions): Promise<SyncResult> {\n const localConfig = JSON.parse(\n readFileSync(join(projectDir, \"bos.config.json\"), \"utf-8\"),\n ) as Record<string, unknown>;\n\n const extendsRef = localConfig.extends as string | undefined;\n if (!extendsRef?.startsWith(\"bos://\")) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No extends field found in bos.config.json — cannot determine parent\",\n };\n }\n\n const extendsMatch = extendsRef.match(/^bos:\\/\\/([^/]+)\\/(.+)$/);\n if (!extendsMatch) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: `Invalid extends reference: ${extendsRef}`,\n };\n }\n\n const extendsAccount = extendsMatch[1];\n const extendsGateway = extendsMatch[2];\n\n const { sourceDir, parentConfig, cleanup } = await resolveSourceDir({\n extendsAccount,\n extendsGateway,\n });\n\n try {\n const patterns = await readTemplatekeep(sourceDir);\n if (patterns.length === 0) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No .templatekeep found in template source\",\n };\n }\n\n const parentExcludes = await readTemplatesyncExclude(sourceDir);\n const localExcludes = readLocalSyncExcludes(projectDir);\n const excludePatterns = [...parentExcludes, ...localExcludes];\n\n const allTemplateFiles = new Set<string>();\n for (const pattern of patterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n allTemplateFiles.add(match);\n }\n }\n\n const childPlugins =\n localConfig.plugins && typeof localConfig.plugins === \"object\"\n ? Object.keys(localConfig.plugins as Record<string, unknown>)\n : [];\n\n const pluginRoutes: Record<string, string[]> = {};\n if (parentConfig.plugins) {\n for (const [key, ref] of Object.entries(parentConfig.plugins)) {\n if (ref.routes && ref.routes.length > 0) {\n pluginRoutes[key] = ref.routes;\n }\n }\n }\n\n const excludedRoutePatterns: string[] = [];\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n\n const filteredFiles = new Set<string>();\n for (const filePath of allTemplateFiles) {\n const pluginMatch = filePath.match(/^plugins\\/([^/]+)/);\n if (pluginMatch && !childPlugins.includes(pluginMatch[1])) continue;\n if (isExcluded(filePath, excludedRoutePatterns)) continue;\n filteredFiles.add(filePath);\n }\n\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isExcluded(match, excludedRoutePatterns)) {\n filteredFiles.add(match);\n }\n }\n }\n }\n\n const snapshot = await readSnapshot(projectDir);\n\n const updated: string[] = [];\n const skipped: string[] = [];\n const added: string[] = [];\n\n for (const filePath of filteredFiles) {\n const destPath = toDestPath(filePath);\n const frameworkOwned = isFrameworkOwnedSyncFile(destPath);\n if (isExcluded(destPath, excludePatterns) && !frameworkOwned) continue;\n\n const localHash = computeLocalHash(projectDir, destPath);\n const sourceContent = readFileSync(join(sourceDir, filePath));\n const sourceHash = createHash(\"sha256\").update(sourceContent).digest(\"hex\").substring(0, 16);\n\n if (localHash === null) {\n added.push(destPath);\n continue;\n }\n\n if (localHash === sourceHash) continue;\n\n if (frameworkOwned) {\n updated.push(destPath);\n continue;\n }\n\n const snapshotHash = snapshot?.files[destPath];\n\n if (snapshotHash === undefined) {\n updated.push(destPath);\n continue;\n }\n\n if (localHash === snapshotHash) {\n updated.push(destPath);\n } else {\n if (options.force) {\n updated.push(destPath);\n } else {\n skipped.push(destPath);\n }\n }\n }\n\n if (options.dryRun) {\n return {\n status: \"dry-run\",\n updated,\n skipped,\n added,\n };\n }\n\n const filesToWrite = [...updated, ...added].filter(\n (f) => isFrameworkOwnedSyncFile(f) || !isExcluded(f, excludePatterns),\n );\n\n const destToSource = new Map<string, string>();\n for (const filePath of filteredFiles) {\n destToSource.set(toDestPath(filePath), filePath);\n }\n\n if (filesToWrite.length > 0) {\n backupFiles(projectDir, filesToWrite);\n\n for (const destPath of filesToWrite) {\n const sourcePath = destToSource.get(destPath) ?? destPath;\n writeSyncedFile(sourceDir, projectDir, sourcePath);\n }\n }\n\n const newSnapshotFiles: Record<string, string> = {};\n for (const filePath of filteredFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n const content = readFileSync(src);\n newSnapshotFiles[toDestPath(filePath)] = createHash(\"sha256\")\n .update(content)\n .digest(\"hex\")\n .substring(0, 16);\n }\n\n await writeSnapshot(projectDir, {\n parentRef: `bos://${extendsAccount}/${extendsGateway}`,\n files: newSnapshotFiles,\n });\n\n const account = (localConfig.account as string) || extendsAccount;\n const domain = (localConfig.domain as string) || extendsGateway;\n\n await personalizeConfig(projectDir, {\n extendsAccount,\n extendsGateway,\n account,\n domain,\n plugins: childPlugins,\n pluginRoutes,\n workspaceOpts: { sourceDir },\n mode: \"sync\",\n });\n\n if (!options.noInstall) {\n await runBunInstall(projectDir);\n await runTypesGen(projectDir);\n }\n\n return {\n status: \"synced\",\n updated,\n skipped,\n added,\n };\n } finally {\n await cleanup();\n }\n}\n"],"mappings":"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"}
package/dist/cli/sync.mjs CHANGED
@@ -6,6 +6,33 @@ import { createHash } from "node:crypto";
6
6
  import { glob } from "glob";
7
7
 
8
8
  //#region src/cli/sync.ts
9
+ const FRAMEWORK_OWNED_SYNC_FILES = new Set([
10
+ ".gitignore",
11
+ "biome.json",
12
+ "bos.config.json",
13
+ "package.json",
14
+ ".github/workflows/release-sync.yml",
15
+ "ui/package.json",
16
+ "ui/postcss.config.mjs",
17
+ "ui/rsbuild.config.ts",
18
+ "ui/tsconfig.json",
19
+ "ui/src/app.ts",
20
+ "ui/src/hydrate.tsx",
21
+ "ui/src/lib/api.ts",
22
+ "ui/src/lib/auth.ts",
23
+ "ui/src/router.server.tsx",
24
+ "ui/src/router.tsx",
25
+ "ui/src/routes/__root.tsx",
26
+ "api/package.json",
27
+ "api/plugin.dev.ts",
28
+ "api/rspack.config.js",
29
+ "api/tsconfig.contract.json",
30
+ "api/tsconfig.json",
31
+ "api/src/lib/auth.ts"
32
+ ]);
33
+ function isFrameworkOwnedSyncFile(filePath) {
34
+ return FRAMEWORK_OWNED_SYNC_FILES.has(filePath);
35
+ }
9
36
  function readExcludeFile(filePath) {
10
37
  if (!existsSync(filePath)) return [];
11
38
  return readFileSync(filePath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
@@ -86,8 +113,33 @@ function mergeBosConfig(local, template) {
86
113
  for (const key of orderedKeys) merged[key] = mergeJsonValues(local[key], template[key]);
87
114
  return merged;
88
115
  }
89
- function mergePackageJson(local, template) {
90
- const merged = { ...template };
116
+ function mergeStringMaps(local, template) {
117
+ if (!local && !template) return void 0;
118
+ const merged = { ...local ?? {} };
119
+ for (const [name, value] of Object.entries(template ?? {})) merged[name] = value;
120
+ return Object.keys(merged).length > 0 ? merged : void 0;
121
+ }
122
+ function mergeWorkspacePackages(local, template) {
123
+ const localPackages = Array.isArray(local) ? local : [];
124
+ const templatePackages = Array.isArray(template) ? template : [];
125
+ if (localPackages.length === 0 && templatePackages.length === 0) return void 0;
126
+ const ordered = /* @__PURE__ */ new Set();
127
+ for (const entry of templatePackages) if (typeof entry === "string" && entry.length > 0) ordered.add(entry);
128
+ for (const entry of localPackages) if (typeof entry === "string" && entry.length > 0) ordered.add(entry);
129
+ return ordered.size > 0 ? [...ordered] : void 0;
130
+ }
131
+ function mergePackageJson(filePath, local, template) {
132
+ const merged = {
133
+ ...local,
134
+ ...template
135
+ };
136
+ if (filePath === "package.json") {
137
+ for (const key of [
138
+ "name",
139
+ "private",
140
+ "version"
141
+ ]) if (key in local) merged[key] = local[key];
142
+ } else if ("version" in local) merged.version = local.version;
91
143
  for (const depField of [
92
144
  "dependencies",
93
145
  "devDependencies",
@@ -96,17 +148,31 @@ function mergePackageJson(local, template) {
96
148
  ]) {
97
149
  const localDeps = local[depField];
98
150
  const templateDeps = template[depField];
99
- if (!localDeps && !templateDeps) continue;
100
- const mergedDeps = { ...templateDeps ?? {} };
101
- if (localDeps) {
102
- for (const [name, version] of Object.entries(localDeps)) if (!(name in mergedDeps)) mergedDeps[name] = version;
103
- }
104
- if (Object.keys(mergedDeps).length > 0) merged[depField] = mergedDeps;
151
+ const mergedDeps = mergeStringMaps(localDeps, templateDeps);
152
+ if (mergedDeps) merged[depField] = mergedDeps;
153
+ else delete merged[depField];
154
+ }
155
+ if (local.scripts && typeof local.scripts === "object" || template.scripts && typeof template.scripts === "object") {
156
+ const mergedScripts = mergeStringMaps(local.scripts, template.scripts);
157
+ if (mergedScripts) merged.scripts = mergedScripts;
158
+ else delete merged.scripts;
159
+ }
160
+ if (local.workspaces && typeof local.workspaces === "object" || template.workspaces && typeof template.workspaces === "object") {
161
+ const localWorkspaces = local.workspaces ?? {};
162
+ const templateWorkspaces = template.workspaces ?? {};
163
+ const mergedWorkspaces = {
164
+ ...localWorkspaces,
165
+ ...templateWorkspaces
166
+ };
167
+ const mergedPackages = mergeWorkspacePackages(localWorkspaces.packages, templateWorkspaces.packages);
168
+ if (mergedPackages) mergedWorkspaces.packages = mergedPackages;
169
+ else delete mergedWorkspaces.packages;
170
+ const mergedCatalog = mergeStringMaps(localWorkspaces.catalog, templateWorkspaces.catalog);
171
+ if (mergedCatalog) mergedWorkspaces.catalog = mergedCatalog;
172
+ else delete mergedWorkspaces.catalog;
173
+ if (Object.keys(mergedWorkspaces).length > 0) merged.workspaces = mergedWorkspaces;
174
+ else delete merged.workspaces;
105
175
  }
106
- if (local.scripts && typeof local.scripts === "object") merged.scripts = {
107
- ...template.scripts ?? {},
108
- ...local.scripts
109
- };
110
176
  return merged;
111
177
  }
112
178
  function toDestPath(filePath) {
@@ -114,7 +180,8 @@ function toDestPath(filePath) {
114
180
  }
115
181
  function writeSyncedFile(sourceDir, projectDir, filePath) {
116
182
  const src = join(sourceDir, filePath);
117
- const dest = join(projectDir, filePath.startsWith(".github/templates/") ? filePath.replace(/^\.github\/templates\//, ".github/") : filePath);
183
+ const destPath = filePath.startsWith(".github/templates/") ? filePath.replace(/^\.github\/templates\//, ".github/") : filePath;
184
+ const dest = join(projectDir, destPath);
118
185
  mkdirSync(dirname(dest), { recursive: true });
119
186
  if (filePath.endsWith("bos.config.json")) {
120
187
  const localContent = existsSync(dest) ? readFileSync(dest, "utf-8") : null;
@@ -129,7 +196,7 @@ function writeSyncedFile(sourceDir, projectDir, filePath) {
129
196
  const localContent = existsSync(dest) ? readFileSync(dest, "utf-8") : null;
130
197
  const templateContent = readFileSync(src, "utf-8");
131
198
  if (localContent) {
132
- const merged = mergePackageJson(JSON.parse(localContent), JSON.parse(templateContent));
199
+ const merged = mergePackageJson(destPath, JSON.parse(localContent), JSON.parse(templateContent));
133
200
  writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\n`);
134
201
  return;
135
202
  }
@@ -214,7 +281,8 @@ async function syncTemplate(projectDir, options) {
214
281
  const added = [];
215
282
  for (const filePath of filteredFiles) {
216
283
  const destPath = toDestPath(filePath);
217
- if (isExcluded(destPath, excludePatterns)) continue;
284
+ const frameworkOwned = isFrameworkOwnedSyncFile(destPath);
285
+ if (isExcluded(destPath, excludePatterns) && !frameworkOwned) continue;
218
286
  const localHash = computeLocalHash(projectDir, destPath);
219
287
  const sourceContent = readFileSync(join(sourceDir, filePath));
220
288
  const sourceHash = createHash("sha256").update(sourceContent).digest("hex").substring(0, 16);
@@ -223,6 +291,10 @@ async function syncTemplate(projectDir, options) {
223
291
  continue;
224
292
  }
225
293
  if (localHash === sourceHash) continue;
294
+ if (frameworkOwned) {
295
+ updated.push(destPath);
296
+ continue;
297
+ }
226
298
  const snapshotHash = snapshot?.files[destPath];
227
299
  if (snapshotHash === void 0) {
228
300
  updated.push(destPath);
@@ -238,7 +310,7 @@ async function syncTemplate(projectDir, options) {
238
310
  skipped,
239
311
  added
240
312
  };
241
- const filesToWrite = [...updated, ...added].filter((f) => !isExcluded(f, excludePatterns));
313
+ const filesToWrite = [...updated, ...added].filter((f) => isFrameworkOwnedSyncFile(f) || !isExcluded(f, excludePatterns));
242
314
  const destToSource = /* @__PURE__ */ new Map();
243
315
  for (const filePath of filteredFiles) destToSource.set(toDestPath(filePath), filePath);
244
316
  if (filesToWrite.length > 0) {