@absolutejs/absolute 0.19.0-beta.169 → 0.19.0-beta.170
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/.absolutejs/prettier.cache.json +3 -3
- package/ROADMAP.md +582 -0
- package/dist/build.js +14 -2
- package/dist/build.js.map +3 -3
- package/dist/cli/index.js +713 -307
- package/dist/index.js +14 -2
- package/dist/index.js.map +3 -3
- package/dist/src/cli/scripts/compile.d.ts +1 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -17,6 +17,293 @@ var __export = (target, all) => {
|
|
|
17
17
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
18
18
|
var __require = import.meta.require;
|
|
19
19
|
|
|
20
|
+
// src/constants.ts
|
|
21
|
+
var ANSI_ESCAPE_LENGTH = 3, ASCII_SPACE = 32, BASE_36_RADIX = 36, BYTES_PER_KILOBYTE = 1024, CLI_ARGS_OFFSET = 3, DEFAULT_PORT = 3000, HOURS_IN_DAY = 24, HOURS_IN_HALF_DAY = 12, MAX_ERROR_LENGTH = 200, MILLISECONDS_IN_A_SECOND = 1000, MINUTES_IN_AN_HOUR = 60, SECONDS_IN_A_MINUTE = 60, MILLISECONDS_IN_A_MINUTE, MILLISECONDS_IN_A_DAY, SIGINT_EXIT_CODE = 130, SIGTERM_EXIT_CODE = 143, TIME_PRECISION = 2, TWO_THIRDS, UNFOUND_INDEX = -1;
|
|
22
|
+
var init_constants = __esm(() => {
|
|
23
|
+
MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
|
|
24
|
+
MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
|
|
25
|
+
TWO_THIRDS = 2 / 3;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// src/utils/getDurationString.ts
|
|
29
|
+
var getDurationString = (duration) => {
|
|
30
|
+
let durationString;
|
|
31
|
+
if (duration < MILLISECONDS_IN_A_SECOND) {
|
|
32
|
+
durationString = `${duration.toFixed(TIME_PRECISION)}ms`;
|
|
33
|
+
} else if (duration < MILLISECONDS_IN_A_MINUTE) {
|
|
34
|
+
durationString = `${(duration / MILLISECONDS_IN_A_SECOND).toFixed(TIME_PRECISION)}s`;
|
|
35
|
+
} else {
|
|
36
|
+
durationString = `${(duration / MILLISECONDS_IN_A_MINUTE).toFixed(TIME_PRECISION)}m`;
|
|
37
|
+
}
|
|
38
|
+
return durationString;
|
|
39
|
+
};
|
|
40
|
+
var init_getDurationString = __esm(() => {
|
|
41
|
+
init_constants();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// src/utils/startupBanner.ts
|
|
45
|
+
var MONTHS, formatTimestamp = () => {
|
|
46
|
+
const now = new Date;
|
|
47
|
+
const month = MONTHS[now.getMonth()];
|
|
48
|
+
const day = now.getDate().toString().padStart(2, "0");
|
|
49
|
+
let hours = now.getHours();
|
|
50
|
+
const minutes = now.getMinutes().toString().padStart(2, "0");
|
|
51
|
+
const seconds = now.getSeconds().toString().padStart(2, "0");
|
|
52
|
+
const ampm = hours >= HOURS_IN_HALF_DAY ? "PM" : "AM";
|
|
53
|
+
hours = hours % HOURS_IN_HALF_DAY || HOURS_IN_HALF_DAY;
|
|
54
|
+
return `${month} ${day} ${hours}:${minutes}:${seconds} ${ampm}`;
|
|
55
|
+
};
|
|
56
|
+
var init_startupBanner = __esm(() => {
|
|
57
|
+
init_constants();
|
|
58
|
+
init_getDurationString();
|
|
59
|
+
MONTHS = [
|
|
60
|
+
"Jan",
|
|
61
|
+
"Feb",
|
|
62
|
+
"Mar",
|
|
63
|
+
"Apr",
|
|
64
|
+
"May",
|
|
65
|
+
"Jun",
|
|
66
|
+
"Jul",
|
|
67
|
+
"Aug",
|
|
68
|
+
"Sep",
|
|
69
|
+
"Oct",
|
|
70
|
+
"Nov",
|
|
71
|
+
"Dec"
|
|
72
|
+
];
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// src/cli/scripts/telemetry.ts
|
|
76
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
77
|
+
import { homedir } from "os";
|
|
78
|
+
import { join } from "path";
|
|
79
|
+
var configDir, configPath, getTelemetryConfig = () => {
|
|
80
|
+
try {
|
|
81
|
+
if (!existsSync(configPath))
|
|
82
|
+
return null;
|
|
83
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
84
|
+
const config = JSON.parse(raw);
|
|
85
|
+
return config;
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}, saveTelemetryConfig = (config) => {
|
|
90
|
+
mkdirSync(configDir, { recursive: true });
|
|
91
|
+
writeFileSync(configPath, `${JSON.stringify(config, null, "\t")}
|
|
92
|
+
`);
|
|
93
|
+
}, enable = () => {
|
|
94
|
+
const existing = getTelemetryConfig();
|
|
95
|
+
const config = {
|
|
96
|
+
anonymousId: existing?.anonymousId ?? crypto.randomUUID(),
|
|
97
|
+
createdAt: existing?.createdAt ?? new Date().toISOString(),
|
|
98
|
+
enabled: true
|
|
99
|
+
};
|
|
100
|
+
saveTelemetryConfig(config);
|
|
101
|
+
console.log("Telemetry enabled.");
|
|
102
|
+
console.log(`Anonymous ID: ${config.anonymousId}`);
|
|
103
|
+
}, disable = () => {
|
|
104
|
+
const existing = getTelemetryConfig();
|
|
105
|
+
if (existing) {
|
|
106
|
+
saveTelemetryConfig({ ...existing, enabled: false });
|
|
107
|
+
}
|
|
108
|
+
console.log("Telemetry disabled.");
|
|
109
|
+
}, status = () => {
|
|
110
|
+
const config = getTelemetryConfig();
|
|
111
|
+
if (!config || !config.enabled) {
|
|
112
|
+
console.log("Telemetry is disabled.");
|
|
113
|
+
} else {
|
|
114
|
+
console.log("Telemetry is enabled.");
|
|
115
|
+
console.log(`Anonymous ID: ${config.anonymousId}`);
|
|
116
|
+
}
|
|
117
|
+
}, telemetry = (args) => {
|
|
118
|
+
const [subcommand] = args;
|
|
119
|
+
if (subcommand === "enable") {
|
|
120
|
+
enable();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (subcommand === "disable") {
|
|
124
|
+
disable();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (subcommand === "status") {
|
|
128
|
+
status();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (!subcommand) {
|
|
132
|
+
status();
|
|
133
|
+
console.log("");
|
|
134
|
+
console.log("Usage: absolute telemetry <command>");
|
|
135
|
+
console.log("Commands:");
|
|
136
|
+
console.log(" enable Enable anonymous telemetry");
|
|
137
|
+
console.log(" disable Disable telemetry");
|
|
138
|
+
console.log(" status Show current telemetry status");
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
console.error(`Unknown telemetry command: ${subcommand}`);
|
|
142
|
+
console.error("Usage: absolute telemetry <enable|disable|status>");
|
|
143
|
+
process.exit(1);
|
|
144
|
+
};
|
|
145
|
+
var init_telemetry = __esm(() => {
|
|
146
|
+
configDir = join(homedir(), ".absolutejs");
|
|
147
|
+
configPath = join(configDir, "telemetry.json");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// src/cli/telemetryEvent.ts
|
|
151
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
152
|
+
import { arch, platform } from "os";
|
|
153
|
+
import { dirname, join as join2, parse } from "path";
|
|
154
|
+
var checkCandidate = (candidate) => {
|
|
155
|
+
if (!existsSync2(candidate)) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
const pkg = JSON.parse(readFileSync2(candidate, "utf-8"));
|
|
159
|
+
if (pkg.name === "@absolutejs/absolute") {
|
|
160
|
+
const ver = pkg.version;
|
|
161
|
+
return ver;
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}, getVersion = () => {
|
|
165
|
+
try {
|
|
166
|
+
return findPackageVersion();
|
|
167
|
+
} catch {
|
|
168
|
+
return "unknown";
|
|
169
|
+
}
|
|
170
|
+
}, findPackageVersion = () => {
|
|
171
|
+
let { dir } = import.meta;
|
|
172
|
+
while (dir !== parse(dir).root) {
|
|
173
|
+
const candidate = join2(dir, "package.json");
|
|
174
|
+
const version = checkCandidate(candidate);
|
|
175
|
+
if (version) {
|
|
176
|
+
return version;
|
|
177
|
+
}
|
|
178
|
+
dir = dirname(dir);
|
|
179
|
+
}
|
|
180
|
+
return "unknown";
|
|
181
|
+
}, sendTelemetryEvent = (event, payload) => {
|
|
182
|
+
try {
|
|
183
|
+
if (process.env.TELEMETRY_OFF === "1")
|
|
184
|
+
return;
|
|
185
|
+
const config = getTelemetryConfig();
|
|
186
|
+
if (!config?.enabled)
|
|
187
|
+
return;
|
|
188
|
+
const body = {
|
|
189
|
+
anonymousId: config.anonymousId,
|
|
190
|
+
arch: arch(),
|
|
191
|
+
bunVersion: Bun.version,
|
|
192
|
+
event,
|
|
193
|
+
os: platform(),
|
|
194
|
+
payload,
|
|
195
|
+
timestamp: new Date().toISOString(),
|
|
196
|
+
version: getVersion()
|
|
197
|
+
};
|
|
198
|
+
fetch("https://absolutejs.com/api/telemetry", {
|
|
199
|
+
body: JSON.stringify(body),
|
|
200
|
+
headers: { "Content-Type": "application/json" },
|
|
201
|
+
method: "POST"
|
|
202
|
+
}).catch(() => {
|
|
203
|
+
return;
|
|
204
|
+
});
|
|
205
|
+
} catch {}
|
|
206
|
+
};
|
|
207
|
+
var init_telemetryEvent = __esm(() => {
|
|
208
|
+
init_telemetry();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// src/utils/loadConfig.ts
|
|
212
|
+
import { resolve } from "path";
|
|
213
|
+
var loadConfig = async (configPath2) => {
|
|
214
|
+
const resolved = resolve(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts");
|
|
215
|
+
const mod = await import(resolved);
|
|
216
|
+
const config = mod.default ?? mod.config;
|
|
217
|
+
if (!config) {
|
|
218
|
+
throw new Error(`Config file "${resolved}" does not export a valid configuration.
|
|
219
|
+
Expected: export default defineConfig({ ... })`);
|
|
220
|
+
}
|
|
221
|
+
return config;
|
|
222
|
+
};
|
|
223
|
+
var init_loadConfig = () => {};
|
|
224
|
+
|
|
225
|
+
// src/cli/utils.ts
|
|
226
|
+
var {$ } = globalThis.Bun;
|
|
227
|
+
import { execSync } from "child_process";
|
|
228
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
229
|
+
import { resolve as resolve2 } from "path";
|
|
230
|
+
var COMPOSE_PATH = "db/docker-compose.db.yml", DEFAULT_SERVER_ENTRY = "src/backend/server.ts", isWSLEnvironment = () => {
|
|
231
|
+
try {
|
|
232
|
+
const release = readFileSync3("/proc/version", "utf-8");
|
|
233
|
+
return /microsoft|wsl/i.test(release);
|
|
234
|
+
} catch {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}, safeKill = (pid) => {
|
|
238
|
+
try {
|
|
239
|
+
process.kill(pid, "SIGTERM");
|
|
240
|
+
} catch {}
|
|
241
|
+
}, killStaleProcesses = (port) => {
|
|
242
|
+
let output;
|
|
243
|
+
try {
|
|
244
|
+
output = execSync(`lsof -ti tcp:${port} -sTCP:LISTEN 2>/dev/null`, {
|
|
245
|
+
encoding: "utf-8"
|
|
246
|
+
}).trim();
|
|
247
|
+
} catch {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (!output) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const pids = output.split(`
|
|
254
|
+
`).map(Number).filter((pid) => pid !== process.pid && pid > 0);
|
|
255
|
+
if (pids.length === 0) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
pids.forEach(safeKill);
|
|
259
|
+
console.log(`\x1B[2m${formatTimestamp()}\x1B[0m \x1B[33m[cli]\x1B[0m \x1B[33mKilled ${pids.length} stale ${pids.length === 1 ? "process" : "processes"} on port ${port}.\x1B[0m`);
|
|
260
|
+
}, printHelp = () => {
|
|
261
|
+
console.log("");
|
|
262
|
+
console.log("\x1B[1mShortcuts:\x1B[0m");
|
|
263
|
+
console.log(" \x1B[36mr\x1B[0m / restart \u2014 Restart server");
|
|
264
|
+
console.log(" \x1B[36mp\x1B[0m / pause \u2014 Pause/resume server");
|
|
265
|
+
console.log(" \x1B[36mo\x1B[0m / open \u2014 Open in browser");
|
|
266
|
+
console.log(" \x1B[36mc\x1B[0m / clear \u2014 Clear terminal");
|
|
267
|
+
console.log(" \x1B[36mq\x1B[0m / quit \u2014 Graceful shutdown");
|
|
268
|
+
console.log(" \x1B[36mh\x1B[0m / help \u2014 Show this help");
|
|
269
|
+
console.log(" \x1B[36m$\x1B[0m \u2014 Run a shell command");
|
|
270
|
+
console.log(" \x1B[36m\u2191\x1B[0m / \x1B[36m\u2193\x1B[0m \u2014 Command history");
|
|
271
|
+
console.log("");
|
|
272
|
+
}, printHint = () => {
|
|
273
|
+
console.log("\x1B[90mpress h + enter to show shortcuts\x1B[0m");
|
|
274
|
+
}, readDbScripts = async () => {
|
|
275
|
+
const pkgPath = resolve2("package.json");
|
|
276
|
+
if (!existsSync3(pkgPath))
|
|
277
|
+
return null;
|
|
278
|
+
const pkg = await Bun.file(pkgPath).json();
|
|
279
|
+
const upCommand = pkg.scripts?.["db:up"];
|
|
280
|
+
const downCommand = pkg.scripts?.["db:down"];
|
|
281
|
+
if (!upCommand || !downCommand)
|
|
282
|
+
return null;
|
|
283
|
+
return { downCommand, upCommand };
|
|
284
|
+
}, startDatabase = async (scripts) => {
|
|
285
|
+
await timed("Starting database container...", async () => {
|
|
286
|
+
const { exitCode } = await $`${{ raw: scripts.upCommand }}`.quiet().nothrow();
|
|
287
|
+
if (exitCode !== 0)
|
|
288
|
+
process.exit(exitCode);
|
|
289
|
+
});
|
|
290
|
+
}, stopDatabase = async (scripts) => {
|
|
291
|
+
console.log(`
|
|
292
|
+
Stopping database container...`);
|
|
293
|
+
await $`${{ raw: scripts.downCommand }}`.quiet().nothrow();
|
|
294
|
+
}, timed = async (label, task) => {
|
|
295
|
+
process.stdout.write(label);
|
|
296
|
+
const start = performance.now();
|
|
297
|
+
await task();
|
|
298
|
+
const duration = ((performance.now() - start) / MILLISECONDS_IN_A_SECOND).toFixed(2);
|
|
299
|
+
process.stdout.write(` \x1B[90m${duration}s\x1B[0m
|
|
300
|
+
`);
|
|
301
|
+
};
|
|
302
|
+
var init_utils = __esm(() => {
|
|
303
|
+
init_constants();
|
|
304
|
+
init_startupBanner();
|
|
305
|
+
});
|
|
306
|
+
|
|
20
307
|
// src/dev/devCert.ts
|
|
21
308
|
var exports_devCert = {};
|
|
22
309
|
__export(exports_devCert, {
|
|
@@ -233,79 +520,414 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => c
|
|
|
233
520
|
devLog("mkcert installed \u2014 HTTPS certificates are now locally trusted");
|
|
234
521
|
return true;
|
|
235
522
|
};
|
|
236
|
-
var init_devCert = __esm(() => {
|
|
237
|
-
CERT_DIR = join3(process.cwd(), ".absolutejs");
|
|
238
|
-
CERT_PATH = join3(CERT_DIR, "cert.pem");
|
|
239
|
-
KEY_PATH = join3(CERT_DIR, "key.pem");
|
|
523
|
+
var init_devCert = __esm(() => {
|
|
524
|
+
CERT_DIR = join3(process.cwd(), ".absolutejs");
|
|
525
|
+
CERT_PATH = join3(CERT_DIR, "cert.pem");
|
|
526
|
+
KEY_PATH = join3(CERT_DIR, "key.pem");
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// src/cli/scripts/compile.ts
|
|
530
|
+
var exports_compile = {};
|
|
531
|
+
__export(exports_compile, {
|
|
532
|
+
compile: () => compile
|
|
533
|
+
});
|
|
534
|
+
var {env: env3 } = globalThis.Bun;
|
|
535
|
+
import {
|
|
536
|
+
existsSync as existsSync8,
|
|
537
|
+
mkdirSync as mkdirSync3,
|
|
538
|
+
readdirSync,
|
|
539
|
+
readFileSync as readFileSync7,
|
|
540
|
+
unlinkSync
|
|
541
|
+
} from "fs";
|
|
542
|
+
import { basename as basename2, join as join5, relative, resolve as resolve6 } from "path";
|
|
543
|
+
var cliTag3 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`, collectFiles2 = (dir) => {
|
|
544
|
+
const results = [];
|
|
545
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
546
|
+
const fullPath = join5(dir, entry.name);
|
|
547
|
+
if (entry.isDirectory()) {
|
|
548
|
+
results.push(...collectFiles2(fullPath));
|
|
549
|
+
} else {
|
|
550
|
+
results.push(fullPath);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return results;
|
|
554
|
+
}, resolvePackageVersion2 = (candidates) => {
|
|
555
|
+
for (const candidate of candidates) {
|
|
556
|
+
try {
|
|
557
|
+
const pkg = JSON.parse(readFileSync7(candidate, "utf-8"));
|
|
558
|
+
if (pkg.name === "@absolutejs/absolute")
|
|
559
|
+
return pkg.version;
|
|
560
|
+
} catch {}
|
|
561
|
+
}
|
|
562
|
+
return "";
|
|
563
|
+
}, resolveBuildModule2 = async (candidates) => {
|
|
564
|
+
for (const candidate of candidates) {
|
|
565
|
+
try {
|
|
566
|
+
const mod = await import(candidate);
|
|
567
|
+
return mod.build;
|
|
568
|
+
} catch {}
|
|
569
|
+
}
|
|
570
|
+
return;
|
|
571
|
+
}, prerender = async (port, outDir) => {
|
|
572
|
+
const prerenderDir = join5(outDir, "_prerendered");
|
|
573
|
+
mkdirSync3(prerenderDir, { recursive: true });
|
|
574
|
+
const baseUrl = `http://localhost:${port}`;
|
|
575
|
+
const visited = new Set;
|
|
576
|
+
const queue = ["/"];
|
|
577
|
+
const savedFiles = [];
|
|
578
|
+
while (queue.length > 0) {
|
|
579
|
+
const path = queue.shift();
|
|
580
|
+
if (visited.has(path))
|
|
581
|
+
continue;
|
|
582
|
+
visited.add(path);
|
|
583
|
+
try {
|
|
584
|
+
const res = await fetch(`${baseUrl}${path}`);
|
|
585
|
+
if (!res.ok)
|
|
586
|
+
continue;
|
|
587
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
588
|
+
if (!contentType.includes("text/html"))
|
|
589
|
+
continue;
|
|
590
|
+
const html = await res.text();
|
|
591
|
+
const fileName = path === "/" ? "index.html" : `${path.slice(1).replace(/\//g, "-")}.html`;
|
|
592
|
+
const filePath = join5(prerenderDir, fileName);
|
|
593
|
+
await Bun.write(filePath, html);
|
|
594
|
+
savedFiles.push(filePath);
|
|
595
|
+
console.log(cliTag3("\x1B[36m", ` Pre-rendered ${path} \u2192 ${fileName} (${html.length} bytes)`));
|
|
596
|
+
const linkRegex = /href=["'](\/[^"']*?)["']/g;
|
|
597
|
+
let match;
|
|
598
|
+
while ((match = linkRegex.exec(html)) !== null) {
|
|
599
|
+
const href = match[1] ?? "";
|
|
600
|
+
if (!href || href.includes(".") || href.includes("#") || visited.has(href))
|
|
601
|
+
continue;
|
|
602
|
+
queue.push(href);
|
|
603
|
+
}
|
|
604
|
+
} catch {}
|
|
605
|
+
}
|
|
606
|
+
return savedFiles;
|
|
607
|
+
}, generateEntrypoint = (distDir, serverEntry, prerenderMap) => {
|
|
608
|
+
const allFiles = collectFiles2(distDir);
|
|
609
|
+
const serverBundleName = basename2(serverEntry).replace(/\.[^.]+$/, "") + ".js";
|
|
610
|
+
const skip = new Set([
|
|
611
|
+
serverBundleName,
|
|
612
|
+
"manifest.json",
|
|
613
|
+
"_compile_entrypoint.ts"
|
|
614
|
+
]);
|
|
615
|
+
const clientFiles = allFiles.filter((f) => {
|
|
616
|
+
const rel = relative(distDir, f);
|
|
617
|
+
if (skip.has(rel))
|
|
618
|
+
return false;
|
|
619
|
+
if (rel.includes(".generated"))
|
|
620
|
+
return false;
|
|
621
|
+
if (rel.includes("/server/"))
|
|
622
|
+
return false;
|
|
623
|
+
return true;
|
|
624
|
+
});
|
|
625
|
+
const imports = [];
|
|
626
|
+
const mappings = [];
|
|
627
|
+
clientFiles.forEach((filePath, idx) => {
|
|
628
|
+
const rel = relative(distDir, filePath).replace(/\\/g, "/");
|
|
629
|
+
const varName = `__a${idx}`;
|
|
630
|
+
const urlPath = "/" + rel;
|
|
631
|
+
imports.push(`import ${varName} from "./${rel}" with { type: "file" };`);
|
|
632
|
+
mappings.push(` "${urlPath}": ${varName},`);
|
|
633
|
+
if (rel.startsWith("workers/") && rel.endsWith(".js")) {
|
|
634
|
+
const parts = rel.match(/^(workers\/[^.]+\.worker)\.[a-z0-9]+\.js$/);
|
|
635
|
+
if (parts) {
|
|
636
|
+
mappings.push(` "/${parts[1]}.js": ${varName},`);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
const pageVarMap = new Map;
|
|
641
|
+
for (const [route, filePath] of prerenderMap) {
|
|
642
|
+
const rel = relative(distDir, filePath).replace(/\\/g, "/");
|
|
643
|
+
const idx = clientFiles.findIndex((f) => relative(distDir, f).replace(/\\/g, "/") === rel);
|
|
644
|
+
if (idx >= 0) {
|
|
645
|
+
pageVarMap.set(route, `__a${idx}`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
const routeEntries = Array.from(pageVarMap.entries()).map(([route, varName]) => ` "${route}": ${varName},`).join(`
|
|
649
|
+
`);
|
|
650
|
+
return `// Auto-generated compile entrypoint
|
|
651
|
+
import { Elysia } from "elysia";
|
|
652
|
+
|
|
653
|
+
// \u2500\u2500 Embedded asset imports \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
654
|
+
${imports.join(`
|
|
655
|
+
`)}
|
|
656
|
+
|
|
657
|
+
// \u2500\u2500 Asset URL \u2192 embedded path map \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
658
|
+
const ASSETS: Record<string, string> = {
|
|
659
|
+
${mappings.join(`
|
|
660
|
+
`)}
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
// \u2500\u2500 Pre-rendered page routes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
664
|
+
const PAGES: Record<string, string> = {
|
|
665
|
+
${routeEntries}
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
// \u2500\u2500 MIME types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
669
|
+
const MIME: Record<string, string> = {
|
|
670
|
+
".js": "application/javascript; charset=utf-8",
|
|
671
|
+
".css": "text/css; charset=utf-8",
|
|
672
|
+
".html": "text/html; charset=utf-8",
|
|
673
|
+
".json": "application/json",
|
|
674
|
+
".png": "image/png",
|
|
675
|
+
".jpg": "image/jpeg",
|
|
676
|
+
".svg": "image/svg+xml",
|
|
677
|
+
".ico": "image/x-icon",
|
|
678
|
+
".woff": "font/woff",
|
|
679
|
+
".woff2": "font/woff2",
|
|
680
|
+
".ttf": "font/ttf",
|
|
681
|
+
".webp": "image/webp",
|
|
682
|
+
".avif": "image/avif",
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
const getMime = (p: string) =>
|
|
686
|
+
MIME[p.substring(p.lastIndexOf("."))] ?? "application/octet-stream";
|
|
687
|
+
|
|
688
|
+
// \u2500\u2500 Server \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
689
|
+
const port = Number(process.env.PORT) || ${DEFAULT_PORT};
|
|
690
|
+
|
|
691
|
+
const servePage = (path: string) =>
|
|
692
|
+
new Response(Bun.file(path), {
|
|
693
|
+
headers: { "content-type": "text/html; charset=utf-8" },
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
const app = new Elysia()
|
|
697
|
+
// Static assets from embedded filesystem
|
|
698
|
+
.onRequest(({ request, set }) => {
|
|
699
|
+
const url = new URL(request.url);
|
|
700
|
+
|
|
701
|
+
// Check for pre-rendered page
|
|
702
|
+
const page = PAGES[url.pathname];
|
|
703
|
+
if (page) return servePage(page);
|
|
704
|
+
|
|
705
|
+
// Check for embedded asset
|
|
706
|
+
const embedded = ASSETS[url.pathname];
|
|
707
|
+
if (!embedded) return;
|
|
708
|
+
set.headers["content-type"] = getMime(url.pathname);
|
|
709
|
+
set.headers["cache-control"] = "public, max-age=31536000, immutable";
|
|
710
|
+
return new Response(Bun.file(embedded));
|
|
711
|
+
})
|
|
712
|
+
.listen(port);
|
|
713
|
+
|
|
714
|
+
const assetCount = Object.keys(ASSETS).length;
|
|
715
|
+
const pageCount = Object.keys(PAGES).length;
|
|
716
|
+
console.log(\`
|
|
717
|
+
\\x1b[36m\\x1b[1mABSOLUTEJS\\x1b[0m \\x1b[2mcompiled executable\\x1b[0m
|
|
718
|
+
|
|
719
|
+
\\x1b[32m\u279C\\x1b[0m \\x1b[1mLocal:\\x1b[0m http://localhost:\${port}/
|
|
720
|
+
|
|
721
|
+
\\x1b[2m\${pageCount} pre-rendered pages, \${assetCount} embedded assets\\x1b[0m
|
|
722
|
+
\\x1b[2mNo external files needed\\x1b[0m
|
|
723
|
+
\`);
|
|
724
|
+
`;
|
|
725
|
+
}, createStubPlugin = () => ({
|
|
726
|
+
name: "stub-framework-sources",
|
|
727
|
+
setup(bld) {
|
|
728
|
+
bld.onLoad({ filter: /\.(svelte|vue)$/ }, () => ({
|
|
729
|
+
contents: "export default {}",
|
|
730
|
+
loader: "js"
|
|
731
|
+
}));
|
|
732
|
+
bld.onLoad({ filter: /devBuild\.ts$/ }, () => ({
|
|
733
|
+
contents: "export const devBuild = () => {}",
|
|
734
|
+
loader: "js"
|
|
735
|
+
}));
|
|
736
|
+
bld.onLoad({ filter: /core\/build\.ts$/ }, () => ({
|
|
737
|
+
contents: "export const build = () => ({})",
|
|
738
|
+
loader: "js"
|
|
739
|
+
}));
|
|
740
|
+
bld.onLoad({ filter: /src\/build\.ts$/ }, () => ({
|
|
741
|
+
contents: "export const build = () => ({}); export const devBuild = () => {};",
|
|
742
|
+
loader: "js"
|
|
743
|
+
}));
|
|
744
|
+
bld.onLoad({ filter: /plugins\/hmr\.ts$/ }, () => ({
|
|
745
|
+
contents: "export const hmr = () => (app) => app;",
|
|
746
|
+
loader: "js"
|
|
747
|
+
}));
|
|
748
|
+
bld.onLoad({
|
|
749
|
+
filter: /dev\/(assetStore|clientManager|webSocket|moduleVersionTracker|buildHMRClient)\.ts$/
|
|
750
|
+
}, () => ({ contents: "export {};", loader: "js" }));
|
|
751
|
+
bld.onLoad({ filter: /cli\/(telemetryEvent|scripts\/telemetry)\.ts$/ }, () => ({
|
|
752
|
+
contents: "export const sendTelemetryEvent = () => {}; export const getTelemetryConfig = () => null; export const telemetry = () => {};",
|
|
753
|
+
loader: "js"
|
|
754
|
+
}));
|
|
755
|
+
bld.onLoad({
|
|
756
|
+
filter: /react-dom-server-legacy\.browser\.(production|development)\.js$/
|
|
757
|
+
}, () => ({
|
|
758
|
+
contents: "exports.renderToString = undefined; exports.renderToStaticMarkup = undefined;",
|
|
759
|
+
loader: "js"
|
|
760
|
+
}));
|
|
761
|
+
bld.onLoad({ filter: /node_modules\/debug/ }, () => ({
|
|
762
|
+
contents: "module.exports = () => { const noop = () => {}; noop.enabled = false; return noop; }; module.exports.enable = () => {}; module.exports.disable = () => {}; module.exports.enabled = () => false;",
|
|
763
|
+
loader: "js"
|
|
764
|
+
}));
|
|
765
|
+
bld.onLoad({ filter: /\.ts$/ }, async (args) => {
|
|
766
|
+
if (args.path.includes("node_modules"))
|
|
767
|
+
return;
|
|
768
|
+
const text = await Bun.file(args.path).text();
|
|
769
|
+
const stripped = text.replace(/`(?:[^`\\]|\\.)*`/gs, "").replace(/'(?:[^'\\]|\\.)*'/g, "").replace(/"(?:[^"\\]|\\.)*"/g, "");
|
|
770
|
+
if (stripped.includes("@Component")) {
|
|
771
|
+
return { contents: "export default {}", loader: "js" };
|
|
772
|
+
}
|
|
773
|
+
return;
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
}), FRAMEWORK_EXTERNALS, compile = async (serverEntry, outdir, outfile, configPath2) => {
|
|
777
|
+
const prerenderPort = Number(env3.COMPILE_PORT) || Number(env3.PORT) || DEFAULT_PORT + 1;
|
|
778
|
+
killStaleProcesses(prerenderPort);
|
|
779
|
+
const entryName = basename2(serverEntry).replace(/\.[^.]+$/, "");
|
|
780
|
+
const resolvedOutdir = resolve6(outdir ?? "dist");
|
|
781
|
+
const resolvedOutfile = resolve6(outfile ?? "compiled-server");
|
|
782
|
+
const absoluteVersion = resolvePackageVersion2([
|
|
783
|
+
resolve6(import.meta.dir, "..", "..", "..", "package.json"),
|
|
784
|
+
resolve6(import.meta.dir, "..", "..", "package.json")
|
|
785
|
+
]);
|
|
786
|
+
const totalStart = performance.now();
|
|
787
|
+
const buildStart = performance.now();
|
|
788
|
+
process.stdout.write(cliTag3("\x1B[36m", "Building assets"));
|
|
789
|
+
const buildConfig = await loadConfig(configPath2);
|
|
790
|
+
buildConfig.buildDirectory = resolvedOutdir;
|
|
791
|
+
buildConfig.mode = "production";
|
|
792
|
+
try {
|
|
793
|
+
const build = await resolveBuildModule2([
|
|
794
|
+
resolve6(import.meta.dir, "..", "..", "core", "build"),
|
|
795
|
+
resolve6(import.meta.dir, "..", "build")
|
|
796
|
+
]);
|
|
797
|
+
if (!build)
|
|
798
|
+
throw new Error("Could not locate build module");
|
|
799
|
+
await build(buildConfig);
|
|
800
|
+
} catch (err) {
|
|
801
|
+
console.error(cliTag3("\x1B[31m", "Build step failed."));
|
|
802
|
+
console.error(err);
|
|
803
|
+
process.exit(1);
|
|
804
|
+
}
|
|
805
|
+
console.log(` \x1B[2m(${getDurationString(performance.now() - buildStart)})\x1B[0m`);
|
|
806
|
+
const bundleStart = performance.now();
|
|
807
|
+
process.stdout.write(cliTag3("\x1B[36m", "Bundling production server"));
|
|
808
|
+
const serverBundle = await Bun.build({
|
|
809
|
+
define: { "process.env.NODE_ENV": '"production"' },
|
|
810
|
+
entrypoints: [resolve6(serverEntry)],
|
|
811
|
+
external: FRAMEWORK_EXTERNALS,
|
|
812
|
+
outdir: resolvedOutdir,
|
|
813
|
+
plugins: [createStubPlugin()],
|
|
814
|
+
target: "bun"
|
|
815
|
+
});
|
|
816
|
+
if (!serverBundle.success) {
|
|
817
|
+
serverBundle.logs.forEach((log) => console.error(log));
|
|
818
|
+
console.error(cliTag3("\x1B[31m", "Server bundle failed."));
|
|
819
|
+
process.exit(1);
|
|
820
|
+
}
|
|
821
|
+
const outputPath = resolve6(resolvedOutdir, `${entryName}.js`);
|
|
822
|
+
if (!existsSync8(outputPath)) {
|
|
823
|
+
console.error(cliTag3("\x1B[31m", `Expected output not found: ${outputPath}`));
|
|
824
|
+
process.exit(1);
|
|
825
|
+
}
|
|
826
|
+
console.log(` \x1B[2m(${getDurationString(performance.now() - bundleStart)})\x1B[0m`);
|
|
827
|
+
const prerenderStart = performance.now();
|
|
828
|
+
console.log(cliTag3("\x1B[36m", "Pre-rendering pages..."));
|
|
829
|
+
const serverProcess = Bun.spawn(["bun", "run", outputPath], {
|
|
830
|
+
cwd: process.cwd(),
|
|
831
|
+
env: {
|
|
832
|
+
...process.env,
|
|
833
|
+
ABSOLUTE_BUILD_DIR: resolvedOutdir,
|
|
834
|
+
ABSOLUTE_VERSION: absoluteVersion,
|
|
835
|
+
FORCE_COLOR: "0",
|
|
836
|
+
NODE_ENV: "production",
|
|
837
|
+
PORT: String(prerenderPort),
|
|
838
|
+
...configPath2 ? { ABSOLUTE_CONFIG: configPath2 } : {}
|
|
839
|
+
},
|
|
840
|
+
stdout: "pipe",
|
|
841
|
+
stderr: "pipe"
|
|
842
|
+
});
|
|
843
|
+
let ready = false;
|
|
844
|
+
for (let i = 0;i < 50; i++) {
|
|
845
|
+
try {
|
|
846
|
+
const res = await fetch(`http://localhost:${prerenderPort}/`);
|
|
847
|
+
if (res.ok) {
|
|
848
|
+
ready = true;
|
|
849
|
+
break;
|
|
850
|
+
}
|
|
851
|
+
} catch {}
|
|
852
|
+
await Bun.sleep(100);
|
|
853
|
+
}
|
|
854
|
+
if (!ready) {
|
|
855
|
+
serverProcess.kill();
|
|
856
|
+
console.error(cliTag3("\x1B[31m", "Server failed to start for pre-rendering."));
|
|
857
|
+
process.exit(1);
|
|
858
|
+
}
|
|
859
|
+
const prerenderFiles = await prerender(prerenderPort, resolvedOutdir);
|
|
860
|
+
serverProcess.kill();
|
|
861
|
+
await serverProcess.exited;
|
|
862
|
+
const prerenderMap = new Map;
|
|
863
|
+
for (const filePath of prerenderFiles) {
|
|
864
|
+
const fileName = basename2(filePath, ".html");
|
|
865
|
+
const route = fileName === "index" ? "/" : `/${fileName}`;
|
|
866
|
+
prerenderMap.set(route, filePath);
|
|
867
|
+
}
|
|
868
|
+
console.log(cliTag3("\x1B[36m", `Pre-rendered ${prerenderMap.size} pages (${getDurationString(performance.now() - prerenderStart)})`));
|
|
869
|
+
const compileStart = performance.now();
|
|
870
|
+
process.stdout.write(cliTag3("\x1B[36m", "Compiling standalone executable"));
|
|
871
|
+
const entrypointCode = generateEntrypoint(resolvedOutdir, serverEntry, prerenderMap);
|
|
872
|
+
const entrypointPath = join5(resolvedOutdir, "_compile_entrypoint.ts");
|
|
873
|
+
await Bun.write(entrypointPath, entrypointCode);
|
|
874
|
+
const result = await Bun.build({
|
|
875
|
+
entrypoints: [entrypointPath],
|
|
876
|
+
compile: { outfile: resolvedOutfile },
|
|
877
|
+
define: { "process.env.NODE_ENV": '"production"' },
|
|
878
|
+
target: "bun"
|
|
879
|
+
});
|
|
880
|
+
if (!result.success) {
|
|
881
|
+
result.logs.forEach((log) => console.error(log));
|
|
882
|
+
console.error(cliTag3("\x1B[31m", "Compilation failed."));
|
|
883
|
+
process.exit(1);
|
|
884
|
+
}
|
|
885
|
+
console.log(` \x1B[2m(${getDurationString(performance.now() - compileStart)})\x1B[0m`);
|
|
886
|
+
try {
|
|
887
|
+
unlinkSync(entrypointPath);
|
|
888
|
+
} catch {}
|
|
889
|
+
const size = (Bun.file(resolvedOutfile).size / 1048576).toFixed(0);
|
|
890
|
+
const totalDuration = getDurationString(performance.now() - totalStart);
|
|
891
|
+
console.log(cliTag3("\x1B[32m", `Compiled to ${resolvedOutfile} (${size}MB) in ${totalDuration}`));
|
|
892
|
+
console.log(cliTag3("\x1B[2m", `Run with: ./${basename2(resolvedOutfile)}`));
|
|
893
|
+
sendTelemetryEvent("compile:complete", {
|
|
894
|
+
durationMs: Math.round(performance.now() - totalStart),
|
|
895
|
+
entry: serverEntry,
|
|
896
|
+
pages: prerenderMap.size
|
|
897
|
+
});
|
|
898
|
+
};
|
|
899
|
+
var init_compile = __esm(() => {
|
|
900
|
+
init_constants();
|
|
901
|
+
init_getDurationString();
|
|
902
|
+
init_loadConfig();
|
|
903
|
+
init_startupBanner();
|
|
904
|
+
init_telemetryEvent();
|
|
905
|
+
init_utils();
|
|
906
|
+
FRAMEWORK_EXTERNALS = [
|
|
907
|
+
"vue",
|
|
908
|
+
"vue/*",
|
|
909
|
+
"@vue/compiler-sfc",
|
|
910
|
+
"@vue/server-renderer",
|
|
911
|
+
"svelte",
|
|
912
|
+
"svelte/*",
|
|
913
|
+
"@angular/compiler",
|
|
914
|
+
"@angular/compiler-cli",
|
|
915
|
+
"@angular/core",
|
|
916
|
+
"@angular/common",
|
|
917
|
+
"@angular/platform-browser",
|
|
918
|
+
"@angular/platform-server"
|
|
919
|
+
];
|
|
240
920
|
});
|
|
241
921
|
|
|
242
922
|
// src/cli/scripts/dev.ts
|
|
923
|
+
init_constants();
|
|
924
|
+
init_startupBanner();
|
|
243
925
|
var {$: $2, env } = globalThis.Bun;
|
|
244
926
|
import { existsSync as existsSync5 } from "fs";
|
|
245
927
|
import { resolve as resolve3 } from "path";
|
|
246
928
|
|
|
247
|
-
// src/constants.ts
|
|
248
|
-
var ANSI_ESCAPE_LENGTH = 3;
|
|
249
|
-
var ASCII_SPACE = 32;
|
|
250
|
-
var BASE_36_RADIX = 36;
|
|
251
|
-
var BYTES_PER_KILOBYTE = 1024;
|
|
252
|
-
var CLI_ARGS_OFFSET = 3;
|
|
253
|
-
var DEFAULT_PORT = 3000;
|
|
254
|
-
var HOURS_IN_DAY = 24;
|
|
255
|
-
var HOURS_IN_HALF_DAY = 12;
|
|
256
|
-
var MAX_ERROR_LENGTH = 200;
|
|
257
|
-
var MILLISECONDS_IN_A_SECOND = 1000;
|
|
258
|
-
var MINUTES_IN_AN_HOUR = 60;
|
|
259
|
-
var SECONDS_IN_A_MINUTE = 60;
|
|
260
|
-
var MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
|
|
261
|
-
var MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
|
|
262
|
-
var SIGINT_EXIT_CODE = 130;
|
|
263
|
-
var SIGTERM_EXIT_CODE = 143;
|
|
264
|
-
var TIME_PRECISION = 2;
|
|
265
|
-
var TWO_THIRDS = 2 / 3;
|
|
266
|
-
var UNFOUND_INDEX = -1;
|
|
267
|
-
|
|
268
|
-
// src/utils/getDurationString.ts
|
|
269
|
-
var getDurationString = (duration) => {
|
|
270
|
-
let durationString;
|
|
271
|
-
if (duration < MILLISECONDS_IN_A_SECOND) {
|
|
272
|
-
durationString = `${duration.toFixed(TIME_PRECISION)}ms`;
|
|
273
|
-
} else if (duration < MILLISECONDS_IN_A_MINUTE) {
|
|
274
|
-
durationString = `${(duration / MILLISECONDS_IN_A_SECOND).toFixed(TIME_PRECISION)}s`;
|
|
275
|
-
} else {
|
|
276
|
-
durationString = `${(duration / MILLISECONDS_IN_A_MINUTE).toFixed(TIME_PRECISION)}m`;
|
|
277
|
-
}
|
|
278
|
-
return durationString;
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
// src/utils/startupBanner.ts
|
|
282
|
-
var MONTHS = [
|
|
283
|
-
"Jan",
|
|
284
|
-
"Feb",
|
|
285
|
-
"Mar",
|
|
286
|
-
"Apr",
|
|
287
|
-
"May",
|
|
288
|
-
"Jun",
|
|
289
|
-
"Jul",
|
|
290
|
-
"Aug",
|
|
291
|
-
"Sep",
|
|
292
|
-
"Oct",
|
|
293
|
-
"Nov",
|
|
294
|
-
"Dec"
|
|
295
|
-
];
|
|
296
|
-
var formatTimestamp = () => {
|
|
297
|
-
const now = new Date;
|
|
298
|
-
const month = MONTHS[now.getMonth()];
|
|
299
|
-
const day = now.getDate().toString().padStart(2, "0");
|
|
300
|
-
let hours = now.getHours();
|
|
301
|
-
const minutes = now.getMinutes().toString().padStart(2, "0");
|
|
302
|
-
const seconds = now.getSeconds().toString().padStart(2, "0");
|
|
303
|
-
const ampm = hours >= HOURS_IN_HALF_DAY ? "PM" : "AM";
|
|
304
|
-
hours = hours % HOURS_IN_HALF_DAY || HOURS_IN_HALF_DAY;
|
|
305
|
-
return `${month} ${day} ${hours}:${minutes}:${seconds} ${ampm}`;
|
|
306
|
-
};
|
|
307
|
-
|
|
308
929
|
// src/cli/interactive.ts
|
|
930
|
+
init_constants();
|
|
309
931
|
import { openSync } from "fs";
|
|
310
932
|
import { ReadStream } from "tty";
|
|
311
933
|
var SHORTCUTS = {
|
|
@@ -565,249 +1187,10 @@ var createInteractiveHandler = (actions) => {
|
|
|
565
1187
|
return { clearPrompt, dispose, showPrompt };
|
|
566
1188
|
};
|
|
567
1189
|
|
|
568
|
-
// src/cli/telemetryEvent.ts
|
|
569
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
570
|
-
import { arch, platform } from "os";
|
|
571
|
-
import { dirname, join as join2, parse } from "path";
|
|
572
|
-
|
|
573
|
-
// src/cli/scripts/telemetry.ts
|
|
574
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
575
|
-
import { homedir } from "os";
|
|
576
|
-
import { join } from "path";
|
|
577
|
-
var configDir = join(homedir(), ".absolutejs");
|
|
578
|
-
var configPath = join(configDir, "telemetry.json");
|
|
579
|
-
var getTelemetryConfig = () => {
|
|
580
|
-
try {
|
|
581
|
-
if (!existsSync(configPath))
|
|
582
|
-
return null;
|
|
583
|
-
const raw = readFileSync(configPath, "utf-8");
|
|
584
|
-
const config = JSON.parse(raw);
|
|
585
|
-
return config;
|
|
586
|
-
} catch {
|
|
587
|
-
return null;
|
|
588
|
-
}
|
|
589
|
-
};
|
|
590
|
-
var saveTelemetryConfig = (config) => {
|
|
591
|
-
mkdirSync(configDir, { recursive: true });
|
|
592
|
-
writeFileSync(configPath, `${JSON.stringify(config, null, "\t")}
|
|
593
|
-
`);
|
|
594
|
-
};
|
|
595
|
-
var enable = () => {
|
|
596
|
-
const existing = getTelemetryConfig();
|
|
597
|
-
const config = {
|
|
598
|
-
anonymousId: existing?.anonymousId ?? crypto.randomUUID(),
|
|
599
|
-
createdAt: existing?.createdAt ?? new Date().toISOString(),
|
|
600
|
-
enabled: true
|
|
601
|
-
};
|
|
602
|
-
saveTelemetryConfig(config);
|
|
603
|
-
console.log("Telemetry enabled.");
|
|
604
|
-
console.log(`Anonymous ID: ${config.anonymousId}`);
|
|
605
|
-
};
|
|
606
|
-
var disable = () => {
|
|
607
|
-
const existing = getTelemetryConfig();
|
|
608
|
-
if (existing) {
|
|
609
|
-
saveTelemetryConfig({ ...existing, enabled: false });
|
|
610
|
-
}
|
|
611
|
-
console.log("Telemetry disabled.");
|
|
612
|
-
};
|
|
613
|
-
var status = () => {
|
|
614
|
-
const config = getTelemetryConfig();
|
|
615
|
-
if (!config || !config.enabled) {
|
|
616
|
-
console.log("Telemetry is disabled.");
|
|
617
|
-
} else {
|
|
618
|
-
console.log("Telemetry is enabled.");
|
|
619
|
-
console.log(`Anonymous ID: ${config.anonymousId}`);
|
|
620
|
-
}
|
|
621
|
-
};
|
|
622
|
-
var telemetry = (args) => {
|
|
623
|
-
const [subcommand] = args;
|
|
624
|
-
if (subcommand === "enable") {
|
|
625
|
-
enable();
|
|
626
|
-
return;
|
|
627
|
-
}
|
|
628
|
-
if (subcommand === "disable") {
|
|
629
|
-
disable();
|
|
630
|
-
return;
|
|
631
|
-
}
|
|
632
|
-
if (subcommand === "status") {
|
|
633
|
-
status();
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
if (!subcommand) {
|
|
637
|
-
status();
|
|
638
|
-
console.log("");
|
|
639
|
-
console.log("Usage: absolute telemetry <command>");
|
|
640
|
-
console.log("Commands:");
|
|
641
|
-
console.log(" enable Enable anonymous telemetry");
|
|
642
|
-
console.log(" disable Disable telemetry");
|
|
643
|
-
console.log(" status Show current telemetry status");
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
console.error(`Unknown telemetry command: ${subcommand}`);
|
|
647
|
-
console.error("Usage: absolute telemetry <enable|disable|status>");
|
|
648
|
-
process.exit(1);
|
|
649
|
-
};
|
|
650
|
-
|
|
651
|
-
// src/cli/telemetryEvent.ts
|
|
652
|
-
var checkCandidate = (candidate) => {
|
|
653
|
-
if (!existsSync2(candidate)) {
|
|
654
|
-
return null;
|
|
655
|
-
}
|
|
656
|
-
const pkg = JSON.parse(readFileSync2(candidate, "utf-8"));
|
|
657
|
-
if (pkg.name === "@absolutejs/absolute") {
|
|
658
|
-
const ver = pkg.version;
|
|
659
|
-
return ver;
|
|
660
|
-
}
|
|
661
|
-
return null;
|
|
662
|
-
};
|
|
663
|
-
var getVersion = () => {
|
|
664
|
-
try {
|
|
665
|
-
return findPackageVersion();
|
|
666
|
-
} catch {
|
|
667
|
-
return "unknown";
|
|
668
|
-
}
|
|
669
|
-
};
|
|
670
|
-
var findPackageVersion = () => {
|
|
671
|
-
let { dir } = import.meta;
|
|
672
|
-
while (dir !== parse(dir).root) {
|
|
673
|
-
const candidate = join2(dir, "package.json");
|
|
674
|
-
const version = checkCandidate(candidate);
|
|
675
|
-
if (version) {
|
|
676
|
-
return version;
|
|
677
|
-
}
|
|
678
|
-
dir = dirname(dir);
|
|
679
|
-
}
|
|
680
|
-
return "unknown";
|
|
681
|
-
};
|
|
682
|
-
var sendTelemetryEvent = (event, payload) => {
|
|
683
|
-
try {
|
|
684
|
-
if (process.env.TELEMETRY_OFF === "1")
|
|
685
|
-
return;
|
|
686
|
-
const config = getTelemetryConfig();
|
|
687
|
-
if (!config?.enabled)
|
|
688
|
-
return;
|
|
689
|
-
const body = {
|
|
690
|
-
anonymousId: config.anonymousId,
|
|
691
|
-
arch: arch(),
|
|
692
|
-
bunVersion: Bun.version,
|
|
693
|
-
event,
|
|
694
|
-
os: platform(),
|
|
695
|
-
payload,
|
|
696
|
-
timestamp: new Date().toISOString(),
|
|
697
|
-
version: getVersion()
|
|
698
|
-
};
|
|
699
|
-
fetch("https://absolutejs.com/api/telemetry", {
|
|
700
|
-
body: JSON.stringify(body),
|
|
701
|
-
headers: { "Content-Type": "application/json" },
|
|
702
|
-
method: "POST"
|
|
703
|
-
}).catch(() => {
|
|
704
|
-
return;
|
|
705
|
-
});
|
|
706
|
-
} catch {}
|
|
707
|
-
};
|
|
708
|
-
|
|
709
|
-
// src/utils/loadConfig.ts
|
|
710
|
-
import { resolve } from "path";
|
|
711
|
-
var loadConfig = async (configPath2) => {
|
|
712
|
-
const resolved = resolve(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts");
|
|
713
|
-
const mod = await import(resolved);
|
|
714
|
-
const config = mod.default ?? mod.config;
|
|
715
|
-
if (!config) {
|
|
716
|
-
throw new Error(`Config file "${resolved}" does not export a valid configuration.
|
|
717
|
-
Expected: export default defineConfig({ ... })`);
|
|
718
|
-
}
|
|
719
|
-
return config;
|
|
720
|
-
};
|
|
721
|
-
|
|
722
|
-
// src/cli/utils.ts
|
|
723
|
-
var {$ } = globalThis.Bun;
|
|
724
|
-
import { execSync } from "child_process";
|
|
725
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
726
|
-
import { resolve as resolve2 } from "path";
|
|
727
|
-
var COMPOSE_PATH = "db/docker-compose.db.yml";
|
|
728
|
-
var DEFAULT_SERVER_ENTRY = "src/backend/server.ts";
|
|
729
|
-
var isWSLEnvironment = () => {
|
|
730
|
-
try {
|
|
731
|
-
const release = readFileSync3("/proc/version", "utf-8");
|
|
732
|
-
return /microsoft|wsl/i.test(release);
|
|
733
|
-
} catch {
|
|
734
|
-
return false;
|
|
735
|
-
}
|
|
736
|
-
};
|
|
737
|
-
var safeKill = (pid) => {
|
|
738
|
-
try {
|
|
739
|
-
process.kill(pid, "SIGTERM");
|
|
740
|
-
} catch {}
|
|
741
|
-
};
|
|
742
|
-
var killStaleProcesses = (port) => {
|
|
743
|
-
let output;
|
|
744
|
-
try {
|
|
745
|
-
output = execSync(`lsof -ti tcp:${port} -sTCP:LISTEN 2>/dev/null`, {
|
|
746
|
-
encoding: "utf-8"
|
|
747
|
-
}).trim();
|
|
748
|
-
} catch {
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
if (!output) {
|
|
752
|
-
return;
|
|
753
|
-
}
|
|
754
|
-
const pids = output.split(`
|
|
755
|
-
`).map(Number).filter((pid) => pid !== process.pid && pid > 0);
|
|
756
|
-
if (pids.length === 0) {
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
pids.forEach(safeKill);
|
|
760
|
-
console.log(`\x1B[2m${formatTimestamp()}\x1B[0m \x1B[33m[cli]\x1B[0m \x1B[33mKilled ${pids.length} stale ${pids.length === 1 ? "process" : "processes"} on port ${port}.\x1B[0m`);
|
|
761
|
-
};
|
|
762
|
-
var printHelp = () => {
|
|
763
|
-
console.log("");
|
|
764
|
-
console.log("\x1B[1mShortcuts:\x1B[0m");
|
|
765
|
-
console.log(" \x1B[36mr\x1B[0m / restart \u2014 Restart server");
|
|
766
|
-
console.log(" \x1B[36mp\x1B[0m / pause \u2014 Pause/resume server");
|
|
767
|
-
console.log(" \x1B[36mo\x1B[0m / open \u2014 Open in browser");
|
|
768
|
-
console.log(" \x1B[36mc\x1B[0m / clear \u2014 Clear terminal");
|
|
769
|
-
console.log(" \x1B[36mq\x1B[0m / quit \u2014 Graceful shutdown");
|
|
770
|
-
console.log(" \x1B[36mh\x1B[0m / help \u2014 Show this help");
|
|
771
|
-
console.log(" \x1B[36m$\x1B[0m \u2014 Run a shell command");
|
|
772
|
-
console.log(" \x1B[36m\u2191\x1B[0m / \x1B[36m\u2193\x1B[0m \u2014 Command history");
|
|
773
|
-
console.log("");
|
|
774
|
-
};
|
|
775
|
-
var printHint = () => {
|
|
776
|
-
console.log("\x1B[90mpress h + enter to show shortcuts\x1B[0m");
|
|
777
|
-
};
|
|
778
|
-
var readDbScripts = async () => {
|
|
779
|
-
const pkgPath = resolve2("package.json");
|
|
780
|
-
if (!existsSync3(pkgPath))
|
|
781
|
-
return null;
|
|
782
|
-
const pkg = await Bun.file(pkgPath).json();
|
|
783
|
-
const upCommand = pkg.scripts?.["db:up"];
|
|
784
|
-
const downCommand = pkg.scripts?.["db:down"];
|
|
785
|
-
if (!upCommand || !downCommand)
|
|
786
|
-
return null;
|
|
787
|
-
return { downCommand, upCommand };
|
|
788
|
-
};
|
|
789
|
-
var startDatabase = async (scripts) => {
|
|
790
|
-
await timed("Starting database container...", async () => {
|
|
791
|
-
const { exitCode } = await $`${{ raw: scripts.upCommand }}`.quiet().nothrow();
|
|
792
|
-
if (exitCode !== 0)
|
|
793
|
-
process.exit(exitCode);
|
|
794
|
-
});
|
|
795
|
-
};
|
|
796
|
-
var stopDatabase = async (scripts) => {
|
|
797
|
-
console.log(`
|
|
798
|
-
Stopping database container...`);
|
|
799
|
-
await $`${{ raw: scripts.downCommand }}`.quiet().nothrow();
|
|
800
|
-
};
|
|
801
|
-
var timed = async (label, task) => {
|
|
802
|
-
process.stdout.write(label);
|
|
803
|
-
const start = performance.now();
|
|
804
|
-
await task();
|
|
805
|
-
const duration = ((performance.now() - start) / MILLISECONDS_IN_A_SECOND).toFixed(2);
|
|
806
|
-
process.stdout.write(` \x1B[90m${duration}s\x1B[0m
|
|
807
|
-
`);
|
|
808
|
-
};
|
|
809
|
-
|
|
810
1190
|
// src/cli/scripts/dev.ts
|
|
1191
|
+
init_telemetryEvent();
|
|
1192
|
+
init_loadConfig();
|
|
1193
|
+
init_utils();
|
|
811
1194
|
var cliTag = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`;
|
|
812
1195
|
var confirmPrompt = (message, defaultYes = true) => new Promise((_resolve) => {
|
|
813
1196
|
let selected = defaultYes;
|
|
@@ -1104,6 +1487,8 @@ var eslint = async (args) => {
|
|
|
1104
1487
|
};
|
|
1105
1488
|
|
|
1106
1489
|
// src/cli/scripts/info.ts
|
|
1490
|
+
init_constants();
|
|
1491
|
+
init_utils();
|
|
1107
1492
|
import { execSync as execSync2 } from "child_process";
|
|
1108
1493
|
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
1109
1494
|
import { arch as arch2, cpus, platform as platform3, totalmem, version } from "os";
|
|
@@ -1242,6 +1627,7 @@ var info = () => {
|
|
|
1242
1627
|
};
|
|
1243
1628
|
|
|
1244
1629
|
// src/cli/cache.ts
|
|
1630
|
+
init_constants();
|
|
1245
1631
|
import { mkdir } from "fs/promises";
|
|
1246
1632
|
import { join as join4 } from "path";
|
|
1247
1633
|
var {Glob } = globalThis.Bun;
|
|
@@ -1377,6 +1763,12 @@ var prettier = async (args) => {
|
|
|
1377
1763
|
};
|
|
1378
1764
|
|
|
1379
1765
|
// src/cli/scripts/start.ts
|
|
1766
|
+
init_constants();
|
|
1767
|
+
init_getDurationString();
|
|
1768
|
+
init_loadConfig();
|
|
1769
|
+
init_startupBanner();
|
|
1770
|
+
init_telemetryEvent();
|
|
1771
|
+
init_utils();
|
|
1380
1772
|
var {env: env2 } = globalThis.Bun;
|
|
1381
1773
|
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
1382
1774
|
import { basename, resolve as resolve5 } from "path";
|
|
@@ -1625,6 +2017,10 @@ var start = async (serverEntry, outdir, configPath2) => {
|
|
|
1625
2017
|
};
|
|
1626
2018
|
|
|
1627
2019
|
// src/cli/index.ts
|
|
2020
|
+
init_telemetry();
|
|
2021
|
+
init_telemetryEvent();
|
|
2022
|
+
init_constants();
|
|
2023
|
+
init_utils();
|
|
1628
2024
|
var [command] = process.argv.slice(2);
|
|
1629
2025
|
var args = process.argv.slice(CLI_ARGS_OFFSET);
|
|
1630
2026
|
var parseNamedArg = (flag) => {
|
|
@@ -1664,6 +2060,15 @@ if (command === "dev") {
|
|
|
1664
2060
|
} else if (command === "telemetry") {
|
|
1665
2061
|
sendTelemetryEvent("cli:command", { command });
|
|
1666
2062
|
telemetry(args);
|
|
2063
|
+
} else if (command === "compile") {
|
|
2064
|
+
sendTelemetryEvent("cli:command", { command });
|
|
2065
|
+
const outdir = parseNamedArg("--outdir");
|
|
2066
|
+
const outfile = parseNamedArg("--outfile");
|
|
2067
|
+
const configPath2 = parseNamedArg("--config");
|
|
2068
|
+
const positionalArgs = stripNamedArgs("--outdir", "--outfile", "--config");
|
|
2069
|
+
const serverEntry = positionalArgs[0] ?? DEFAULT_SERVER_ENTRY;
|
|
2070
|
+
const { compile: compile2 } = await Promise.resolve().then(() => (init_compile(), exports_compile));
|
|
2071
|
+
await compile2(serverEntry, outdir, outfile, configPath2);
|
|
1667
2072
|
} else if (command === "mkcert") {
|
|
1668
2073
|
sendTelemetryEvent("cli:command", { command });
|
|
1669
2074
|
const { setupMkcert: setupMkcert2 } = await Promise.resolve().then(() => (init_devCert(), exports_devCert));
|
|
@@ -1675,6 +2080,7 @@ if (command === "dev") {
|
|
|
1675
2080
|
console.error("Commands:");
|
|
1676
2081
|
console.error(" dev [entry] Start development server");
|
|
1677
2082
|
console.error(" start [entry] [--outdir dir] Start production server");
|
|
2083
|
+
console.error(" compile [entry] [--outdir dir] [--outfile path] Compile standalone executable");
|
|
1678
2084
|
console.error(" eslint Run ESLint (cached)");
|
|
1679
2085
|
console.error(" info Print system info for bug reports");
|
|
1680
2086
|
console.error(" prettier Run Prettier check (cached)");
|