create-flow-os 0.0.47-dev.1772043993 → 0.0.47-dev.1772047467

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-flow-os",
3
- "version": "0.0.47-dev.1772043993",
3
+ "version": "0.0.47-dev.1772047467",
4
4
  "license": "PolyForm-Shield-1.0.0",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -13,7 +13,7 @@
13
13
  "create": "bun run src/create/index.ts",
14
14
  "dev": "flow-os dev",
15
15
  "build": "flow-os build",
16
- "start": "bun run server/start.ts"
16
+ "start": "bun ./node_modules/@flow-os/server/src/bin.ts"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@types/node": "^25.3.0",
package/src/init/index.ts CHANGED
@@ -1,12 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- // Clear subito per nascondere resolving/installing di Bun
3
- process.stdout.write("\x1b[2J\x1b[H");
4
2
 
5
3
  import * as readline from "readline";
6
4
  import { join, dirname } from "path";
7
5
  import { fileURLToPath } from "url";
8
6
  import { libsWithConfig, toShortName, toPkgName } from "./lib";
9
- import { initLib, fetchFlowPackageVersions, shouldUseWorkspace } from "./scaffold";
7
+ import { initLib, fetchFlowPackageVersions, shouldUseWorkspace, findFlowOsRepoRoot } from "./scaffold";
10
8
  import { bannerBox, withLoading, colors } from "./ui";
11
9
 
12
10
  const { V, V_LIGHT, Y, E, R, B } = colors;
@@ -16,7 +14,8 @@ const { V, V_LIGHT, Y, E, R, B } = colors;
16
14
  // ───────────────────────────────────────────────────────────────────────────────
17
15
  const cwd = process.cwd();
18
16
  const cliRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
19
- const available = libsWithConfig(cliRoot).map(toShortName);
17
+ const flowOsRepoRoot = findFlowOsRepoRoot(cwd);
18
+ const available = libsWithConfig(cliRoot, flowOsRepoRoot).map(toShortName);
20
19
 
21
20
  let libs = [...new Set(process.argv.slice(3))];
22
21
 
package/src/init/lib.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync } from "fs";
1
+ import { existsSync, readFileSync, readdirSync } from "fs";
2
2
  import { join, dirname } from "path";
3
3
  import { fileURLToPath } from "url";
4
4
 
@@ -21,7 +21,27 @@ export function flowDeps(root: string): string[] {
21
21
  return Object.keys(deps).filter((k) => k.startsWith(FLOW_PREFIX));
22
22
  }
23
23
 
24
- export function libsWithConfig(cliRoot: string): string[] {
24
+ /** Scansiona packages/ per tutti gli @flow-os/* (con o senza config) */
25
+ function scanPackagesDir(packagesDir: string): string[] {
26
+ const found: string[] = [];
27
+ try {
28
+ for (const name of readdirSync(packagesDir, { withFileTypes: true })) {
29
+ if (!name.isDirectory()) continue;
30
+ const pkgPath = join(packagesDir, name.name, "package.json");
31
+ if (!existsSync(pkgPath)) continue;
32
+ try {
33
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as { name?: string };
34
+ if (pkg?.name?.startsWith(FLOW_PREFIX)) found.push(pkg.name);
35
+ } catch {}
36
+ }
37
+ } catch {}
38
+ return found;
39
+ }
40
+
41
+ export function libsWithConfig(cliRoot: string, flowOsRepoRoot?: string | null): string[] {
42
+ const packagesDir = flowOsRepoRoot ? join(flowOsRepoRoot, "packages") : join(cliRoot, "..");
43
+ const fromScan = scanPackagesDir(packagesDir);
44
+ if (fromScan.length > 0) return fromScan;
25
45
  const pkg = JSON.parse(readFileSync(join(cliRoot, "package.json"), "utf-8"));
26
46
  const deps = Object.keys(pkg.dependencies ?? {}).filter((k) => k.startsWith(FLOW_PREFIX));
27
47
  return deps.filter((name) => existsSync(join(pkgRoot(name), "config")));
package/src/init/merge.ts CHANGED
@@ -136,7 +136,19 @@ export function mergeCodeTemplates(a: string, b: string): string {
136
136
  return replaceObjectArg(base, extB, merged);
137
137
  }
138
138
 
139
- /** Merge per file con pattern fn({...}) - config(), defineConfig(), ecc. */
139
+ /** Estrae la riga "export default fn(...)" dal template */
140
+ function extractExportDefault(content: string): string | null {
141
+ const m = content.match(/export\s+default\s+\w+\s*\([^)]*\)\s*;?/);
142
+ return m ? m[0].trim() : null;
143
+ }
144
+
145
+ /** True se user ha già export default (anche su righe diverse, formattazione diversa) */
146
+ function userHasExportDefault(content: string): boolean {
147
+ const normalized = content.replace(/\s+/g, " ");
148
+ return /\bexport\s+default\b/.test(normalized);
149
+ }
150
+
151
+ /** Merge per file con pattern fn({...}) - config(), defineConfig(), ecc. Additivo: non sovrascrive. */
140
152
  export function mergeCodeObject(userContent: string, templateContent: string, _path: string): { merged: string; conflicts: Conflict[]; ext: Extracted | null } {
141
153
  const conflicts: Conflict[] = [];
142
154
  const userExt = extractObjectArg(userContent);
@@ -146,7 +158,14 @@ export function mergeCodeObject(userContent: string, templateContent: string, _p
146
158
  const templateObj = templateExt.obj;
147
159
  const mergedObj = deepMergeAdditive(userObj, templateObj, "", conflicts);
148
160
  const base = userContent || templateContent;
149
- const merged = replaceObjectArg(base, templateExt, mergedObj);
161
+ const extToReplace = userExt ?? templateExt;
162
+ let merged = replaceObjectArg(base, extToReplace, mergedObj);
163
+ const templateExport = extractExportDefault(templateContent);
164
+ if (templateExport && !userHasExportDefault(userContent)) {
165
+ const userTrimmed = merged.trimEnd();
166
+ const sep = userTrimmed.endsWith("\n") || !userTrimmed ? "" : "\n\n";
167
+ merged = userTrimmed + sep + templateExport;
168
+ }
150
169
  return { merged, conflicts, ext: templateExt };
151
170
  }
152
171
 
@@ -192,7 +192,7 @@ async function fetchConfigFromNpm(
192
192
  /** Sostituisce workspace:* e 0.0.1 con versione concreta (workspace va bene solo dentro flow-os) */
193
193
  function resolveFlowDeps(
194
194
  deps: Record<string, string> | undefined,
195
- configDir: string,
195
+ pkgRootDir: string,
196
196
  versionsFromNpm: Map<string, string>,
197
197
  useWorkspace: boolean
198
198
  ): Record<string, string> {
@@ -204,7 +204,7 @@ function resolveFlowDeps(
204
204
  }
205
205
  return resolved;
206
206
  }
207
- const ownerPkgPath = join(configDir, "..", "package.json");
207
+ const ownerPkgPath = join(pkgRootDir, "package.json");
208
208
  let ownerVersion: string | undefined;
209
209
  if (existsSync(ownerPkgPath)) {
210
210
  try {
@@ -235,6 +235,24 @@ function resolveFlowDeps(
235
235
  return resolved;
236
236
  }
237
237
 
238
+ const PKG_KEY_ORDER = ["name", "version", "private", "type", "scripts", "dependencies", "devDependencies"];
239
+
240
+ function sortPkgKeys(pkg: Record<string, unknown>): Record<string, unknown> {
241
+ const ordered: Record<string, unknown> = {};
242
+ for (const k of PKG_KEY_ORDER) {
243
+ if (k in pkg) {
244
+ const v = pkg[k];
245
+ ordered[k] = k === "scripts" && v && typeof v === "object" && !Array.isArray(v)
246
+ ? Object.fromEntries(Object.entries(v as Record<string, string>).sort(([a], [b]) => a.localeCompare(b)))
247
+ : v;
248
+ }
249
+ }
250
+ for (const k of Object.keys(pkg).sort()) {
251
+ if (!(k in ordered)) ordered[k] = pkg[k];
252
+ }
253
+ return ordered;
254
+ }
255
+
238
256
  function mergePkg(configDir: string, cwd: string, versionsFromNpm: Map<string, string>, useWorkspace: boolean): void {
239
257
  const configPkg = join(configDir, "package.json");
240
258
  if (!existsSync(configPkg)) return;
@@ -245,12 +263,75 @@ function mergePkg(configDir: string, cwd: string, versionsFromNpm: Map<string, s
245
263
  : { ...config, name: basename(cwd) || "flow-app" };
246
264
  target.dependencies = { ...target.dependencies, ...config.dependencies };
247
265
  target.devDependencies = { ...target.devDependencies, ...config.devDependencies };
248
- for (const [k, v] of Object.entries(resolveFlowDeps(config.dependencies, configDir, versionsFromNpm, useWorkspace)))
266
+ const pkgRootDir = join(configDir, "..");
267
+ for (const [k, v] of Object.entries(resolveFlowDeps(config.dependencies, pkgRootDir, versionsFromNpm, useWorkspace)))
249
268
  target.dependencies[k] = v;
250
- for (const [k, v] of Object.entries(resolveFlowDeps(config.devDependencies, configDir, versionsFromNpm, useWorkspace)))
269
+ for (const [k, v] of Object.entries(resolveFlowDeps(config.devDependencies, pkgRootDir, versionsFromNpm, useWorkspace)))
251
270
  target.devDependencies[k] = v;
252
271
  target.scripts = { ...target.scripts, ...config.scripts };
253
- writeFileSync(targetPath, JSON.stringify(target, null, 2));
272
+ writeFileSync(targetPath, JSON.stringify(sortPkgKeys(target), null, 2));
273
+ }
274
+
275
+ type FlowOsInit = { dependencies?: Record<string, string>; devDependencies?: Record<string, string>; scripts?: Record<string, string>; name?: string; version?: string; type?: string };
276
+
277
+ /** Merge da flow-os-init: root package.json (se in repo) oppure package (npm) */
278
+ function mergeFlowOsInit(
279
+ packageRoot: string,
280
+ pkgName: string,
281
+ cwd: string,
282
+ versionsFromNpm: Map<string, string>,
283
+ useWorkspace: boolean,
284
+ flowOsRepoRoot: string | null
285
+ ): void {
286
+ let init: FlowOsInit | undefined;
287
+ if (flowOsRepoRoot) {
288
+ const rootPkgPath = join(flowOsRepoRoot, "package.json");
289
+ if (existsSync(rootPkgPath)) {
290
+ try {
291
+ const rootPkg = JSON.parse(readFileSync(rootPkgPath, "utf-8")) as { "flow-os-init"?: Record<string, FlowOsInit> };
292
+ init = rootPkg["flow-os-init"]?.[pkgName];
293
+ } catch {}
294
+ }
295
+ }
296
+ if (!init) {
297
+ const pkgPath = join(packageRoot, "package.json");
298
+ if (!existsSync(pkgPath)) return;
299
+ try {
300
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as { "flow-os-init"?: FlowOsInit };
301
+ init = pkg["flow-os-init"];
302
+ } catch {
303
+ return;
304
+ }
305
+ }
306
+ if (!init) return;
307
+ const targetPath = join(cwd, "package.json");
308
+ const target = existsSync(targetPath)
309
+ ? JSON.parse(readFileSync(targetPath, "utf-8"))
310
+ : { name: init.name ?? (basename(cwd) || "flow-app"), version: init.version ?? "0.0.1", type: init.type ?? "module" };
311
+ target.dependencies = { ...target.dependencies, ...init.dependencies };
312
+ target.devDependencies = { ...target.devDependencies, ...init.devDependencies };
313
+ for (const [k, v] of Object.entries(resolveFlowDeps(init.dependencies, packageRoot, versionsFromNpm, useWorkspace)))
314
+ target.dependencies[k] = v;
315
+ for (const [k, v] of Object.entries(resolveFlowDeps(init.devDependencies, packageRoot, versionsFromNpm, useWorkspace)))
316
+ target.devDependencies[k] = v;
317
+ target.scripts = { ...target.scripts, ...init.scripts };
318
+ writeFileSync(targetPath, JSON.stringify(sortPkgKeys(target), null, 2));
319
+ }
320
+
321
+ /** Aggiunge dipendenza per pacchetti senza config (solo install) */
322
+ function addDependencyForPackage(
323
+ pkgName: string,
324
+ cwd: string,
325
+ versionsFromNpm: Map<string, string>,
326
+ useWorkspace: boolean
327
+ ): void {
328
+ const targetPath = join(cwd, "package.json");
329
+ if (!existsSync(targetPath)) return;
330
+ const target = JSON.parse(readFileSync(targetPath, "utf-8"));
331
+ target.dependencies = target.dependencies ?? {};
332
+ const spec = useWorkspace ? "workspace:*" : (versionsFromNpm.get(pkgName) ? `^${versionsFromNpm.get(pkgName)}` : undefined);
333
+ if (spec) target.dependencies[pkgName] = spec;
334
+ writeFileSync(targetPath, JSON.stringify(sortPkgKeys(target), null, 2));
254
335
  }
255
336
 
256
337
  /** Assicura che versions abbia le versioni per pkgNames (fetch lazy) */
@@ -382,7 +463,42 @@ export async function initLib(
382
463
  await collectAllTemplates(libs, combined, done, order, versions, tmpDirs, flowOsRepoRoot);
383
464
 
384
465
  for (const configDir of order) {
385
- mergePkg(configDir, cwd, versions, useWorkspace);
466
+ const packageRoot = join(configDir, "..");
467
+ const pkgName = (() => {
468
+ try {
469
+ const p = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf-8")) as { name?: string };
470
+ return p?.name ?? "";
471
+ } catch {
472
+ return "";
473
+ }
474
+ })();
475
+ if (existsSync(join(configDir, "package.json"))) {
476
+ mergePkg(configDir, cwd, versions, useWorkspace);
477
+ } else {
478
+ mergeFlowOsInit(packageRoot, pkgName, cwd, versions, useWorkspace, flowOsRepoRoot);
479
+ }
480
+ }
481
+
482
+ const targetPath = join(cwd, "package.json");
483
+ if (!existsSync(targetPath)) {
484
+ writeFileSync(
485
+ targetPath,
486
+ JSON.stringify(sortPkgKeys({ name: basename(cwd) || "flow-app", version: "0.0.1", private: true, type: "module" }), null, 2)
487
+ );
488
+ }
489
+
490
+ const toAdd = new Set<string>(pkgNames);
491
+ for (const configDir of order) {
492
+ const packageRoot = join(configDir, "..");
493
+ for (const peer of flowDepsFromPkg(packageRoot)) toAdd.add(peer);
494
+ }
495
+ for (const pkgName of pkgNames) {
496
+ const packageRoot = flowOsRepoRoot ? join(flowOsRepoRoot, "packages", toShortName(pkgName)) : (() => { try { return pkgRoot(pkgName); } catch { return ""; } })();
497
+ if (packageRoot) for (const peer of flowDepsFromPkg(packageRoot)) toAdd.add(peer);
498
+ }
499
+ await ensureVersions(versions, [...toAdd]);
500
+ for (const pkgName of toAdd) {
501
+ addDependencyForPackage(pkgName, cwd, versions, useWorkspace);
386
502
  }
387
503
 
388
504
  onProgress?.("write");
@@ -390,7 +506,7 @@ export async function initLib(
390
506
 
391
507
  onProgress?.("install");
392
508
  const installCwd = useWorkspace && flowOsRepoRoot ? flowOsRepoRoot : cwd;
393
- const proc = Bun.spawn(["bun", "install"], { cwd: installCwd, stdout: "inherit", stderr: "inherit" });
509
+ const proc = Bun.spawn(["bun", "install"], { cwd: installCwd, stdout: "pipe", stderr: "pipe" });
394
510
  const exitCode = await proc.exited;
395
511
  if (exitCode !== 0) {
396
512
  throw new Error(`bun install exited with code ${exitCode}`);