@hot-updater/plugin-core 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 (161) hide show
  1. package/dist/assetStorageLayout.cjs +36 -18
  2. package/dist/assetStorageLayout.d.cts +21 -6
  3. package/dist/assetStorageLayout.d.mts +21 -6
  4. package/dist/assetStorageLayout.mjs +36 -18
  5. package/dist/bundleStorageLayout.cjs +2 -3
  6. package/dist/bundleStorageLayout.mjs +2 -3
  7. package/dist/contentAddressedAssets.cjs +5 -0
  8. package/dist/contentAddressedAssets.d.cts +2 -1
  9. package/dist/contentAddressedAssets.d.mts +2 -1
  10. package/dist/contentAddressedAssets.mjs +5 -1
  11. package/dist/createDatabasePlugin.cjs +775 -284
  12. package/dist/createDatabasePlugin.d.cts +20 -64
  13. package/dist/createDatabasePlugin.d.mts +20 -64
  14. package/dist/createDatabasePlugin.mjs +773 -284
  15. package/dist/createStorageKeyBuilder.cjs +9 -2
  16. package/dist/createStorageKeyBuilder.mjs +9 -2
  17. package/dist/createStoragePlugin.cjs +6 -145
  18. package/dist/createStoragePlugin.d.cts +7 -52
  19. package/dist/createStoragePlugin.d.mts +7 -52
  20. package/dist/createStoragePlugin.mjs +5 -143
  21. package/dist/databaseClient.cjs +112 -0
  22. package/dist/databaseClient.d.cts +33 -0
  23. package/dist/databaseClient.d.mts +33 -0
  24. package/dist/databaseClient.mjs +110 -0
  25. package/dist/databaseClientReads.cjs +113 -0
  26. package/dist/databaseClientReads.mjs +112 -0
  27. package/dist/databaseClientUpdates.cjs +39 -0
  28. package/dist/databaseClientUpdates.d.cts +11 -0
  29. package/dist/databaseClientUpdates.d.mts +11 -0
  30. package/dist/databaseClientUpdates.mjs +36 -0
  31. package/dist/databaseJsonValue.cjs +37 -0
  32. package/dist/databaseJsonValue.d.cts +5 -0
  33. package/dist/databaseJsonValue.d.mts +5 -0
  34. package/dist/databaseJsonValue.mjs +37 -0
  35. package/dist/databaseMetadata.cjs +13 -0
  36. package/dist/databaseMetadata.mjs +12 -0
  37. package/dist/databasePluginCrud.cjs +86 -0
  38. package/dist/databasePluginCrud.d.cts +1 -0
  39. package/dist/databasePluginCrud.d.mts +1 -0
  40. package/dist/databasePluginCrud.mjs +86 -0
  41. package/dist/databasePluginCrudValidation.d.cts +1 -0
  42. package/dist/databasePluginCrudValidation.d.mts +1 -0
  43. package/dist/databasePluginCrudValidationErrors.cjs +10 -0
  44. package/dist/databasePluginCrudValidationErrors.d.cts +9 -0
  45. package/dist/databasePluginCrudValidationErrors.d.mts +9 -0
  46. package/dist/databasePluginCrudValidationErrors.mjs +10 -0
  47. package/dist/databasePluginCrudValidationFields.cjs +291 -0
  48. package/dist/databasePluginCrudValidationFields.d.cts +1 -0
  49. package/dist/databasePluginCrudValidationFields.d.mts +1 -0
  50. package/dist/databasePluginCrudValidationFields.mjs +281 -0
  51. package/dist/databasePluginCrudValidationMutations.cjs +88 -0
  52. package/dist/databasePluginCrudValidationMutations.d.cts +1 -0
  53. package/dist/databasePluginCrudValidationMutations.d.mts +1 -0
  54. package/dist/databasePluginCrudValidationMutations.mjs +82 -0
  55. package/dist/databasePluginCrudValidationQueries.cjs +103 -0
  56. package/dist/databasePluginCrudValidationQueries.d.cts +1 -0
  57. package/dist/databasePluginCrudValidationQueries.d.mts +1 -0
  58. package/dist/databasePluginCrudValidationQueries.mjs +97 -0
  59. package/dist/databasePluginCrudValidationRows.cjs +55 -0
  60. package/dist/databasePluginCrudValidationRows.d.cts +1 -0
  61. package/dist/databasePluginCrudValidationRows.d.mts +1 -0
  62. package/dist/databasePluginCrudValidationRows.mjs +53 -0
  63. package/dist/databasePluginTransaction.cjs +7 -0
  64. package/dist/databasePluginTransaction.mjs +7 -0
  65. package/dist/databaseRows.cjs +149 -0
  66. package/dist/databaseRows.d.cts +34 -0
  67. package/dist/databaseRows.d.mts +34 -0
  68. package/dist/databaseRows.mjs +141 -0
  69. package/dist/index.cjs +76 -24
  70. package/dist/index.d.cts +22 -12
  71. package/dist/index.d.mts +22 -12
  72. package/dist/index.mjs +21 -14
  73. package/dist/internal.cjs +6 -0
  74. package/dist/internal.d.cts +9 -0
  75. package/dist/internal.d.mts +9 -0
  76. package/dist/internal.mjs +3 -0
  77. package/dist/node_modules/.pnpm/verkit@0.3.2/node_modules/verkit/dist/index.cjs +345 -0
  78. package/dist/node_modules/.pnpm/verkit@0.3.2/node_modules/verkit/dist/index.mjs +341 -0
  79. package/dist/paginateBundles.d.cts +6 -4
  80. package/dist/paginateBundles.d.mts +6 -4
  81. package/dist/parseStorageUri.cjs +46 -30
  82. package/dist/parseStorageUri.d.cts +20 -17
  83. package/dist/parseStorageUri.d.mts +20 -17
  84. package/dist/parseStorageUri.mjs +46 -30
  85. package/dist/queryBundles.cjs +1 -7
  86. package/dist/queryBundles.d.cts +3 -1
  87. package/dist/queryBundles.d.mts +3 -1
  88. package/dist/queryBundles.mjs +1 -7
  89. package/dist/releaseCatalogCompiler.cjs +391 -0
  90. package/dist/releaseCatalogCompiler.d.cts +55 -0
  91. package/dist/releaseCatalogCompiler.d.mts +55 -0
  92. package/dist/releaseCatalogCompiler.mjs +386 -0
  93. package/dist/releaseCatalogMutation.cjs +303 -0
  94. package/dist/releaseCatalogMutation.d.cts +86 -0
  95. package/dist/releaseCatalogMutation.d.mts +86 -0
  96. package/dist/releaseCatalogMutation.mjs +297 -0
  97. package/dist/releaseManagement.cjs +231 -0
  98. package/dist/releaseManagement.d.cts +48 -0
  99. package/dist/releaseManagement.d.mts +48 -0
  100. package/dist/releaseManagement.mjs +224 -0
  101. package/dist/remoteBundleSigning.cjs +296 -0
  102. package/dist/remoteBundleSigning.d.cts +43 -0
  103. package/dist/remoteBundleSigning.d.mts +43 -0
  104. package/dist/remoteBundleSigning.mjs +291 -0
  105. package/dist/requestBundleCache.cjs +22 -0
  106. package/dist/requestBundleCache.d.cts +9 -0
  107. package/dist/requestBundleCache.d.mts +9 -0
  108. package/dist/requestBundleCache.mjs +22 -0
  109. package/dist/semverSatisfies.cjs +3 -4
  110. package/dist/semverSatisfies.mjs +1 -1
  111. package/dist/storageDownloadPath.cjs +48 -0
  112. package/dist/storageDownloadPath.d.cts +15 -0
  113. package/dist/storageDownloadPath.d.mts +15 -0
  114. package/dist/storageDownloadPath.mjs +46 -0
  115. package/dist/types/database.d.cts +4 -0
  116. package/dist/types/database.d.mts +4 -0
  117. package/dist/types/databaseFields.cjs +92 -0
  118. package/dist/types/databaseFields.d.cts +12 -0
  119. package/dist/types/databaseFields.d.mts +12 -0
  120. package/dist/types/databaseFields.mjs +92 -0
  121. package/dist/types/databaseOperations.d.cts +173 -0
  122. package/dist/types/databaseOperations.d.mts +173 -0
  123. package/dist/types/databasePlugin.d.cts +240 -0
  124. package/dist/types/databasePlugin.d.mts +240 -0
  125. package/dist/types/databaseQuery.d.cts +47 -0
  126. package/dist/types/databaseQuery.d.mts +47 -0
  127. package/dist/types/databaseRows.d.cts +120 -0
  128. package/dist/types/databaseRows.d.mts +120 -0
  129. package/dist/types/index.d.cts +120 -162
  130. package/dist/types/index.d.mts +120 -162
  131. package/dist/types/internal.d.cts +7 -0
  132. package/dist/types/internal.d.mts +7 -0
  133. package/dist/types/public.d.cts +3 -0
  134. package/dist/types/public.d.mts +3 -0
  135. package/dist/uuidv7.cjs +9 -0
  136. package/dist/uuidv7.d.cts +4 -1
  137. package/dist/uuidv7.d.mts +4 -1
  138. package/dist/uuidv7.mjs +8 -1
  139. package/package.json +10 -5
  140. package/dist/bundleUnitOfWork.cjs +0 -158
  141. package/dist/bundleUnitOfWork.mjs +0 -158
  142. package/dist/bundleUnitOfWorkStore.cjs +0 -15
  143. package/dist/bundleUnitOfWorkStore.mjs +0 -15
  144. package/dist/createBlobDatabasePlugin.cjs +0 -382
  145. package/dist/createBlobDatabasePlugin.d.cts +0 -29
  146. package/dist/createBlobDatabasePlugin.d.mts +0 -29
  147. package/dist/createBlobDatabasePlugin.mjs +0 -381
  148. package/dist/legacyAssetStorageLayout.cjs +0 -12
  149. package/dist/legacyAssetStorageLayout.mjs +0 -12
  150. package/dist/requestUpdateBundleState.cjs +0 -26
  151. package/dist/requestUpdateBundleState.d.cts +0 -12
  152. package/dist/requestUpdateBundleState.d.mts +0 -12
  153. package/dist/requestUpdateBundleState.mjs +0 -24
  154. package/dist/resolveUpdateInfoFromBundles.cjs +0 -14
  155. package/dist/resolveUpdateInfoFromBundles.d.cts +0 -16
  156. package/dist/resolveUpdateInfoFromBundles.d.mts +0 -16
  157. package/dist/resolveUpdateInfoFromBundles.mjs +0 -13
  158. package/dist/storageProfile.cjs +0 -15
  159. package/dist/storageProfile.d.cts +0 -9
  160. package/dist/storageProfile.d.mts +0 -9
  161. package/dist/storageProfile.mjs +0 -12
@@ -16,19 +16,13 @@ function bundleIdMatchesFilter(id, filter) {
16
16
  }
17
17
  function bundleMatchesQueryWhere(bundle, where) {
18
18
  if (!where) return true;
19
- if (where.channel !== void 0 && bundle.channel !== where.channel) return false;
20
19
  if (where.platform !== void 0 && bundle.platform !== where.platform) return false;
21
- if (where.enabled !== void 0 && bundle.enabled !== where.enabled) return false;
22
20
  if (!bundleIdMatchesFilter(bundle.id, where.id)) return false;
23
- if (where.targetAppVersionNotNull === true && bundle.targetAppVersion === null) return false;
24
- if (where.targetAppVersion !== void 0 && bundle.targetAppVersion !== where.targetAppVersion) return false;
25
- if (where.targetAppVersionIn && !where.targetAppVersionIn.includes(bundle.targetAppVersion ?? "")) return false;
26
- if (where.fingerprintHash !== void 0 && bundle.fingerprintHash !== where.fingerprintHash) return false;
27
21
  return true;
28
22
  }
29
23
  function sortBundles(bundles, orderBy) {
30
24
  const direction = orderBy?.direction ?? "desc";
31
- if (orderBy && orderBy.field !== "id") return bundles;
25
+ if (orderBy && orderBy.field !== "id") return [...bundles];
32
26
  return bundles.slice().sort((a, b) => {
33
27
  const result = a.id.localeCompare(b.id);
34
28
  return direction === "asc" ? result : -result;
@@ -3,6 +3,8 @@ import { Bundle, DatabaseBundleIdFilter, DatabaseBundleQueryOrder, DatabaseBundl
3
3
  //#region src/queryBundles.d.ts
4
4
  declare function bundleIdMatchesFilter(id: string, filter: DatabaseBundleIdFilter | undefined): boolean;
5
5
  declare function bundleMatchesQueryWhere(bundle: Bundle, where: DatabaseBundleQueryWhere | undefined): boolean;
6
- declare function sortBundles(bundles: Bundle[], orderBy: DatabaseBundleQueryOrder | undefined): Bundle[];
6
+ declare function sortBundles<TBundle extends {
7
+ readonly id: string;
8
+ }>(bundles: readonly TBundle[], orderBy: DatabaseBundleQueryOrder | undefined): TBundle[];
7
9
  //#endregion
8
10
  export { bundleIdMatchesFilter, bundleMatchesQueryWhere, sortBundles };
@@ -3,6 +3,8 @@ import { Bundle, DatabaseBundleIdFilter, DatabaseBundleQueryOrder, DatabaseBundl
3
3
  //#region src/queryBundles.d.ts
4
4
  declare function bundleIdMatchesFilter(id: string, filter: DatabaseBundleIdFilter | undefined): boolean;
5
5
  declare function bundleMatchesQueryWhere(bundle: Bundle, where: DatabaseBundleQueryWhere | undefined): boolean;
6
- declare function sortBundles(bundles: Bundle[], orderBy: DatabaseBundleQueryOrder | undefined): Bundle[];
6
+ declare function sortBundles<TBundle extends {
7
+ readonly id: string;
8
+ }>(bundles: readonly TBundle[], orderBy: DatabaseBundleQueryOrder | undefined): TBundle[];
7
9
  //#endregion
8
10
  export { bundleIdMatchesFilter, bundleMatchesQueryWhere, sortBundles };
@@ -16,19 +16,13 @@ function bundleIdMatchesFilter(id, filter) {
16
16
  }
17
17
  function bundleMatchesQueryWhere(bundle, where) {
18
18
  if (!where) return true;
19
- if (where.channel !== void 0 && bundle.channel !== where.channel) return false;
20
19
  if (where.platform !== void 0 && bundle.platform !== where.platform) return false;
21
- if (where.enabled !== void 0 && bundle.enabled !== where.enabled) return false;
22
20
  if (!bundleIdMatchesFilter(bundle.id, where.id)) return false;
23
- if (where.targetAppVersionNotNull === true && bundle.targetAppVersion === null) return false;
24
- if (where.targetAppVersion !== void 0 && bundle.targetAppVersion !== where.targetAppVersion) return false;
25
- if (where.targetAppVersionIn && !where.targetAppVersionIn.includes(bundle.targetAppVersion ?? "")) return false;
26
- if (where.fingerprintHash !== void 0 && bundle.fingerprintHash !== where.fingerprintHash) return false;
27
21
  return true;
28
22
  }
29
23
  function sortBundles(bundles, orderBy) {
30
24
  const direction = orderBy?.direction ?? "desc";
31
- if (orderBy && orderBy.field !== "id") return bundles;
25
+ if (orderBy && orderBy.field !== "id") return [...bundles];
32
26
  return bundles.slice().sort((a, b) => {
33
27
  const result = a.id.localeCompare(b.id);
34
28
  return direction === "asc" ? result : -result;
@@ -0,0 +1,391 @@
1
+ require("./_virtual/_rolldown/runtime.cjs");
2
+ const require_index = require("./node_modules/.pnpm/verkit@0.3.2/node_modules/verkit/dist/index.cjs");
3
+ let _hot_updater_core = require("@hot-updater/core");
4
+ //#region src/releaseCatalogCompiler.ts
5
+ var ReleaseCatalogCompilationError = class extends Error {
6
+ code;
7
+ constructor(code, message) {
8
+ super(message);
9
+ this.name = "ReleaseCatalogCompilationError";
10
+ this.code = code;
11
+ }
12
+ };
13
+ function compareBounds(left, right) {
14
+ return require_index.compare(left, right);
15
+ }
16
+ function strongerLower(current, candidate) {
17
+ if (current === null) return candidate;
18
+ const compared = compareBounds(candidate.version, current.version);
19
+ if (compared > 0) return candidate;
20
+ if (compared < 0) return current;
21
+ return {
22
+ version: current.version,
23
+ inclusive: current.inclusive && candidate.inclusive
24
+ };
25
+ }
26
+ function strongerUpper(current, candidate) {
27
+ if (current === null) return candidate;
28
+ const compared = compareBounds(candidate.version, current.version);
29
+ if (compared < 0) return candidate;
30
+ if (compared > 0) return current;
31
+ return {
32
+ version: current.version,
33
+ inclusive: current.inclusive && candidate.inclusive
34
+ };
35
+ }
36
+ function comparatorVersion(comparator) {
37
+ const version = comparator.version && require_index.normalize(comparator.version);
38
+ if (version === null) throw new ReleaseCatalogCompilationError("INVALID_TARGET_RANGE", `Invalid comparator: ${comparator.value}`);
39
+ return version;
40
+ }
41
+ function comparatorSetToInterval(comparators) {
42
+ let lower = null;
43
+ let upper = null;
44
+ for (const comparator of comparators) {
45
+ if (comparator.version === null) continue;
46
+ const version = comparatorVersion(comparator);
47
+ if (comparator.operator === "") {
48
+ lower = strongerLower(lower, {
49
+ inclusive: true,
50
+ version
51
+ });
52
+ upper = strongerUpper(upper, {
53
+ inclusive: true,
54
+ version
55
+ });
56
+ } else if (comparator.operator === ">") lower = strongerLower(lower, {
57
+ inclusive: false,
58
+ version
59
+ });
60
+ else if (comparator.operator === ">=") lower = strongerLower(lower, {
61
+ inclusive: true,
62
+ version
63
+ });
64
+ else if (comparator.operator === "<") upper = strongerUpper(upper, {
65
+ inclusive: false,
66
+ version
67
+ });
68
+ else if (comparator.operator === "<=") upper = strongerUpper(upper, {
69
+ inclusive: true,
70
+ version
71
+ });
72
+ }
73
+ if (lower !== null && upper !== null) {
74
+ const compared = compareBounds(lower.version, upper.version);
75
+ if (compared > 0 || compared === 0 && (!lower.inclusive || !upper.inclusive)) return null;
76
+ }
77
+ return {
78
+ lower,
79
+ upper
80
+ };
81
+ }
82
+ function rangeToIntervals(range) {
83
+ try {
84
+ return require_index.parseRange(range).sets.flatMap((comparators) => {
85
+ const interval = comparatorSetToInterval(comparators);
86
+ return interval === null ? [] : [interval];
87
+ });
88
+ } catch {
89
+ throw new ReleaseCatalogCompilationError("INVALID_TARGET_RANGE", `Invalid target app-version range: ${range}`);
90
+ }
91
+ }
92
+ function releaseDescriptor(release) {
93
+ return {
94
+ releaseId: release.id,
95
+ kind: release.kind,
96
+ bundleId: release.bundleId,
97
+ rolloutCohortCount: release.rolloutCohortCount,
98
+ targetCohorts: [...new Set(release.targetCohorts)].sort(),
99
+ shouldForceUpdate: release.shouldForceUpdate,
100
+ message: release.message
101
+ };
102
+ }
103
+ function validateReleases(releases, strategy) {
104
+ const enabled = releases.filter((release) => release.enabled).sort((left, right) => right.id.localeCompare(left.id));
105
+ const seenIds = /* @__PURE__ */ new Set();
106
+ for (const release of enabled) {
107
+ if (!(0, _hot_updater_core.isUUIDv7)(release.id)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} must use a canonical lowercase UUIDv7 ID`);
108
+ if (seenIds.has(release.id)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Duplicate Release ID: ${release.id}`);
109
+ seenIds.add(release.id);
110
+ if (release.strategy !== strategy) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has the wrong strategy`);
111
+ if (release.kind === "BUNDLE" && release.bundleId === null || release.kind === "EMBEDDED" && release.bundleId !== null) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has an invalid kind/Bundle combination`);
112
+ if (release.targetCohorts.length > _hot_updater_core.MAX_TARGET_COHORTS_PER_RELEASE) throw new ReleaseCatalogCompilationError("TARGET_COHORT_LIMIT", `Release ${release.id} exceeds ${_hot_updater_core.MAX_TARGET_COHORTS_PER_RELEASE} target cohorts`);
113
+ if (strategy === "APP_VERSION" && (release.targetAppVersion === null || release.fingerprintHash !== null)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has an invalid app-version target`);
114
+ if (strategy === "FINGERPRINT" && (release.fingerprintHash === null || release.targetAppVersion !== null)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has an invalid fingerprint target`);
115
+ }
116
+ return enabled.filter((release) => release.kind === "BUNDLE");
117
+ }
118
+ function collectFrontierReleaseIds(releases) {
119
+ if (releases.every((release) => (0, _hot_updater_core.normalizeRolloutCohortCount)(release.rolloutCohortCount) >= _hot_updater_core.NUMERIC_COHORT_SIZE)) {
120
+ const retained = /* @__PURE__ */ new Set();
121
+ const bundleIds = /* @__PURE__ */ new Set();
122
+ let embedded = false;
123
+ for (const release of releases) {
124
+ if (release.kind === "EMBEDDED") {
125
+ if (!embedded) {
126
+ embedded = true;
127
+ retained.add(release.id);
128
+ }
129
+ } else if (release.bundleId !== null && bundleIds.size < 11 && !bundleIds.has(release.bundleId)) {
130
+ bundleIds.add(release.bundleId);
131
+ retained.add(release.id);
132
+ }
133
+ if (embedded && bundleIds.size >= 11) break;
134
+ }
135
+ return retained;
136
+ }
137
+ const explicitTargetCohorts = new Set(releases.flatMap((release) => release.targetCohorts));
138
+ const cohortStates = [
139
+ void 0,
140
+ ...Array.from({ length: 1e3 }, (_, index) => String(index + 1)),
141
+ ...[...explicitTargetCohorts].sort(),
142
+ "release-catalog-non-targeted-cohort"
143
+ ].map((cohort) => {
144
+ const normalized = cohort === void 0 ? void 0 : (0, _hot_updater_core.normalizeCohortValue)(cohort);
145
+ return {
146
+ isCustom: normalized !== void 0 && (0, _hot_updater_core.isCustomCohort)(normalized),
147
+ normalized,
148
+ numeric: normalized === void 0 ? null : (0, _hot_updater_core.getNumericCohortValue)(normalized),
149
+ bundleIds: /* @__PURE__ */ new Set(),
150
+ embedded: false,
151
+ complete: false
152
+ };
153
+ });
154
+ const stateIndexesByCohort = /* @__PURE__ */ new Map();
155
+ const fullRolloutStateIndexes = [];
156
+ for (const [index, state] of cohortStates.entries()) {
157
+ if (state.normalized !== void 0) {
158
+ const indexes = stateIndexesByCohort.get(state.normalized) ?? [];
159
+ indexes.push(index);
160
+ stateIndexesByCohort.set(state.normalized, indexes);
161
+ }
162
+ if (state.normalized === void 0 || state.numeric !== null || state.isCustom) fullRolloutStateIndexes.push(index);
163
+ }
164
+ const retained = /* @__PURE__ */ new Set();
165
+ let incompleteStateCount = cohortStates.length;
166
+ for (const release of releases) {
167
+ const rolloutCohortCount = (0, _hot_updater_core.normalizeRolloutCohortCount)(release.rolloutCohortCount);
168
+ const eligibleStateIndexes = /* @__PURE__ */ new Set();
169
+ if (rolloutCohortCount >= _hot_updater_core.NUMERIC_COHORT_SIZE) for (const index of fullRolloutStateIndexes) eligibleStateIndexes.add(index);
170
+ else if (rolloutCohortCount > 0) for (const numericCohort of (0, _hot_updater_core.getRolledOutNumericCohorts)(release.id, rolloutCohortCount)) for (const index of stateIndexesByCohort.get(String(numericCohort)) ?? []) eligibleStateIndexes.add(index);
171
+ for (const targetCohort of new Set(release.targetCohorts.map(_hot_updater_core.normalizeCohortValue))) for (const index of stateIndexesByCohort.get(targetCohort) ?? []) eligibleStateIndexes.add(index);
172
+ for (const index of eligibleStateIndexes) {
173
+ const state = cohortStates[index];
174
+ if (state.complete) continue;
175
+ if (release.kind === "EMBEDDED") {
176
+ if (!state.embedded) {
177
+ state.embedded = true;
178
+ retained.add(release.id);
179
+ }
180
+ } else if (release.bundleId !== null && state.bundleIds.size < 11 && !state.bundleIds.has(release.bundleId)) {
181
+ state.bundleIds.add(release.bundleId);
182
+ retained.add(release.id);
183
+ }
184
+ if (state.embedded && state.bundleIds.size >= 11) {
185
+ state.complete = true;
186
+ incompleteStateCount -= 1;
187
+ }
188
+ }
189
+ if (incompleteStateCount === 0) break;
190
+ }
191
+ return retained;
192
+ }
193
+ function intervalContainsSegment(interval, segment) {
194
+ if (interval.lower !== null) {
195
+ if (segment.lower === null) return false;
196
+ const compared = compareBounds(segment.lower.version, interval.lower.version);
197
+ if (compared < 0 || compared === 0 && segment.lower.inclusive && !interval.lower.inclusive) return false;
198
+ }
199
+ if (interval.upper !== null) {
200
+ if (segment.upper === null) return false;
201
+ const compared = compareBounds(segment.upper.version, interval.upper.version);
202
+ if (compared > 0 || compared === 0 && segment.upper.inclusive && !interval.upper.inclusive) return false;
203
+ }
204
+ return true;
205
+ }
206
+ function atomicVersionSegments(intervals) {
207
+ const endpoints = [...new Set(intervals.flatMap((interval) => [interval.lower?.version, interval.upper?.version].filter((version) => version !== void 0)))].sort(compareBounds);
208
+ if (endpoints.length === 0) return [{
209
+ lower: null,
210
+ upper: null
211
+ }];
212
+ const segments = [{
213
+ lower: null,
214
+ upper: {
215
+ inclusive: false,
216
+ version: endpoints[0]
217
+ }
218
+ }];
219
+ for (let index = 0; index < endpoints.length; index += 1) {
220
+ const endpoint = endpoints[index];
221
+ segments.push({
222
+ lower: {
223
+ inclusive: true,
224
+ version: endpoint
225
+ },
226
+ upper: {
227
+ inclusive: true,
228
+ version: endpoint
229
+ }
230
+ });
231
+ const next = endpoints[index + 1];
232
+ if (next !== void 0) segments.push({
233
+ lower: {
234
+ inclusive: false,
235
+ version: endpoint
236
+ },
237
+ upper: {
238
+ inclusive: false,
239
+ version: next
240
+ }
241
+ });
242
+ }
243
+ segments.push({
244
+ lower: {
245
+ inclusive: false,
246
+ version: endpoints.at(-1)
247
+ },
248
+ upper: null
249
+ });
250
+ return segments;
251
+ }
252
+ function sameIndexes(left, right) {
253
+ return left.length === right.length && left.every((value, index) => value === right[index]);
254
+ }
255
+ function mergeSegments(segments) {
256
+ const merged = [];
257
+ for (const segment of segments) {
258
+ const previous = merged.at(-1);
259
+ if (previous && sameIndexes(previous.releaseIndexes, segment.releaseIndexes) && sameIndexes(previous.rollbackReleaseIndexes, segment.rollbackReleaseIndexes)) merged[merged.length - 1] = {
260
+ ...previous,
261
+ upper: segment.upper
262
+ };
263
+ else merged.push(segment);
264
+ }
265
+ return merged;
266
+ }
267
+ function compileAppVersion(releases) {
268
+ const ranges = /* @__PURE__ */ new Map();
269
+ for (const release of releases) ranges.set(release.id, rangeToIntervals(release.targetAppVersion));
270
+ const segmentReleases = atomicVersionSegments([...ranges.values()].flat()).map((segment) => {
271
+ const applicable = releases.filter((release) => ranges.get(release.id).some((range) => intervalContainsSegment(range, segment)));
272
+ return {
273
+ rollbackIds: new Set(applicable.map(({ id }) => id)),
274
+ segment,
275
+ retainedIds: collectFrontierReleaseIds(applicable)
276
+ };
277
+ });
278
+ const retainedIds = new Set(segmentReleases.flatMap(({ retainedIds, rollbackIds }) => [...retainedIds, ...rollbackIds]));
279
+ const retainedReleases = releases.filter((release) => retainedIds.has(release.id));
280
+ const descriptorIndex = new Map(retainedReleases.map((release, index) => [release.id, index]));
281
+ return {
282
+ envelope: {
283
+ fallbackPolicy: _hot_updater_core.RELEASE_CATALOG_FALLBACK_POLICY,
284
+ schemaVersion: _hot_updater_core.RELEASE_CATALOG_SCHEMA_VERSION,
285
+ segments: mergeSegments(segmentReleases.map(({ segment, retainedIds, rollbackIds }) => ({
286
+ ...segment,
287
+ releaseIndexes: releases.filter((release) => retainedIds.has(release.id)).map((release) => descriptorIndex.get(release.id)).filter((index) => index !== void 0),
288
+ rollbackReleaseIndexes: releases.filter((release) => rollbackIds.has(release.id)).map((release) => descriptorIndex.get(release.id)).filter((index) => index !== void 0)
289
+ })).filter((segment) => segment.releaseIndexes.length > 0 || segment.rollbackReleaseIndexes.length > 0)),
290
+ strategy: "APP_VERSION"
291
+ },
292
+ retainedReleases
293
+ };
294
+ }
295
+ function compileFingerprint(releases) {
296
+ const retainedIds = collectFrontierReleaseIds(releases);
297
+ const retainedReleases = releases;
298
+ return {
299
+ envelope: {
300
+ fallbackPolicy: _hot_updater_core.RELEASE_CATALOG_FALLBACK_POLICY,
301
+ releaseIndexes: retainedReleases.map((release, index) => retainedIds.has(release.id) ? index : -1).filter((index) => index >= 0),
302
+ rollbackReleaseIndexes: retainedReleases.map((_, index) => index),
303
+ schemaVersion: _hot_updater_core.RELEASE_CATALOG_SCHEMA_VERSION,
304
+ strategy: "FINGERPRINT"
305
+ },
306
+ retainedReleases
307
+ };
308
+ }
309
+ async function sha256(value) {
310
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
311
+ return `sha256:${[...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
312
+ }
313
+ function canonicalStringify(value) {
314
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
315
+ if (Array.isArray(value)) return `[${value.map(canonicalStringify).join(",")}]`;
316
+ const record = value;
317
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalStringify(record[key])}`).join(",")}}`;
318
+ }
319
+ async function compileReleaseCatalog(input) {
320
+ const releases = validateReleases(input.releases, input.strategy);
321
+ const targetCohorts = new Set(releases.flatMap((release) => release.targetCohorts));
322
+ if (targetCohorts.size > _hot_updater_core.MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE) throw new ReleaseCatalogCompilationError("DISTINCT_TARGET_COHORT_LIMIT", `Scope exceeds ${_hot_updater_core.MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE} distinct target cohorts`);
323
+ const { envelope, retainedReleases } = input.strategy === "APP_VERSION" ? compileAppVersion(releases) : compileFingerprint(releases);
324
+ const payload = {
325
+ ...envelope,
326
+ releaseDescriptors: retainedReleases.map(releaseDescriptor)
327
+ };
328
+ const canonicalPayload = canonicalStringify(payload);
329
+ const byteSize = new TextEncoder().encode(canonicalPayload).byteLength;
330
+ if (byteSize > _hot_updater_core.MAX_COMPILED_CATALOG_BYTES) throw new ReleaseCatalogCompilationError("CATALOG_OVERSIZE", `Compiled Release catalog is ${byteSize} bytes; maximum is ${_hot_updater_core.MAX_COMPILED_CATALOG_BYTES}`);
331
+ return {
332
+ byteSize,
333
+ canonicalPayload,
334
+ catalogHash: await sha256(canonicalPayload),
335
+ diagnostics: {
336
+ byteSize,
337
+ descriptorCount: payload.releaseDescriptors.length,
338
+ distinctTargetCohortCount: targetCohorts.size,
339
+ releaseCount: releases.length,
340
+ segmentCount: payload.strategy === "APP_VERSION" ? payload.segments.length : 1
341
+ },
342
+ payload
343
+ };
344
+ }
345
+ function versionInSegment(version, segment) {
346
+ if (segment.lower !== null) {
347
+ const compared = compareBounds(version, segment.lower.version);
348
+ if (compared < 0 || compared === 0 && !segment.lower.inclusive) return false;
349
+ }
350
+ if (segment.upper !== null) {
351
+ const compared = compareBounds(version, segment.upper.version);
352
+ if (compared > 0 || compared === 0 && !segment.upper.inclusive) return false;
353
+ }
354
+ return true;
355
+ }
356
+ function canonicalizeAppVersion(appVersion) {
357
+ return require_index.coerce(appVersion);
358
+ }
359
+ function projectCompiledCatalog(catalog, appVersion) {
360
+ let indexes;
361
+ if (catalog.strategy === "FINGERPRINT") indexes = catalog.releaseIndexes;
362
+ else {
363
+ const canonicalVersion = appVersion && canonicalizeAppVersion(appVersion);
364
+ if (!canonicalVersion) return [];
365
+ indexes = catalog.segments.find((segment) => versionInSegment(canonicalVersion, segment))?.releaseIndexes ?? [];
366
+ }
367
+ return indexes.map((index) => catalog.releaseDescriptors[index]);
368
+ }
369
+ function projectCompiledRollbackCatalog(catalog, appVersion) {
370
+ let indexes;
371
+ if (catalog.strategy === "FINGERPRINT") indexes = catalog.rollbackReleaseIndexes ?? catalog.releaseIndexes;
372
+ else {
373
+ const canonicalVersion = appVersion && canonicalizeAppVersion(appVersion);
374
+ if (!canonicalVersion) return [];
375
+ const segment = catalog.segments.find((candidate) => versionInSegment(canonicalVersion, candidate));
376
+ indexes = segment?.rollbackReleaseIndexes ?? segment?.releaseIndexes ?? [];
377
+ }
378
+ return indexes.map((index) => catalog.releaseDescriptors[index]);
379
+ }
380
+ function referenceCompatibleReleases(releases, appVersion) {
381
+ const canonicalVersion = canonicalizeAppVersion(appVersion);
382
+ if (!canonicalVersion) return [];
383
+ return releases.filter((release) => release.enabled && release.targetAppVersion !== null && require_index.satisfies(canonicalVersion, release.targetAppVersion));
384
+ }
385
+ //#endregion
386
+ exports.ReleaseCatalogCompilationError = ReleaseCatalogCompilationError;
387
+ exports.canonicalizeAppVersion = canonicalizeAppVersion;
388
+ exports.compileReleaseCatalog = compileReleaseCatalog;
389
+ exports.projectCompiledCatalog = projectCompiledCatalog;
390
+ exports.projectCompiledRollbackCatalog = projectCompiledRollbackCatalog;
391
+ exports.referenceCompatibleReleases = referenceCompatibleReleases;
@@ -0,0 +1,55 @@
1
+ import { RELEASE_CATALOG_FALLBACK_POLICY, RELEASE_CATALOG_SCHEMA_VERSION, Release, ReleaseCatalogDescriptor, ReleaseStrategy } from "@hot-updater/core";
2
+
3
+ //#region src/releaseCatalogCompiler.d.ts
4
+ interface CatalogVersionBound {
5
+ readonly version: string;
6
+ readonly inclusive: boolean;
7
+ }
8
+ interface CompiledCatalogSegment {
9
+ readonly lower: CatalogVersionBound | null;
10
+ readonly upper: CatalogVersionBound | null;
11
+ readonly releaseIndexes: readonly number[];
12
+ readonly rollbackReleaseIndexes: readonly number[];
13
+ }
14
+ type CompiledReleaseCatalog = {
15
+ readonly schemaVersion: typeof RELEASE_CATALOG_SCHEMA_VERSION;
16
+ readonly strategy: "APP_VERSION";
17
+ readonly fallbackPolicy: typeof RELEASE_CATALOG_FALLBACK_POLICY;
18
+ readonly releaseDescriptors: readonly ReleaseCatalogDescriptor[];
19
+ readonly segments: readonly CompiledCatalogSegment[];
20
+ } | {
21
+ readonly schemaVersion: typeof RELEASE_CATALOG_SCHEMA_VERSION;
22
+ readonly strategy: "FINGERPRINT";
23
+ readonly fallbackPolicy: typeof RELEASE_CATALOG_FALLBACK_POLICY;
24
+ readonly releaseDescriptors: readonly ReleaseCatalogDescriptor[];
25
+ readonly releaseIndexes: readonly number[];
26
+ readonly rollbackReleaseIndexes: readonly number[];
27
+ };
28
+ interface ReleaseCatalogCompilerDiagnostics {
29
+ readonly releaseCount: number;
30
+ readonly descriptorCount: number;
31
+ readonly segmentCount: number;
32
+ readonly distinctTargetCohortCount: number;
33
+ readonly byteSize: number;
34
+ }
35
+ interface ReleaseCatalogCompilation {
36
+ readonly payload: CompiledReleaseCatalog;
37
+ readonly canonicalPayload: string;
38
+ readonly catalogHash: string;
39
+ readonly byteSize: number;
40
+ readonly diagnostics: ReleaseCatalogCompilerDiagnostics;
41
+ }
42
+ declare class ReleaseCatalogCompilationError extends Error {
43
+ readonly code: "INVALID_RELEASE" | "INVALID_TARGET_RANGE" | "TARGET_COHORT_LIMIT" | "DISTINCT_TARGET_COHORT_LIMIT" | "CATALOG_OVERSIZE";
44
+ constructor(code: "INVALID_RELEASE" | "INVALID_TARGET_RANGE" | "TARGET_COHORT_LIMIT" | "DISTINCT_TARGET_COHORT_LIMIT" | "CATALOG_OVERSIZE", message: string);
45
+ }
46
+ declare function compileReleaseCatalog(input: {
47
+ readonly strategy: ReleaseStrategy;
48
+ readonly releases: readonly Release[];
49
+ }): Promise<ReleaseCatalogCompilation>;
50
+ declare function canonicalizeAppVersion(appVersion: string): string | null;
51
+ declare function projectCompiledCatalog(catalog: CompiledReleaseCatalog, appVersion?: string): readonly ReleaseCatalogDescriptor[];
52
+ declare function projectCompiledRollbackCatalog(catalog: CompiledReleaseCatalog, appVersion?: string): readonly ReleaseCatalogDescriptor[];
53
+ declare function referenceCompatibleReleases(releases: readonly Release[], appVersion: string): readonly Release[];
54
+ //#endregion
55
+ export { CatalogVersionBound, CompiledCatalogSegment, CompiledReleaseCatalog, ReleaseCatalogCompilation, ReleaseCatalogCompilationError, ReleaseCatalogCompilerDiagnostics, canonicalizeAppVersion, compileReleaseCatalog, projectCompiledCatalog, projectCompiledRollbackCatalog, referenceCompatibleReleases };
@@ -0,0 +1,55 @@
1
+ import { RELEASE_CATALOG_FALLBACK_POLICY, RELEASE_CATALOG_SCHEMA_VERSION, Release, ReleaseCatalogDescriptor, ReleaseStrategy } from "@hot-updater/core";
2
+
3
+ //#region src/releaseCatalogCompiler.d.ts
4
+ interface CatalogVersionBound {
5
+ readonly version: string;
6
+ readonly inclusive: boolean;
7
+ }
8
+ interface CompiledCatalogSegment {
9
+ readonly lower: CatalogVersionBound | null;
10
+ readonly upper: CatalogVersionBound | null;
11
+ readonly releaseIndexes: readonly number[];
12
+ readonly rollbackReleaseIndexes: readonly number[];
13
+ }
14
+ type CompiledReleaseCatalog = {
15
+ readonly schemaVersion: typeof RELEASE_CATALOG_SCHEMA_VERSION;
16
+ readonly strategy: "APP_VERSION";
17
+ readonly fallbackPolicy: typeof RELEASE_CATALOG_FALLBACK_POLICY;
18
+ readonly releaseDescriptors: readonly ReleaseCatalogDescriptor[];
19
+ readonly segments: readonly CompiledCatalogSegment[];
20
+ } | {
21
+ readonly schemaVersion: typeof RELEASE_CATALOG_SCHEMA_VERSION;
22
+ readonly strategy: "FINGERPRINT";
23
+ readonly fallbackPolicy: typeof RELEASE_CATALOG_FALLBACK_POLICY;
24
+ readonly releaseDescriptors: readonly ReleaseCatalogDescriptor[];
25
+ readonly releaseIndexes: readonly number[];
26
+ readonly rollbackReleaseIndexes: readonly number[];
27
+ };
28
+ interface ReleaseCatalogCompilerDiagnostics {
29
+ readonly releaseCount: number;
30
+ readonly descriptorCount: number;
31
+ readonly segmentCount: number;
32
+ readonly distinctTargetCohortCount: number;
33
+ readonly byteSize: number;
34
+ }
35
+ interface ReleaseCatalogCompilation {
36
+ readonly payload: CompiledReleaseCatalog;
37
+ readonly canonicalPayload: string;
38
+ readonly catalogHash: string;
39
+ readonly byteSize: number;
40
+ readonly diagnostics: ReleaseCatalogCompilerDiagnostics;
41
+ }
42
+ declare class ReleaseCatalogCompilationError extends Error {
43
+ readonly code: "INVALID_RELEASE" | "INVALID_TARGET_RANGE" | "TARGET_COHORT_LIMIT" | "DISTINCT_TARGET_COHORT_LIMIT" | "CATALOG_OVERSIZE";
44
+ constructor(code: "INVALID_RELEASE" | "INVALID_TARGET_RANGE" | "TARGET_COHORT_LIMIT" | "DISTINCT_TARGET_COHORT_LIMIT" | "CATALOG_OVERSIZE", message: string);
45
+ }
46
+ declare function compileReleaseCatalog(input: {
47
+ readonly strategy: ReleaseStrategy;
48
+ readonly releases: readonly Release[];
49
+ }): Promise<ReleaseCatalogCompilation>;
50
+ declare function canonicalizeAppVersion(appVersion: string): string | null;
51
+ declare function projectCompiledCatalog(catalog: CompiledReleaseCatalog, appVersion?: string): readonly ReleaseCatalogDescriptor[];
52
+ declare function projectCompiledRollbackCatalog(catalog: CompiledReleaseCatalog, appVersion?: string): readonly ReleaseCatalogDescriptor[];
53
+ declare function referenceCompatibleReleases(releases: readonly Release[], appVersion: string): readonly Release[];
54
+ //#endregion
55
+ export { CatalogVersionBound, CompiledCatalogSegment, CompiledReleaseCatalog, ReleaseCatalogCompilation, ReleaseCatalogCompilationError, ReleaseCatalogCompilerDiagnostics, canonicalizeAppVersion, compileReleaseCatalog, projectCompiledCatalog, projectCompiledRollbackCatalog, referenceCompatibleReleases };