@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.
- package/dist/index.js +792 -428
- package/package.json +7 -7
- package/src/builder/build-cache.ts +79 -0
- package/src/builder/hash.ts +100 -0
- package/src/builder/module-builder.ts +392 -196
- package/src/builder/parallel.ts +26 -0
- package/src/commands/platform-build.ts +2 -2
- package/src/commands/platform-deploy.ts +1 -1
- package/src/commands/platform-dev.ts +28 -45
- package/src/index.ts +8 -2
- package/src/platform/server.ts +10 -0
- package/src/platform/shared.ts +349 -143
|
@@ -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
|
-
//
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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")
|
package/src/platform/server.ts
CHANGED
|
@@ -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({
|