hot-updater 1.0.4 → 1.0.6
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.mjs +9 -18
- package/dist/infra-templates/aws/lambda/index.cjs +217 -17
- package/dist/infra-templates/aws/template.json +10 -10
- package/dist/infra-templates/azure/azure/function/index.mjs +134 -4
- package/dist/infra-templates/azure/template.json +11 -11
- package/dist/infra-templates/cloudflare/template.json +10 -10
- package/dist/infra-templates/cloudflare/worker/dist/README.md +1 -1
- package/dist/infra-templates/cloudflare/worker/dist/index.js +142 -4
- package/dist/infra-templates/cloudflare/worker/dist/index.js.map +2 -2
- package/dist/infra-templates/firebase/firebase/functions/index.cjs +189 -10
- package/dist/infra-templates/firebase/template.json +11 -11
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-plugin-core/dist/serverPlugin/coreReads.d.cts +12 -2
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-plugin-core/dist/serverPlugin/coreReads.d.mts +12 -2
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-protocol/dist/index.cjs +9 -0
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-protocol/dist/index.d.cts +33 -1
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-protocol/dist/index.d.mts +33 -1
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-protocol/dist/index.mjs +8 -1
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-server/dist/core/api.d.cts +2 -0
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-server/dist/core/api.d.mts +2 -0
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-server/dist/core/reads.cjs +17 -3
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-server/dist/core/reads.mjs +19 -5
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-server/dist/db/updateArtifacts.cjs +99 -0
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-server/dist/db/updateArtifacts.mjs +99 -2
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-server/dist/handler.cjs +2 -0
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-server/dist/handler.mjs +2 -0
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-server/dist/handlerReleaseCatalogRoutes.cjs +37 -0
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-server/dist/handlerReleaseCatalogRoutes.mjs +38 -1
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-server/dist/package.cjs +1 -1
- package/dist/infra-templates/supabase/supabase/functions/hot-updater-v1/_hot-updater/hot-updater-server/dist/package.mjs +1 -1
- package/dist/infra-templates/supabase/template.json +10 -10
- package/dist/infra-templates/vercel/SETUP.md +2 -4
- package/dist/infra-templates/vercel/template.json +11 -11
- package/dist/infra-templates/vercel/vercel/api/index.mjs +134 -4
- package/package.json +24 -24
package/dist/index.mjs
CHANGED
|
@@ -25866,13 +25866,6 @@ const createNativeBuild = async ({ platform, outputPath, buildAdapter, builder }
|
|
|
25866
25866
|
p.log.info(`Artifact stored at ${colors.blueBright(relativePath)}`);
|
|
25867
25867
|
};
|
|
25868
25868
|
//#endregion
|
|
25869
|
-
//#region src/utils/setIosMinBundleIdSlotIntoInfoPlist.ts
|
|
25870
|
-
const setIosMinBundleIdSlotIntoInfoPlist = async ({ infoPlistPaths }) => {
|
|
25871
|
-
const iosParser = new IosConfigParser(infoPlistPaths);
|
|
25872
|
-
if (!await iosParser.exists()) throw new Error("No iOS Info.plist files found");
|
|
25873
|
-
await iosParser.set("HOT_UPDATER_MIN_BUNDLE_ID", "$(HOT_UPDATER_MIN_BUNDLE_ID)");
|
|
25874
|
-
};
|
|
25875
|
-
//#endregion
|
|
25876
25869
|
//#region src/utils/native/prepareNativeBuild.ts
|
|
25877
25870
|
async function prepareNativeBuild(options) {
|
|
25878
25871
|
const cwd = getCwd();
|
|
@@ -25946,17 +25939,15 @@ async function prepareNativeBuild(options) {
|
|
|
25946
25939
|
}
|
|
25947
25940
|
const artifactResultStorePath = options.outputPath ?? path.join(HotUpdateDirUtil.getDefaultOutputPath({ cwd }), "build", platform, scheme);
|
|
25948
25941
|
const resolvedOutputPath = path.isAbsolute(artifactResultStorePath) ? artifactResultStorePath : path.join(cwd, artifactResultStorePath);
|
|
25949
|
-
if (platform === "ios") {
|
|
25950
|
-
|
|
25951
|
-
|
|
25952
|
-
|
|
25953
|
-
config,
|
|
25954
|
-
|
|
25955
|
-
|
|
25956
|
-
|
|
25957
|
-
|
|
25958
|
-
};
|
|
25959
|
-
} else if (platform === "android") return {
|
|
25942
|
+
if (platform === "ios") return {
|
|
25943
|
+
outputPath: resolvedOutputPath,
|
|
25944
|
+
config,
|
|
25945
|
+
iosSchemeConfig: await enrichNativeBuildIosScheme({
|
|
25946
|
+
scheme: config.nativeBuild.ios[scheme],
|
|
25947
|
+
hotUpdaterSchemeName: scheme
|
|
25948
|
+
})
|
|
25949
|
+
};
|
|
25950
|
+
else if (platform === "android") return {
|
|
25960
25951
|
outputPath: resolvedOutputPath,
|
|
25961
25952
|
config,
|
|
25962
25953
|
androidSchemeConfig: await enrichNativeBuildAndroidScheme({
|
|
@@ -30,7 +30,6 @@ let _aws_sdk_client_cloudfront = require("@aws-sdk/client-cloudfront");
|
|
|
30
30
|
let _aws_sdk_lib_dynamodb = require("@aws-sdk/lib-dynamodb");
|
|
31
31
|
let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
|
|
32
32
|
let _aws_sdk_lib_storage = require("@aws-sdk/lib-storage");
|
|
33
|
-
//#region ../plugin-core/dist/contentAddressedAssets.mjs
|
|
34
33
|
const SHA256_FILE_HASH_RE = /^[0-9a-f]{64}$/;
|
|
35
34
|
const isContentAddressedAssetFileHash = (value) => {
|
|
36
35
|
return typeof value === "string" && SHA256_FILE_HASH_RE.test(value);
|
|
@@ -4550,7 +4549,7 @@ const createKvAdapter = ({ store, tablePrefix = "" }) => {
|
|
|
4550
4549
|
};
|
|
4551
4550
|
//#endregion
|
|
4552
4551
|
//#region ../../packages/server/dist/version.mjs
|
|
4553
|
-
const HOT_UPDATER_SERVER_VERSION = "1.0.
|
|
4552
|
+
const HOT_UPDATER_SERVER_VERSION = "1.0.6";
|
|
4554
4553
|
//#endregion
|
|
4555
4554
|
//#region ../../packages/server/dist/handlerVersionRoutes.mjs
|
|
4556
4555
|
/**
|
|
@@ -4841,6 +4840,11 @@ async function resolveAssets(plan, resolveFileUrl) {
|
|
|
4841
4840
|
const resolvedEntries = entries.filter((entry) => entry !== null);
|
|
4842
4841
|
return { assets: Object.fromEntries(resolvedEntries.map((entry) => entry.asset)) };
|
|
4843
4842
|
}
|
|
4843
|
+
/**
|
|
4844
|
+
* Legacy: artifact protocol v1. Signs a URL for every file of the target, which grows with the
|
|
4845
|
+
* Bundle's file count; apps whose SDK predates protocol v2 still ask for it.
|
|
4846
|
+
* Remove with the `/artifacts/v1` route once no supported app uses it.
|
|
4847
|
+
*/
|
|
4844
4848
|
async function resolveManifestArtifacts({ currentBundle, resolveFileUrl, readStorageText, targetBundle }) {
|
|
4845
4849
|
const manifestStorageUri = targetBundle ? getManifestStorageUri(targetBundle) : null;
|
|
4846
4850
|
const manifestFileHash = targetBundle ? getManifestFileHash(targetBundle) : null;
|
|
@@ -4874,6 +4878,98 @@ async function resolveManifestArtifacts({ currentBundle, resolveFileUrl, readSto
|
|
|
4874
4878
|
...archiveUrl ? { archiveUrl } : {}
|
|
4875
4879
|
};
|
|
4876
4880
|
}
|
|
4881
|
+
/** The target's manifest and its plan, or null for a Bundle without one. */
|
|
4882
|
+
async function loadTargetPlan({ currentBundle, readStorageText, targetBundle }) {
|
|
4883
|
+
const manifestStorageUri = getManifestStorageUri(targetBundle);
|
|
4884
|
+
const manifestFileHash = getManifestFileHash(targetBundle);
|
|
4885
|
+
const assetBaseStorageUri = getAssetBaseStorageUri(targetBundle);
|
|
4886
|
+
if (!manifestStorageUri || !manifestFileHash || !assetBaseStorageUri) return null;
|
|
4887
|
+
const targetManifest = await fetchBundleManifest(manifestStorageUri, readStorageText);
|
|
4888
|
+
if (!targetManifest) return null;
|
|
4889
|
+
return {
|
|
4890
|
+
manifestFileHash,
|
|
4891
|
+
manifestStorageUri,
|
|
4892
|
+
plan: createManifestArtifactPlan({
|
|
4893
|
+
assetBaseStorageUri,
|
|
4894
|
+
currentBundle,
|
|
4895
|
+
targetBundle,
|
|
4896
|
+
targetManifest
|
|
4897
|
+
}),
|
|
4898
|
+
targetManifest
|
|
4899
|
+
};
|
|
4900
|
+
}
|
|
4901
|
+
/**
|
|
4902
|
+
* Artifact protocol v2: the manifest, the archive, and the one Hermes patch,
|
|
4903
|
+
* but no URL per file. A device asks {@link resolveManifestArtifactFiles}
|
|
4904
|
+
* for the files it downloads one by one, so signing and the response stay
|
|
4905
|
+
* the same size however many files the Bundle has.
|
|
4906
|
+
*/
|
|
4907
|
+
async function resolveManifestArtifactsV2({ currentBundle, resolveFileUrl, readStorageText, targetBundle }) {
|
|
4908
|
+
const target = await loadTargetPlan({
|
|
4909
|
+
currentBundle,
|
|
4910
|
+
readStorageText,
|
|
4911
|
+
targetBundle
|
|
4912
|
+
});
|
|
4913
|
+
if (!target) return null;
|
|
4914
|
+
const manifestUrl = await resolveFileUrl(target.manifestStorageUri);
|
|
4915
|
+
if (!manifestUrl) return null;
|
|
4916
|
+
const patched = target.plan.assets.find((asset) => asset.patch !== null);
|
|
4917
|
+
let patch;
|
|
4918
|
+
if (patched?.patch && !(patched.file.byteSize !== null && patched.patch.byteSize !== null && patched.patch.byteSize >= patched.file.byteSize)) try {
|
|
4919
|
+
const patchUrl = await resolveFileUrl(patched.patch.storageUri);
|
|
4920
|
+
if (patchUrl) patch = {
|
|
4921
|
+
algorithm: "bsdiff",
|
|
4922
|
+
assetPath: patched.assetPath,
|
|
4923
|
+
baseBundleId: patched.patch.baseBundleId,
|
|
4924
|
+
baseFileHash: patched.patch.baseFileHash,
|
|
4925
|
+
patchFileHash: patched.patch.patchFileHash,
|
|
4926
|
+
patchUrl,
|
|
4927
|
+
...patched.patch.byteSize === null ? {} : { byteSize: patched.patch.byteSize }
|
|
4928
|
+
};
|
|
4929
|
+
} catch {}
|
|
4930
|
+
let archiveUrl = null;
|
|
4931
|
+
const archive = target.targetManifest.archive;
|
|
4932
|
+
if (archive && isContentAddressedAssetFileHash(archive.downloadFileHash) && (asByteSize(archive.downloadByteSize) ?? 0) > 0 && (asByteSize(archive.tarByteSize) ?? 0) > 0) try {
|
|
4933
|
+
archiveUrl = await resolveFileUrl(getBundleArchiveStorageUri({
|
|
4934
|
+
manifestStorageUri: target.manifestStorageUri,
|
|
4935
|
+
bundleId: targetBundle.id
|
|
4936
|
+
}));
|
|
4937
|
+
} catch {}
|
|
4938
|
+
return {
|
|
4939
|
+
artifactProtocolVersion: 2,
|
|
4940
|
+
manifestFileHash: target.manifestFileHash,
|
|
4941
|
+
manifestUrl,
|
|
4942
|
+
...archiveUrl ? { archiveUrl } : {},
|
|
4943
|
+
...patch ? { patch } : {}
|
|
4944
|
+
};
|
|
4945
|
+
}
|
|
4946
|
+
/**
|
|
4947
|
+
* Download URLs for the files a device downloads one by one. Only paths the
|
|
4948
|
+
* target's manifest names are signed, at most
|
|
4949
|
+
* {@link ARTIFACT_FILES_REQUEST_LIMIT}; null when any path is unknown or a
|
|
4950
|
+
* URL cannot be resolved.
|
|
4951
|
+
*/
|
|
4952
|
+
async function resolveManifestArtifactFiles({ paths, resolveFileUrl, readStorageText, targetBundle }) {
|
|
4953
|
+
if (paths.length === 0 || paths.length > 100 || new Set(paths).size !== paths.length) return null;
|
|
4954
|
+
const target = await loadTargetPlan({
|
|
4955
|
+
currentBundle: null,
|
|
4956
|
+
readStorageText,
|
|
4957
|
+
targetBundle
|
|
4958
|
+
});
|
|
4959
|
+
if (!target) return null;
|
|
4960
|
+
const planned = new Map(target.plan.assets.map((asset) => [asset.assetPath, asset]));
|
|
4961
|
+
const requested = paths.map((path) => planned.get(path));
|
|
4962
|
+
if (requested.some((asset) => asset === void 0)) return null;
|
|
4963
|
+
const files = await Promise.all(requested.map(async (asset) => {
|
|
4964
|
+
const url = await resolveFileUrl(asset.file.storageUri);
|
|
4965
|
+
if (!url) return null;
|
|
4966
|
+
const file = { url };
|
|
4967
|
+
if (asset.file.compression) file.compression = asset.file.compression;
|
|
4968
|
+
return [asset.assetPath, file];
|
|
4969
|
+
}));
|
|
4970
|
+
if (files.some((file) => file === null)) return null;
|
|
4971
|
+
return { files: Object.fromEntries(files.filter((file) => file !== null)) };
|
|
4972
|
+
}
|
|
4877
4973
|
//#endregion
|
|
4878
4974
|
//#region ../../node_modules/.pnpm/verkit@0.4.0/node_modules/verkit/dist/comparison-DenM3wCn.js
|
|
4879
4975
|
const LETTER_DASH_NUMBER = "[a-zA-Z0-9-]";
|
|
@@ -5347,20 +5443,34 @@ const createCoreReads = (db, storage) => {
|
|
|
5347
5443
|
return projectReleaseCatalogRow(row === null ? null : toCatalogRow(row), input, scopeKey);
|
|
5348
5444
|
},
|
|
5349
5445
|
/** Artifact resolution: one batch read of both bundles and one unique read of their patch. */
|
|
5350
|
-
async
|
|
5446
|
+
getArtifactInfo: (async (targetBundleId, currentBundleId, artifactProtocolVersion) => {
|
|
5351
5447
|
const { readStorageText } = storage;
|
|
5352
|
-
if (artifactProtocolVersion !== 1 || readStorageText === void 0) return null;
|
|
5448
|
+
if (artifactProtocolVersion !== 1 && artifactProtocolVersion !== 2 || readStorageText === void 0) return null;
|
|
5353
5449
|
const withCurrent = currentBundleId !== NIL_UUID;
|
|
5354
5450
|
const [[target, current], patch] = await Promise.all([db.findByKeys("bundles", withCurrent ? [{ id: targetBundleId }, { id: currentBundleId }] : [{ id: targetBundleId }]), withCurrent ? db.findOne("bundle_patches", {
|
|
5355
5451
|
bundle_id: targetBundleId,
|
|
5356
5452
|
base_bundle_id: currentBundleId
|
|
5357
5453
|
}) : null]);
|
|
5358
5454
|
if (target === null || target === void 0) return null;
|
|
5359
|
-
|
|
5455
|
+
const input = {
|
|
5360
5456
|
currentBundle: current ? rowToBundle(toBundleRow(current)) : null,
|
|
5361
5457
|
readStorageText,
|
|
5362
5458
|
resolveFileUrl: storage.resolveFileUrl,
|
|
5363
5459
|
targetBundle: rowToBundle(toBundleRow(target), patch === null ? [] : [toPatchRow(patch)])
|
|
5460
|
+
};
|
|
5461
|
+
return artifactProtocolVersion === 2 ? resolveManifestArtifactsV2(input) : resolveManifestArtifacts(input);
|
|
5462
|
+
}),
|
|
5463
|
+
/** A Bundle's file URLs: one point read of the bundle and its manifest. */
|
|
5464
|
+
async getArtifactFiles(bundleId, paths) {
|
|
5465
|
+
const { readStorageText } = storage;
|
|
5466
|
+
if (readStorageText === void 0) return null;
|
|
5467
|
+
const row = await db.findOne("bundles", { id: bundleId });
|
|
5468
|
+
if (row === null) return null;
|
|
5469
|
+
return resolveManifestArtifactFiles({
|
|
5470
|
+
paths,
|
|
5471
|
+
readStorageText,
|
|
5472
|
+
resolveFileUrl: storage.resolveFileUrl,
|
|
5473
|
+
targetBundle: rowToBundle(toBundleRow(row))
|
|
5364
5474
|
});
|
|
5365
5475
|
},
|
|
5366
5476
|
/** A bundle, its patches, and how many patches start from it. */
|
|
@@ -6709,6 +6819,7 @@ const ADMIN_ROUTES = [
|
|
|
6709
6819
|
//#region ../../packages/server/dist/handlerReleaseCatalogRoutes.mjs
|
|
6710
6820
|
const CATALOG_CONTENT_TYPE = "application/vnd.hot-updater.release-catalog+json; version=1";
|
|
6711
6821
|
const ARTIFACT_CONTENT_TYPE = "application/vnd.hot-updater.artifact+json; version=1";
|
|
6822
|
+
const ARTIFACT_V2_CONTENT_TYPE = "application/vnd.hot-updater.artifact+json; version=2";
|
|
6712
6823
|
const ORIGIN_CACHE_TTL_MS = 5e3;
|
|
6713
6824
|
const ORIGIN_CACHE_MAX_ENTRIES = 128;
|
|
6714
6825
|
/** Marks the 404 of a scope that has no catalog. */
|
|
@@ -6731,6 +6842,18 @@ const CATALOG_CACHE_CONTROL = "public, max-age=0, s-maxage=5";
|
|
|
6731
6842
|
* `Cache-Control`.
|
|
6732
6843
|
*/
|
|
6733
6844
|
const CATALOG_CDN_CACHE_CONTROL = "public, max-age=5, stale-while-revalidate=5, stale-if-error=0";
|
|
6845
|
+
/** The paths of an artifact files request, or null when malformed. */
|
|
6846
|
+
const readArtifactFilePaths = async (request) => {
|
|
6847
|
+
let body;
|
|
6848
|
+
try {
|
|
6849
|
+
body = await request.json();
|
|
6850
|
+
} catch {
|
|
6851
|
+
return null;
|
|
6852
|
+
}
|
|
6853
|
+
const paths = body?.paths;
|
|
6854
|
+
if (!Array.isArray(paths) || paths.length === 0 || paths.length > 100 || !paths.every((path) => typeof path === "string" && path.length > 0) || new Set(paths).size !== paths.length) return null;
|
|
6855
|
+
return paths;
|
|
6856
|
+
};
|
|
6734
6857
|
const privateNotFound = () => Response.json({ error: "Not found" }, {
|
|
6735
6858
|
status: 404,
|
|
6736
6859
|
headers: { "cache-control": "private, no-store" }
|
|
@@ -6851,6 +6974,30 @@ const createReleaseCatalogRouteHandlers = () => {
|
|
|
6851
6974
|
if (invalid) return invalid;
|
|
6852
6975
|
return catalogResponse(await loadCatalog(`fingerprint:${JSON.stringify(input)}`, () => core.getReleaseCatalog(input)), request);
|
|
6853
6976
|
},
|
|
6977
|
+
artifactV2: async (params, _request, { core }) => {
|
|
6978
|
+
const info = await core.getArtifactInfo(requireRouteParam(params, "targetBundleId"), requireRouteParam(params, "currentBundleId"), 2);
|
|
6979
|
+
if (info === null) return privateNotFound();
|
|
6980
|
+
return new Response(JSON.stringify(info), {
|
|
6981
|
+
status: 200,
|
|
6982
|
+
headers: {
|
|
6983
|
+
"cache-control": "private, no-store",
|
|
6984
|
+
"content-type": ARTIFACT_V2_CONTENT_TYPE
|
|
6985
|
+
}
|
|
6986
|
+
});
|
|
6987
|
+
},
|
|
6988
|
+
artifactFilesV2: async (params, request, { core }) => {
|
|
6989
|
+
const paths = await readArtifactFilePaths(request);
|
|
6990
|
+
if (paths === null) return badRequest(`paths must be 1 to 100 unique strings`);
|
|
6991
|
+
const files = await core.getArtifactFiles(requireRouteParam(params, "targetBundleId"), paths);
|
|
6992
|
+
if (files === null) return privateNotFound();
|
|
6993
|
+
return new Response(JSON.stringify(files), {
|
|
6994
|
+
status: 200,
|
|
6995
|
+
headers: {
|
|
6996
|
+
"cache-control": "private, no-store",
|
|
6997
|
+
"content-type": ARTIFACT_V2_CONTENT_TYPE
|
|
6998
|
+
}
|
|
6999
|
+
});
|
|
7000
|
+
},
|
|
6854
7001
|
artifactV1: async (params, _request, { core }) => {
|
|
6855
7002
|
const info = await core.getArtifactInfo(requireRouteParam(params, "targetBundleId"), requireRouteParam(params, "currentBundleId"), 1);
|
|
6856
7003
|
if (info === null) return privateNotFound();
|
|
@@ -7002,6 +7149,8 @@ function createHotUpdaterHandlers({ api, clientPolicy, endpoints = [], plugins =
|
|
|
7002
7149
|
addClientRoute("GET", "/release-catalogs/app-version/:platform/:channelKey/:appVersion", "appVersionReleaseCatalog");
|
|
7003
7150
|
addClientRoute("GET", "/release-catalogs/fingerprint/:platform/:channelKey/:fingerprintHash", "fingerprintReleaseCatalog");
|
|
7004
7151
|
addClientRoute("GET", "/artifacts/v1/:targetBundleId/from/:currentBundleId", "artifactV1");
|
|
7152
|
+
addClientRoute("GET", "/artifacts/v2/:targetBundleId/from/:currentBundleId", "artifactV2");
|
|
7153
|
+
addClientRoute("POST", "/artifacts/v2/:targetBundleId/files", "artifactFilesV2");
|
|
7005
7154
|
const adminRouter = createRouter();
|
|
7006
7155
|
const addAdminRoute = mount(adminRouter, true);
|
|
7007
7156
|
addAdminRoute("GET", "/version", "adminVersion");
|
|
@@ -13240,6 +13389,8 @@ const applySsmRuntimeAwsConfig = (config) => {
|
|
|
13240
13389
|
//#endregion
|
|
13241
13390
|
//#region src/cloudFrontDownloadUrl.ts
|
|
13242
13391
|
const ONE_YEAR_IN_SECONDS = 31536e3;
|
|
13392
|
+
/** Signatures kept per process; one per Bundle plus one for shared assets. */
|
|
13393
|
+
const MAX_CACHED_SIGNATURES = 1e3;
|
|
13243
13394
|
const privateKeyCache = /* @__PURE__ */ new Map();
|
|
13244
13395
|
const getPrivateKeyFromSsm = async (region, parameterName) => {
|
|
13245
13396
|
const value = (await new _aws_sdk_client_ssm.SSM(applySsmRuntimeAwsConfig({ region })).getParameter({
|
|
@@ -13263,18 +13414,67 @@ const resolvePrivateKey = (config) => {
|
|
|
13263
13414
|
privateKeyCache.set(cacheKey, pending);
|
|
13264
13415
|
return pending;
|
|
13265
13416
|
};
|
|
13266
|
-
|
|
13267
|
-
|
|
13268
|
-
|
|
13269
|
-
|
|
13270
|
-
|
|
13271
|
-
|
|
13272
|
-
|
|
13273
|
-
|
|
13274
|
-
|
|
13275
|
-
|
|
13276
|
-
|
|
13277
|
-
|
|
13417
|
+
/**
|
|
13418
|
+
* The URL path one signature covers: every file of one Bundle
|
|
13419
|
+
* (`bundles/<bundleId>/*`), or every content-addressed asset (`assets/*`),
|
|
13420
|
+
* whose SHA-256 names cannot be listed without a manifest that names them.
|
|
13421
|
+
* Any other object is signed alone.
|
|
13422
|
+
*/
|
|
13423
|
+
const getSignedPathScope = (pathname) => {
|
|
13424
|
+
const segments = pathname.split("/");
|
|
13425
|
+
const assets = segments.length - 4;
|
|
13426
|
+
if (segments[assets] === "assets" && segments[assets + 1] === "sha256") return `${segments.slice(0, assets + 1).join("/")}/*`;
|
|
13427
|
+
const bundles = segments.lastIndexOf(BUNDLE_STORAGE_PREFIX);
|
|
13428
|
+
if (bundles >= 0 && segments.length - bundles > 2) return `${segments.slice(0, bundles + 2).join("/")}/*`;
|
|
13429
|
+
return pathname;
|
|
13430
|
+
};
|
|
13431
|
+
/**
|
|
13432
|
+
* Download URLs signed with a CloudFront key pair. An update check returns a
|
|
13433
|
+
* URL for every file of the Bundle, hundreds for an app with many assets, so
|
|
13434
|
+
* one custom-policy signature covers a Bundle's files and another the shared
|
|
13435
|
+
* assets: each is reused for every URL under it until half its lifetime has
|
|
13436
|
+
* passed, instead of one RSA signature per file.
|
|
13437
|
+
*/
|
|
13438
|
+
const cloudFrontDownloadUrl = (config) => {
|
|
13439
|
+
/**
|
|
13440
|
+
* Signed query strings by policy resource, for this configuration only.
|
|
13441
|
+
* A signing in flight is shared, so a batch of URLs asks the private key
|
|
13442
|
+
* once per scope, and a cached signature needs no key at all.
|
|
13443
|
+
*/
|
|
13444
|
+
const signatureCache = /* @__PURE__ */ new Map();
|
|
13445
|
+
return async ({ storageUri }) => {
|
|
13446
|
+
const storageUrl = new URL(storageUri);
|
|
13447
|
+
if (storageUrl.protocol !== "s3:") throw new Error("CloudFront download URLs require an s3 storage URI.");
|
|
13448
|
+
const url = new URL(config.publicBaseUrl);
|
|
13449
|
+
url.pathname = storageUrl.pathname;
|
|
13450
|
+
url.search = "";
|
|
13451
|
+
url.hash = "";
|
|
13452
|
+
const resource = `${url.origin}${getSignedPathScope(url.pathname)}`;
|
|
13453
|
+
const cached = signatureCache.get(resource);
|
|
13454
|
+
if (cached && Date.now() < cached.renewAt) return { url: `${url}${await cached.query}` };
|
|
13455
|
+
const expiresSeconds = config.expiresSeconds ?? ONE_YEAR_IN_SECONDS;
|
|
13456
|
+
const signedAt = Date.now();
|
|
13457
|
+
const query = resolvePrivateKey(config).then((privateKey) => new URL((0, _aws_sdk_cloudfront_signer.getSignedUrl)({
|
|
13458
|
+
url: url.toString(),
|
|
13459
|
+
keyPairId: config.keyPairId,
|
|
13460
|
+
privateKey,
|
|
13461
|
+
policy: JSON.stringify({ Statement: [{
|
|
13462
|
+
Resource: resource,
|
|
13463
|
+
Condition: { DateLessThan: { "AWS:EpochTime": Math.floor((signedAt + expiresSeconds * 1e3) / 1e3) } }
|
|
13464
|
+
}] })
|
|
13465
|
+
})).search);
|
|
13466
|
+
const entry = {
|
|
13467
|
+
query,
|
|
13468
|
+
renewAt: signedAt + expiresSeconds * 1e3 / 2
|
|
13469
|
+
};
|
|
13470
|
+
signatureCache.delete(resource);
|
|
13471
|
+
signatureCache.set(resource, entry);
|
|
13472
|
+
if (signatureCache.size > MAX_CACHED_SIGNATURES) signatureCache.delete(signatureCache.keys().next().value);
|
|
13473
|
+
query.catch(() => {
|
|
13474
|
+
if (signatureCache.get(resource) === entry) signatureCache.delete(resource);
|
|
13475
|
+
});
|
|
13476
|
+
return { url: `${url}${await query}` };
|
|
13477
|
+
};
|
|
13278
13478
|
};
|
|
13279
13479
|
//#endregion
|
|
13280
13480
|
//#region src/cloudFrontInvalidation.ts
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"provider": "aws",
|
|
4
|
-
"cliVersion": "1.0.
|
|
5
|
-
"providerVersion": "1.0.
|
|
6
|
-
"serverVersion": "1.0.
|
|
4
|
+
"cliVersion": "1.0.6",
|
|
5
|
+
"providerVersion": "1.0.6",
|
|
6
|
+
"serverVersion": "1.0.6",
|
|
7
7
|
"infrastructureGeneration": 1,
|
|
8
8
|
"clientAuth": {
|
|
9
9
|
"plugin": "apiKeys",
|
|
@@ -65,13 +65,13 @@
|
|
|
65
65
|
}
|
|
66
66
|
],
|
|
67
67
|
"packages": {
|
|
68
|
-
"hot-updater": "1.0.
|
|
69
|
-
"@hot-updater/server": "1.0.
|
|
70
|
-
"@hot-updater/react-native": "1.0.
|
|
71
|
-
"@hot-updater/aws": "1.0.
|
|
72
|
-
"@hot-updater/bare": "1.0.
|
|
73
|
-
"@hot-updater/rock": "1.0.
|
|
74
|
-
"@hot-updater/expo": "1.0.
|
|
68
|
+
"hot-updater": "1.0.6",
|
|
69
|
+
"@hot-updater/server": "1.0.6",
|
|
70
|
+
"@hot-updater/react-native": "1.0.6",
|
|
71
|
+
"@hot-updater/aws": "1.0.6",
|
|
72
|
+
"@hot-updater/bare": "1.0.6",
|
|
73
|
+
"@hot-updater/rock": "1.0.6",
|
|
74
|
+
"@hot-updater/expo": "1.0.6",
|
|
75
75
|
"@aws-sdk/credential-providers": "3.1066.0"
|
|
76
76
|
},
|
|
77
77
|
"requiredInputs": {
|
|
@@ -7998,7 +7998,7 @@ const createKvAdapter = ({ store, tablePrefix = "" }) => {
|
|
|
7998
7998
|
};
|
|
7999
7999
|
//#endregion
|
|
8000
8000
|
//#region ../../packages/server/dist/version.mjs
|
|
8001
|
-
const HOT_UPDATER_SERVER_VERSION = "1.0.
|
|
8001
|
+
const HOT_UPDATER_SERVER_VERSION = "1.0.6";
|
|
8002
8002
|
//#endregion
|
|
8003
8003
|
//#region ../../packages/server/dist/handlerVersionRoutes.mjs
|
|
8004
8004
|
const createVersionRouteHandlers = (plugins = []) => {
|
|
@@ -8287,6 +8287,85 @@ async function resolveManifestArtifacts({ currentBundle, resolveFileUrl, readSto
|
|
|
8287
8287
|
...archiveUrl ? { archiveUrl } : {}
|
|
8288
8288
|
};
|
|
8289
8289
|
}
|
|
8290
|
+
async function loadTargetPlan({ currentBundle, readStorageText, targetBundle }) {
|
|
8291
|
+
const manifestStorageUri = getManifestStorageUri(targetBundle);
|
|
8292
|
+
const manifestFileHash = getManifestFileHash(targetBundle);
|
|
8293
|
+
const assetBaseStorageUri = getAssetBaseStorageUri(targetBundle);
|
|
8294
|
+
if (!manifestStorageUri || !manifestFileHash || !assetBaseStorageUri) return null;
|
|
8295
|
+
const targetManifest = await fetchBundleManifest(manifestStorageUri, readStorageText);
|
|
8296
|
+
if (!targetManifest) return null;
|
|
8297
|
+
return {
|
|
8298
|
+
manifestFileHash,
|
|
8299
|
+
manifestStorageUri,
|
|
8300
|
+
plan: createManifestArtifactPlan({
|
|
8301
|
+
assetBaseStorageUri,
|
|
8302
|
+
currentBundle,
|
|
8303
|
+
targetBundle,
|
|
8304
|
+
targetManifest
|
|
8305
|
+
}),
|
|
8306
|
+
targetManifest
|
|
8307
|
+
};
|
|
8308
|
+
}
|
|
8309
|
+
async function resolveManifestArtifactsV2({ currentBundle, resolveFileUrl, readStorageText, targetBundle }) {
|
|
8310
|
+
const target = await loadTargetPlan({
|
|
8311
|
+
currentBundle,
|
|
8312
|
+
readStorageText,
|
|
8313
|
+
targetBundle
|
|
8314
|
+
});
|
|
8315
|
+
if (!target) return null;
|
|
8316
|
+
const manifestUrl = await resolveFileUrl(target.manifestStorageUri);
|
|
8317
|
+
if (!manifestUrl) return null;
|
|
8318
|
+
const patched = target.plan.assets.find((asset) => asset.patch !== null);
|
|
8319
|
+
let patch;
|
|
8320
|
+
if (patched?.patch && !(patched.file.byteSize !== null && patched.patch.byteSize !== null && patched.patch.byteSize >= patched.file.byteSize)) try {
|
|
8321
|
+
const patchUrl = await resolveFileUrl(patched.patch.storageUri);
|
|
8322
|
+
if (patchUrl) patch = {
|
|
8323
|
+
algorithm: "bsdiff",
|
|
8324
|
+
assetPath: patched.assetPath,
|
|
8325
|
+
baseBundleId: patched.patch.baseBundleId,
|
|
8326
|
+
baseFileHash: patched.patch.baseFileHash,
|
|
8327
|
+
patchFileHash: patched.patch.patchFileHash,
|
|
8328
|
+
patchUrl,
|
|
8329
|
+
...patched.patch.byteSize === null ? {} : { byteSize: patched.patch.byteSize }
|
|
8330
|
+
};
|
|
8331
|
+
} catch {}
|
|
8332
|
+
let archiveUrl = null;
|
|
8333
|
+
const archive = target.targetManifest.archive;
|
|
8334
|
+
if (archive && isContentAddressedAssetFileHash(archive.downloadFileHash) && (asByteSize(archive.downloadByteSize) ?? 0) > 0 && (asByteSize(archive.tarByteSize) ?? 0) > 0) try {
|
|
8335
|
+
archiveUrl = await resolveFileUrl(getBundleArchiveStorageUri({
|
|
8336
|
+
manifestStorageUri: target.manifestStorageUri,
|
|
8337
|
+
bundleId: targetBundle.id
|
|
8338
|
+
}));
|
|
8339
|
+
} catch {}
|
|
8340
|
+
return {
|
|
8341
|
+
artifactProtocolVersion: 2,
|
|
8342
|
+
manifestFileHash: target.manifestFileHash,
|
|
8343
|
+
manifestUrl,
|
|
8344
|
+
...archiveUrl ? { archiveUrl } : {},
|
|
8345
|
+
...patch ? { patch } : {}
|
|
8346
|
+
};
|
|
8347
|
+
}
|
|
8348
|
+
async function resolveManifestArtifactFiles({ paths, resolveFileUrl, readStorageText, targetBundle }) {
|
|
8349
|
+
if (paths.length === 0 || paths.length > 100 || new Set(paths).size !== paths.length) return null;
|
|
8350
|
+
const target = await loadTargetPlan({
|
|
8351
|
+
currentBundle: null,
|
|
8352
|
+
readStorageText,
|
|
8353
|
+
targetBundle
|
|
8354
|
+
});
|
|
8355
|
+
if (!target) return null;
|
|
8356
|
+
const planned = new Map(target.plan.assets.map((asset) => [asset.assetPath, asset]));
|
|
8357
|
+
const requested = paths.map((path) => planned.get(path));
|
|
8358
|
+
if (requested.some((asset) => asset === void 0)) return null;
|
|
8359
|
+
const files = await Promise.all(requested.map(async (asset) => {
|
|
8360
|
+
const url = await resolveFileUrl(asset.file.storageUri);
|
|
8361
|
+
if (!url) return null;
|
|
8362
|
+
const file = { url };
|
|
8363
|
+
if (asset.file.compression) file.compression = asset.file.compression;
|
|
8364
|
+
return [asset.assetPath, file];
|
|
8365
|
+
}));
|
|
8366
|
+
if (files.some((file) => file === null)) return null;
|
|
8367
|
+
return { files: Object.fromEntries(files.filter((file) => file !== null)) };
|
|
8368
|
+
}
|
|
8290
8369
|
//#endregion
|
|
8291
8370
|
//#region ../../node_modules/.pnpm/verkit@0.4.0/node_modules/verkit/dist/comparison-DenM3wCn.js
|
|
8292
8371
|
const LETTER_DASH_NUMBER = "[a-zA-Z0-9-]";
|
|
@@ -8748,20 +8827,33 @@ const createCoreReads = (db, storage) => {
|
|
|
8748
8827
|
const row = await db.findOne("release_catalogs", { scope_key: scopeKey });
|
|
8749
8828
|
return projectReleaseCatalogRow(row === null ? null : toCatalogRow(row), input, scopeKey);
|
|
8750
8829
|
},
|
|
8751
|
-
async
|
|
8830
|
+
getArtifactInfo: (async (targetBundleId, currentBundleId, artifactProtocolVersion) => {
|
|
8752
8831
|
const { readStorageText } = storage;
|
|
8753
|
-
if (artifactProtocolVersion !== 1 || readStorageText === void 0) return null;
|
|
8832
|
+
if (artifactProtocolVersion !== 1 && artifactProtocolVersion !== 2 || readStorageText === void 0) return null;
|
|
8754
8833
|
const withCurrent = currentBundleId !== NIL_UUID;
|
|
8755
8834
|
const [[target, current], patch] = await Promise.all([db.findByKeys("bundles", withCurrent ? [{ id: targetBundleId }, { id: currentBundleId }] : [{ id: targetBundleId }]), withCurrent ? db.findOne("bundle_patches", {
|
|
8756
8835
|
bundle_id: targetBundleId,
|
|
8757
8836
|
base_bundle_id: currentBundleId
|
|
8758
8837
|
}) : null]);
|
|
8759
8838
|
if (target === null || target === void 0) return null;
|
|
8760
|
-
|
|
8839
|
+
const input = {
|
|
8761
8840
|
currentBundle: current ? rowToBundle(toBundleRow(current)) : null,
|
|
8762
8841
|
readStorageText,
|
|
8763
8842
|
resolveFileUrl: storage.resolveFileUrl,
|
|
8764
8843
|
targetBundle: rowToBundle(toBundleRow(target), patch === null ? [] : [toPatchRow(patch)])
|
|
8844
|
+
};
|
|
8845
|
+
return artifactProtocolVersion === 2 ? resolveManifestArtifactsV2(input) : resolveManifestArtifacts(input);
|
|
8846
|
+
}),
|
|
8847
|
+
async getArtifactFiles(bundleId, paths) {
|
|
8848
|
+
const { readStorageText } = storage;
|
|
8849
|
+
if (readStorageText === void 0) return null;
|
|
8850
|
+
const row = await db.findOne("bundles", { id: bundleId });
|
|
8851
|
+
if (row === null) return null;
|
|
8852
|
+
return resolveManifestArtifactFiles({
|
|
8853
|
+
paths,
|
|
8854
|
+
readStorageText,
|
|
8855
|
+
resolveFileUrl: storage.resolveFileUrl,
|
|
8856
|
+
targetBundle: rowToBundle(toBundleRow(row))
|
|
8765
8857
|
});
|
|
8766
8858
|
},
|
|
8767
8859
|
async getBundle(id) {
|
|
@@ -10038,11 +10130,23 @@ const ADMIN_ROUTES = [
|
|
|
10038
10130
|
//#region ../../packages/server/dist/handlerReleaseCatalogRoutes.mjs
|
|
10039
10131
|
const CATALOG_CONTENT_TYPE = "application/vnd.hot-updater.release-catalog+json; version=1";
|
|
10040
10132
|
const ARTIFACT_CONTENT_TYPE = "application/vnd.hot-updater.artifact+json; version=1";
|
|
10133
|
+
const ARTIFACT_V2_CONTENT_TYPE = "application/vnd.hot-updater.artifact+json; version=2";
|
|
10041
10134
|
const ORIGIN_CACHE_TTL_MS = 5e3;
|
|
10042
10135
|
const ORIGIN_CACHE_MAX_ENTRIES = 128;
|
|
10043
10136
|
const NO_CATALOG_HEADER = "x-hot-updater-catalog";
|
|
10044
10137
|
const CATALOG_CACHE_CONTROL = "public, max-age=0, s-maxage=5";
|
|
10045
10138
|
const CATALOG_CDN_CACHE_CONTROL = "public, max-age=5, stale-while-revalidate=5, stale-if-error=0";
|
|
10139
|
+
const readArtifactFilePaths = async (request) => {
|
|
10140
|
+
let body;
|
|
10141
|
+
try {
|
|
10142
|
+
body = await request.json();
|
|
10143
|
+
} catch {
|
|
10144
|
+
return null;
|
|
10145
|
+
}
|
|
10146
|
+
const paths = body?.paths;
|
|
10147
|
+
if (!Array.isArray(paths) || paths.length === 0 || paths.length > 100 || !paths.every((path) => typeof path === "string" && path.length > 0) || new Set(paths).size !== paths.length) return null;
|
|
10148
|
+
return paths;
|
|
10149
|
+
};
|
|
10046
10150
|
const privateNotFound = () => Response.json({ error: "Not found" }, {
|
|
10047
10151
|
status: 404,
|
|
10048
10152
|
headers: { "cache-control": "private, no-store" }
|
|
@@ -10152,6 +10256,30 @@ const createReleaseCatalogRouteHandlers = () => {
|
|
|
10152
10256
|
if (invalid) return invalid;
|
|
10153
10257
|
return catalogResponse(await loadCatalog(`fingerprint:${JSON.stringify(input)}`, () => core.getReleaseCatalog(input)), request);
|
|
10154
10258
|
},
|
|
10259
|
+
artifactV2: async (params, _request, { core }) => {
|
|
10260
|
+
const info = await core.getArtifactInfo(requireRouteParam(params, "targetBundleId"), requireRouteParam(params, "currentBundleId"), 2);
|
|
10261
|
+
if (info === null) return privateNotFound();
|
|
10262
|
+
return new Response(JSON.stringify(info), {
|
|
10263
|
+
status: 200,
|
|
10264
|
+
headers: {
|
|
10265
|
+
"cache-control": "private, no-store",
|
|
10266
|
+
"content-type": ARTIFACT_V2_CONTENT_TYPE
|
|
10267
|
+
}
|
|
10268
|
+
});
|
|
10269
|
+
},
|
|
10270
|
+
artifactFilesV2: async (params, request, { core }) => {
|
|
10271
|
+
const paths = await readArtifactFilePaths(request);
|
|
10272
|
+
if (paths === null) return badRequest(`paths must be 1 to 100 unique strings`);
|
|
10273
|
+
const files = await core.getArtifactFiles(requireRouteParam(params, "targetBundleId"), paths);
|
|
10274
|
+
if (files === null) return privateNotFound();
|
|
10275
|
+
return new Response(JSON.stringify(files), {
|
|
10276
|
+
status: 200,
|
|
10277
|
+
headers: {
|
|
10278
|
+
"cache-control": "private, no-store",
|
|
10279
|
+
"content-type": ARTIFACT_V2_CONTENT_TYPE
|
|
10280
|
+
}
|
|
10281
|
+
});
|
|
10282
|
+
},
|
|
10155
10283
|
artifactV1: async (params, _request, { core }) => {
|
|
10156
10284
|
const info = await core.getArtifactInfo(requireRouteParam(params, "targetBundleId"), requireRouteParam(params, "currentBundleId"), 1);
|
|
10157
10285
|
if (info === null) return privateNotFound();
|
|
@@ -10302,6 +10430,8 @@ function createHotUpdaterHandlers({ api, clientPolicy, endpoints = [], plugins =
|
|
|
10302
10430
|
addClientRoute("GET", "/release-catalogs/app-version/:platform/:channelKey/:appVersion", "appVersionReleaseCatalog");
|
|
10303
10431
|
addClientRoute("GET", "/release-catalogs/fingerprint/:platform/:channelKey/:fingerprintHash", "fingerprintReleaseCatalog");
|
|
10304
10432
|
addClientRoute("GET", "/artifacts/v1/:targetBundleId/from/:currentBundleId", "artifactV1");
|
|
10433
|
+
addClientRoute("GET", "/artifacts/v2/:targetBundleId/from/:currentBundleId", "artifactV2");
|
|
10434
|
+
addClientRoute("POST", "/artifacts/v2/:targetBundleId/files", "artifactFilesV2");
|
|
10305
10435
|
const adminRouter = createRouter();
|
|
10306
10436
|
const addAdminRoute = mount(adminRouter, true);
|
|
10307
10437
|
addAdminRoute("GET", "/version", "adminVersion");
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"provider": "azure",
|
|
4
|
-
"cliVersion": "1.0.
|
|
5
|
-
"providerVersion": "1.0.
|
|
6
|
-
"serverVersion": "1.0.
|
|
4
|
+
"cliVersion": "1.0.6",
|
|
5
|
+
"providerVersion": "1.0.6",
|
|
6
|
+
"serverVersion": "1.0.6",
|
|
7
7
|
"infrastructureGeneration": 1,
|
|
8
8
|
"clientAuth": {
|
|
9
9
|
"plugin": "apiKeys",
|
|
@@ -60,14 +60,14 @@
|
|
|
60
60
|
}
|
|
61
61
|
],
|
|
62
62
|
"packages": {
|
|
63
|
-
"hot-updater": "1.0.
|
|
64
|
-
"@hot-updater/server": "1.0.
|
|
65
|
-
"@hot-updater/react-native": "1.0.
|
|
66
|
-
"@hot-updater/azure": "1.0.
|
|
67
|
-
"@hot-updater/bare": "1.0.
|
|
68
|
-
"@hot-updater/rock": "1.0.
|
|
69
|
-
"@hot-updater/expo": "1.0.
|
|
70
|
-
"@hot-updater/plugin-core": "1.0.
|
|
63
|
+
"hot-updater": "1.0.6",
|
|
64
|
+
"@hot-updater/server": "1.0.6",
|
|
65
|
+
"@hot-updater/react-native": "1.0.6",
|
|
66
|
+
"@hot-updater/azure": "1.0.6",
|
|
67
|
+
"@hot-updater/bare": "1.0.6",
|
|
68
|
+
"@hot-updater/rock": "1.0.6",
|
|
69
|
+
"@hot-updater/expo": "1.0.6",
|
|
70
|
+
"@hot-updater/plugin-core": "1.0.6"
|
|
71
71
|
},
|
|
72
72
|
"requiredInputs": {
|
|
73
73
|
"subscriptionId": null,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"provider": "cloudflare",
|
|
4
|
-
"cliVersion": "1.0.
|
|
5
|
-
"providerVersion": "1.0.
|
|
6
|
-
"serverVersion": "1.0.
|
|
4
|
+
"cliVersion": "1.0.6",
|
|
5
|
+
"providerVersion": "1.0.6",
|
|
6
|
+
"serverVersion": "1.0.6",
|
|
7
7
|
"infrastructureGeneration": 1,
|
|
8
8
|
"clientAuth": {
|
|
9
9
|
"plugin": "apiKeys",
|
|
@@ -61,13 +61,13 @@
|
|
|
61
61
|
}
|
|
62
62
|
],
|
|
63
63
|
"packages": {
|
|
64
|
-
"hot-updater": "1.0.
|
|
65
|
-
"@hot-updater/server": "1.0.
|
|
66
|
-
"@hot-updater/react-native": "1.0.
|
|
67
|
-
"@hot-updater/cloudflare": "1.0.
|
|
68
|
-
"@hot-updater/bare": "1.0.
|
|
69
|
-
"@hot-updater/rock": "1.0.
|
|
70
|
-
"@hot-updater/expo": "1.0.
|
|
64
|
+
"hot-updater": "1.0.6",
|
|
65
|
+
"@hot-updater/server": "1.0.6",
|
|
66
|
+
"@hot-updater/react-native": "1.0.6",
|
|
67
|
+
"@hot-updater/cloudflare": "1.0.6",
|
|
68
|
+
"@hot-updater/bare": "1.0.6",
|
|
69
|
+
"@hot-updater/rock": "1.0.6",
|
|
70
|
+
"@hot-updater/expo": "1.0.6"
|
|
71
71
|
},
|
|
72
72
|
"requiredInputs": {
|
|
73
73
|
"workerName": null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
This folder contains the built output assets for the worker "hot-updater" generated at 2026-10-
|
|
1
|
+
This folder contains the built output assets for the worker "hot-updater" generated at 2026-10-14T16:15:36.859Z.
|