gemi 0.107.0 → 0.108.0

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/bin/gemi.js CHANGED
@@ -36002,10 +36002,10 @@ function extractAgent(ts, reference, { cwd = process.cwd(), name } = {}) {
36002
36002
  ].join(`
36003
36003
  `);
36004
36004
  const host = ts.createCompilerHost(options);
36005
- const { getSourceFile, fileExists, readFile: readFile2 } = host;
36005
+ const { getSourceFile, fileExists, readFile: readFile3 } = host;
36006
36006
  host.getSourceFile = (fileName, languageVersion, ...rest) => fileName === probe ? ts.createSourceFile(fileName, source, languageVersion) : getSourceFile.call(host, fileName, languageVersion, ...rest);
36007
36007
  host.fileExists = (fileName) => fileName === probe || fileExists.call(host, fileName);
36008
- host.readFile = (fileName) => fileName === probe ? source : readFile2.call(host, fileName);
36008
+ host.readFile = (fileName) => fileName === probe ? source : readFile3.call(host, fileName);
36009
36009
  const second = ts.createProgram({ rootNames: [file, probe], options, host, oldProgram: first });
36010
36010
  const probeFile = second.getSourceFile(probe);
36011
36011
  const alias = (aliasName) => {
@@ -40174,7 +40174,9 @@ async function stagePreviousAssets(options, stagingDir, now = new Date) {
40174
40174
  }
40175
40175
  const carried = await readRecord(from);
40176
40176
  const carriedFiles = new Set(carried.flatMap((release) => release.files));
40177
- const own = (await listFiles(from, ASSETS_DIR)).filter((file) => isCarriable(file) && !carriedFiles.has(file));
40177
+ const listed = await listFiles(from, ASSETS_DIR);
40178
+ const listedSet = new Set(listed);
40179
+ const own = listed.filter((file) => isCarriable(file) && !carriedFiles.has(file) && !isSiblingIn(file, listedSet));
40178
40180
  const releases = [
40179
40181
  { retiredAt: now.toISOString(), files: own },
40180
40182
  ...carried.filter((release) => now.getTime() - Date.parse(release.retiredAt) < options.maxAgeMs)
@@ -40190,6 +40192,7 @@ async function stagePreviousAssets(options, stagingDir, now = new Date) {
40190
40192
  const target = join4(stagingDir, file);
40191
40193
  await mkdir2(dirname2(target), { recursive: true });
40192
40194
  await copyFile(source, target);
40195
+ await copySiblings(source, target);
40193
40196
  files.push(file);
40194
40197
  }
40195
40198
  if (files.length > 0) {
@@ -40200,6 +40203,7 @@ async function stagePreviousAssets(options, stagingDir, now = new Date) {
40200
40203
  }
40201
40204
  async function restorePreviousAssets(stagingDir, clientDir, releases) {
40202
40205
  const record = [];
40206
+ const paths = [];
40203
40207
  let count = 0;
40204
40208
  for (const release of releases) {
40205
40209
  const files = [];
@@ -40210,7 +40214,9 @@ async function restorePreviousAssets(stagingDir, clientDir, releases) {
40210
40214
  }
40211
40215
  await mkdir2(dirname2(target), { recursive: true });
40212
40216
  await copyFile(join4(stagingDir, file), target);
40217
+ await copySiblings(join4(stagingDir, file), target);
40213
40218
  files.push(file);
40219
+ paths.push(file);
40214
40220
  }
40215
40221
  if (files.length > 0) {
40216
40222
  record.push({ retiredAt: release.retiredAt, files });
@@ -40221,7 +40227,19 @@ async function restorePreviousAssets(stagingDir, clientDir, releases) {
40221
40227
  await mkdir2(dirname2(recordPath), { recursive: true });
40222
40228
  await writeFile2(recordPath, `${JSON.stringify({ releases: record }, null, 2)}
40223
40229
  `);
40224
- return { files: count, releases: record.length };
40230
+ return { files: count, releases: record.length, paths };
40231
+ }
40232
+ var SIBLING_SUFFIXES = [".br", ".gz"];
40233
+ function isSiblingIn(file, files) {
40234
+ const suffix = SIBLING_SUFFIXES.find((ext) => file.endsWith(ext));
40235
+ return suffix !== undefined && files.has(file.slice(0, -suffix.length));
40236
+ }
40237
+ async function copySiblings(source, target) {
40238
+ for (const ext of SIBLING_SUFFIXES) {
40239
+ if (await isFile(source + ext)) {
40240
+ await copyFile(source + ext, target + ext);
40241
+ }
40242
+ }
40225
40243
  }
40226
40244
  async function readRecord(clientDir) {
40227
40245
  let raw;
@@ -40282,6 +40300,115 @@ async function exists(path3) {
40282
40300
  }
40283
40301
  }
40284
40302
 
40303
+ // vite/precompressAssets.ts
40304
+ import { mkdir as mkdir3, readFile as readFile2, stat as stat2, writeFile as writeFile3 } from "fs/promises";
40305
+ import { availableParallelism } from "os";
40306
+ import { dirname as dirname3, join as join5 } from "path";
40307
+ import { promisify } from "util";
40308
+ import { brotliCompress, constants, gzip } from "zlib";
40309
+ var STATIC_ASSETS_RECORD = ".vite/static-assets.json";
40310
+ var PRECOMPRESS_MIN_BYTES = 1024;
40311
+ var COMPRESSIBLE = /\.(?:m?js|cjs|css|svg|json|webmanifest|wasm|txt|xml|ttf|otf|eot)$/i;
40312
+ var TEXT = /\.(?:m?js|cjs|css|svg|json|webmanifest|txt|xml)$/i;
40313
+ var FONT = /\.(?:ttf|otf|eot)$/i;
40314
+ var SIBLING_EXTENSION = {
40315
+ br: ".br",
40316
+ gzip: ".gz"
40317
+ };
40318
+ var ENCODINGS = ["br", "gzip"];
40319
+ var brotli = promisify(brotliCompress);
40320
+ var gzipAsync = promisify(gzip);
40321
+ function isPrecompressible(file) {
40322
+ return COMPRESSIBLE.test(file);
40323
+ }
40324
+ async function encode2(encoding, file, input) {
40325
+ if (encoding === "gzip") {
40326
+ return gzipAsync(input, { level: 9 });
40327
+ }
40328
+ return brotli(input, {
40329
+ params: {
40330
+ [constants.BROTLI_PARAM_QUALITY]: constants.BROTLI_MAX_QUALITY,
40331
+ [constants.BROTLI_PARAM_MODE]: TEXT.test(file) ? constants.BROTLI_MODE_TEXT : FONT.test(file) ? constants.BROTLI_MODE_FONT : constants.BROTLI_MODE_GENERIC,
40332
+ [constants.BROTLI_PARAM_SIZE_HINT]: input.length
40333
+ }
40334
+ });
40335
+ }
40336
+ async function fileSize(path3) {
40337
+ try {
40338
+ const info = await stat2(path3);
40339
+ return info.isFile() ? info.size : null;
40340
+ } catch {
40341
+ return null;
40342
+ }
40343
+ }
40344
+ async function precompressAssets(clientDir, files, options = {}) {
40345
+ const minBytes = options.minBytes ?? PRECOMPRESS_MIN_BYTES;
40346
+ const record = { files: {} };
40347
+ const stats = {
40348
+ files: 0,
40349
+ identityBytes: 0,
40350
+ brBytes: 0,
40351
+ gzipBytes: 0
40352
+ };
40353
+ const queue = [...new Set(files)];
40354
+ for (const file of queue) {
40355
+ record.files[file] = [];
40356
+ }
40357
+ async function work(file) {
40358
+ if (!isPrecompressible(file)) {
40359
+ return;
40360
+ }
40361
+ const path3 = join5(clientDir, file);
40362
+ const size = await fileSize(path3);
40363
+ if (size === null || size < minBytes) {
40364
+ return;
40365
+ }
40366
+ let input = null;
40367
+ const available = [];
40368
+ const sizes = {};
40369
+ for (const encoding of ENCODINGS) {
40370
+ const siblingPath = path3 + SIBLING_EXTENSION[encoding];
40371
+ const existing = options.reuseExisting ? await fileSize(siblingPath) : null;
40372
+ if (existing !== null) {
40373
+ if (existing < size) {
40374
+ available.push(encoding);
40375
+ sizes[encoding] = existing;
40376
+ }
40377
+ continue;
40378
+ }
40379
+ input ??= await readFile2(path3);
40380
+ const output = await encode2(encoding, file, input);
40381
+ if (output.length < size) {
40382
+ await writeFile3(siblingPath, output);
40383
+ available.push(encoding);
40384
+ sizes[encoding] = output.length;
40385
+ }
40386
+ }
40387
+ record.files[file] = available;
40388
+ if (available.length > 0) {
40389
+ stats.files += 1;
40390
+ stats.identityBytes += size;
40391
+ stats.brBytes += sizes.br ?? size;
40392
+ stats.gzipBytes += sizes.gzip ?? size;
40393
+ }
40394
+ }
40395
+ const concurrency = Math.max(1, options.concurrency ?? Math.min(8, availableParallelism()));
40396
+ let next = 0;
40397
+ await Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
40398
+ while (next < queue.length) {
40399
+ await work(queue[next++]);
40400
+ }
40401
+ }));
40402
+ return { record, stats };
40403
+ }
40404
+ async function writeStaticAssetsRecord(clientDir, record) {
40405
+ const path3 = join5(clientDir, STATIC_ASSETS_RECORD);
40406
+ await mkdir3(dirname3(path3), { recursive: true });
40407
+ const files = Object.fromEntries(Object.entries(record.files).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0));
40408
+ await writeFile3(path3, `${JSON.stringify({ files }, null, 2)}
40409
+ `);
40410
+ }
40411
+
40285
40412
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/esm.mjs
40286
40413
  var import__ = __toESM(require_commander(), 1);
40287
40414
  var {
@@ -40304,7 +40431,7 @@ import { relative as relative3, resolve as resolve2 } from "path";
40304
40431
 
40305
40432
  // support/discover.ts
40306
40433
  import { existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
40307
- import { join as join5, relative as relative2 } from "path";
40434
+ import { join as join6, relative as relative2 } from "path";
40308
40435
  function sourceFiles(dir, ignore = []) {
40309
40436
  const out = [];
40310
40437
  const ignored = (path3) => {
@@ -40313,13 +40440,13 @@ function sourceFiles(dir, ignore = []) {
40313
40440
  };
40314
40441
  const walk3 = (current) => {
40315
40442
  for (const entry of readdirSync2(current, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
40316
- const path3 = join5(current, entry.name);
40443
+ const path3 = join6(current, entry.name);
40317
40444
  if (ignored(path3))
40318
40445
  continue;
40319
40446
  if (entry.isDirectory()) {
40320
40447
  if (entry.name.startsWith(".") || entry.name === "node_modules")
40321
40448
  continue;
40322
- if (existsSync3(join5(path3, "package.json")))
40449
+ if (existsSync3(join6(path3, "package.json")))
40323
40450
  continue;
40324
40451
  walk3(path3);
40325
40452
  continue;
@@ -40515,7 +40642,7 @@ function indent(message) {
40515
40642
 
40516
40643
  // bin/ide/generateApiManifest.ts
40517
40644
  var import_recast2 = __toESM(require_main2(), 1);
40518
- import { join as join6 } from "path";
40645
+ import { join as join7 } from "path";
40519
40646
  import { existsSync as existsSync4 } from "fs";
40520
40647
 
40521
40648
  // utils/toCamelCase.ts
@@ -40567,7 +40694,7 @@ class ApiManifestGenerator {
40567
40694
  rootRouterBody;
40568
40695
  routerBodyCache = new Map;
40569
40696
  controllerCache = new Map;
40570
- manifestCacheDir = join6(ROOT, ".gemi/cache/api-routes-manifest");
40697
+ manifestCacheDir = join7(ROOT, ".gemi/cache/api-routes-manifest");
40571
40698
  async writeManifest(cacheFilePath) {
40572
40699
  const manifest = {};
40573
40700
  for (const [routeName, method] of this.routes.entries()) {
@@ -40599,7 +40726,7 @@ class ApiManifestGenerator {
40599
40726
  if (path4.startsWith("@/app")) {
40600
40727
  return path4.replace("@", "") + ".ts";
40601
40728
  }
40602
- return join6(parentPath, path4) + ".ts";
40729
+ return join7(parentPath, path4) + ".ts";
40603
40730
  }
40604
40731
  isFileShouldBeParsed(path4) {
40605
40732
  return path4.includes("app/http/router") || path4.includes("app/http/controllers");
@@ -40647,7 +40774,7 @@ class ApiManifestGenerator {
40647
40774
  if (normalizedPath.startsWith("/app/http/controllers")) {
40648
40775
  for (const specifier of node.specifiers) {
40649
40776
  const name = specifier.type === "ImportDefaultSpecifier" ? specifier.local.name : specifier.imported.name;
40650
- await this.parseControllerFile(await this.parseFile(join6(ROOT, normalizedPath)), name, normalizedPath);
40777
+ await this.parseControllerFile(await this.parseFile(join7(ROOT, normalizedPath)), name, normalizedPath);
40651
40778
  }
40652
40779
  }
40653
40780
  if (normalizedPath.startsWith("/app/http/routes")) {
@@ -40751,7 +40878,7 @@ class ApiManifestGenerator {
40751
40878
  }
40752
40879
  async parse(cacheFilePath, filePath, isRoot = true, name = "") {
40753
40880
  console.log({ ROOT, filePath });
40754
- const file = await this.parseFile(join6(ROOT, filePath));
40881
+ const file = await this.parseFile(join7(ROOT, filePath));
40755
40882
  for (const body of file.program.body) {
40756
40883
  if (!this.isNodeShouldBeParsed(body))
40757
40884
  continue;
@@ -40787,7 +40914,7 @@ class ApiManifestGenerator {
40787
40914
  }
40788
40915
  async run(filePath) {
40789
40916
  const cacheId = generateGitDiffId();
40790
- const cacheFilePath = join6(this.manifestCacheDir, `${cacheId}.json`);
40917
+ const cacheFilePath = join7(this.manifestCacheDir, `${cacheId}.json`);
40791
40918
  if (existsSync4(cacheFilePath)) {
40792
40919
  console.log(await Bun.file(cacheFilePath).text());
40793
40920
  } else {
@@ -41266,7 +41393,7 @@ gemi is now ${resolved}.`);
41266
41393
  }
41267
41394
 
41268
41395
  // bin/forwardSignals.ts
41269
- import { constants } from "os";
41396
+ import { constants as constants2 } from "os";
41270
41397
  var FORWARDED = ["SIGTERM", "SIGINT"];
41271
41398
  async function spawnForwardingSignals(options) {
41272
41399
  const proc = Bun.spawn({
@@ -41296,7 +41423,7 @@ async function spawnForwardingSignals(options) {
41296
41423
  function exitCodeOf(proc) {
41297
41424
  if (proc.exitCode !== null)
41298
41425
  return proc.exitCode;
41299
- const signal = proc.signalCode && constants.signals[proc.signalCode];
41426
+ const signal = proc.signalCode && constants2.signals[proc.signalCode];
41300
41427
  return signal ? 128 + signal : 1;
41301
41428
  }
41302
41429
 
@@ -41343,23 +41470,47 @@ program.command("build").action(async () => {
41343
41470
  process.env.GEMI_INPUT = JSON.stringify(input);
41344
41471
  const previousAssets = resolvePreviousAssets(config.previousAssets, rootDir);
41345
41472
  const stagingDir = previousAssets ? await mkdtemp(path9.join(tmpdir(), "gemi-previous-assets-")) : undefined;
41473
+ const clientDir = path9.join(rootDir, "dist", "client");
41474
+ const emitted = [];
41475
+ let carriedPaths = [];
41346
41476
  try {
41347
41477
  const retained = previousAssets && stagingDir ? await stagePreviousAssets(previousAssets, stagingDir) : [];
41348
41478
  console.log("Building client...");
41349
41479
  await build({
41350
41480
  configFile: false,
41351
- plugins: [vite_default()],
41481
+ plugins: [
41482
+ vite_default(),
41483
+ {
41484
+ name: "gemi:emitted-assets",
41485
+ apply: "build",
41486
+ writeBundle(_options, bundle) {
41487
+ for (const file of Object.keys(bundle)) {
41488
+ if (file.startsWith("assets/"))
41489
+ emitted.push(file);
41490
+ }
41491
+ }
41492
+ }
41493
+ ],
41352
41494
  build: { outDir: "dist/client" }
41353
41495
  });
41354
41496
  if (stagingDir) {
41355
- const carried = await restorePreviousAssets(stagingDir, path9.join(rootDir, "dist", "client"), retained);
41356
- console.log(`Kept ${carried.files} asset file(s) from ${carried.releases} earlier release(s).`);
41497
+ const carried2 = await restorePreviousAssets(stagingDir, clientDir, retained);
41498
+ carriedPaths = carried2.paths;
41499
+ console.log(`Kept ${carried2.files} asset file(s) from ${carried2.releases} earlier release(s).`);
41357
41500
  }
41358
41501
  } finally {
41359
41502
  if (stagingDir) {
41360
41503
  await rm(stagingDir, { recursive: true, force: true });
41361
41504
  }
41362
41505
  }
41506
+ const startedAt = performance.now();
41507
+ const fresh = await precompressAssets(clientDir, emitted);
41508
+ const carried = await precompressAssets(clientDir, carriedPaths, { reuseExisting: true });
41509
+ await writeStaticAssetsRecord(clientDir, {
41510
+ files: { ...carried.record.files, ...fresh.record.files }
41511
+ });
41512
+ const kb = (bytes) => `${(bytes / 1024).toFixed(1)} kB`;
41513
+ console.log(`Precompressed ${fresh.stats.files} asset(s): ${kb(fresh.stats.identityBytes)} \u2192 ` + `${kb(fresh.stats.brBytes)} br / ${kb(fresh.stats.gzipBytes)} gzip ` + `(${Math.round(performance.now() - startedAt)} ms).`);
41363
41514
  console.log("Building server...");
41364
41515
  await build({
41365
41516
  configFile: false,
@@ -41515,5 +41666,5 @@ program.command("app:route-manifest").action(async () => {
41515
41666
  });
41516
41667
  program.parse();
41517
41668
 
41518
- //# debugId=D394A7B3011B327964756E2164756E21
41669
+ //# debugId=45F966AEDDDBFC5864756E2164756E21
41519
41670
  //# sourceMappingURL=gemi.js.map