@hot-updater/azure 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/functions/index.mjs +134 -4
- package/package.json +6 -6
package/dist/functions/index.mjs
CHANGED
|
@@ -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");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/azure",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.6",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=22"
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
"@azure/core-auth": "1.11.0",
|
|
43
43
|
"@azure/cosmos": "4.10.1",
|
|
44
44
|
"@azure/storage-blob": "12.34.0",
|
|
45
|
-
"@hot-updater/cli-tools": "1.0.
|
|
46
|
-
"@hot-updater/plugin-core": "1.0.
|
|
47
|
-
"@hot-updater/protocol": "1.0.
|
|
48
|
-
"@hot-updater/server": "1.0.
|
|
45
|
+
"@hot-updater/cli-tools": "1.0.6",
|
|
46
|
+
"@hot-updater/plugin-core": "1.0.6",
|
|
47
|
+
"@hot-updater/protocol": "1.0.6",
|
|
48
|
+
"@hot-updater/server": "1.0.6"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@azure/functions": "4.16.5",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"execa": "9.5.2",
|
|
54
54
|
"jszip": "^3.10.1",
|
|
55
55
|
"vitest": "4.1.4",
|
|
56
|
-
"@hot-updater/test-utils": "1.0.
|
|
56
|
+
"@hot-updater/test-utils": "1.0.6"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"build": "tsdown",
|