@hot-updater/protocol 0.0.0-stage → 1.0.0-rc.20

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 ADDED
@@ -0,0 +1,418 @@
1
+ //#region src/bundleArtifacts.ts
2
+ const stripBundleArtifactMetadata = (metadata) => metadata;
3
+ const getManifestStorageUri = (bundle) => bundle.manifestStorageUri;
4
+ const getManifestFileHash = (bundle) => bundle.manifestFileHash;
5
+ const getAssetBaseStorageUri = (bundle) => bundle.assetBaseStorageUri;
6
+ const isBundlePatchArtifact = (value) => {
7
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
8
+ const candidate = value;
9
+ return typeof candidate.baseBundleId === "string" && typeof candidate.baseFileHash === "string" && typeof candidate.patchFileHash === "string" && typeof candidate.patchStorageUri === "string";
10
+ };
11
+ const readBundlePatchArray = (patches) => {
12
+ if (!Array.isArray(patches)) return [];
13
+ return patches.filter(isBundlePatchArtifact);
14
+ };
15
+ const getBundlePatches = (bundle) => {
16
+ const patches = readBundlePatchArray(bundle.patches);
17
+ const seenBaseBundleIds = /* @__PURE__ */ new Set();
18
+ return patches.filter((patch) => {
19
+ if (seenBaseBundleIds.has(patch.baseBundleId)) return false;
20
+ seenBaseBundleIds.add(patch.baseBundleId);
21
+ return true;
22
+ });
23
+ };
24
+ const getBundlePatch = (bundle, baseBundleId) => {
25
+ return getBundlePatches(bundle).find((patch) => patch.baseBundleId === baseBundleId) ?? null;
26
+ };
27
+ const getPrimaryPatch = (bundle) => {
28
+ return getBundlePatches(bundle)[0] ?? null;
29
+ };
30
+ const getPatchBaseBundleId = (bundle) => getPrimaryPatch(bundle)?.baseBundleId ?? null;
31
+ const getPatchBaseFileHash = (bundle) => getPrimaryPatch(bundle)?.baseFileHash ?? null;
32
+ const getPatchFileHash = (bundle) => getPrimaryPatch(bundle)?.patchFileHash ?? null;
33
+ const getPatchStorageUri = (bundle) => getPrimaryPatch(bundle)?.patchStorageUri ?? null;
34
+ //#endregion
35
+ //#region src/clientPlugin.ts
36
+ /** Declares a client plugin; built-in and third-party plugins use the same contract. */
37
+ const defineClientPlugin = (plugin) => plugin;
38
+ //#endregion
39
+ //#region src/rollout.ts
40
+ const NUMERIC_COHORT_SIZE = 1e3;
41
+ const DEFAULT_ROLLOUT_COHORT_COUNT = NUMERIC_COHORT_SIZE;
42
+ const MAX_COHORT_LENGTH = 64;
43
+ const INVALID_COHORT_ERROR_MESSAGE = `Invalid cohort. Use 1-1000 or a lowercase slug without spaces, up to 64 characters.`;
44
+ const CUSTOM_COHORT_PATTERN = /^[a-z0-9-]+$/;
45
+ function parseNumericCohortValue(cohort) {
46
+ if (!/^\d+$/.test(cohort)) return null;
47
+ const parsed = Number.parseInt(cohort, 10);
48
+ if (Number.isNaN(parsed) || parsed < 1 || parsed > 1e3) return null;
49
+ return parsed;
50
+ }
51
+ function positiveMod(value, modulus) {
52
+ return (value % modulus + modulus) % modulus;
53
+ }
54
+ function hashString(value) {
55
+ let hash = 0;
56
+ for (let i = 0; i < value.length; i++) {
57
+ const char = value.charCodeAt(i);
58
+ hash = (hash << 5) - hash + char;
59
+ hash |= 0;
60
+ }
61
+ return hash;
62
+ }
63
+ function gcd(a, b) {
64
+ let x = Math.abs(a);
65
+ let y = Math.abs(b);
66
+ while (y !== 0) {
67
+ const next = x % y;
68
+ x = y;
69
+ y = next;
70
+ }
71
+ return x;
72
+ }
73
+ function modularInverse(value, modulus) {
74
+ let t = 0;
75
+ let newT = 1;
76
+ let r = modulus;
77
+ let newR = positiveMod(value, modulus);
78
+ while (newR !== 0) {
79
+ const quotient = Math.floor(r / newR);
80
+ [t, newT] = [newT, t - quotient * newT];
81
+ [r, newR] = [newR, r - quotient * newR];
82
+ }
83
+ if (r > 1) throw new Error(`No modular inverse for ${value} mod ${modulus}`);
84
+ return positiveMod(t, modulus);
85
+ }
86
+ function getRolloutShuffleParameters(bundleId) {
87
+ let multiplier = positiveMod(hashString(`${bundleId}:multiplier`), 997);
88
+ if (multiplier === 0) multiplier = 1;
89
+ while (gcd(multiplier, NUMERIC_COHORT_SIZE) !== 1) {
90
+ multiplier = positiveMod(multiplier + 1, NUMERIC_COHORT_SIZE);
91
+ if (multiplier === 0) multiplier = 1;
92
+ }
93
+ const offset = positiveMod(hashString(`${bundleId}:offset`), NUMERIC_COHORT_SIZE);
94
+ return {
95
+ multiplier,
96
+ offset,
97
+ inverseMultiplier: modularInverse(multiplier, NUMERIC_COHORT_SIZE)
98
+ };
99
+ }
100
+ function normalizeRolloutCohortCount(rolloutCohortCount) {
101
+ if (rolloutCohortCount === null || rolloutCohortCount === void 0) return DEFAULT_ROLLOUT_COHORT_COUNT;
102
+ if (rolloutCohortCount <= 0) return 0;
103
+ if (rolloutCohortCount >= 1e3) return NUMERIC_COHORT_SIZE;
104
+ return Math.floor(rolloutCohortCount);
105
+ }
106
+ function normalizeCohortValue(cohort) {
107
+ const normalized = cohort.trim().toLowerCase();
108
+ const numericCohort = parseNumericCohortValue(normalized);
109
+ if (numericCohort !== null) return String(numericCohort);
110
+ return normalized;
111
+ }
112
+ function getNumericCohortValue(cohort) {
113
+ return parseNumericCohortValue(normalizeCohortValue(cohort));
114
+ }
115
+ function isNumericCohort(cohort) {
116
+ return getNumericCohortValue(cohort) !== null;
117
+ }
118
+ function isCustomCohort(cohort) {
119
+ const normalized = normalizeCohortValue(cohort);
120
+ return normalized.length > 0 && normalized.length <= 64 && !/^\d+$/.test(normalized) && CUSTOM_COHORT_PATTERN.test(normalized);
121
+ }
122
+ function isValidCohort(cohort) {
123
+ const normalized = normalizeCohortValue(cohort);
124
+ return isNumericCohort(normalized) || isCustomCohort(normalized);
125
+ }
126
+ function getDefaultNumericCohort(identifier) {
127
+ const cohortValue = positiveMod(hashString(identifier), NUMERIC_COHORT_SIZE) + 1;
128
+ return String(cohortValue);
129
+ }
130
+ function getNumericCohortRolloutPosition(bundleId, cohortValue) {
131
+ if (cohortValue < 1 || cohortValue > 1e3) throw new Error(`Invalid numeric cohort: ${cohortValue}`);
132
+ const { offset, inverseMultiplier } = getRolloutShuffleParameters(bundleId);
133
+ return positiveMod(inverseMultiplier * (cohortValue - 1 - offset), NUMERIC_COHORT_SIZE);
134
+ }
135
+ function getRolledOutNumericCohorts(bundleId, rolloutCohortCount) {
136
+ const normalizedRolloutCount = normalizeRolloutCohortCount(rolloutCohortCount);
137
+ if (normalizedRolloutCount <= 0) return [];
138
+ if (normalizedRolloutCount >= 1e3) return Array.from({ length: NUMERIC_COHORT_SIZE }, (_, index) => index + 1);
139
+ const { multiplier, offset } = getRolloutShuffleParameters(bundleId);
140
+ return Array.from({ length: normalizedRolloutCount }, (_, position) => positiveMod(multiplier * position + offset, NUMERIC_COHORT_SIZE)).map((zeroBasedCohort) => zeroBasedCohort + 1).sort((left, right) => left - right);
141
+ }
142
+ function isCohortEligibleForUpdate(bundleId, cohort, rolloutCohortCount, targetCohorts) {
143
+ const normalizedCohort = cohort === null || cohort === void 0 ? void 0 : normalizeCohortValue(cohort);
144
+ const normalizedTargetCohorts = targetCohorts?.map((targetCohort) => normalizeCohortValue(targetCohort)) ?? [];
145
+ if (normalizedCohort !== void 0 && normalizedTargetCohorts.includes(normalizedCohort)) return true;
146
+ const normalizedRolloutCount = normalizeRolloutCohortCount(rolloutCohortCount);
147
+ if (normalizedRolloutCount <= 0) return false;
148
+ if (normalizedCohort === void 0) return normalizedRolloutCount >= NUMERIC_COHORT_SIZE;
149
+ const numericCohort = getNumericCohortValue(normalizedCohort);
150
+ if (numericCohort === null) return false;
151
+ if (normalizedRolloutCount >= 1e3) return true;
152
+ return getNumericCohortRolloutPosition(bundleId, numericCohort) < normalizedRolloutCount;
153
+ }
154
+ //#endregion
155
+ //#region src/releaseCatalog.ts
156
+ const RELEASE_CATALOG_SCHEMA_VERSION = 1;
157
+ const RELEASE_CATALOG_FALLBACK_POLICY = "BUILTIN_IF_ACTIVE_INELIGIBLE";
158
+ const MAX_CRASHED_BUNDLES = 10;
159
+ const MAX_TARGET_COHORTS_PER_RELEASE = 100;
160
+ const MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE = 512;
161
+ const MAX_COMPILED_CATALOG_BYTES = 262144;
162
+ const hashSelectionContext = (value) => {
163
+ let first = 2166136261;
164
+ let second = 2654435769;
165
+ for (let index = 0; index < value.length; index += 1) {
166
+ const code = value.charCodeAt(index);
167
+ first = Math.imul(first ^ code, 16777619);
168
+ second = Math.imul(second ^ code, 2246822507);
169
+ }
170
+ return `v1:${(first >>> 0).toString(16).padStart(8, "0")}${(second >>> 0).toString(16).padStart(8, "0")}`;
171
+ };
172
+ function createReleaseSelectionContextHash(input) {
173
+ const crashedBundleIds = [...new Set(input.crashedBundleIds)].sort().slice(0, 10);
174
+ const canonical = JSON.stringify({
175
+ activeBundleId: input.activeBundleId ?? null,
176
+ activeReleaseId: input.activeReleaseId ?? null,
177
+ cohort: normalizeCohortValue(input.cohort ?? ""),
178
+ crashedBundleIds,
179
+ minimumReleaseId: input.minimumReleaseId,
180
+ selectorSchemaVersion: 1,
181
+ strategy: input.strategy,
182
+ strategyValue: input.strategyValue
183
+ });
184
+ return hashSelectionContext(canonical);
185
+ }
186
+ function isReleaseEligibleForCohort(release, cohort) {
187
+ if (isCohortEligibleForUpdate(release.releaseId, cohort, release.rolloutCohortCount, release.targetCohorts)) return true;
188
+ return cohort !== null && cohort !== void 0 && isCustomCohort(normalizeCohortValue(cohort)) && normalizeRolloutCohortCount(release.rolloutCohortCount) === 1e3;
189
+ }
190
+ function selectDesiredRelease(catalog, input) {
191
+ const crashedBundleIds = new Set(input.crashedBundleIds);
192
+ const activeReleaseId = input.activeReleaseId ?? null;
193
+ const hasActiveBundle = input.currentBundleId !== input.builtInBundleId;
194
+ const isSafeBundleRelease = (release) => release.kind === "BUNDLE" && release.bundleId !== null && !crashedBundleIds.has(release.bundleId) && release.bundleId >= input.minimumReleaseId;
195
+ const desiredBundle = (release, status) => ({
196
+ bundleId: release.bundleId,
197
+ kind: "BUNDLE",
198
+ release,
199
+ releaseId: release.releaseId,
200
+ status
201
+ });
202
+ for (const release of catalog.releases) {
203
+ if (!isReleaseEligibleForCohort(release, input.cohort)) continue;
204
+ if (activeReleaseId !== null && release.releaseId <= activeReleaseId || activeReleaseId === null && hasActiveBundle && (release.bundleId === null || release.bundleId <= input.currentBundleId)) continue;
205
+ if (isSafeBundleRelease(release)) return desiredBundle(release, "UPDATE");
206
+ if (release.bundleId !== null) continue;
207
+ return {
208
+ bundleId: input.builtInBundleId,
209
+ kind: "EMBEDDED",
210
+ release,
211
+ releaseId: release.releaseId,
212
+ status: "ROLLBACK"
213
+ };
214
+ }
215
+ if (activeReleaseId !== null || hasActiveBundle) {
216
+ const rollbackReleases = catalog.rollbackReleases ?? catalog.releases;
217
+ const current = rollbackReleases.find((release) => release.releaseId === activeReleaseId || activeReleaseId === null && release.bundleId === input.currentBundleId);
218
+ if (current !== void 0 && isSafeBundleRelease(current) && isReleaseEligibleForCohort(current, input.cohort)) return desiredBundle(current, "UPDATE");
219
+ const rollback = rollbackReleases.find((release) => isSafeBundleRelease(release) && (activeReleaseId !== null ? release.releaseId < activeReleaseId : release.bundleId < input.currentBundleId));
220
+ if (rollback !== void 0 && isSafeBundleRelease(rollback)) return desiredBundle(rollback, "ROLLBACK");
221
+ if (input.currentBundleId <= input.minimumReleaseId) return null;
222
+ }
223
+ if (catalog.fallbackPolicy !== "BUILTIN_IF_ACTIVE_INELIGIBLE") return null;
224
+ return {
225
+ bundleId: input.builtInBundleId,
226
+ kind: "BUILTIN",
227
+ release: null,
228
+ releaseId: null,
229
+ status: "ROLLBACK"
230
+ };
231
+ }
232
+ function assessCatalogAcceptance(incoming, highestSeen) {
233
+ if (highestSeen === null) return {
234
+ accepted: true,
235
+ shouldAdvanceHighWater: true
236
+ };
237
+ if (incoming.generation < highestSeen.generation) return {
238
+ accepted: false,
239
+ reason: "STALE_GENERATION"
240
+ };
241
+ if (incoming.generation === highestSeen.generation && incoming.catalogHash !== highestSeen.catalogHash) return {
242
+ accepted: false,
243
+ reason: "GENERATION_HASH_MISMATCH"
244
+ };
245
+ return {
246
+ accepted: true,
247
+ shouldAdvanceHighWater: incoming.generation > highestSeen.generation
248
+ };
249
+ }
250
+ function authorizeReleaseTransition(input) {
251
+ const { active, desired, explicitScopeSwitch } = input;
252
+ if (active === null || active.scopeKey === null || active.generation === null) return {
253
+ authorized: true,
254
+ reason: "FIRST_AUTHENTICATED_SELECTION"
255
+ };
256
+ if (desired.catalogId !== active.catalogId || desired.scopeKey !== active.scopeKey) {
257
+ if (!explicitScopeSwitch) return {
258
+ authorized: false,
259
+ reason: "UNSOLICITED_SCOPE"
260
+ };
261
+ if (desired.releaseId === null || desired.kind === "BUILTIN") return {
262
+ authorized: false,
263
+ reason: "EMPTY_TARGET_SCOPE"
264
+ };
265
+ return {
266
+ authorized: true,
267
+ reason: "EXPLICIT_SCOPE_SWITCH"
268
+ };
269
+ }
270
+ if (desired.generation === null || desired.generation < active.generation) return {
271
+ authorized: false,
272
+ reason: "STALE_GENERATION"
273
+ };
274
+ if (desired.generation > active.generation) return {
275
+ authorized: true,
276
+ reason: "NEWER_POLICY"
277
+ };
278
+ if (desired.selectionContextHash !== active.selectionContextHash) return {
279
+ authorized: true,
280
+ reason: "CONTEXT_RESELECTION"
281
+ };
282
+ return {
283
+ authorized: false,
284
+ reason: "BACKWARD_NOT_AUTHORIZED"
285
+ };
286
+ }
287
+ //#endregion
288
+ //#region src/releaseCatalogScope.ts
289
+ const BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
290
+ const MAX_CATALOG_SEGMENT_LENGTH = 255;
291
+ function assertCatalogSegment(value, name) {
292
+ if (value.length === 0 || value.length > MAX_CATALOG_SEGMENT_LENGTH || !/^[A-Za-z0-9._~-]+$/.test(value)) throw new Error(`${name} must be 1-${MAX_CATALOG_SEGMENT_LENGTH} URL-safe ASCII characters`);
293
+ }
294
+ function encodeUtf8(value) {
295
+ const bytes = [];
296
+ for (const codePointText of value) {
297
+ const codePoint = codePointText.codePointAt(0);
298
+ if (codePoint <= 127) bytes.push(codePoint);
299
+ else if (codePoint <= 2047) bytes.push(192 | codePoint >> 6, 128 | codePoint & 63);
300
+ else if (codePoint <= 65535) bytes.push(224 | codePoint >> 12, 128 | codePoint >> 6 & 63, 128 | codePoint & 63);
301
+ else bytes.push(240 | codePoint >> 18, 128 | codePoint >> 12 & 63, 128 | codePoint >> 6 & 63, 128 | codePoint & 63);
302
+ }
303
+ return bytes;
304
+ }
305
+ function decodeUtf8(bytes) {
306
+ let result = "";
307
+ for (let index = 0; index < bytes.length;) {
308
+ const first = bytes[index++];
309
+ let codePoint;
310
+ let continuationCount;
311
+ if (first <= 127) {
312
+ codePoint = first;
313
+ continuationCount = 0;
314
+ } else if ((first & 224) === 192) {
315
+ codePoint = first & 31;
316
+ continuationCount = 1;
317
+ } else if ((first & 240) === 224) {
318
+ codePoint = first & 15;
319
+ continuationCount = 2;
320
+ } else if ((first & 248) === 240) {
321
+ codePoint = first & 7;
322
+ continuationCount = 3;
323
+ } else throw new Error("Invalid UTF-8 channel key");
324
+ for (let offset = 0; offset < continuationCount; offset += 1) {
325
+ const continuation = bytes[index++];
326
+ if (continuation === void 0 || (continuation & 192) !== 128) throw new Error("Invalid UTF-8 channel key");
327
+ codePoint = codePoint << 6 | continuation & 63;
328
+ }
329
+ if (codePoint > 1114111 || codePoint >= 55296 && codePoint <= 57343 || continuationCount === 1 && codePoint < 128 || continuationCount === 2 && codePoint < 2048 || continuationCount === 3 && codePoint < 65536) throw new Error("Invalid UTF-8 channel key");
330
+ result += String.fromCodePoint(codePoint);
331
+ }
332
+ return result;
333
+ }
334
+ function base64UrlEncode(bytes) {
335
+ let result = "";
336
+ for (let index = 0; index < bytes.length; index += 3) {
337
+ const first = bytes[index];
338
+ const second = bytes[index + 1];
339
+ const third = bytes[index + 2];
340
+ const value = first << 16 | (second ?? 0) << 8 | (third ?? 0);
341
+ result += BASE64URL_ALPHABET[value >> 18 & 63];
342
+ result += BASE64URL_ALPHABET[value >> 12 & 63];
343
+ if (second !== void 0) result += BASE64URL_ALPHABET[value >> 6 & 63];
344
+ if (third !== void 0) result += BASE64URL_ALPHABET[value & 63];
345
+ }
346
+ return result;
347
+ }
348
+ function base64UrlDecode(value) {
349
+ if (!/^[A-Za-z0-9_-]*$/.test(value) || value.length % 4 === 1) throw new Error("Invalid channel key");
350
+ const bytes = [];
351
+ for (let index = 0; index < value.length; index += 4) {
352
+ const chunk = value.slice(index, index + 4);
353
+ const encoded = [...chunk].map((character) => BASE64URL_ALPHABET.indexOf(character));
354
+ if (encoded.some((part) => part < 0)) throw new Error("Invalid channel key");
355
+ const bits = encoded[0] << 18 | encoded[1] << 12 | (encoded[2] ?? 0) << 6 | (encoded[3] ?? 0);
356
+ bytes.push(bits >> 16 & 255);
357
+ if (chunk.length >= 3) bytes.push(bits >> 8 & 255);
358
+ if (chunk.length === 4) bytes.push(bits & 255);
359
+ }
360
+ return bytes;
361
+ }
362
+ function normalizeChannelName(channel) {
363
+ return channel.trim().normalize("NFC");
364
+ }
365
+ function assertCanonicalChannelName(channel) {
366
+ const normalized = normalizeChannelName(channel);
367
+ if (normalized.length === 0 || normalized !== channel) throw new Error("Channel name must be non-empty, trimmed, and NFC-normalized");
368
+ let codePointCount = 0;
369
+ for (const _codePoint of normalized) {
370
+ codePointCount += 1;
371
+ if (codePointCount > 255) throw new Error("Channel name must not exceed 255 characters");
372
+ }
373
+ }
374
+ function encodeChannelKey(channel) {
375
+ assertCanonicalChannelName(channel);
376
+ return base64UrlEncode(encodeUtf8(channel));
377
+ }
378
+ function decodeChannelKey(channelKey) {
379
+ const channel = decodeUtf8(base64UrlDecode(channelKey));
380
+ assertCanonicalChannelName(channel);
381
+ if (encodeChannelKey(channel) !== channelKey) throw new Error("Channel key is not canonically encoded");
382
+ return channel;
383
+ }
384
+ function createReleaseCatalogScopeKey(input) {
385
+ decodeChannelKey(input.channelKey);
386
+ if (input.strategy === "APP_VERSION") return `v1:app-version:${input.platform}:${input.channelKey}`;
387
+ assertCatalogSegment(input.fingerprintHash, "Fingerprint hash");
388
+ return `v1:fingerprint:${input.platform}:${input.channelKey}:${input.fingerprintHash}`;
389
+ }
390
+ function parseReleaseCatalogScopeKey(scopeKey) {
391
+ const segments = scopeKey.split(":");
392
+ const [version, strategy, platform, channelKey] = segments;
393
+ if (version !== "v1" || platform !== "ios" && platform !== "android" || channelKey === void 0) throw new Error("Invalid release catalog scope key");
394
+ const input = strategy === "app-version" && segments.length === 4 ? {
395
+ channelKey,
396
+ platform,
397
+ strategy: "APP_VERSION"
398
+ } : strategy === "fingerprint" && segments.length === 5 && segments[4] !== void 0 ? {
399
+ channelKey,
400
+ fingerprintHash: segments[4],
401
+ platform,
402
+ strategy: "FINGERPRINT"
403
+ } : (() => {
404
+ throw new Error("Invalid release catalog scope key");
405
+ })();
406
+ if (createReleaseCatalogScopeKey(input) !== scopeKey) throw new Error("Release catalog scope key is not canonical");
407
+ return input;
408
+ }
409
+ //#endregion
410
+ //#region src/types.ts
411
+ const ARTIFACT_PROTOCOL_VERSION = 1;
412
+ //#endregion
413
+ //#region src/uuid.ts
414
+ const NIL_UUID = "00000000-0000-0000-0000-000000000000";
415
+ const UUID_V7_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
416
+ const isUUIDv7 = (value) => typeof value === "string" && UUID_V7_PATTERN.test(value);
417
+ //#endregion
418
+ export { ARTIFACT_PROTOCOL_VERSION, DEFAULT_ROLLOUT_COHORT_COUNT, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, MAX_COMPILED_CATALOG_BYTES, MAX_CRASHED_BUNDLES, MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE, MAX_TARGET_COHORTS_PER_RELEASE, NIL_UUID, NUMERIC_COHORT_SIZE, RELEASE_CATALOG_FALLBACK_POLICY, RELEASE_CATALOG_SCHEMA_VERSION, assertCanonicalChannelName, assessCatalogAcceptance, authorizeReleaseTransition, createReleaseCatalogScopeKey, createReleaseSelectionContextHash, decodeChannelKey, defineClientPlugin, encodeChannelKey, getAssetBaseStorageUri, getBundlePatch, getBundlePatches, getDefaultNumericCohort, getManifestFileHash, getManifestStorageUri, getNumericCohortRolloutPosition, getNumericCohortValue, getPatchBaseBundleId, getPatchBaseFileHash, getPatchFileHash, getPatchStorageUri, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isReleaseEligibleForCohort, isUUIDv7, isValidCohort, normalizeChannelName, normalizeCohortValue, normalizeRolloutCohortCount, parseReleaseCatalogScopeKey, selectDesiredRelease, stripBundleArtifactMetadata };
package/package.json CHANGED
@@ -1,6 +1,51 @@
1
1
  {
2
2
  "name": "@hot-updater/protocol",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.0.0-rc.20",
4
+ "type": "module",
5
+ "description": "Data formats and pure computations shared by the Hot Updater app and server.",
6
+ "sideEffects": false,
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.mjs",
9
+ "types": "./dist/index.d.cts",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "package.json"
20
+ ],
21
+ "keywords": [
22
+ "react-native",
23
+ "react-native-code-push",
24
+ "code-push",
25
+ "eas",
26
+ "eas-update",
27
+ "expo",
28
+ "expo-update",
29
+ "self-hosted"
30
+ ],
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/gronxb/hot-updater"
35
+ },
36
+ "author": "gronxb <gron1gh1@gmail.com> (https://github.com/gronxb)",
37
+ "bugs": {
38
+ "url": "https://github.com/gronxb/hot-updater/issues"
39
+ },
40
+ "homepage": "https://github.com/gronxb/hot-updater#readme",
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^20"
46
+ },
47
+ "scripts": {
48
+ "build": "tsdown",
49
+ "test:type": "tsc --noEmit"
50
+ }
6
51
  }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.