@hot-updater/cli-tools 1.0.0-rc.45 → 1.0.0-rc.47

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.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Readable } from "stream";
2
2
  import { Key as Key$1 } from "node:readline";
3
- import { AnyHotUpdaterPlugin, Bundle, BundleSigningAdapter, ConfigInput, ConfiguredDatabase, EngineDatabase, HotUpdaterCoreApi, LocalSigningConfig, Migrator, Platform, PluginClientPlugin, RequiredDeep, SchemaGenerator, SigningConfig, StorageAdapter, StorageAdapterWith, StoragePutResult, ToolingDatabase } from "@hot-updater/plugin-core";
3
+ import { AnyHotUpdaterPlugin, BundleSigningAdapter, ConfigInput, ConfiguredDatabase, EngineDatabase, HotUpdaterCoreApi, LocalSigningConfig, Migrator, Platform, PluginClientPlugin, RequiredDeep, SchemaGenerator, SigningConfig, StorageAdapter, StorageAdapterWith, StoragePutResult, ToolingDatabase } from "@hot-updater/plugin-core";
4
4
  import { Readable as Readable$1, Writable } from "node:stream";
5
5
  import { ManifestArchive } from "@hot-updater/protocol";
6
6
  //#region src/BuildLogger.d.ts
@@ -369,17 +369,13 @@ declare const writeHotUpdaterConfig: (scaffold: HotUpdaterConfigScaffold, filePa
369
369
  /**
370
370
  * Writes hot-updater.config.ts and says what it did. `settings` names the
371
371
  * provider in messages, such as "Supabase". A config it cannot merge is
372
- * kept, with what to add to it. The plugins file an older init wrote is
373
- * removed only after a successful config write and when the config no longer
374
- * references it. A file the project wrote is kept with a cleanup notice.
372
+ * kept, with what to add to it.
375
373
  */
376
374
  declare const writeHotUpdaterFiles: (scaffold: HotUpdaterConfigScaffold, { cwd, settings }: {
377
375
  cwd?: string;
378
376
  settings: string;
379
377
  }) => Promise<{
380
378
  readonly config: WriteHotUpdaterConfigResult;
381
- /** What became of hotUpdater.plugins.ts, when there was one. */
382
- readonly pluginsFile?: "removed" | "kept";
383
379
  }>;
384
380
  //#endregion
385
381
  //#region src/hotUpdaterEnv.d.ts
@@ -687,28 +683,6 @@ declare const makeEnv: (newEnvVars: Record<string, EnvVarValue>, filePath?: stri
687
683
  */
688
684
  declare const moduleSpecifiersOf: (fileName: string, source: string) => string[];
689
685
  //#endregion
690
- //#region src/promoteBundle.d.ts
691
- type PromoteStorageAdapter = StorageAdapterWith<"get" | "put" | "exists" | "delete">;
692
- declare const LEGACY_BUNDLE_ERROR = "This OTA bundle was created by a version that does not support manifest.json. Copy bundle is not available.";
693
- declare function createCopiedBundleArtifacts({ bundle, config, nextBundleId, storageAdapter }: {
694
- bundle: Bundle;
695
- config: ConfigResponse;
696
- nextBundleId: string;
697
- storageAdapter: PromoteStorageAdapter;
698
- }): Promise<{
699
- bundle: {
700
- id: string;
701
- metadata: import("@hot-updater/protocol").BundleMetadata | undefined;
702
- assetBaseStorageUri: string;
703
- patches: never[];
704
- manifestFileHash: string;
705
- manifestStorageUri: string;
706
- platform: import("@hot-updater/protocol").Platform;
707
- gitCommitHash: string | null;
708
- };
709
- uploadedStorageUris: string[];
710
- }>;
711
- //#endregion
712
686
  //#region ../../node_modules/.pnpm/@clack+core@1.4.3/node_modules/@clack/core/dist/index.d.mts
713
687
  declare const actions: readonly ["up", "down", "left", "right", "space", "enter", "cancel"];
714
688
  type Action = (typeof actions)[number];
@@ -1933,8 +1907,6 @@ declare const getStorageFileByteSize: (filePath: string) => Promise<number>;
1933
1907
  declare const putStorageFile: (storage: StorageAdapterWith<"put">, key: string, filePath: string) => Promise<StoragePutResult & {
1934
1908
  byteSize: number;
1935
1909
  }>;
1936
- declare const writeStorageFile: (storage: StorageAdapterWith<"get">, storageUri: string, filePath: string) => Promise<void>;
1937
- declare const writeStorageResponseFile: (response: Response, filePath: string) => Promise<void>;
1938
1910
  //#endregion
1939
1911
  //#region src/transformEnv.d.ts
1940
1912
  declare const transformEnv: <T extends Record<string, string>>(filename: string, env: T) => string;
@@ -1953,4 +1925,4 @@ type TransformTemplateArgs<T extends string> = { [Key in ExtractPlaceholders<T>]
1953
1925
  */
1954
1926
  declare function transformTemplate<T extends string>(templateString: T, values: TransformTemplateArgs<T>): string;
1955
1927
  //#endregion
1956
- export { AppClientCredential, AppClientPlugin, AssembleServerOptions, AssembledServer, BuildLogger, BuildLoggerConfig, BuildType, BundleSigningSession, ClientAuthSpec, ClientCredentialSpec, ConfigBuilder, ConfigBuilderScaffold, ConfigResponse, CreateHotUpdaterConfigScaffoldFromBuilderOptions, CreateHotUpdaterConfigScaffoldOptions, HOT_UPDATER_SERVER_PACKAGE_VERSION_ENV, HotUpdateDirUtil, HotUpdaterConfigOptions, HotUpdaterConfigScaffold, HotUpdaterInitEnv, HotUpdaterLogWriter, ImportInfo, InitEnvFileError, InitError, InitProviderDefinition, InitProviderInputDefinition, InitProviderInputPersistence, LEGACY_BUNDLE_ERROR, LegacyInfrastructureError, ManagedHelperStatement, ManagedHelperStrategy, MissingInitInputsError, PromptProgress, PromptSpinner, ProviderConfig, ProvisionedClientCredential, ReactNativeMetadata, ReadPackageUpResult, RunInitOptions, ServerClientEndpoint, ServerDefinition, ServerDefinitionError, WriteHotUpdaterConfigResult, assembleServer, assertInfrastructureGenerationAtUrl, assertInfrastructureGenerationPayload, assertInitInputs, assertInitProviderInputs, banner, clientAuthOf, typedColors as colors, confirmInitInputPersistence, copyDirToTmp, createCopiedBundleArtifacts, createHotUpdaterConfigScaffold, createHotUpdaterConfigScaffoldFromBuilder, createLogWriter, createMigrator, createTarBrTargetFiles, createZip, decryptJson, defineInitProvider, encryptJson, ensureInstallPackages, generateClientCredential, generateSchema, generatesSchema, getAndroidSdkPath, getBundleSigningPublicKey, getCwd, getHotUpdaterEnvValue, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, getMissingInitInputs, getMissingInitProviderInputs, getPackageManager, getReactNativeMetadatas, getStorageFileByteSize, isServerDefinition, link, loadConfig, loadPlatformConfigs, log, makeEnv, moduleSpecifiersOf, p, prepareBundleSigning, printAppSetup, printBanner, provisionClientCredential, putStorageFile, readBundleSigningPublicKeyFile, readHotUpdaterEnv, readHotUpdaterInitEnv, readPackageUp, renderAppImports, renderAppSetup, renderImportStatements, resolveHotUpdaterServerVersion, resolveInitProviderInput, resolveInitProviderInputs, resolvePackageVersion, serverDefinitionOf, shouldAutoSelectOnlyInitResource, stripAnsi, transformEnv, transformTemplate, writeHotUpdaterConfig, writeHotUpdaterFiles, writeStorageFile, writeStorageResponseFile };
1928
+ export { AppClientCredential, AppClientPlugin, AssembleServerOptions, AssembledServer, BuildLogger, BuildLoggerConfig, BuildType, BundleSigningSession, ClientAuthSpec, ClientCredentialSpec, ConfigBuilder, ConfigBuilderScaffold, ConfigResponse, CreateHotUpdaterConfigScaffoldFromBuilderOptions, CreateHotUpdaterConfigScaffoldOptions, HOT_UPDATER_SERVER_PACKAGE_VERSION_ENV, HotUpdateDirUtil, HotUpdaterConfigOptions, HotUpdaterConfigScaffold, HotUpdaterInitEnv, HotUpdaterLogWriter, ImportInfo, InitEnvFileError, InitError, InitProviderDefinition, InitProviderInputDefinition, InitProviderInputPersistence, LegacyInfrastructureError, ManagedHelperStatement, ManagedHelperStrategy, MissingInitInputsError, PromptProgress, PromptSpinner, ProviderConfig, ProvisionedClientCredential, ReactNativeMetadata, ReadPackageUpResult, RunInitOptions, ServerClientEndpoint, ServerDefinition, ServerDefinitionError, WriteHotUpdaterConfigResult, assembleServer, assertInfrastructureGenerationAtUrl, assertInfrastructureGenerationPayload, assertInitInputs, assertInitProviderInputs, banner, clientAuthOf, typedColors as colors, confirmInitInputPersistence, copyDirToTmp, createHotUpdaterConfigScaffold, createHotUpdaterConfigScaffoldFromBuilder, createLogWriter, createMigrator, createTarBrTargetFiles, createZip, decryptJson, defineInitProvider, encryptJson, ensureInstallPackages, generateClientCredential, generateSchema, generatesSchema, getAndroidSdkPath, getBundleSigningPublicKey, getCwd, getHotUpdaterEnvValue, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, getMissingInitInputs, getMissingInitProviderInputs, getPackageManager, getReactNativeMetadatas, getStorageFileByteSize, isServerDefinition, link, loadConfig, loadPlatformConfigs, log, makeEnv, moduleSpecifiersOf, p, prepareBundleSigning, printAppSetup, printBanner, provisionClientCredential, putStorageFile, readBundleSigningPublicKeyFile, readHotUpdaterEnv, readHotUpdaterInitEnv, readPackageUp, renderAppImports, renderAppSetup, renderImportStatements, resolveHotUpdaterServerVersion, resolveInitProviderInput, resolveInitProviderInputs, resolvePackageVersion, serverDefinitionOf, shouldAutoSelectOnlyInitResource, stripAnsi, transformEnv, transformTemplate, writeHotUpdaterConfig, writeHotUpdaterFiles };
package/dist/index.mjs CHANGED
@@ -16,14 +16,14 @@ import l__default from "node:readline";
16
16
  import tty, { ReadStream } from "node:tty";
17
17
  import Ei, { appendFileSync, createReadStream, createWriteStream, existsSync, lstatSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
18
18
  import path$1, { basename, dirname as dirname$1, join, posix, win32 } from "node:path";
19
- import { createBundleStorageKey, createMemoryAdapter, createStorageAdapter, createStorageRootUriWithPath, getContentType, getManifestAssetDownloadPath, getManifestAssetStoragePath, isContentAddressedAssetFileHash, isRemoteDatabase, resolveManifestAssetStorageUri, toolingTargetOf } from "@hot-updater/plugin-core";
19
+ import { createMemoryAdapter, createStorageAdapter, getContentType, isRemoteDatabase, toolingTargetOf } from "@hot-updater/plugin-core";
20
20
  import { createHotUpdater } from "@hot-updater/server";
21
21
  import os, { constants } from "node:os";
22
22
  import crypto$1, { createHash, randomBytes, randomUUID } from "node:crypto";
23
23
  import fs$2 from "node:fs/promises";
24
24
  import As, { Duplex, PassThrough as PassThrough$1, Readable as Readable$1, Transform, Writable, getDefaultHighWaterMark } from "node:stream";
25
25
  import { finished, pipeline } from "node:stream/promises";
26
- import { brotliDecompressSync, constants as constants$1, createBrotliCompress } from "node:zlib";
26
+ import { constants as constants$1, createBrotliCompress } from "node:zlib";
27
27
  import { EventEmitter as EventEmitter$1, addAbortListener, on, once, setMaxListeners } from "node:events";
28
28
  import { StringDecoder } from "node:string_decoder";
29
29
  import * as Ms from "zlib";
@@ -36,7 +36,6 @@ import { serialize } from "node:v8";
36
36
  import { Buffer as Buffer$2 } from "node:buffer";
37
37
  import { Visitor, parseSync } from "oxc-parser";
38
38
  import { loadConfig as loadConfig$1 } from "unconfig";
39
- import { getManifestFileHash, stripBundleArtifactMetadata } from "@hot-updater/protocol";
40
39
  import { transformSync } from "oxc-transform";
41
40
  //#endregion
42
41
  //#region src/colors.ts
@@ -25107,8 +25106,6 @@ const normalizeImportInfos = (imports) => {
25107
25106
  return Array.from(collectedImports.entries()).sort(([a], [b]) => {
25108
25107
  const isABuild = a.startsWith("@hot-updater/");
25109
25108
  if (isABuild !== b.startsWith("@hot-updater/")) return isABuild ? -1 : 1;
25110
- if (a === "dotenv/config") return -1;
25111
- if (b === "dotenv/config") return 1;
25112
25109
  const isAdminA = a === "firebase-admin";
25113
25110
  if (isAdminA !== (b === "firebase-admin")) return isAdminA ? -1 : 1;
25114
25111
  return a.localeCompare(b);
@@ -44141,8 +44138,7 @@ const MANAGED_IMPORT_PACKAGES = /* @__PURE__ */ new Set([
44141
44138
  const MANAGED_HELPER_NAMES = /* @__PURE__ */ new Set([
44142
44139
  "awsOptions",
44143
44140
  "commonOptions",
44144
- "credential",
44145
- "storageOptions"
44141
+ "credential"
44146
44142
  ]);
44147
44143
  const KNOWN_BUILD_CALLEES = /* @__PURE__ */ new Set([
44148
44144
  "bare",
@@ -44801,20 +44797,10 @@ const renderServerSettings = (scaffold) => {
44801
44797
  ].join("\n")
44802
44798
  ].filter(Boolean).join("\n\n");
44803
44799
  };
44804
- /** Where an older init wrote a managed server's plugins, which the config lists now. */
44805
- const PLUGINS_FILE_PATH = "hotUpdater.plugins.ts";
44806
- /**
44807
- * Whether `text` is the plugins file an older init wrote: comments and a
44808
- * re-export of a provider package's former `plugins`, which the config's
44809
- * `plugins` list replaces.
44810
- */
44811
- const isGeneratedPluginsFile = (text) => /^export\{plugins\}from(["'])@hot-updater\/[\w-]+\1;?$/u.test(text.replace(/^\s*\/\/.*$/gmu, "").replace(/\s+/gu, ""));
44812
44800
  /**
44813
44801
  * Writes hot-updater.config.ts and says what it did. `settings` names the
44814
44802
  * provider in messages, such as "Supabase". A config it cannot merge is
44815
- * kept, with what to add to it. The plugins file an older init wrote is
44816
- * removed only after a successful config write and when the config no longer
44817
- * references it. A file the project wrote is kept with a cleanup notice.
44803
+ * kept, with what to add to it.
44818
44804
  */
44819
44805
  const writeHotUpdaterFiles = async (scaffold, { cwd = process.cwd(), settings }) => {
44820
44806
  const config = await writeHotUpdaterConfig(scaffold, path.join(cwd, HOT_UPDATER_CONFIG_PATH));
@@ -44826,33 +44812,7 @@ const writeHotUpdaterFiles = async (scaffold, { cwd = process.cwd(), settings })
44826
44812
  "",
44827
44813
  renderServerSettings(scaffold)
44828
44814
  ].join("\n"));
44829
- const pluginsPath = path.join(cwd, PLUGINS_FILE_PATH);
44830
- const pluginsText = await readTextFile(pluginsPath);
44831
- if (pluginsText === null) return { config };
44832
- if (config.status === "skipped") return {
44833
- config,
44834
- pluginsFile: "kept"
44835
- };
44836
- if ((await fs$1.readFile(config.path, "utf-8")).includes("./hotUpdater.plugins")) {
44837
- p.log.warn(`Kept '${PLUGINS_FILE_PATH}': '${HOT_UPDATER_CONFIG_PATH}' still references it.`);
44838
- return {
44839
- config,
44840
- pluginsFile: "kept"
44841
- };
44842
- }
44843
- if (isGeneratedPluginsFile(pluginsText)) {
44844
- await fs$1.rm(pluginsPath);
44845
- p.log.success(`Removed '${PLUGINS_FILE_PATH}': \`plugins\` in '${HOT_UPDATER_CONFIG_PATH}' lists the plugins the server runs.`);
44846
- return {
44847
- config,
44848
- pluginsFile: "removed"
44849
- };
44850
- }
44851
- p.log.warn(`Nothing reads '${pluginsPath}' anymore: \`plugins\` in '${HOT_UPDATER_CONFIG_PATH}' lists the plugins the server runs. Delete it.`);
44852
- return {
44853
- config,
44854
- pluginsFile: "kept"
44855
- };
44815
+ return { config };
44856
44816
  };
44857
44817
  //#endregion
44858
44818
  //#region src/initOptions.ts
@@ -45280,7 +45240,6 @@ const isServerDefinition = (value) => isRecord(value) && isRecord(value.database
45280
45240
  */
45281
45241
  const serverDefinitionOf = (value, source = "The server definition") => {
45282
45242
  if (isServerDefinition(value)) return value;
45283
- if (isRecord(value) && "handlers" in value) throw new ServerDefinitionError(`${source} exports a hotUpdater from an older @hot-updater/server. Upgrade @hot-updater/server to the version of hot-updater.`);
45284
45243
  throw new ServerDefinitionError(`${source} must export hotUpdater: the server createHotUpdater({ database, storage, plugins }) returns.`);
45285
45244
  };
45286
45245
  const fail = (message) => {
@@ -45496,305 +45455,6 @@ const moduleSpecifiersOf = (fileName, source) => {
45496
45455
  ])];
45497
45456
  };
45498
45457
  //#endregion
45499
- //#region src/storageFiles.ts
45500
- const getStorageFileByteSize = async (filePath) => {
45501
- const { size } = await fs$2.stat(filePath, { bigint: true });
45502
- if (size < 0n || size > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error("Storage file size must be a non-negative safe integer.");
45503
- return Number(size);
45504
- };
45505
- const putStorageFile = async (storage, key, filePath) => {
45506
- const byteSize = await getStorageFileByteSize(filePath);
45507
- const source = createReadStream(filePath);
45508
- try {
45509
- return {
45510
- ...await storage.put({
45511
- key: path$1.posix.join(key, path$1.basename(filePath)),
45512
- body: Readable$1.toWeb(source),
45513
- contentLength: byteSize,
45514
- contentType: getContentType(filePath)
45515
- }),
45516
- byteSize
45517
- };
45518
- } finally {
45519
- source.destroy();
45520
- }
45521
- };
45522
- const writeStorageFile = async (storage, storageUri, filePath) => {
45523
- const { response } = await storage.get({ storageUri });
45524
- if (response === null) throw new Error(`Storage object not found: ${storageUri}`);
45525
- await writeStorageResponseFile(response, filePath);
45526
- };
45527
- const writeStorageResponseFile = async (response, filePath) => {
45528
- await fs$2.mkdir(path$1.dirname(filePath), { recursive: true });
45529
- if (response.body === null) {
45530
- await fs$2.writeFile(filePath, /* @__PURE__ */ new Uint8Array());
45531
- return;
45532
- }
45533
- try {
45534
- await pipeline(Readable$1.fromWeb(response.body), createWriteStream(filePath));
45535
- } catch (error) {
45536
- await fs$2.rm(filePath, { force: true });
45537
- throw error;
45538
- }
45539
- };
45540
- //#endregion
45541
- //#region src/promoteBundle.ts
45542
- const LEGACY_BUNDLE_ERROR = "This OTA bundle was created by a version that does not support manifest.json. Copy bundle is not available.";
45543
- const SIGNED_HASH_PREFIX = "sig:";
45544
- const PROMOTE_ASSET_CONCURRENCY = 8;
45545
- function isSignedFileHash(fileHash) {
45546
- return fileHash.startsWith(SIGNED_HASH_PREFIX);
45547
- }
45548
- async function getFileHash(filepath) {
45549
- const file = await fs$2.readFile(filepath);
45550
- return crypto$1.createHash("sha256").update(file).digest("hex");
45551
- }
45552
- async function runWithConcurrency(items, concurrency, task) {
45553
- let nextIndex = 0;
45554
- const workerCount = Math.min(concurrency, items.length);
45555
- await Promise.all(Array.from({ length: workerCount }, async () => {
45556
- while (nextIndex < items.length) {
45557
- const itemIndex = nextIndex;
45558
- nextIndex += 1;
45559
- await task(items[itemIndex]);
45560
- }
45561
- }));
45562
- }
45563
- function verifySignedFileHash({ actualFileHash, publicKey, signedFileHash }) {
45564
- try {
45565
- return crypto$1.verify("RSA-SHA256", Buffer.from(actualFileHash, "hex"), publicKey, Buffer.from(signedFileHash.slice(4), "base64"));
45566
- } catch {
45567
- return false;
45568
- }
45569
- }
45570
- const getRelativeStorageDir = (relativePath) => {
45571
- const normalized = relativePath.replace(/\\/g, "/");
45572
- const dirname = path$1.posix.dirname(normalized);
45573
- return dirname === "." ? "" : dirname;
45574
- };
45575
- function resolvePreparedUploadPath(rootDir, assetPath) {
45576
- const normalizedAssetPath = assetPath.replaceAll("\\", "/");
45577
- const outputPath = path$1.resolve(rootDir, "upload-artifacts", `${normalizedAssetPath}.br`);
45578
- const relativePath = path$1.relative(rootDir, outputPath);
45579
- if (relativePath.startsWith("..") || path$1.isAbsolute(relativePath) || normalizedAssetPath.startsWith("/")) throw new Error(`Invalid manifest asset path: ${assetPath}`);
45580
- return outputPath;
45581
- }
45582
- async function prepareManifestAssetUploadFile({ assetPath, sourcePath, workDir }) {
45583
- if (getManifestAssetDownloadPath(assetPath) === assetPath) return sourcePath;
45584
- const uploadPath = resolvePreparedUploadPath(workDir, assetPath);
45585
- await fs$2.mkdir(path$1.dirname(uploadPath), { recursive: true });
45586
- await pipeline(createReadStream(sourcePath), createBrotliCompress({ params: { [constants$1.BROTLI_PARAM_QUALITY]: 11 } }), createWriteStream(uploadPath));
45587
- return uploadPath;
45588
- }
45589
- async function prepareContentAddressedUploadFile({ sourcePath, storagePath, workDir }) {
45590
- const filename = path$1.posix.basename(storagePath);
45591
- if (path$1.basename(sourcePath) === filename) return sourcePath;
45592
- const uploadPath = path$1.join(workDir, "upload-artifacts", "content-addressed", filename);
45593
- await fs$2.mkdir(path$1.dirname(uploadPath), { recursive: true });
45594
- await fs$2.copyFile(sourcePath, uploadPath);
45595
- return uploadPath;
45596
- }
45597
- async function prepareManifestAssetUploadTargets({ extractDir, manifest, workDir }) {
45598
- const targets = /* @__PURE__ */ new Map();
45599
- const assetPaths = Object.keys(manifest.assets ?? {}).sort((left, right) => left.localeCompare(right));
45600
- for (const assetPath of assetPaths) {
45601
- const asset = manifest.assets?.[assetPath];
45602
- if (!asset?.fileHash) throw new Error(`Manifest file hash not found for ${assetPath}`);
45603
- const sourcePath = resolveExtractedPath(extractDir, assetPath);
45604
- const uploadSourcePath = await prepareManifestAssetUploadFile({
45605
- assetPath,
45606
- sourcePath,
45607
- workDir
45608
- });
45609
- const downloadByteSize = await getStorageFileByteSize(uploadSourcePath);
45610
- const downloadPath = getManifestAssetDownloadPath(assetPath);
45611
- const downloadFileHash = downloadPath !== assetPath ? await getFileHash(uploadSourcePath) : void 0;
45612
- if (downloadFileHash !== void 0 && !isContentAddressedAssetFileHash(downloadFileHash)) throw new Error(`Prepared asset hash must be a lowercase SHA-256 hash: ${assetPath}`);
45613
- const nextAsset = {
45614
- ...asset,
45615
- byteSize: (await fs$2.stat(sourcePath)).size,
45616
- downloadByteSize
45617
- };
45618
- delete nextAsset.downloadFileHash;
45619
- if (downloadFileHash !== void 0) nextAsset.downloadFileHash = downloadFileHash;
45620
- manifest.assets[assetPath] = nextAsset;
45621
- const storagePath = getManifestAssetStoragePath({
45622
- assetPath: downloadPath,
45623
- downloadFileHash,
45624
- fileHash: asset.fileHash
45625
- });
45626
- const contentAddressedUploadPath = await prepareContentAddressedUploadFile({
45627
- sourcePath: uploadSourcePath,
45628
- storagePath,
45629
- workDir
45630
- });
45631
- targets.set(storagePath, {
45632
- assetPath: downloadPath,
45633
- downloadFileHash,
45634
- fileHash: asset.fileHash,
45635
- storagePath,
45636
- uploadSourcePath: contentAddressedUploadPath
45637
- });
45638
- }
45639
- return [...targets.values()];
45640
- }
45641
- function resolveExtractedPath(rootDir, entryName) {
45642
- const normalizedEntryName = entryName.replaceAll("\\", "/");
45643
- const entryPath = path$1.resolve(rootDir, normalizedEntryName);
45644
- const relativePath = path$1.relative(rootDir, entryPath);
45645
- if (relativePath.startsWith("..") || path$1.isAbsolute(relativePath) || normalizedEntryName.startsWith("/")) throw new Error(`Invalid manifest asset path: ${entryName}`);
45646
- return entryPath;
45647
- }
45648
- async function downloadStorageObject(storageUri, storageAdapter, outputPath) {
45649
- const protocol = new URL(storageUri).protocol.replace(":", "");
45650
- if (storageAdapter?.protocol === protocol) {
45651
- await writeStorageFile(storageAdapter, storageUri, outputPath);
45652
- return;
45653
- }
45654
- if (protocol === "http" || protocol === "https") {
45655
- await downloadFromUrl(storageUri, outputPath);
45656
- return;
45657
- }
45658
- throw new Error(`No storage adapter for protocol: ${protocol}`);
45659
- }
45660
- async function downloadFromUrl(fileUrl, filePath) {
45661
- const response = await fetch(fileUrl);
45662
- if (!response.ok) throw new Error(`Failed to download storage object: ${response.statusText}`);
45663
- await writeStorageResponseFile(response, filePath);
45664
- }
45665
- async function downloadManifestAssets({ bundle, manifest, outputDir, storageAdapter, workDir }) {
45666
- await runWithConcurrency(Object.keys(manifest.assets ?? {}).sort((left, right) => left.localeCompare(right)), PROMOTE_ASSET_CONCURRENCY, async (assetPath) => {
45667
- const asset = manifest.assets?.[assetPath];
45668
- if (!asset?.fileHash) throw new Error(`Manifest file hash not found for ${assetPath}`);
45669
- const downloadPath = getManifestAssetDownloadPath(assetPath);
45670
- const storageUri = resolveManifestAssetStorageUri({
45671
- assetBaseStorageUri: bundle.assetBaseStorageUri,
45672
- assetPath: downloadPath,
45673
- downloadFileHash: asset.downloadFileHash,
45674
- fileHash: asset.fileHash
45675
- });
45676
- const transferPath = resolveExtractedPath(workDir, `downloads/${downloadPath}`);
45677
- await fs$2.mkdir(path$1.dirname(transferPath), { recursive: true });
45678
- await downloadStorageObject(storageUri, storageAdapter, transferPath);
45679
- if (asset.downloadFileHash) {
45680
- if (await getFileHash(transferPath) !== asset.downloadFileHash.toLowerCase()) throw new Error(`Manifest download hash mismatch for ${assetPath}`);
45681
- }
45682
- const outputPath = resolveExtractedPath(outputDir, assetPath);
45683
- await fs$2.mkdir(path$1.dirname(outputPath), { recursive: true });
45684
- if (downloadPath === assetPath) await fs$2.copyFile(transferPath, outputPath);
45685
- else await fs$2.writeFile(outputPath, brotliDecompressSync(await fs$2.readFile(transferPath)));
45686
- if (await getFileHash(outputPath) !== asset.fileHash.toLowerCase()) throw new Error(`Manifest file hash mismatch for ${assetPath}`);
45687
- });
45688
- }
45689
- async function createCopiedBundleArtifacts({ bundle, config, nextBundleId, storageAdapter }) {
45690
- const workDir = await fs$2.mkdtemp(path$1.join(os.tmpdir(), "hot-updater-console-promote-"));
45691
- const extractDir = path$1.join(workDir, "bundle");
45692
- const sourceManifestPath = path$1.join(workDir, "source-manifest.json");
45693
- const manifestPath = path$1.join(extractDir, "manifest.json");
45694
- const uploadedStorageUris = [];
45695
- await fs$2.mkdir(extractDir, { recursive: true });
45696
- try {
45697
- await downloadStorageObject(bundle.manifestStorageUri, storageAdapter, sourceManifestPath);
45698
- const actualManifestHash = await getFileHash(sourceManifestPath);
45699
- const signingSession = await prepareBundleSigning(config.signing);
45700
- if (isSignedFileHash(bundle.manifestFileHash)) {
45701
- if (!signingSession) throw new Error("Cannot copy a signed bundle without enabled bundle signing configuration.");
45702
- if (!verifySignedFileHash({
45703
- actualFileHash: actualManifestHash,
45704
- publicKey: signingSession.publicKey,
45705
- signedFileHash: bundle.manifestFileHash
45706
- })) throw new Error("Source manifest signature verification failed.");
45707
- } else if (actualManifestHash !== bundle.manifestFileHash.toLowerCase()) throw new Error("Source manifest file hash verification failed.");
45708
- const manifest = JSON.parse(await fs$2.readFile(sourceManifestPath, "utf8"));
45709
- if (!manifest.assets || typeof manifest.assets !== "object") throw new Error(LEGACY_BUNDLE_ERROR);
45710
- await downloadManifestAssets({
45711
- bundle,
45712
- manifest,
45713
- outputDir: extractDir,
45714
- storageAdapter,
45715
- workDir
45716
- });
45717
- manifest.bundleId = nextBundleId;
45718
- const assetPaths = Object.keys(manifest.assets ?? {}).sort((left, right) => left.localeCompare(right));
45719
- const sourceIsSigned = isSignedFileHash(getManifestFileHash(bundle));
45720
- const manifestHasSignatures = assetPaths.some((assetPath) => Boolean(manifest.assets?.[assetPath]?.signature));
45721
- if (!signingSession && (sourceIsSigned || manifestHasSignatures)) throw new Error("Cannot copy a signed bundle without enabled bundle signing configuration.");
45722
- if (signingSession) {
45723
- await runWithConcurrency(assetPaths, PROMOTE_ASSET_CONCURRENCY, async (assetPath) => {
45724
- const asset = manifest.assets?.[assetPath];
45725
- if (!asset?.fileHash) throw new Error(`Manifest file hash not found for ${assetPath}`);
45726
- if (await getFileHash(resolveExtractedPath(extractDir, assetPath)) !== asset.fileHash.toLowerCase()) throw new Error(`Manifest file hash mismatch for ${assetPath}`);
45727
- });
45728
- await runWithConcurrency(assetPaths, PROMOTE_ASSET_CONCURRENCY, async (assetPath) => {
45729
- const asset = manifest.assets?.[assetPath];
45730
- if (!asset?.fileHash) throw new Error(`Manifest file hash not found for ${assetPath}`);
45731
- asset.signature = await signingSession.signFileHash(asset.fileHash);
45732
- });
45733
- }
45734
- const assetUploadTargets = await prepareManifestAssetUploadTargets({
45735
- extractDir,
45736
- manifest,
45737
- workDir
45738
- });
45739
- const archivePath = path$1.join(workDir, "bundle.tar.br");
45740
- manifest.archive = await createTarBrTargetFiles({
45741
- outfile: archivePath,
45742
- targetFiles: assetPaths.map((name) => ({
45743
- name,
45744
- path: resolveExtractedPath(extractDir, name)
45745
- }))
45746
- });
45747
- await fs$2.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
45748
- const manifestHash = await getFileHash(manifestPath);
45749
- const nextManifestFileHash = signingSession ? `${SIGNED_HASH_PREFIX}${await signingSession.signFileHash(manifestHash)}` : manifestHash;
45750
- const archiveUpload = await putStorageFile(storageAdapter, createBundleStorageKey(nextBundleId), archivePath);
45751
- uploadedStorageUris.push(archiveUpload.storageUri);
45752
- const manifestUpload = await putStorageFile(storageAdapter, createBundleStorageKey(nextBundleId), manifestPath);
45753
- uploadedStorageUris.push(manifestUpload.storageUri);
45754
- const assetBaseStorageUri = createStorageRootUriWithPath(manifestUpload.storageUri, nextBundleId, "assets");
45755
- for (const assetUploadTarget of assetUploadTargets) {
45756
- const storageUri = resolveManifestAssetStorageUri({
45757
- assetBaseStorageUri,
45758
- assetPath: assetUploadTarget.assetPath,
45759
- downloadFileHash: assetUploadTarget.downloadFileHash,
45760
- fileHash: assetUploadTarget.fileHash
45761
- });
45762
- const { exists } = await storageAdapter.exists({ storageUri });
45763
- if (!exists) await putStorageFile(storageAdapter, getRelativeStorageDir(assetUploadTarget.storagePath) ? `assets/${getRelativeStorageDir(assetUploadTarget.storagePath)}` : "assets", assetUploadTarget.uploadSourcePath);
45764
- }
45765
- return {
45766
- bundle: {
45767
- ...bundle,
45768
- id: nextBundleId,
45769
- metadata: stripBundleArtifactMetadata(bundle.metadata),
45770
- assetBaseStorageUri,
45771
- patches: [],
45772
- manifestFileHash: nextManifestFileHash,
45773
- manifestStorageUri: manifestUpload.storageUri
45774
- },
45775
- uploadedStorageUris
45776
- };
45777
- } catch (error) {
45778
- await deleteUploadedCopy(storageAdapter, uploadedStorageUris);
45779
- throw error;
45780
- } finally {
45781
- await fs$2.rm(workDir, {
45782
- recursive: true,
45783
- force: true
45784
- });
45785
- }
45786
- }
45787
- async function deleteUploadedCopy(storageAdapter, storageUris) {
45788
- if (storageUris.length === 0) return;
45789
- for (const storageUri of new Set(storageUris)) try {
45790
- const protocol = new URL(storageUri).protocol.replace(":", "");
45791
- if (storageAdapter.protocol === protocol) await storageAdapter.delete({ storageUri });
45792
- else if (protocol !== "http" && protocol !== "https") throw new Error(`No storage adapter for protocol: ${protocol}`);
45793
- } catch (error) {
45794
- console.error("Failed to delete uploaded bundle copy:", error);
45795
- }
45796
- }
45797
- //#endregion
45798
45458
  //#region src/resolvePackageVersion.ts
45799
45459
  const require$1 = createRequire(import.meta.url);
45800
45460
  const HOT_UPDATER_SERVER_PACKAGE_VERSION_ENV = "HOT_UPDATER_SERVER_PACKAGE_VERSION";
@@ -45836,6 +45496,30 @@ const resolveHotUpdaterServerVersion = (currentPackageName, options) => {
45836
45496
  return process.env["HOT_UPDATER_SERVER_PACKAGE_VERSION"]?.trim() || resolvePackageVersion(currentPackageName, options);
45837
45497
  };
45838
45498
  //#endregion
45499
+ //#region src/storageFiles.ts
45500
+ const getStorageFileByteSize = async (filePath) => {
45501
+ const { size } = await fs$2.stat(filePath, { bigint: true });
45502
+ if (size < 0n || size > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error("Storage file size must be a non-negative safe integer.");
45503
+ return Number(size);
45504
+ };
45505
+ const putStorageFile = async (storage, key, filePath) => {
45506
+ const byteSize = await getStorageFileByteSize(filePath);
45507
+ const source = createReadStream(filePath);
45508
+ try {
45509
+ return {
45510
+ ...await storage.put({
45511
+ key: path$1.posix.join(key, path$1.basename(filePath)),
45512
+ body: Readable$1.toWeb(source),
45513
+ contentLength: byteSize,
45514
+ contentType: getContentType(filePath)
45515
+ }),
45516
+ byteSize
45517
+ };
45518
+ } finally {
45519
+ source.destroy();
45520
+ }
45521
+ };
45522
+ //#endregion
45839
45523
  //#region src/transformEnv.ts
45840
45524
  const transformEnv = (filename, env) => {
45841
45525
  const code = fs.readFileSync(filename, "utf-8");
@@ -45862,4 +45546,4 @@ function transformTemplate(templateString, values) {
45862
45546
  return result;
45863
45547
  }
45864
45548
  //#endregion
45865
- export { BuildLogger, ConfigBuilder, HOT_UPDATER_SERVER_PACKAGE_VERSION_ENV, HotUpdateDirUtil, InitEnvFileError, InitError, LEGACY_BUNDLE_ERROR, LegacyInfrastructureError, MissingInitInputsError, ServerDefinitionError, assembleServer, assertInfrastructureGenerationAtUrl, assertInfrastructureGenerationPayload, assertInitInputs, assertInitProviderInputs, banner, clientAuthOf, typedColors as colors, confirmInitInputPersistence, copyDirToTmp, createCopiedBundleArtifacts, createHotUpdaterConfigScaffold, createHotUpdaterConfigScaffoldFromBuilder, createLogWriter, createMigrator, createTarBrTargetFiles, createZip, decryptJson, defineInitProvider, encryptJson, ensureInstallPackages, generateClientCredential, generateSchema, generatesSchema, getAndroidSdkPath, getBundleSigningPublicKey, getCwd, getHotUpdaterEnvValue, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, getMissingInitInputs, getMissingInitProviderInputs, getPackageManager, getReactNativeMetadatas, getStorageFileByteSize, isServerDefinition, link, loadConfig, loadPlatformConfigs, log, makeEnv, moduleSpecifiersOf, p, prepareBundleSigning, printAppSetup, printBanner, provisionClientCredential, putStorageFile, readBundleSigningPublicKeyFile, readHotUpdaterEnv, readHotUpdaterInitEnv, readPackageUp, renderAppImports, renderAppSetup, renderImportStatements, resolveHotUpdaterServerVersion, resolveInitProviderInput, resolveInitProviderInputs, resolvePackageVersion, serverDefinitionOf, shouldAutoSelectOnlyInitResource, stripAnsi, transformEnv, transformTemplate, writeHotUpdaterConfig, writeHotUpdaterFiles, writeStorageFile, writeStorageResponseFile };
45549
+ export { BuildLogger, ConfigBuilder, HOT_UPDATER_SERVER_PACKAGE_VERSION_ENV, HotUpdateDirUtil, InitEnvFileError, InitError, LegacyInfrastructureError, MissingInitInputsError, ServerDefinitionError, assembleServer, assertInfrastructureGenerationAtUrl, assertInfrastructureGenerationPayload, assertInitInputs, assertInitProviderInputs, banner, clientAuthOf, typedColors as colors, confirmInitInputPersistence, copyDirToTmp, createHotUpdaterConfigScaffold, createHotUpdaterConfigScaffoldFromBuilder, createLogWriter, createMigrator, createTarBrTargetFiles, createZip, decryptJson, defineInitProvider, encryptJson, ensureInstallPackages, generateClientCredential, generateSchema, generatesSchema, getAndroidSdkPath, getBundleSigningPublicKey, getCwd, getHotUpdaterEnvValue, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, getMissingInitInputs, getMissingInitProviderInputs, getPackageManager, getReactNativeMetadatas, getStorageFileByteSize, isServerDefinition, link, loadConfig, loadPlatformConfigs, log, makeEnv, moduleSpecifiersOf, p, prepareBundleSigning, printAppSetup, printBanner, provisionClientCredential, putStorageFile, readBundleSigningPublicKeyFile, readHotUpdaterEnv, readHotUpdaterInitEnv, readPackageUp, renderAppImports, renderAppSetup, renderImportStatements, resolveHotUpdaterServerVersion, resolveInitProviderInput, resolveInitProviderInputs, resolvePackageVersion, serverDefinitionOf, shouldAutoSelectOnlyInitResource, stripAnsi, transformEnv, transformTemplate, writeHotUpdaterConfig, writeHotUpdaterFiles };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hot-updater/cli-tools",
3
- "version": "1.0.0-rc.45",
3
+ "version": "1.0.0-rc.47",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=20.19.0"
@@ -49,9 +49,9 @@
49
49
  "oxc-parser": "0.141.0",
50
50
  "oxc-transform": "0.141.0",
51
51
  "unconfig": "7.5.0",
52
- "@hot-updater/protocol": "1.0.0-rc.45",
53
- "@hot-updater/plugin-core": "1.0.0-rc.45",
54
- "@hot-updater/server": "1.0.0-rc.45"
52
+ "@hot-updater/protocol": "1.0.0-rc.47",
53
+ "@hot-updater/plugin-core": "1.0.0-rc.47",
54
+ "@hot-updater/server": "1.0.0-rc.47"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@clack/prompts": "1.7.0",
@@ -65,7 +65,7 @@
65
65
  "tar": "^7.5.16",
66
66
  "verkit": "0.4.0",
67
67
  "workspace-tools": "^0.41.7",
68
- "@hot-updater/test-utils": "1.0.0-rc.45"
68
+ "@hot-updater/test-utils": "1.0.0-rc.47"
69
69
  },
70
70
  "inlinedDependencies": {
71
71
  "@babel/code-frame": "7.29.0",