@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 +3 -31
- package/dist/index.mjs +30 -346
- package/package.json +5 -5
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,
|
|
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.
|
|
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,
|
|
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 {
|
|
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 {
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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.
|
|
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.
|
|
53
|
-
"@hot-updater/plugin-core": "1.0.0-rc.
|
|
54
|
-
"@hot-updater/server": "1.0.0-rc.
|
|
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.
|
|
68
|
+
"@hot-updater/test-utils": "1.0.0-rc.47"
|
|
69
69
|
},
|
|
70
70
|
"inlinedDependencies": {
|
|
71
71
|
"@babel/code-frame": "7.29.0",
|