@hot-updater/cli-tools 1.0.0-rc.44 → 1.0.0-rc.46

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
@@ -687,28 +687,6 @@ declare const makeEnv: (newEnvVars: Record<string, EnvVarValue>, filePath?: stri
687
687
  */
688
688
  declare const moduleSpecifiersOf: (fileName: string, source: string) => string[];
689
689
  //#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
690
  //#region ../../node_modules/.pnpm/@clack+core@1.4.3/node_modules/@clack/core/dist/index.d.mts
713
691
  declare const actions: readonly ["up", "down", "left", "right", "space", "enter", "cancel"];
714
692
  type Action = (typeof actions)[number];
@@ -1933,8 +1911,6 @@ declare const getStorageFileByteSize: (filePath: string) => Promise<number>;
1933
1911
  declare const putStorageFile: (storage: StorageAdapterWith<"put">, key: string, filePath: string) => Promise<StoragePutResult & {
1934
1912
  byteSize: number;
1935
1913
  }>;
1936
- declare const writeStorageFile: (storage: StorageAdapterWith<"get">, storageUri: string, filePath: string) => Promise<void>;
1937
- declare const writeStorageResponseFile: (response: Response, filePath: string) => Promise<void>;
1938
1914
  //#endregion
1939
1915
  //#region src/transformEnv.d.ts
1940
1916
  declare const transformEnv: <T extends Record<string, string>>(filename: string, env: T) => string;
@@ -1953,4 +1929,4 @@ type TransformTemplateArgs<T extends string> = { [Key in ExtractPlaceholders<T>]
1953
1929
  */
1954
1930
  declare function transformTemplate<T extends string>(templateString: T, values: TransformTemplateArgs<T>): string;
1955
1931
  //#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 };
1932
+ 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",
@@ -44805,7 +44801,8 @@ const renderServerSettings = (scaffold) => {
44805
44801
  const PLUGINS_FILE_PATH = "hotUpdater.plugins.ts";
44806
44802
  /**
44807
44803
  * Whether `text` is the plugins file an older init wrote: comments and a
44808
- * re-export of a provider package's `plugins`, which the config imports now.
44804
+ * re-export of a provider package's former `plugins`, which the config's
44805
+ * `plugins` list replaces.
44809
44806
  */
44810
44807
  const isGeneratedPluginsFile = (text) => /^export\{plugins\}from(["'])@hot-updater\/[\w-]+\1;?$/u.test(text.replace(/^\s*\/\/.*$/gmu, "").replace(/\s+/gu, ""));
44811
44808
  /**
@@ -45495,305 +45492,6 @@ const moduleSpecifiersOf = (fileName, source) => {
45495
45492
  ])];
45496
45493
  };
45497
45494
  //#endregion
45498
- //#region src/storageFiles.ts
45499
- const getStorageFileByteSize = async (filePath) => {
45500
- const { size } = await fs$2.stat(filePath, { bigint: true });
45501
- if (size < 0n || size > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error("Storage file size must be a non-negative safe integer.");
45502
- return Number(size);
45503
- };
45504
- const putStorageFile = async (storage, key, filePath) => {
45505
- const byteSize = await getStorageFileByteSize(filePath);
45506
- const source = createReadStream(filePath);
45507
- try {
45508
- return {
45509
- ...await storage.put({
45510
- key: path$1.posix.join(key, path$1.basename(filePath)),
45511
- body: Readable$1.toWeb(source),
45512
- contentLength: byteSize,
45513
- contentType: getContentType(filePath)
45514
- }),
45515
- byteSize
45516
- };
45517
- } finally {
45518
- source.destroy();
45519
- }
45520
- };
45521
- const writeStorageFile = async (storage, storageUri, filePath) => {
45522
- const { response } = await storage.get({ storageUri });
45523
- if (response === null) throw new Error(`Storage object not found: ${storageUri}`);
45524
- await writeStorageResponseFile(response, filePath);
45525
- };
45526
- const writeStorageResponseFile = async (response, filePath) => {
45527
- await fs$2.mkdir(path$1.dirname(filePath), { recursive: true });
45528
- if (response.body === null) {
45529
- await fs$2.writeFile(filePath, /* @__PURE__ */ new Uint8Array());
45530
- return;
45531
- }
45532
- try {
45533
- await pipeline(Readable$1.fromWeb(response.body), createWriteStream(filePath));
45534
- } catch (error) {
45535
- await fs$2.rm(filePath, { force: true });
45536
- throw error;
45537
- }
45538
- };
45539
- //#endregion
45540
- //#region src/promoteBundle.ts
45541
- const LEGACY_BUNDLE_ERROR = "This OTA bundle was created by a version that does not support manifest.json. Copy bundle is not available.";
45542
- const SIGNED_HASH_PREFIX = "sig:";
45543
- const PROMOTE_ASSET_CONCURRENCY = 8;
45544
- function isSignedFileHash(fileHash) {
45545
- return fileHash.startsWith(SIGNED_HASH_PREFIX);
45546
- }
45547
- async function getFileHash(filepath) {
45548
- const file = await fs$2.readFile(filepath);
45549
- return crypto$1.createHash("sha256").update(file).digest("hex");
45550
- }
45551
- async function runWithConcurrency(items, concurrency, task) {
45552
- let nextIndex = 0;
45553
- const workerCount = Math.min(concurrency, items.length);
45554
- await Promise.all(Array.from({ length: workerCount }, async () => {
45555
- while (nextIndex < items.length) {
45556
- const itemIndex = nextIndex;
45557
- nextIndex += 1;
45558
- await task(items[itemIndex]);
45559
- }
45560
- }));
45561
- }
45562
- function verifySignedFileHash({ actualFileHash, publicKey, signedFileHash }) {
45563
- try {
45564
- return crypto$1.verify("RSA-SHA256", Buffer.from(actualFileHash, "hex"), publicKey, Buffer.from(signedFileHash.slice(4), "base64"));
45565
- } catch {
45566
- return false;
45567
- }
45568
- }
45569
- const getRelativeStorageDir = (relativePath) => {
45570
- const normalized = relativePath.replace(/\\/g, "/");
45571
- const dirname = path$1.posix.dirname(normalized);
45572
- return dirname === "." ? "" : dirname;
45573
- };
45574
- function resolvePreparedUploadPath(rootDir, assetPath) {
45575
- const normalizedAssetPath = assetPath.replaceAll("\\", "/");
45576
- const outputPath = path$1.resolve(rootDir, "upload-artifacts", `${normalizedAssetPath}.br`);
45577
- const relativePath = path$1.relative(rootDir, outputPath);
45578
- if (relativePath.startsWith("..") || path$1.isAbsolute(relativePath) || normalizedAssetPath.startsWith("/")) throw new Error(`Invalid manifest asset path: ${assetPath}`);
45579
- return outputPath;
45580
- }
45581
- async function prepareManifestAssetUploadFile({ assetPath, sourcePath, workDir }) {
45582
- if (getManifestAssetDownloadPath(assetPath) === assetPath) return sourcePath;
45583
- const uploadPath = resolvePreparedUploadPath(workDir, assetPath);
45584
- await fs$2.mkdir(path$1.dirname(uploadPath), { recursive: true });
45585
- await pipeline(createReadStream(sourcePath), createBrotliCompress({ params: { [constants$1.BROTLI_PARAM_QUALITY]: 11 } }), createWriteStream(uploadPath));
45586
- return uploadPath;
45587
- }
45588
- async function prepareContentAddressedUploadFile({ sourcePath, storagePath, workDir }) {
45589
- const filename = path$1.posix.basename(storagePath);
45590
- if (path$1.basename(sourcePath) === filename) return sourcePath;
45591
- const uploadPath = path$1.join(workDir, "upload-artifacts", "content-addressed", filename);
45592
- await fs$2.mkdir(path$1.dirname(uploadPath), { recursive: true });
45593
- await fs$2.copyFile(sourcePath, uploadPath);
45594
- return uploadPath;
45595
- }
45596
- async function prepareManifestAssetUploadTargets({ extractDir, manifest, workDir }) {
45597
- const targets = /* @__PURE__ */ new Map();
45598
- const assetPaths = Object.keys(manifest.assets ?? {}).sort((left, right) => left.localeCompare(right));
45599
- for (const assetPath of assetPaths) {
45600
- const asset = manifest.assets?.[assetPath];
45601
- if (!asset?.fileHash) throw new Error(`Manifest file hash not found for ${assetPath}`);
45602
- const sourcePath = resolveExtractedPath(extractDir, assetPath);
45603
- const uploadSourcePath = await prepareManifestAssetUploadFile({
45604
- assetPath,
45605
- sourcePath,
45606
- workDir
45607
- });
45608
- const downloadByteSize = await getStorageFileByteSize(uploadSourcePath);
45609
- const downloadPath = getManifestAssetDownloadPath(assetPath);
45610
- const downloadFileHash = downloadPath !== assetPath ? await getFileHash(uploadSourcePath) : void 0;
45611
- if (downloadFileHash !== void 0 && !isContentAddressedAssetFileHash(downloadFileHash)) throw new Error(`Prepared asset hash must be a lowercase SHA-256 hash: ${assetPath}`);
45612
- const nextAsset = {
45613
- ...asset,
45614
- byteSize: (await fs$2.stat(sourcePath)).size,
45615
- downloadByteSize
45616
- };
45617
- delete nextAsset.downloadFileHash;
45618
- if (downloadFileHash !== void 0) nextAsset.downloadFileHash = downloadFileHash;
45619
- manifest.assets[assetPath] = nextAsset;
45620
- const storagePath = getManifestAssetStoragePath({
45621
- assetPath: downloadPath,
45622
- downloadFileHash,
45623
- fileHash: asset.fileHash
45624
- });
45625
- const contentAddressedUploadPath = await prepareContentAddressedUploadFile({
45626
- sourcePath: uploadSourcePath,
45627
- storagePath,
45628
- workDir
45629
- });
45630
- targets.set(storagePath, {
45631
- assetPath: downloadPath,
45632
- downloadFileHash,
45633
- fileHash: asset.fileHash,
45634
- storagePath,
45635
- uploadSourcePath: contentAddressedUploadPath
45636
- });
45637
- }
45638
- return [...targets.values()];
45639
- }
45640
- function resolveExtractedPath(rootDir, entryName) {
45641
- const normalizedEntryName = entryName.replaceAll("\\", "/");
45642
- const entryPath = path$1.resolve(rootDir, normalizedEntryName);
45643
- const relativePath = path$1.relative(rootDir, entryPath);
45644
- if (relativePath.startsWith("..") || path$1.isAbsolute(relativePath) || normalizedEntryName.startsWith("/")) throw new Error(`Invalid manifest asset path: ${entryName}`);
45645
- return entryPath;
45646
- }
45647
- async function downloadStorageObject(storageUri, storageAdapter, outputPath) {
45648
- const protocol = new URL(storageUri).protocol.replace(":", "");
45649
- if (storageAdapter?.protocol === protocol) {
45650
- await writeStorageFile(storageAdapter, storageUri, outputPath);
45651
- return;
45652
- }
45653
- if (protocol === "http" || protocol === "https") {
45654
- await downloadFromUrl(storageUri, outputPath);
45655
- return;
45656
- }
45657
- throw new Error(`No storage adapter for protocol: ${protocol}`);
45658
- }
45659
- async function downloadFromUrl(fileUrl, filePath) {
45660
- const response = await fetch(fileUrl);
45661
- if (!response.ok) throw new Error(`Failed to download storage object: ${response.statusText}`);
45662
- await writeStorageResponseFile(response, filePath);
45663
- }
45664
- async function downloadManifestAssets({ bundle, manifest, outputDir, storageAdapter, workDir }) {
45665
- await runWithConcurrency(Object.keys(manifest.assets ?? {}).sort((left, right) => left.localeCompare(right)), PROMOTE_ASSET_CONCURRENCY, async (assetPath) => {
45666
- const asset = manifest.assets?.[assetPath];
45667
- if (!asset?.fileHash) throw new Error(`Manifest file hash not found for ${assetPath}`);
45668
- const downloadPath = getManifestAssetDownloadPath(assetPath);
45669
- const storageUri = resolveManifestAssetStorageUri({
45670
- assetBaseStorageUri: bundle.assetBaseStorageUri,
45671
- assetPath: downloadPath,
45672
- downloadFileHash: asset.downloadFileHash,
45673
- fileHash: asset.fileHash
45674
- });
45675
- const transferPath = resolveExtractedPath(workDir, `downloads/${downloadPath}`);
45676
- await fs$2.mkdir(path$1.dirname(transferPath), { recursive: true });
45677
- await downloadStorageObject(storageUri, storageAdapter, transferPath);
45678
- if (asset.downloadFileHash) {
45679
- if (await getFileHash(transferPath) !== asset.downloadFileHash.toLowerCase()) throw new Error(`Manifest download hash mismatch for ${assetPath}`);
45680
- }
45681
- const outputPath = resolveExtractedPath(outputDir, assetPath);
45682
- await fs$2.mkdir(path$1.dirname(outputPath), { recursive: true });
45683
- if (downloadPath === assetPath) await fs$2.copyFile(transferPath, outputPath);
45684
- else await fs$2.writeFile(outputPath, brotliDecompressSync(await fs$2.readFile(transferPath)));
45685
- if (await getFileHash(outputPath) !== asset.fileHash.toLowerCase()) throw new Error(`Manifest file hash mismatch for ${assetPath}`);
45686
- });
45687
- }
45688
- async function createCopiedBundleArtifacts({ bundle, config, nextBundleId, storageAdapter }) {
45689
- const workDir = await fs$2.mkdtemp(path$1.join(os.tmpdir(), "hot-updater-console-promote-"));
45690
- const extractDir = path$1.join(workDir, "bundle");
45691
- const sourceManifestPath = path$1.join(workDir, "source-manifest.json");
45692
- const manifestPath = path$1.join(extractDir, "manifest.json");
45693
- const uploadedStorageUris = [];
45694
- await fs$2.mkdir(extractDir, { recursive: true });
45695
- try {
45696
- await downloadStorageObject(bundle.manifestStorageUri, storageAdapter, sourceManifestPath);
45697
- const actualManifestHash = await getFileHash(sourceManifestPath);
45698
- const signingSession = await prepareBundleSigning(config.signing);
45699
- if (isSignedFileHash(bundle.manifestFileHash)) {
45700
- if (!signingSession) throw new Error("Cannot copy a signed bundle without enabled bundle signing configuration.");
45701
- if (!verifySignedFileHash({
45702
- actualFileHash: actualManifestHash,
45703
- publicKey: signingSession.publicKey,
45704
- signedFileHash: bundle.manifestFileHash
45705
- })) throw new Error("Source manifest signature verification failed.");
45706
- } else if (actualManifestHash !== bundle.manifestFileHash.toLowerCase()) throw new Error("Source manifest file hash verification failed.");
45707
- const manifest = JSON.parse(await fs$2.readFile(sourceManifestPath, "utf8"));
45708
- if (!manifest.assets || typeof manifest.assets !== "object") throw new Error(LEGACY_BUNDLE_ERROR);
45709
- await downloadManifestAssets({
45710
- bundle,
45711
- manifest,
45712
- outputDir: extractDir,
45713
- storageAdapter,
45714
- workDir
45715
- });
45716
- manifest.bundleId = nextBundleId;
45717
- const assetPaths = Object.keys(manifest.assets ?? {}).sort((left, right) => left.localeCompare(right));
45718
- const sourceIsSigned = isSignedFileHash(getManifestFileHash(bundle));
45719
- const manifestHasSignatures = assetPaths.some((assetPath) => Boolean(manifest.assets?.[assetPath]?.signature));
45720
- if (!signingSession && (sourceIsSigned || manifestHasSignatures)) throw new Error("Cannot copy a signed bundle without enabled bundle signing configuration.");
45721
- if (signingSession) {
45722
- await runWithConcurrency(assetPaths, PROMOTE_ASSET_CONCURRENCY, async (assetPath) => {
45723
- const asset = manifest.assets?.[assetPath];
45724
- if (!asset?.fileHash) throw new Error(`Manifest file hash not found for ${assetPath}`);
45725
- if (await getFileHash(resolveExtractedPath(extractDir, assetPath)) !== asset.fileHash.toLowerCase()) throw new Error(`Manifest file hash mismatch for ${assetPath}`);
45726
- });
45727
- await runWithConcurrency(assetPaths, PROMOTE_ASSET_CONCURRENCY, async (assetPath) => {
45728
- const asset = manifest.assets?.[assetPath];
45729
- if (!asset?.fileHash) throw new Error(`Manifest file hash not found for ${assetPath}`);
45730
- asset.signature = await signingSession.signFileHash(asset.fileHash);
45731
- });
45732
- }
45733
- const assetUploadTargets = await prepareManifestAssetUploadTargets({
45734
- extractDir,
45735
- manifest,
45736
- workDir
45737
- });
45738
- const archivePath = path$1.join(workDir, "bundle.tar.br");
45739
- manifest.archive = await createTarBrTargetFiles({
45740
- outfile: archivePath,
45741
- targetFiles: assetPaths.map((name) => ({
45742
- name,
45743
- path: resolveExtractedPath(extractDir, name)
45744
- }))
45745
- });
45746
- await fs$2.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
45747
- const manifestHash = await getFileHash(manifestPath);
45748
- const nextManifestFileHash = signingSession ? `${SIGNED_HASH_PREFIX}${await signingSession.signFileHash(manifestHash)}` : manifestHash;
45749
- const archiveUpload = await putStorageFile(storageAdapter, createBundleStorageKey(nextBundleId), archivePath);
45750
- uploadedStorageUris.push(archiveUpload.storageUri);
45751
- const manifestUpload = await putStorageFile(storageAdapter, createBundleStorageKey(nextBundleId), manifestPath);
45752
- uploadedStorageUris.push(manifestUpload.storageUri);
45753
- const assetBaseStorageUri = createStorageRootUriWithPath(manifestUpload.storageUri, nextBundleId, "assets");
45754
- for (const assetUploadTarget of assetUploadTargets) {
45755
- const storageUri = resolveManifestAssetStorageUri({
45756
- assetBaseStorageUri,
45757
- assetPath: assetUploadTarget.assetPath,
45758
- downloadFileHash: assetUploadTarget.downloadFileHash,
45759
- fileHash: assetUploadTarget.fileHash
45760
- });
45761
- const { exists } = await storageAdapter.exists({ storageUri });
45762
- if (!exists) await putStorageFile(storageAdapter, getRelativeStorageDir(assetUploadTarget.storagePath) ? `assets/${getRelativeStorageDir(assetUploadTarget.storagePath)}` : "assets", assetUploadTarget.uploadSourcePath);
45763
- }
45764
- return {
45765
- bundle: {
45766
- ...bundle,
45767
- id: nextBundleId,
45768
- metadata: stripBundleArtifactMetadata(bundle.metadata),
45769
- assetBaseStorageUri,
45770
- patches: [],
45771
- manifestFileHash: nextManifestFileHash,
45772
- manifestStorageUri: manifestUpload.storageUri
45773
- },
45774
- uploadedStorageUris
45775
- };
45776
- } catch (error) {
45777
- await deleteUploadedCopy(storageAdapter, uploadedStorageUris);
45778
- throw error;
45779
- } finally {
45780
- await fs$2.rm(workDir, {
45781
- recursive: true,
45782
- force: true
45783
- });
45784
- }
45785
- }
45786
- async function deleteUploadedCopy(storageAdapter, storageUris) {
45787
- if (storageUris.length === 0) return;
45788
- for (const storageUri of new Set(storageUris)) try {
45789
- const protocol = new URL(storageUri).protocol.replace(":", "");
45790
- if (storageAdapter.protocol === protocol) await storageAdapter.delete({ storageUri });
45791
- else if (protocol !== "http" && protocol !== "https") throw new Error(`No storage adapter for protocol: ${protocol}`);
45792
- } catch (error) {
45793
- console.error("Failed to delete uploaded bundle copy:", error);
45794
- }
45795
- }
45796
- //#endregion
45797
45495
  //#region src/resolvePackageVersion.ts
45798
45496
  const require$1 = createRequire(import.meta.url);
45799
45497
  const HOT_UPDATER_SERVER_PACKAGE_VERSION_ENV = "HOT_UPDATER_SERVER_PACKAGE_VERSION";
@@ -45835,6 +45533,30 @@ const resolveHotUpdaterServerVersion = (currentPackageName, options) => {
45835
45533
  return process.env["HOT_UPDATER_SERVER_PACKAGE_VERSION"]?.trim() || resolvePackageVersion(currentPackageName, options);
45836
45534
  };
45837
45535
  //#endregion
45536
+ //#region src/storageFiles.ts
45537
+ const getStorageFileByteSize = async (filePath) => {
45538
+ const { size } = await fs$2.stat(filePath, { bigint: true });
45539
+ if (size < 0n || size > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error("Storage file size must be a non-negative safe integer.");
45540
+ return Number(size);
45541
+ };
45542
+ const putStorageFile = async (storage, key, filePath) => {
45543
+ const byteSize = await getStorageFileByteSize(filePath);
45544
+ const source = createReadStream(filePath);
45545
+ try {
45546
+ return {
45547
+ ...await storage.put({
45548
+ key: path$1.posix.join(key, path$1.basename(filePath)),
45549
+ body: Readable$1.toWeb(source),
45550
+ contentLength: byteSize,
45551
+ contentType: getContentType(filePath)
45552
+ }),
45553
+ byteSize
45554
+ };
45555
+ } finally {
45556
+ source.destroy();
45557
+ }
45558
+ };
45559
+ //#endregion
45838
45560
  //#region src/transformEnv.ts
45839
45561
  const transformEnv = (filename, env) => {
45840
45562
  const code = fs.readFileSync(filename, "utf-8");
@@ -45861,4 +45583,4 @@ function transformTemplate(templateString, values) {
45861
45583
  return result;
45862
45584
  }
45863
45585
  //#endregion
45864
- 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 };
45586
+ 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.44",
3
+ "version": "1.0.0-rc.46",
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.44",
53
- "@hot-updater/plugin-core": "1.0.0-rc.44",
54
- "@hot-updater/server": "1.0.0-rc.44"
52
+ "@hot-updater/protocol": "1.0.0-rc.46",
53
+ "@hot-updater/plugin-core": "1.0.0-rc.46",
54
+ "@hot-updater/server": "1.0.0-rc.46"
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.44"
68
+ "@hot-updater/test-utils": "1.0.0-rc.46"
69
69
  },
70
70
  "inlinedDependencies": {
71
71
  "@babel/code-frame": "7.29.0",