@hot-updater/server 0.36.7 → 1.0.0-rc.0

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.
Files changed (291) hide show
  1. package/dist/adapters/databaseConstraintErrors.cjs +23 -0
  2. package/dist/adapters/databaseConstraintErrors.mjs +21 -0
  3. package/dist/adapters/databasePluginUtils.cjs +117 -0
  4. package/dist/adapters/databasePluginUtils.mjs +106 -0
  5. package/dist/adapters/drizzle.cjs +53 -176
  6. package/dist/adapters/drizzle.d.cts +6 -4
  7. package/dist/adapters/drizzle.d.mts +6 -4
  8. package/dist/adapters/drizzle.mjs +54 -177
  9. package/dist/adapters/drizzleCrud.cjs +250 -0
  10. package/dist/adapters/drizzleCrud.mjs +249 -0
  11. package/dist/adapters/drizzleLazyDB.cjs +84 -36
  12. package/dist/adapters/drizzleLazyDB.mjs +84 -36
  13. package/dist/adapters/drizzleQuery.cjs +68 -0
  14. package/dist/adapters/drizzleQuery.mjs +67 -0
  15. package/dist/adapters/kysely.cjs +18 -138
  16. package/dist/adapters/kysely.d.cts +3 -12
  17. package/dist/adapters/kysely.d.mts +3 -12
  18. package/dist/adapters/kysely.mjs +19 -139
  19. package/dist/adapters/kyselyCrud.cjs +254 -0
  20. package/dist/adapters/kyselyCrud.mjs +253 -0
  21. package/dist/adapters/kyselyQuery.cjs +56 -0
  22. package/dist/adapters/kyselyQuery.mjs +55 -0
  23. package/dist/adapters/mongodb.cjs +21 -174
  24. package/dist/adapters/mongodb.d.cts +3 -2
  25. package/dist/adapters/mongodb.d.mts +3 -2
  26. package/dist/adapters/mongodb.mjs +21 -174
  27. package/dist/adapters/mongodbCollections.cjs +36 -0
  28. package/dist/adapters/mongodbCollections.mjs +30 -0
  29. package/dist/adapters/mongodbQuery.cjs +77 -0
  30. package/dist/adapters/mongodbQuery.mjs +70 -0
  31. package/dist/adapters/mongodbReads.cjs +148 -0
  32. package/dist/adapters/mongodbReads.mjs +147 -0
  33. package/dist/adapters/mongodbWrites.cjs +134 -0
  34. package/dist/adapters/mongodbWrites.mjs +133 -0
  35. package/dist/adapters/prisma.cjs +205 -180
  36. package/dist/adapters/prisma.d.cts +3 -3
  37. package/dist/adapters/prisma.d.mts +3 -3
  38. package/dist/adapters/prisma.mjs +206 -181
  39. package/dist/adapters/prismaQuery.cjs +58 -0
  40. package/dist/adapters/prismaQuery.mjs +56 -0
  41. package/dist/adapters/prismaRows.cjs +198 -0
  42. package/dist/adapters/prismaRows.mjs +188 -0
  43. package/dist/analytics/bounded/activeOverview.cjs +87 -0
  44. package/dist/analytics/bounded/activeOverview.mjs +85 -0
  45. package/dist/analytics/bounded/installationSearch.cjs +42 -0
  46. package/dist/analytics/bounded/installationSearch.mjs +42 -0
  47. package/dist/analytics/bounded/persistence.cjs +39 -0
  48. package/dist/analytics/bounded/persistence.mjs +38 -0
  49. package/dist/analytics/bounded/provider.cjs +170 -0
  50. package/dist/analytics/bounded/provider.d.cts +7 -0
  51. package/dist/analytics/bounded/provider.d.mts +7 -0
  52. package/dist/analytics/bounded/provider.mjs +170 -0
  53. package/dist/analytics/bounded/scan.cjs +144 -0
  54. package/dist/analytics/bounded/scan.mjs +138 -0
  55. package/dist/analytics/domain.d.cts +121 -0
  56. package/dist/analytics/domain.d.mts +121 -0
  57. package/dist/analytics/errors.cjs +22 -0
  58. package/dist/analytics/errors.mjs +20 -0
  59. package/dist/analytics/eventInput.cjs +111 -0
  60. package/dist/analytics/eventInput.mjs +111 -0
  61. package/dist/analytics/persistence.d.cts +6 -0
  62. package/dist/analytics/persistence.d.mts +6 -0
  63. package/dist/analytics/queryInput.cjs +58 -0
  64. package/dist/analytics/queryInput.mjs +55 -0
  65. package/dist/analytics/routes.cjs +63 -0
  66. package/dist/analytics/routes.mjs +61 -0
  67. package/dist/analytics/types.d.cts +17 -0
  68. package/dist/analytics/types.d.mts +17 -0
  69. package/dist/apiKeys.cjs +139 -0
  70. package/dist/apiKeys.d.cts +50 -0
  71. package/dist/apiKeys.d.mts +50 -0
  72. package/dist/apiKeys.mjs +133 -0
  73. package/dist/createHotUpdaterCore.cjs +48 -26
  74. package/dist/createHotUpdaterCore.d.cts +49 -17
  75. package/dist/createHotUpdaterCore.d.mts +49 -17
  76. package/dist/createHotUpdaterCore.mjs +51 -29
  77. package/dist/db/createBundleDiff.cjs +45 -68
  78. package/dist/db/createBundleDiff.d.cts +3 -3
  79. package/dist/db/createBundleDiff.d.mts +3 -3
  80. package/dist/db/createBundleDiff.mjs +47 -68
  81. package/dist/db/databasePluginCore.cjs +138 -0
  82. package/dist/db/databasePluginCore.mjs +137 -0
  83. package/dist/db/fixedMigrator.cjs +36 -93
  84. package/dist/db/fixedMigrator.mjs +36 -93
  85. package/dist/db/fixedMigratorMongo.cjs +319 -0
  86. package/dist/db/fixedMigratorMongo.mjs +319 -0
  87. package/dist/db/fixedMigratorShared.cjs +23 -0
  88. package/dist/db/fixedMigratorShared.mjs +18 -0
  89. package/dist/db/mongoMigrationExecution.cjs +11 -0
  90. package/dist/db/mongoMigrationExecution.mjs +11 -0
  91. package/dist/db/releaseCatalog.cjs +66 -0
  92. package/dist/db/releaseCatalog.d.cts +17 -0
  93. package/dist/db/releaseCatalog.d.mts +17 -0
  94. package/dist/db/releaseCatalog.mjs +64 -0
  95. package/dist/db/schema/mongodb.cjs +14 -6
  96. package/dist/db/schema/mongodb.mjs +14 -6
  97. package/dist/db/schema/registry.cjs +0 -2
  98. package/dist/db/schema/registry.mjs +1 -2
  99. package/dist/db/schema/sql.cjs +31 -14
  100. package/dist/db/schema/sql.mjs +32 -13
  101. package/dist/db/schema/sqlOperations.cjs +4 -4
  102. package/dist/db/schema/sqlOperations.mjs +5 -5
  103. package/dist/db/schemaGenerators.cjs +52 -18
  104. package/dist/db/schemaGenerators.mjs +52 -18
  105. package/dist/db/schemaReadiness.cjs +4 -3
  106. package/dist/db/schemaReadiness.mjs +4 -3
  107. package/dist/db/sqlMigrationExecution.cjs +67 -0
  108. package/dist/db/sqlMigrationExecution.mjs +66 -0
  109. package/dist/db/types.cjs +2 -6
  110. package/dist/db/types.d.cts +60 -16
  111. package/dist/db/types.d.mts +60 -16
  112. package/dist/db/types.mjs +3 -6
  113. package/dist/db/updateArtifacts.cjs +114 -60
  114. package/dist/db/updateArtifacts.mjs +117 -62
  115. package/dist/handler.cjs +108 -282
  116. package/dist/handler.d.cts +3 -46
  117. package/dist/handler.d.mts +3 -46
  118. package/dist/handler.mjs +107 -281
  119. package/dist/handlerBundleRoutes.cjs +155 -0
  120. package/dist/handlerBundleRoutes.mjs +155 -0
  121. package/dist/handlerErrors.cjs +9 -0
  122. package/dist/handlerErrors.mjs +9 -0
  123. package/dist/handlerParameters.cjs +39 -0
  124. package/dist/handlerParameters.mjs +34 -0
  125. package/dist/handlerReleaseCatalogRoutes.cjs +111 -0
  126. package/dist/handlerReleaseCatalogRoutes.mjs +110 -0
  127. package/dist/handlerReleaseManagementRoutes.cjs +125 -0
  128. package/dist/handlerReleaseManagementRoutes.mjs +124 -0
  129. package/dist/handlerTypes.d.cts +63 -0
  130. package/dist/handlerTypes.d.mts +63 -0
  131. package/dist/handlerVersionRoutes.cjs +10 -0
  132. package/dist/handlerVersionRoutes.d.cts +4 -0
  133. package/dist/handlerVersionRoutes.d.mts +4 -0
  134. package/dist/handlerVersionRoutes.mjs +9 -0
  135. package/dist/index.cjs +10 -1
  136. package/dist/index.d.cts +9 -3
  137. package/dist/index.d.mts +9 -3
  138. package/dist/index.mjs +5 -2
  139. package/dist/node.cjs +19 -12
  140. package/dist/node.d.cts +15 -10
  141. package/dist/node.d.mts +15 -10
  142. package/dist/node.mjs +19 -13
  143. package/dist/package.cjs +1 -1
  144. package/dist/package.mjs +1 -1
  145. package/dist/schema/dsl.cjs +19 -2
  146. package/dist/schema/dsl.mjs +17 -3
  147. package/dist/schema/index.cjs +1 -8
  148. package/dist/schema/index.mjs +2 -8
  149. package/dist/schema/types.cjs +3 -1
  150. package/dist/schema/types.mjs +3 -2
  151. package/dist/schema/v1_0_0.cjs +364 -0
  152. package/dist/schema/v1_0_0.mjs +357 -0
  153. package/dist/storageAccess.cjs +69 -25
  154. package/dist/storageAccess.mjs +68 -25
  155. package/dist/types/index.d.cts +4 -3
  156. package/dist/types/index.d.mts +5 -4
  157. package/package.json +8 -7
  158. package/src/adapters/databaseConstraintErrors.ts +25 -0
  159. package/src/adapters/databasePluginTestDatabase.ts +106 -0
  160. package/src/adapters/databasePluginUtils.spec.ts +29 -0
  161. package/src/adapters/databasePluginUtils.ts +214 -0
  162. package/src/adapters/drizzle.spec.ts +208 -47
  163. package/src/adapters/drizzle.ts +106 -293
  164. package/src/adapters/drizzleCrud.ts +419 -0
  165. package/src/adapters/drizzleLazyDB.ts +206 -100
  166. package/src/adapters/drizzleQuery.ts +164 -0
  167. package/src/adapters/kysely.spec.ts +126 -88
  168. package/src/adapters/kysely.ts +76 -334
  169. package/src/adapters/kyselyCrud.ts +588 -0
  170. package/src/adapters/kyselyQuery.ts +128 -0
  171. package/src/adapters/mongodb.spec.ts +141 -0
  172. package/src/adapters/mongodb.ts +60 -288
  173. package/src/adapters/mongodbCollections.ts +62 -0
  174. package/src/adapters/mongodbQuery.ts +226 -0
  175. package/src/adapters/mongodbReads.ts +311 -0
  176. package/src/adapters/mongodbRows.spec.ts +64 -0
  177. package/src/adapters/mongodbRows.ts +118 -0
  178. package/src/adapters/mongodbTestClient.ts +342 -0
  179. package/src/adapters/mongodbTestFilter.ts +166 -0
  180. package/src/adapters/mongodbWrites.ts +260 -0
  181. package/src/adapters/prisma.spec.ts +196 -0
  182. package/src/adapters/prisma.ts +417 -292
  183. package/src/adapters/prismaQuery.ts +129 -0
  184. package/src/adapters/prismaRows.spec.ts +98 -0
  185. package/src/adapters/prismaRows.ts +301 -0
  186. package/src/adapters/prismaTestClient.ts +396 -0
  187. package/src/analytics/analytics.integration.spec.ts +144 -0
  188. package/src/analytics/bounded/activeOverview.spec.ts +189 -0
  189. package/src/analytics/bounded/activeOverview.ts +148 -0
  190. package/src/analytics/bounded/installationSearch.spec.ts +130 -0
  191. package/src/analytics/bounded/installationSearch.ts +69 -0
  192. package/src/analytics/bounded/persistence.ts +43 -0
  193. package/src/analytics/bounded/provider.spec.ts +219 -0
  194. package/src/analytics/bounded/provider.ts +229 -0
  195. package/src/analytics/bounded/scan.ts +235 -0
  196. package/src/analytics/domain.ts +139 -0
  197. package/src/analytics/errors.ts +18 -0
  198. package/src/analytics/eventInput.ts +168 -0
  199. package/src/analytics/persistence.ts +10 -0
  200. package/src/analytics/queryInput.ts +102 -0
  201. package/src/analytics/routes.ts +123 -0
  202. package/src/analytics/types.ts +44 -0
  203. package/src/apiKeys.integration.spec.ts +192 -0
  204. package/src/apiKeys.spec.ts +162 -0
  205. package/src/apiKeys.ts +220 -0
  206. package/src/createHotUpdaterCore.ts +164 -100
  207. package/src/db/bundleRows.spec.ts +164 -0
  208. package/src/db/bundleRows.ts +10 -140
  209. package/src/db/createBundleDiff.spec.ts +172 -127
  210. package/src/db/createBundleDiff.ts +91 -110
  211. package/src/db/databasePluginCore.spec.ts +62 -0
  212. package/src/db/databasePluginCore.testFixtures.ts +76 -0
  213. package/src/db/databasePluginCore.ts +185 -0
  214. package/src/db/databasePluginUpdateCheck.bench.ts +107 -0
  215. package/src/db/fixedMigrator.spec.ts +184 -42
  216. package/src/db/fixedMigrator.ts +64 -205
  217. package/src/db/fixedMigratorMongo.spec.ts +192 -0
  218. package/src/db/fixedMigratorMongo.ts +425 -0
  219. package/src/db/fixedMigratorShared.ts +36 -0
  220. package/src/db/hotUpdaterSchema.ts +0 -1
  221. package/src/db/index.spec.ts +333 -1025
  222. package/src/db/mongoMigrationExecution.ts +23 -0
  223. package/src/db/node-sqlite.d.ts +25 -0
  224. package/src/db/releaseCatalog.ts +148 -0
  225. package/src/db/schema/mongodb.ts +20 -6
  226. package/src/db/schema/sql.ts +52 -11
  227. package/src/db/schema/sqlOperations.ts +5 -4
  228. package/src/db/schemaGenerators.ts +107 -23
  229. package/src/db/schemaReadiness.ts +2 -1
  230. package/src/db/sqlMigrationExecution.spec.ts +65 -0
  231. package/src/db/sqlMigrationExecution.ts +108 -0
  232. package/src/db/types.spec.ts +55 -0
  233. package/src/db/types.ts +129 -65
  234. package/src/db/updateArtifacts.spec.ts +359 -0
  235. package/src/db/updateArtifacts.ts +290 -126
  236. package/src/handler-standalone.integration.spec.ts +401 -364
  237. package/src/handler.spec.ts +137 -582
  238. package/src/handler.testFixtures.ts +30 -0
  239. package/src/handler.ts +218 -616
  240. package/src/handlerAuth.integration.spec.ts +33 -0
  241. package/src/handlerBundleRoutes.ts +259 -0
  242. package/src/handlerErrors.ts +7 -0
  243. package/src/handlerManagement.spec.ts +262 -0
  244. package/src/handlerParameters.ts +105 -0
  245. package/src/handlerReleaseCatalogRoutes.ts +154 -0
  246. package/src/handlerReleaseManagementRoutes.ts +213 -0
  247. package/src/handlerTypes.ts +96 -0
  248. package/src/handlerVersionRoutes.ts +12 -0
  249. package/src/index.ts +22 -2
  250. package/src/node.spec.ts +79 -10
  251. package/src/node.ts +20 -17
  252. package/src/releaseCatalogRoutes.integration.spec.ts +238 -0
  253. package/src/runtime.spec.ts +126 -959
  254. package/src/runtime.testFixtures.ts +71 -0
  255. package/src/schema/dsl-all-versions.spec.ts +26 -17
  256. package/src/schema/dsl.ts +41 -2
  257. package/src/schema/index.ts +3 -9
  258. package/src/schema/types.ts +9 -3
  259. package/src/schema/v1_0_0.spec.ts +146 -0
  260. package/src/schema/v1_0_0.ts +465 -0
  261. package/src/storageAccess.spec.ts +127 -32
  262. package/src/storageAccess.ts +106 -55
  263. package/src/types/index.ts +2 -1
  264. package/dist/db/bundleRows.cjs +0 -82
  265. package/dist/db/bundleRows.d.cts +0 -33
  266. package/dist/db/bundleRows.d.mts +0 -33
  267. package/dist/db/bundleRows.mjs +0 -79
  268. package/dist/db/pluginCore.cjs +0 -242
  269. package/dist/db/pluginCore.mjs +0 -241
  270. package/dist/db/schema/sqlMigrations.cjs +0 -74
  271. package/dist/db/schema/sqlMigrations.mjs +0 -73
  272. package/dist/db/schemaEnhancements.cjs +0 -20
  273. package/dist/db/schemaEnhancements.mjs +0 -19
  274. package/dist/route.cjs +0 -7
  275. package/dist/route.mjs +0 -7
  276. package/dist/schema/v0_21_0.cjs +0 -38
  277. package/dist/schema/v0_21_0.mjs +0 -37
  278. package/dist/schema/v0_29_0.cjs +0 -45
  279. package/dist/schema/v0_29_0.mjs +0 -44
  280. package/dist/schema/v0_31_0.cjs +0 -82
  281. package/dist/schema/v0_31_0.mjs +0 -80
  282. package/src/db/pluginCore.spec.ts +0 -1095
  283. package/src/db/pluginCore.ts +0 -475
  284. package/src/db/pluginUpdateCheck.bench.ts +0 -254
  285. package/src/db/schema/sqlMigrations.ts +0 -219
  286. package/src/db/schemaEnhancements.ts +0 -55
  287. package/src/route.ts +0 -7
  288. package/src/schema/v0_21_0.ts +0 -52
  289. package/src/schema/v0_29_0.ts +0 -62
  290. package/src/schema/v0_31_0.spec.ts +0 -73
  291. package/src/schema/v0_31_0.ts +0 -120
@@ -4,33 +4,97 @@ import {
4
4
  getManifestFileHash,
5
5
  getManifestStorageUri,
6
6
  stripBundleArtifactMetadata,
7
- type AppUpdateAvailableInfo,
7
+ type ArtifactInfo,
8
8
  type Bundle,
9
9
  type ChangedAsset,
10
10
  } from "@hot-updater/core";
11
11
  import {
12
12
  getManifestAssetDownloadPath,
13
+ isContentAddressedAssetFileHash,
13
14
  resolveManifestAssetStorageUri,
14
- type HotUpdaterContext,
15
15
  } from "@hot-updater/plugin-core";
16
16
 
17
+ type BundleManifestAsset = {
18
+ downloadByteSize?: unknown;
19
+ downloadFileHash?: unknown;
20
+ fileHash: string;
21
+ signature?: string;
22
+ };
23
+
17
24
  type BundleManifest = {
18
25
  bundleId: string;
19
- assets: Record<string, { fileHash: string; signature?: string }>;
26
+ assets: Record<string, BundleManifestAsset>;
20
27
  };
21
28
 
22
- type ResolveFileUrl<TContext> = (
23
- storageUri: string | null,
24
- context?: HotUpdaterContext<TContext>,
25
- ) => Promise<string | null>;
29
+ type PlannedFile = {
30
+ byteSize: number | null;
31
+ compression?: "br";
32
+ storageUri: string;
33
+ };
26
34
 
27
- type ReadStorageText<TContext> = (
28
- storageUri: string,
29
- context?: HotUpdaterContext<TContext>,
30
- ) => Promise<string | null>;
35
+ type PlannedPatch = {
36
+ baseBundleId: string;
37
+ baseFileHash: string;
38
+ byteSize: number | null;
39
+ patchFileHash: string;
40
+ storageUri: string;
41
+ };
42
+
43
+ type PlannedChangedAsset = {
44
+ assetPath: string;
45
+ file: PlannedFile;
46
+ fileHash: string;
47
+ patch: PlannedPatch | null;
48
+ };
49
+
50
+ type ManifestArtifactPlan = {
51
+ allPossibleByteSizesKnown: boolean;
52
+ changedAssets: PlannedChangedAsset[];
53
+ manifestByteSize: number | null;
54
+ minimumDownloadByteSize: number | null;
55
+ };
56
+
57
+ type ResolvedChangedAssets = {
58
+ changedAssets: Record<string, ChangedAsset>;
59
+ primaryByteSizes: Array<number | null>;
60
+ };
61
+
62
+ type ResolveFileUrl = (storageUri: string | null) => Promise<string | null>;
63
+
64
+ type ReadStorageText = (storageUri: string) => Promise<string | null>;
31
65
 
32
66
  const HBC_ASSET_PATH_RE = /\.bundle$/;
33
67
 
68
+ const asByteSize = (value: unknown): number | null =>
69
+ typeof value === "number" && Number.isSafeInteger(value) && value >= 0
70
+ ? value
71
+ : null;
72
+
73
+ const sumByteSizes = (byteSizes: Array<number | null>): number | null => {
74
+ let total = 0;
75
+
76
+ for (const byteSize of byteSizes) {
77
+ if (byteSize === null || total > Number.MAX_SAFE_INTEGER - byteSize) {
78
+ return null;
79
+ }
80
+ total += byteSize;
81
+ }
82
+
83
+ return total;
84
+ };
85
+
86
+ const shouldUseArchive = (
87
+ targetBundle: Bundle,
88
+ downloadByteSize: number | null,
89
+ ) => {
90
+ const archiveByteSize = asByteSize(targetBundle.archiveByteSize);
91
+ return (
92
+ archiveByteSize !== null &&
93
+ downloadByteSize !== null &&
94
+ downloadByteSize >= archiveByteSize
95
+ );
96
+ };
97
+
34
98
  const resolveUniqueHbcAssetPath = (manifest: BundleManifest) => {
35
99
  const candidates = Object.keys(manifest.assets)
36
100
  .sort((left, right) => left.localeCompare(right))
@@ -133,16 +197,14 @@ export const parseBundleRawMetadata = (
133
197
  return parsedValue as Bundle["metadata"];
134
198
  };
135
199
 
136
- async function fetchBundleManifest<TContext>(
200
+ async function fetchBundleManifest(
137
201
  storageUri: string,
138
- readStorageText: ReadStorageText<TContext>,
139
- resolveFileUrl: ResolveFileUrl<TContext>,
140
- context?: HotUpdaterContext<TContext>,
141
- ): Promise<{ fileUrl: string; manifest: BundleManifest } | null> {
142
- const [storageText, fileUrl] = await Promise.all([
143
- readStorageText(storageUri, context),
144
- resolveFileUrl(storageUri, context),
145
- ]);
202
+ readStorageText: ReadStorageText,
203
+ ): Promise<{
204
+ byteSize: number | null;
205
+ manifest: BundleManifest;
206
+ } | null> {
207
+ const storageText = await readStorageText(storageUri);
146
208
  if (storageText === null) {
147
209
  return null;
148
210
  }
@@ -158,68 +220,191 @@ async function fetchBundleManifest<TContext>(
158
220
  return null;
159
221
  }
160
222
 
161
- if (!fileUrl) {
223
+ return {
224
+ byteSize: asByteSize(new TextEncoder().encode(storageText).byteLength),
225
+ manifest: payload,
226
+ };
227
+ }
228
+
229
+ function resolveHbcPatchPlan({
230
+ currentBundle,
231
+ targetBundle,
232
+ targetManifest,
233
+ }: {
234
+ currentBundle: Bundle | null;
235
+ targetBundle: Bundle;
236
+ targetManifest: BundleManifest;
237
+ }): { assetPath: string; patch: PlannedPatch } | null {
238
+ const matchingPatch = currentBundle
239
+ ? getBundlePatch(targetBundle, currentBundle.id)
240
+ : null;
241
+ const patchAssetPath = resolveUniqueHbcAssetPath(targetManifest);
242
+
243
+ if (
244
+ !currentBundle ||
245
+ !matchingPatch ||
246
+ !patchAssetPath ||
247
+ !matchingPatch.patchStorageUri ||
248
+ !matchingPatch.patchFileHash ||
249
+ !matchingPatch.baseFileHash
250
+ ) {
162
251
  return null;
163
252
  }
164
253
 
165
254
  return {
166
- fileUrl,
167
- manifest: payload,
255
+ assetPath: patchAssetPath,
256
+ patch: {
257
+ baseBundleId: matchingPatch.baseBundleId,
258
+ baseFileHash: matchingPatch.baseFileHash,
259
+ byteSize: asByteSize(matchingPatch.byteSize),
260
+ patchFileHash: matchingPatch.patchFileHash,
261
+ storageUri: matchingPatch.patchStorageUri,
262
+ },
168
263
  };
169
264
  }
170
265
 
171
- async function resolveChangedAssets<TContext>({
266
+ function createManifestArtifactPlan({
172
267
  assetBaseStorageUri,
173
268
  currentManifest,
174
269
  currentBundle,
175
- resolveFileUrl,
176
270
  targetBundle,
177
271
  targetManifest,
178
- context,
272
+ targetManifestByteSize,
179
273
  }: {
180
274
  assetBaseStorageUri: string;
181
275
  currentManifest: BundleManifest | null;
182
276
  currentBundle: Bundle | null;
183
- resolveFileUrl: ResolveFileUrl<TContext>;
184
- targetBundle: Bundle | null;
277
+ targetBundle: Bundle;
185
278
  targetManifest: BundleManifest;
186
- context?: HotUpdaterContext<TContext>;
187
- }): Promise<Record<string, ChangedAsset> | null> {
188
- const patchDescriptor = await resolveHbcPatchDescriptor({
279
+ targetManifestByteSize: number | null;
280
+ }): ManifestArtifactPlan {
281
+ const patchCandidate = resolveHbcPatchPlan({
189
282
  currentBundle,
190
- resolveFileUrl,
191
283
  targetBundle,
192
284
  targetManifest,
193
- context,
194
285
  });
195
- const changedEntries = await Promise.all(
196
- Object.entries(targetManifest.assets).map(async ([assetPath, asset]) => {
286
+ const changedAssets = Object.entries(targetManifest.assets).flatMap(
287
+ ([assetPath, asset]): PlannedChangedAsset[] => {
197
288
  const currentAsset = currentManifest?.assets[assetPath];
198
289
  if (currentAsset?.fileHash === asset.fileHash) {
199
- return null;
290
+ return [];
200
291
  }
201
292
 
293
+ const downloadByteSize = asByteSize(asset.downloadByteSize);
202
294
  const downloadPath = getManifestAssetDownloadPath(assetPath);
203
- const usesBrotliAsset = downloadPath !== assetPath;
204
- const storageUri = resolveManifestAssetStorageUri({
205
- assetBaseStorageUri,
206
- assetPath: downloadPath,
207
- fileHash: asset.fileHash,
208
- });
295
+ const isTransformedDownload = downloadPath !== assetPath;
296
+ const hasValidDownloadFileHash = isContentAddressedAssetFileHash(
297
+ asset.downloadFileHash,
298
+ );
299
+ const hasKnownDownloadByteSize =
300
+ downloadByteSize !== null &&
301
+ (hasValidDownloadFileHash ||
302
+ (!isTransformedDownload && asset.downloadFileHash === undefined));
303
+ const file: PlannedFile = {
304
+ byteSize: hasKnownDownloadByteSize ? downloadByteSize : null,
305
+ storageUri: resolveManifestAssetStorageUri({
306
+ assetBaseStorageUri,
307
+ assetPath: downloadPath,
308
+ downloadFileHash: asset.downloadFileHash,
309
+ fileHash: asset.fileHash,
310
+ }),
311
+ };
312
+ if (isTransformedDownload) {
313
+ file.compression = "br";
314
+ }
315
+
209
316
  const patch =
210
- patchDescriptor?.assetPath === assetPath ? patchDescriptor.patch : null;
317
+ patchCandidate?.assetPath === assetPath ? patchCandidate.patch : null;
318
+
319
+ return [
320
+ {
321
+ assetPath,
322
+ file,
323
+ fileHash: asset.fileHash,
324
+ patch,
325
+ },
326
+ ];
327
+ },
328
+ );
329
+ const allPossibleByteSizesKnown =
330
+ targetManifestByteSize !== null &&
331
+ changedAssets.every(
332
+ (asset) =>
333
+ asset.file.byteSize !== null &&
334
+ (asset.patch === null || asset.patch.byteSize !== null),
335
+ );
336
+ const primaryByteSizes = changedAssets.map((asset) => {
337
+ if (!asset.patch) return asset.file.byteSize;
338
+ if (asset.patch.byteSize === null || asset.file.byteSize === null) {
339
+ return null;
340
+ }
341
+ return Math.min(asset.patch.byteSize, asset.file.byteSize);
342
+ });
343
+
344
+ return {
345
+ allPossibleByteSizesKnown,
346
+ changedAssets,
347
+ manifestByteSize: targetManifestByteSize,
348
+ minimumDownloadByteSize: allPossibleByteSizesKnown
349
+ ? sumByteSizes([targetManifestByteSize, ...primaryByteSizes])
350
+ : null,
351
+ };
352
+ }
353
+
354
+ async function resolveChangedAssets(
355
+ plan: ManifestArtifactPlan,
356
+ resolveFileUrl: ResolveFileUrl,
357
+ ): Promise<ResolvedChangedAssets | null> {
358
+ const patchAsset = plan.changedAssets.find((asset) => asset.patch !== null);
359
+ let resolvedPatch: {
360
+ assetPath: string;
361
+ patch: NonNullable<ChangedAsset["patch"]>;
362
+ } | null = null;
363
+
364
+ if (patchAsset?.patch) {
365
+ const patchUrl = await resolveFileUrl(patchAsset.patch.storageUri);
366
+ if (patchUrl) {
367
+ resolvedPatch = {
368
+ assetPath: patchAsset.assetPath,
369
+ patch: {
370
+ algorithm: "bsdiff",
371
+ baseBundleId: patchAsset.patch.baseBundleId,
372
+ baseFileHash: patchAsset.patch.baseFileHash,
373
+ patchFileHash: patchAsset.patch.patchFileHash,
374
+ patchUrl,
375
+ },
376
+ };
377
+ }
378
+ }
211
379
 
380
+ const changedEntries = await Promise.all(
381
+ plan.changedAssets.map(async (asset) => {
382
+ let patch =
383
+ resolvedPatch?.assetPath === asset.assetPath
384
+ ? resolvedPatch.patch
385
+ : null;
212
386
  let fileUrl: string | null = null;
213
387
  try {
214
- fileUrl = await resolveFileUrl(storageUri, context);
388
+ fileUrl = await resolveFileUrl(asset.file.storageUri);
215
389
  } catch (error) {
216
390
  if (!patch) {
217
391
  throw error;
218
392
  }
219
393
  }
220
394
 
395
+ if (
396
+ fileUrl &&
397
+ patch &&
398
+ asset.file.byteSize !== null &&
399
+ asset.patch !== null &&
400
+ asset.patch.byteSize !== null &&
401
+ asset.patch.byteSize >= asset.file.byteSize
402
+ ) {
403
+ patch = null;
404
+ }
405
+
221
406
  if (!fileUrl && !patch) {
222
- return false;
407
+ return null;
223
408
  }
224
409
 
225
410
  const changedAsset: ChangedAsset = {
@@ -229,93 +414,52 @@ async function resolveChangedAssets<TContext>({
229
414
  changedAsset.file = {
230
415
  url: fileUrl,
231
416
  };
232
- if (usesBrotliAsset) {
233
- changedAsset.file.compression = "br";
417
+ if (asset.file.compression) {
418
+ changedAsset.file.compression = asset.file.compression;
234
419
  }
235
420
  }
236
421
  if (patch) {
237
422
  changedAsset.patch = patch;
238
423
  }
239
424
 
240
- return [assetPath, changedAsset] as const;
425
+ return {
426
+ asset: [asset.assetPath, changedAsset] as const,
427
+ primaryByteSize: patch
428
+ ? (asset.patch?.byteSize ?? null)
429
+ : asset.file.byteSize,
430
+ };
241
431
  }),
242
432
  );
243
433
 
244
- if (changedEntries.some((entry) => entry === false)) {
434
+ if (changedEntries.some((entry) => entry === null)) {
245
435
  return null;
246
436
  }
247
437
 
248
- return Object.fromEntries(
249
- changedEntries.filter(
250
- (entry): entry is readonly [string, ChangedAsset] => entry !== null,
251
- ),
438
+ const resolvedEntries = changedEntries.filter(
439
+ (entry): entry is NonNullable<typeof entry> => entry !== null,
252
440
  );
253
- }
254
-
255
- async function resolveHbcPatchDescriptor<TContext>({
256
- currentBundle,
257
- resolveFileUrl,
258
- targetBundle,
259
- targetManifest,
260
- context,
261
- }: {
262
- currentBundle: Bundle | null;
263
- resolveFileUrl: ResolveFileUrl<TContext>;
264
- targetBundle: Bundle | null;
265
- targetManifest: BundleManifest;
266
- context?: HotUpdaterContext<TContext>;
267
- }): Promise<{
268
- assetPath: string;
269
- patch: ChangedAsset["patch"];
270
- } | null> {
271
- const matchingPatch =
272
- targetBundle && currentBundle
273
- ? getBundlePatch(targetBundle, currentBundle.id)
274
- : null;
275
- const patchAssetPath = resolveUniqueHbcAssetPath(targetManifest);
276
-
277
- if (
278
- !currentBundle ||
279
- !matchingPatch ||
280
- !patchAssetPath ||
281
- !matchingPatch.patchStorageUri ||
282
- !matchingPatch.patchFileHash ||
283
- !matchingPatch.baseFileHash
284
- ) {
285
- return null;
286
- }
287
-
288
- const patchUrl = await resolveFileUrl(matchingPatch.patchStorageUri, context);
289
- if (!patchUrl) {
290
- return null;
291
- }
292
-
293
441
  return {
294
- assetPath: patchAssetPath,
295
- patch: {
296
- algorithm: "bsdiff",
297
- baseBundleId: matchingPatch.baseBundleId,
298
- baseFileHash: matchingPatch.baseFileHash,
299
- patchFileHash: matchingPatch.patchFileHash,
300
- patchUrl,
301
- },
442
+ changedAssets: Object.fromEntries(
443
+ resolvedEntries.map((entry) => entry.asset),
444
+ ),
445
+ primaryByteSizes: resolvedEntries.map((entry) => entry.primaryByteSize),
302
446
  };
303
447
  }
304
448
 
305
- export async function resolveManifestArtifacts<TContext>({
449
+ export async function resolveManifestArtifacts({
450
+ archiveUrlUsable,
306
451
  currentBundle,
307
452
  resolveFileUrl,
308
453
  readStorageText,
309
454
  targetBundle,
310
- context,
311
455
  }: {
456
+ archiveUrlUsable: boolean;
312
457
  currentBundle: Bundle | null;
313
- resolveFileUrl: ResolveFileUrl<TContext>;
314
- readStorageText: ReadStorageText<TContext>;
458
+ resolveFileUrl: ResolveFileUrl;
459
+ readStorageText: ReadStorageText;
315
460
  targetBundle: Bundle | null;
316
- context?: HotUpdaterContext<TContext>;
317
461
  }): Promise<Pick<
318
- AppUpdateAvailableInfo,
462
+ ArtifactInfo,
319
463
  "changedAssets" | "manifestFileHash" | "manifestUrl"
320
464
  > | null> {
321
465
  const manifestStorageUri = targetBundle
@@ -328,7 +472,12 @@ export async function resolveManifestArtifacts<TContext>({
328
472
  ? getAssetBaseStorageUri(targetBundle)
329
473
  : null;
330
474
 
331
- if (!manifestStorageUri || !manifestFileHash || !assetBaseStorageUri) {
475
+ if (
476
+ !targetBundle ||
477
+ !manifestStorageUri ||
478
+ !manifestFileHash ||
479
+ !assetBaseStorageUri
480
+ ) {
332
481
  return null;
333
482
  }
334
483
 
@@ -336,19 +485,9 @@ export async function resolveManifestArtifacts<TContext>({
336
485
  ? getManifestStorageUri(currentBundle)
337
486
  : null;
338
487
  const [targetManifestResult, currentManifestResult] = await Promise.all([
339
- fetchBundleManifest(
340
- manifestStorageUri,
341
- readStorageText,
342
- resolveFileUrl,
343
- context,
344
- ),
488
+ fetchBundleManifest(manifestStorageUri, readStorageText),
345
489
  currentManifestStorageUri
346
- ? fetchBundleManifest(
347
- currentManifestStorageUri,
348
- readStorageText,
349
- resolveFileUrl,
350
- context,
351
- )
490
+ ? fetchBundleManifest(currentManifestStorageUri, readStorageText)
352
491
  : null,
353
492
  ]);
354
493
 
@@ -356,22 +495,47 @@ export async function resolveManifestArtifacts<TContext>({
356
495
  return null;
357
496
  }
358
497
 
359
- const changedAssets = await resolveChangedAssets({
498
+ const plan = createManifestArtifactPlan({
360
499
  assetBaseStorageUri,
361
500
  currentManifest: currentManifestResult?.manifest ?? null,
362
501
  currentBundle,
363
- resolveFileUrl,
364
502
  targetBundle,
365
503
  targetManifest: targetManifestResult.manifest,
366
- context,
504
+ targetManifestByteSize: targetManifestResult.byteSize,
367
505
  });
368
- if (!changedAssets) {
506
+
507
+ if (
508
+ archiveUrlUsable &&
509
+ plan.allPossibleByteSizesKnown &&
510
+ shouldUseArchive(targetBundle, plan.minimumDownloadByteSize)
511
+ ) {
512
+ return null;
513
+ }
514
+
515
+ const manifestUrl = await resolveFileUrl(manifestStorageUri);
516
+ if (!manifestUrl) {
517
+ return null;
518
+ }
519
+
520
+ const resolved = await resolveChangedAssets(plan, resolveFileUrl);
521
+ if (!resolved) {
522
+ return null;
523
+ }
524
+
525
+ const resolvedDownloadByteSize = sumByteSizes([
526
+ plan.manifestByteSize,
527
+ ...resolved.primaryByteSizes,
528
+ ]);
529
+ if (
530
+ archiveUrlUsable &&
531
+ shouldUseArchive(targetBundle, resolvedDownloadByteSize)
532
+ ) {
369
533
  return null;
370
534
  }
371
535
 
372
536
  return {
373
- changedAssets,
537
+ changedAssets: resolved.changedAssets,
374
538
  manifestFileHash,
375
- manifestUrl: targetManifestResult.fileUrl,
539
+ manifestUrl,
376
540
  };
377
541
  }