@arcote.tech/arc-cli 0.5.5 → 0.5.7

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.
@@ -0,0 +1,26 @@
1
+ import { cpus } from "os";
2
+
3
+ export const DEFAULT_CONCURRENCY = Math.max(1, cpus().length - 1);
4
+
5
+ /** Run a list of async tasks with a bounded concurrency. Preserves task→result
6
+ * ordering. Rejects if any task rejects (other in-flight tasks still settle). */
7
+ export async function pAll<T>(
8
+ tasks: Array<() => Promise<T>>,
9
+ concurrency: number = DEFAULT_CONCURRENCY,
10
+ ): Promise<T[]> {
11
+ const results: T[] = new Array(tasks.length);
12
+ let nextIndex = 0;
13
+ const limit = Math.max(1, Math.min(concurrency, tasks.length));
14
+
15
+ async function worker() {
16
+ while (true) {
17
+ const i = nextIndex++;
18
+ if (i >= tasks.length) return;
19
+ results[i] = await tasks[i]();
20
+ }
21
+ }
22
+
23
+ const workers = Array.from({ length: limit }, () => worker());
24
+ await Promise.all(workers);
25
+ return results;
26
+ }
@@ -1,7 +1,7 @@
1
1
  import { buildAll, ok, resolveWorkspace } from "../platform/shared";
2
2
 
3
- export async function platformBuild(): Promise<void> {
3
+ export async function platformBuild(opts: { noCache?: boolean } = {}): Promise<void> {
4
4
  const ws = resolveWorkspace();
5
- const manifest = await buildAll(ws);
5
+ const manifest = await buildAll(ws, { noCache: opts.noCache });
6
6
  ok(`Platform built — ${manifest.modules.length} module(s)`);
7
7
  }
@@ -68,7 +68,7 @@ export async function platformDeploy(
68
68
  const needBuild = options.rebuild || !existsSync(manifestPath);
69
69
  if (needBuild && !options.skipBuild) {
70
70
  log("Building platform...");
71
- await buildAll(ws);
71
+ await buildAll(ws, { noCache: options.rebuild });
72
72
  ok("Build complete");
73
73
  } else if (!existsSync(manifestPath)) {
74
74
  err("No build found and --skip-build was set.");
@@ -1,11 +1,8 @@
1
1
  import { existsSync, watch } from "fs";
2
2
  import { join } from "path";
3
- import { compileAllCatalogs } from "../i18n/compile";
4
3
  import { startPlatformServer } from "../platform/server";
5
4
  import {
6
5
  buildAll,
7
- buildPackages,
8
- buildStyles,
9
6
  collectArcPeerDeps,
10
7
  loadServerContext,
11
8
  log,
@@ -13,14 +10,14 @@ import {
13
10
  resolveWorkspace,
14
11
  } from "../platform/shared";
15
12
 
16
- export async function platformDev(): Promise<void> {
13
+ export async function platformDev(opts: { noCache?: boolean } = {}): Promise<void> {
17
14
  const ws = resolveWorkspace();
18
15
  const port = 5005;
19
16
 
20
- // Build everything
21
- let manifest = await buildAll(ws);
17
+ // Initial build — `--no-cache` (if passed) only forces the startup pass;
18
+ // subsequent rebuilds always respect the cache to keep dev incremental.
19
+ let manifest = await buildAll(ws, { noCache: opts.noCache });
22
20
 
23
- // Load server context
24
21
  log("Loading server context...");
25
22
  const { context, moduleAccess } = await loadServerContext(ws.packages);
26
23
  if (context) {
@@ -29,7 +26,6 @@ export async function platformDev(): Promise<void> {
29
26
  log("No context — server endpoints skipped");
30
27
  }
31
28
 
32
- // Start server (dev mode = SSE reload + no-cache)
33
29
  const arcEntries = collectArcPeerDeps(ws.packages);
34
30
  const platform = await startPlatformServer({
35
31
  ws,
@@ -45,17 +41,36 @@ export async function platformDev(): Promise<void> {
45
41
  ok(`Server on http://localhost:${port}`);
46
42
  if (platform.contextHandler) ok("Commands, queries, WebSocket — all on same port");
47
43
 
48
- // Watch for changes
44
+ // Watch for changes — full buildAll on debounce; cache makes it cheap when
45
+ // only one package changed.
49
46
  log("Watching for changes...");
50
47
  let rebuildTimer: ReturnType<typeof setTimeout> | null = null;
51
48
  let isRebuilding = false;
52
49
 
50
+ const triggerRebuild = () => {
51
+ if (rebuildTimer) clearTimeout(rebuildTimer);
52
+ rebuildTimer = setTimeout(async () => {
53
+ if (isRebuilding) return;
54
+ isRebuilding = true;
55
+ log("Rebuilding...");
56
+ try {
57
+ manifest = await buildAll(ws);
58
+ platform.setManifest(manifest);
59
+ platform.notifyReload(manifest);
60
+ ok(`Rebuilt — ${manifest.modules.length} module(s)`);
61
+ } catch (e) {
62
+ console.error(`Rebuild failed: ${e}`);
63
+ } finally {
64
+ isRebuilding = false;
65
+ }
66
+ }, 300);
67
+ };
68
+
53
69
  for (const pkg of ws.packages) {
54
70
  const srcDir = join(pkg.path, "src");
55
71
  if (!existsSync(srcDir)) continue;
56
72
 
57
73
  watch(srcDir, { recursive: true }, (_event, filename) => {
58
- // Ignore build artifacts and non-source files
59
74
  if (
60
75
  !filename ||
61
76
  filename.includes(".arc") ||
@@ -64,53 +79,21 @@ export async function platformDev(): Promise<void> {
64
79
  filename.includes("dist")
65
80
  ) return;
66
81
 
67
- // Only rebuild for source file changes
68
82
  if (!filename.endsWith(".ts") && !filename.endsWith(".tsx")) return;
69
83
 
70
- if (rebuildTimer) clearTimeout(rebuildTimer);
71
- rebuildTimer = setTimeout(async () => {
72
- if (isRebuilding) return;
73
- isRebuilding = true;
74
- log("Rebuilding...");
75
- try {
76
- manifest = await buildPackages(
77
- ws.rootDir,
78
- ws.modulesDir,
79
- ws.packages,
80
- );
81
- await buildStyles(ws.rootDir, ws.arcDir);
82
- platform.setManifest(manifest);
83
- platform.notifyReload(manifest);
84
- ok(`Rebuilt ${manifest.modules.length} module(s)`);
85
- } catch (e) {
86
- console.error(`Rebuild failed: ${e}`);
87
- } finally {
88
- isRebuilding = false;
89
- }
90
- }, 300);
84
+ triggerRebuild();
91
85
  });
92
86
  }
93
87
 
94
- // Watch locales/*.po for translation changes recompile JSON
88
+ // .po files trigger rebuild so the `translations` cache unit picks them up.
95
89
  const localesDir = join(ws.rootDir, "locales");
96
90
  if (existsSync(localesDir)) {
97
- let poTimer: ReturnType<typeof setTimeout> | null = null;
98
91
  watch(localesDir, { recursive: false }, (_event, filename) => {
99
92
  if (!filename?.endsWith(".po")) return;
100
- if (poTimer) clearTimeout(poTimer);
101
- poTimer = setTimeout(async () => {
102
- try {
103
- compileAllCatalogs(localesDir, join(ws.arcDir, "locales"));
104
- ok("Translations recompiled");
105
- platform.notifyReload(manifest);
106
- } catch (e) {
107
- console.error(`Translation compile failed: ${e}`);
108
- }
109
- }, 200);
93
+ triggerRebuild();
110
94
  });
111
95
  }
112
96
 
113
- // Cleanup
114
97
  const cleanup = () => {
115
98
  platform.stop();
116
99
  process.exit(0);
package/src/index.ts CHANGED
@@ -33,12 +33,18 @@ const platform = program
33
33
  platform
34
34
  .command("dev")
35
35
  .description("Start platform in dev mode (Bun server + Vite HMR)")
36
- .action(platformDev);
36
+ .option("--no-cache", "Force full rebuild on startup")
37
+ .action((opts: { cache?: boolean }) =>
38
+ platformDev({ noCache: opts.cache === false }),
39
+ );
37
40
 
38
41
  platform
39
42
  .command("build")
40
43
  .description("Build platform for production")
41
- .action(platformBuild);
44
+ .option("--no-cache", "Force full rebuild")
45
+ .action((opts: { cache?: boolean }) =>
46
+ platformBuild({ noCache: opts.cache === false }),
47
+ );
42
48
 
43
49
  platform
44
50
  .command("start")
@@ -140,6 +140,9 @@ const MIME: Record<string, string> = {
140
140
  ".woff2": "font/woff2",
141
141
  ".woff": "font/woff",
142
142
  ".ttf": "font/ttf",
143
+ // application/wasm wymagane przez WebAssembly.compileStreaming() —
144
+ // przeglądarki rzucają "Incorrect response MIME type" inaczej.
145
+ ".wasm": "application/wasm",
143
146
  };
144
147
 
145
148
  function getMime(path: string): string {
@@ -287,6 +290,8 @@ function staticFilesHandler(
287
290
  }
288
291
  if (path.startsWith("/locales/"))
289
292
  return serveFile(join(ws.arcDir, path.slice(1)), ctx.corsHeaders);
293
+ if (path.startsWith("/assets/"))
294
+ return serveFile(join(ws.assetsDir, path.slice(8)), ctx.corsHeaders);
290
295
  if (path === "/styles.css")
291
296
  return serveFile(join(ws.arcDir, "styles.css"), ctx.corsHeaders);
292
297
  if (path === "/theme.css")
@@ -431,6 +436,11 @@ export async function startPlatformServer(
431
436
  "Access-Control-Allow-Methods":
432
437
  "GET, POST, PUT, DELETE, PATCH, OPTIONS",
433
438
  "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Arc-Tokens",
439
+ // Cross-origin isolation — wymagane dla SharedArrayBuffer + OPFS
440
+ // (SQLite WASM persistent storage w przeglądarce).
441
+ "Cross-Origin-Opener-Policy": "same-origin",
442
+ "Cross-Origin-Embedder-Policy": "require-corp",
443
+ "Cross-Origin-Resource-Policy": "cross-origin",
434
444
  };
435
445
 
436
446
  const server = Bun.serve({