@arcote.tech/arc-cli 0.7.26 → 0.7.28
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 +1254 -4209
- package/package.json +11 -10
- package/src/commands/platform-dev.ts +2 -3
- package/src/index.ts +3 -2
- package/src/platform/server.ts +31 -6
- package/src/platform/startup.ts +47 -2
- package/src/utils/build.ts +42 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcote.tech/arc-cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.28",
|
|
4
4
|
"description": "CLI tool for Arc framework",
|
|
5
5
|
"module": "index.ts",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
"arc": "./dist/index.js"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"build": "bun build --target=bun ./src/index.ts --outdir=dist --external @arcote.tech/arc --external @arcote.tech/arc-ds --external @arcote.tech/arc-react --external @arcote.tech/platform --external '@opentelemetry/*' && chmod +x dist/index.js"
|
|
12
|
+
"build": "bun build --target=bun ./src/index.ts --outdir=dist --external @arcote.tech/arc --external @arcote.tech/arc-ds --external @arcote.tech/arc-react --external @arcote.tech/platform --external @arcote.tech/arc-map --external '@opentelemetry/*' && chmod +x dist/index.js"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@arcote.tech/arc": "^0.7.
|
|
16
|
-
"@arcote.tech/arc-ds": "^0.7.
|
|
17
|
-
"@arcote.tech/arc-react": "^0.7.
|
|
18
|
-
"@arcote.tech/arc-host": "^0.7.
|
|
19
|
-
"@arcote.tech/arc-adapter-db-sqlite": "^0.7.
|
|
20
|
-
"@arcote.tech/arc-adapter-db-postgres": "^0.7.
|
|
21
|
-
"@arcote.tech/arc-otel": "^0.7.
|
|
15
|
+
"@arcote.tech/arc": "^0.7.28",
|
|
16
|
+
"@arcote.tech/arc-ds": "^0.7.28",
|
|
17
|
+
"@arcote.tech/arc-react": "^0.7.28",
|
|
18
|
+
"@arcote.tech/arc-host": "^0.7.28",
|
|
19
|
+
"@arcote.tech/arc-adapter-db-sqlite": "^0.7.28",
|
|
20
|
+
"@arcote.tech/arc-adapter-db-postgres": "^0.7.28",
|
|
21
|
+
"@arcote.tech/arc-otel": "^0.7.28",
|
|
22
22
|
"@opentelemetry/api": "^1.9.0",
|
|
23
23
|
"@opentelemetry/api-logs": "^0.57.0",
|
|
24
24
|
"@opentelemetry/core": "^1.30.0",
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"@opentelemetry/sdk-trace-base": "^1.30.0",
|
|
32
32
|
"@opentelemetry/sdk-trace-node": "^1.30.0",
|
|
33
33
|
"@opentelemetry/semantic-conventions": "^1.27.0",
|
|
34
|
-
"@arcote.tech/platform": "^0.7.
|
|
34
|
+
"@arcote.tech/platform": "^0.7.28",
|
|
35
|
+
"@arcote.tech/arc-map": "^0.7.28",
|
|
35
36
|
"@clack/prompts": "^0.9.0",
|
|
36
37
|
"commander": "^11.1.0",
|
|
37
38
|
"chokidar": "^3.5.3",
|
|
@@ -3,12 +3,11 @@ import { resolveWorkspace } from "../platform/shared";
|
|
|
3
3
|
|
|
4
4
|
/** `arc platform dev` — dev mode (watcher + SSE reload + no-cache headers). */
|
|
5
5
|
export async function platformDev(
|
|
6
|
-
opts: { noCache?: boolean } = {},
|
|
6
|
+
opts: { noCache?: boolean; map?: boolean } = {},
|
|
7
7
|
): Promise<void> {
|
|
8
8
|
const ws = resolveWorkspace();
|
|
9
9
|
// noCache is consumed by buildAll inside startPlatform when devMode=true.
|
|
10
10
|
// For now the dev startup always uses the cache after the first build;
|
|
11
11
|
// explicit --no-cache here only matters if we wire it through later.
|
|
12
|
-
|
|
13
|
-
await startPlatform({ ws, devMode: true });
|
|
12
|
+
await startPlatform({ ws, devMode: true, map: opts.map });
|
|
14
13
|
}
|
package/src/index.ts
CHANGED
|
@@ -34,8 +34,9 @@ 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
|
-
.
|
|
38
|
-
|
|
37
|
+
.option("--map", "Also start the Arc Context Map dev tool on a separate port")
|
|
38
|
+
.action((opts: { cache?: boolean; map?: boolean }) =>
|
|
39
|
+
platformDev({ noCache: opts.cache === false, map: opts.map }),
|
|
39
40
|
);
|
|
40
41
|
|
|
41
42
|
platform
|
package/src/platform/server.ts
CHANGED
|
@@ -134,8 +134,8 @@ export function generateShellHtml(
|
|
|
134
134
|
<html lang="en">
|
|
135
135
|
<head>
|
|
136
136
|
<meta charset="UTF-8" />
|
|
137
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
138
|
-
<title>${manifest?.title ?? appName}</title>${manifest?.favicon ? `\n <link rel="icon" href="${manifest.favicon}">` : ""}${manifest ? `\n <link rel="manifest" href="/manifest.json">` : ""}
|
|
137
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
|
138
|
+
<title>${manifest?.title ?? appName}</title>${manifest?.favicon ? `\n <link rel="icon" href="${manifest.favicon}">` : ""}${manifest ? `\n <link rel="manifest" href="/manifest.json${stylesQs}">` : ""}
|
|
139
139
|
<link rel="stylesheet" href="/styles.css${stylesQs}" />
|
|
140
140
|
<link rel="stylesheet" href="/theme.css${stylesQs}" />
|
|
141
141
|
<link rel="modulepreload" href="${initialUrl}" />${otelTag}
|
|
@@ -403,9 +403,15 @@ function staticFilesHandler(
|
|
|
403
403
|
"Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable",
|
|
404
404
|
});
|
|
405
405
|
|
|
406
|
-
// Serve manifest.json from root dir
|
|
406
|
+
// Serve manifest.json from root dir. Stable URL, content changes between
|
|
407
|
+
// builds — HTML references it with `?v=<stylesHash>` (generateShellHtml),
|
|
408
|
+
// so the URL changes on rebuild and immutable caching is safe. Handler
|
|
409
|
+
// ignores the query string itself (same pattern as /styles.css).
|
|
407
410
|
if ((path === "/manifest.json" || path === "/manifest.webmanifest") && ws.manifest) {
|
|
408
|
-
return serveFile(ws.manifest.path,
|
|
411
|
+
return serveFile(ws.manifest.path, {
|
|
412
|
+
...ctx.corsHeaders,
|
|
413
|
+
"Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable",
|
|
414
|
+
});
|
|
409
415
|
}
|
|
410
416
|
|
|
411
417
|
// Public files (files with extensions)
|
|
@@ -436,7 +442,18 @@ function apiEndpointsHandler(
|
|
|
436
442
|
payloads = [ctx.tokenPayload];
|
|
437
443
|
}
|
|
438
444
|
return filterManifestForTokens(getManifest(), moduleAccessMap, payloads)
|
|
439
|
-
.then((filtered) =>
|
|
445
|
+
.then((filtered) =>
|
|
446
|
+
Response.json(filtered, {
|
|
447
|
+
headers: {
|
|
448
|
+
...ctx.corsHeaders,
|
|
449
|
+
// Treść zależy od tokenu (filterManifestForTokens) i zmienia się
|
|
450
|
+
// co deploy. Nie jest artefaktem buildu — `private`, by pośredni
|
|
451
|
+
// cache nie podał manifestu jednego usera drugiemu, `no-cache`,
|
|
452
|
+
// by po deployu klient dostał świeże hashe/podpisy chunków.
|
|
453
|
+
"Cache-Control": "no-cache, private",
|
|
454
|
+
},
|
|
455
|
+
}),
|
|
456
|
+
);
|
|
440
457
|
}
|
|
441
458
|
|
|
442
459
|
if (url.pathname === "/api/translations") {
|
|
@@ -501,7 +518,15 @@ function devReloadHandler(
|
|
|
501
518
|
function spaFallbackHandler(getShellHtml: () => string): ArcHttpHandler {
|
|
502
519
|
return (_req, _url, ctx) => {
|
|
503
520
|
return new Response(getShellHtml(), {
|
|
504
|
-
headers: {
|
|
521
|
+
headers: {
|
|
522
|
+
...ctx.corsHeaders,
|
|
523
|
+
"Content-Type": "text/html",
|
|
524
|
+
// Stabilny URL, zmienna treść (embeduje initial.<hash>.js + styles ?v=).
|
|
525
|
+
// Kotwica całego łańcucha — musi być zawsze rewalidowana, inaczej po
|
|
526
|
+
// deployu przeglądarka/CDN podaje stary shell wskazujący na nieistniejące
|
|
527
|
+
// już bundle. Hash nie pomoże: to URL trasy, którego nie da się zahashować.
|
|
528
|
+
"Cache-Control": "no-cache, must-revalidate",
|
|
529
|
+
},
|
|
505
530
|
});
|
|
506
531
|
};
|
|
507
532
|
}
|
package/src/platform/startup.ts
CHANGED
|
@@ -35,6 +35,8 @@ export interface StartPlatformOptions {
|
|
|
35
35
|
dbPath?: string;
|
|
36
36
|
/** Dev mode: rebuild on startup, watch for changes, SSE-notify clients on rebuild. */
|
|
37
37
|
devMode: boolean;
|
|
38
|
+
/** Dev-only: also start the Arc Context Map tool on a separate port. */
|
|
39
|
+
map?: boolean;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
export async function startPlatform(
|
|
@@ -111,9 +113,16 @@ export async function startPlatform(
|
|
|
111
113
|
ok("Commands, queries, WebSocket — all on same port");
|
|
112
114
|
}
|
|
113
115
|
|
|
116
|
+
// 4a. Dev-only: optional Arc Context Map tool on a separate port, starting
|
|
117
|
+
// just above the platform's actual port (auto-increments if busy).
|
|
118
|
+
let onReload: (() => void) | undefined;
|
|
119
|
+
if (devMode && opts.map) {
|
|
120
|
+
onReload = await startMapTool(ws, actualPort + 1);
|
|
121
|
+
}
|
|
122
|
+
|
|
114
123
|
// 4. Dev-only: file watcher + debounced rebuild + SSE notify.
|
|
115
124
|
if (devMode) {
|
|
116
|
-
attachDevWatcher(ws, platform);
|
|
125
|
+
attachDevWatcher(ws, platform, onReload);
|
|
117
126
|
}
|
|
118
127
|
|
|
119
128
|
// 5. Graceful shutdown for both modes.
|
|
@@ -125,7 +134,42 @@ export async function startPlatform(
|
|
|
125
134
|
process.on("SIGINT", cleanup);
|
|
126
135
|
}
|
|
127
136
|
|
|
128
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Start the Arc Context Map dev tool on a separate port. Loaded lazily so the
|
|
139
|
+
* ts-morph dependency stays out of the normal `platform dev` path. Returns a
|
|
140
|
+
* reload hook to wire into the file watcher, or undefined on failure.
|
|
141
|
+
*/
|
|
142
|
+
async function startMapTool(
|
|
143
|
+
ws: WorkspaceInfo,
|
|
144
|
+
port: number,
|
|
145
|
+
): Promise<(() => void) | undefined> {
|
|
146
|
+
try {
|
|
147
|
+
const { startMapServer } = await import("@arcote.tech/arc-map");
|
|
148
|
+
const { getContext } = await import("@arcote.tech/platform");
|
|
149
|
+
const globs = ws.packages
|
|
150
|
+
.map((p) => join(p.path, "src", "**", "*.{ts,tsx}"));
|
|
151
|
+
const packageOf = (absFile: string): string | undefined =>
|
|
152
|
+
ws.packages.find((p) => absFile.startsWith(join(p.path, "src")))?.name;
|
|
153
|
+
|
|
154
|
+
const map = await startMapServer({
|
|
155
|
+
getContext: () => getContext(),
|
|
156
|
+
scan: { globs, workspaceRoot: ws.rootDir, packageOf },
|
|
157
|
+
useCasePackages: ws.packages.map((p) => ({ name: p.name, path: p.path })),
|
|
158
|
+
port,
|
|
159
|
+
});
|
|
160
|
+
ok(`Context map → ${map.url}`);
|
|
161
|
+
return map.notifyReload;
|
|
162
|
+
} catch (e) {
|
|
163
|
+
err(`Context map failed to start: ${(e as Error).message}`);
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function attachDevWatcher(
|
|
169
|
+
ws: WorkspaceInfo,
|
|
170
|
+
platform: PlatformServer,
|
|
171
|
+
onReload?: () => void,
|
|
172
|
+
): void {
|
|
129
173
|
log("Watching for changes...");
|
|
130
174
|
let rebuildTimer: ReturnType<typeof setTimeout> | null = null;
|
|
131
175
|
let isRebuilding = false;
|
|
@@ -140,6 +184,7 @@ function attachDevWatcher(ws: WorkspaceInfo, platform: PlatformServer): void {
|
|
|
140
184
|
const next = await buildAll(ws);
|
|
141
185
|
platform.setManifest(next);
|
|
142
186
|
platform.notifyReload(next);
|
|
187
|
+
onReload?.();
|
|
143
188
|
ok(
|
|
144
189
|
`Rebuilt — initial + ${Object.keys(next.groups).length} group(s)`,
|
|
145
190
|
);
|
package/src/utils/build.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
cpSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
rmSync,
|
|
8
|
+
unlinkSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "fs";
|
|
3
11
|
import { dirname, join } from "path";
|
|
4
12
|
import type { ArcConfig, ContextInfo } from "./config";
|
|
5
13
|
import { generateClientTypes } from "./config";
|
|
@@ -152,6 +160,37 @@ export interface DeclarationResult {
|
|
|
152
160
|
* @param globalsContent Optional ambient declarations (ONLY_SERVER etc.) —
|
|
153
161
|
* written to a temp .d.ts and included in compilation.
|
|
154
162
|
*/
|
|
163
|
+
/**
|
|
164
|
+
* Emituje deklaracje do katalogu TYMCZASOWEGO (siostra `outDir`), a po sukcesie
|
|
165
|
+
* scala je do `outDir`. Zapobiega TS5055 ("Cannot write file … would overwrite
|
|
166
|
+
* input file") gdy SAMO-REFERENCYJNY cykl importów pakietu (np. workspace →
|
|
167
|
+
* billing → workspace) wciąga własny `outDir/*.d.ts` do programu jako input —
|
|
168
|
+
* wtedy emisja wprost do `outDir` kolidowałaby z tym plikiem-inputem. Program ma
|
|
169
|
+
* `outDir` = temp, więc nie pisze po pliku czytanym jako wejście; świeże
|
|
170
|
+
* deklaracje przenosimy na miejsce dopiero po emisji.
|
|
171
|
+
*/
|
|
172
|
+
async function runDeclEmit(
|
|
173
|
+
files: string[],
|
|
174
|
+
outDir: string,
|
|
175
|
+
cwd: string,
|
|
176
|
+
): Promise<DeclarationResult> {
|
|
177
|
+
const tmpOut = `${outDir}.decltmp`;
|
|
178
|
+
rmSync(tmpOut, { recursive: true, force: true });
|
|
179
|
+
try {
|
|
180
|
+
let result = await runTsgo(files, tmpOut, cwd);
|
|
181
|
+
if (result === null) {
|
|
182
|
+
result = await runTsc(files, tmpOut, cwd);
|
|
183
|
+
}
|
|
184
|
+
if (result.success) {
|
|
185
|
+
mkdirSync(outDir, { recursive: true });
|
|
186
|
+
cpSync(tmpOut, outDir, { recursive: true });
|
|
187
|
+
}
|
|
188
|
+
return result;
|
|
189
|
+
} finally {
|
|
190
|
+
rmSync(tmpOut, { recursive: true, force: true });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
155
194
|
export async function buildTypeDeclarations(
|
|
156
195
|
files: string[],
|
|
157
196
|
outDir: string,
|
|
@@ -170,10 +209,7 @@ export async function buildTypeDeclarations(
|
|
|
170
209
|
allFiles.push(globalsPath);
|
|
171
210
|
}
|
|
172
211
|
|
|
173
|
-
|
|
174
|
-
if (result === null) {
|
|
175
|
-
result = await runTsc(allFiles, outDir, rootDir);
|
|
176
|
-
}
|
|
212
|
+
const result = await runDeclEmit(allFiles, outDir, rootDir);
|
|
177
213
|
|
|
178
214
|
// Clean up temp globals
|
|
179
215
|
if (globalsPath && existsSync(globalsPath)) {
|
|
@@ -204,11 +240,7 @@ export async function buildContextDeclarations(
|
|
|
204
240
|
? [context.fullPath, arcTypesPath]
|
|
205
241
|
: [context.fullPath];
|
|
206
242
|
|
|
207
|
-
|
|
208
|
-
if (result === null) {
|
|
209
|
-
result = await runTsc(files, outDir, configDir);
|
|
210
|
-
}
|
|
211
|
-
return result;
|
|
243
|
+
return runDeclEmit(files, outDir, configDir);
|
|
212
244
|
}
|
|
213
245
|
|
|
214
246
|
// ---------------------------------------------------------------------------
|