@hot-updater/core 0.27.1 → 0.29.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.
@@ -0,0 +1,23 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ //#endregion
23
+ exports.__toESM = __toESM;
@@ -0,0 +1,26 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
3
+ let node_path = require("node:path");
4
+ node_path = require_runtime.__toESM(node_path);
5
+ //#region src/hotUpdateDirUtil.ts
6
+ const HOT_UPDATE_DIR_NAME = ".hot-updater";
7
+ const HOT_UPDATE_OUTPUT_DIR_NAME = "output";
8
+ const HOT_UPDATE_LOG_DIR_NAME = "log";
9
+ const HotUpdateDirUtil = {
10
+ dirName: HOT_UPDATE_DIR_NAME,
11
+ outputDirName: HOT_UPDATE_OUTPUT_DIR_NAME,
12
+ logDirName: HOT_UPDATE_LOG_DIR_NAME,
13
+ outputGitignorePath: `${HOT_UPDATE_DIR_NAME}/${HOT_UPDATE_OUTPUT_DIR_NAME}`,
14
+ logGitignorePath: `${HOT_UPDATE_DIR_NAME}/${HOT_UPDATE_LOG_DIR_NAME}`,
15
+ getDirPath: ({ cwd }) => {
16
+ return node_path.default.join(cwd, HOT_UPDATE_DIR_NAME);
17
+ },
18
+ getDefaultOutputPath: ({ cwd }) => {
19
+ return node_path.default.join(cwd, HOT_UPDATE_DIR_NAME, HOT_UPDATE_OUTPUT_DIR_NAME);
20
+ },
21
+ getLogDirPath: ({ cwd }) => {
22
+ return node_path.default.join(cwd, HOT_UPDATE_DIR_NAME, HOT_UPDATE_LOG_DIR_NAME);
23
+ }
24
+ };
25
+ //#endregion
26
+ exports.HotUpdateDirUtil = HotUpdateDirUtil;
@@ -0,0 +1,25 @@
1
+ //#region src/hotUpdateDirUtil.d.ts
2
+ declare const HotUpdateDirUtil: {
3
+ readonly dirName: ".hot-updater";
4
+ readonly outputDirName: "output";
5
+ readonly logDirName: "log";
6
+ readonly outputGitignorePath: ".hot-updater/output";
7
+ readonly logGitignorePath: ".hot-updater/log";
8
+ readonly getDirPath: ({
9
+ cwd
10
+ }: {
11
+ cwd: string;
12
+ }) => string;
13
+ readonly getDefaultOutputPath: ({
14
+ cwd
15
+ }: {
16
+ cwd: string;
17
+ }) => string;
18
+ readonly getLogDirPath: ({
19
+ cwd
20
+ }: {
21
+ cwd: string;
22
+ }) => string;
23
+ };
24
+ //#endregion
25
+ export { HotUpdateDirUtil };
@@ -0,0 +1,25 @@
1
+ //#region src/hotUpdateDirUtil.d.ts
2
+ declare const HotUpdateDirUtil: {
3
+ readonly dirName: ".hot-updater";
4
+ readonly outputDirName: "output";
5
+ readonly logDirName: "log";
6
+ readonly outputGitignorePath: ".hot-updater/output";
7
+ readonly logGitignorePath: ".hot-updater/log";
8
+ readonly getDirPath: ({
9
+ cwd
10
+ }: {
11
+ cwd: string;
12
+ }) => string;
13
+ readonly getDefaultOutputPath: ({
14
+ cwd
15
+ }: {
16
+ cwd: string;
17
+ }) => string;
18
+ readonly getLogDirPath: ({
19
+ cwd
20
+ }: {
21
+ cwd: string;
22
+ }) => string;
23
+ };
24
+ //#endregion
25
+ export { HotUpdateDirUtil };
@@ -1,5 +1,4 @@
1
- import path from "path";
2
-
1
+ import path from "node:path";
3
2
  //#region src/hotUpdateDirUtil.ts
4
3
  const HOT_UPDATE_DIR_NAME = ".hot-updater";
5
4
  const HOT_UPDATE_OUTPUT_DIR_NAME = "output";
@@ -20,10 +19,5 @@ const HotUpdateDirUtil = {
20
19
  return path.join(cwd, HOT_UPDATE_DIR_NAME, HOT_UPDATE_LOG_DIR_NAME);
21
20
  }
22
21
  };
23
-
24
22
  //#endregion
25
- //#region src/uuid.ts
26
- const NIL_UUID = "00000000-0000-0000-0000-000000000000";
27
-
28
- //#endregion
29
- export { HotUpdateDirUtil, NIL_UUID };
23
+ export { HotUpdateDirUtil };
package/dist/index.cjs CHANGED
@@ -1,54 +1,20 @@
1
- //#region rolldown:runtime
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
- key = keys[i];
11
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
- get: ((k) => from[k]).bind(null, key),
13
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
- });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
- value: mod,
20
- enumerable: true
21
- }) : target, mod));
22
-
23
- //#endregion
24
- let path = require("path");
25
- path = __toESM(path);
26
-
27
- //#region src/hotUpdateDirUtil.ts
28
- const HOT_UPDATE_DIR_NAME = ".hot-updater";
29
- const HOT_UPDATE_OUTPUT_DIR_NAME = "output";
30
- const HOT_UPDATE_LOG_DIR_NAME = "log";
31
- const HotUpdateDirUtil = {
32
- dirName: HOT_UPDATE_DIR_NAME,
33
- outputDirName: HOT_UPDATE_OUTPUT_DIR_NAME,
34
- logDirName: HOT_UPDATE_LOG_DIR_NAME,
35
- outputGitignorePath: `${HOT_UPDATE_DIR_NAME}/${HOT_UPDATE_OUTPUT_DIR_NAME}`,
36
- logGitignorePath: `${HOT_UPDATE_DIR_NAME}/${HOT_UPDATE_LOG_DIR_NAME}`,
37
- getDirPath: ({ cwd }) => {
38
- return path.default.join(cwd, HOT_UPDATE_DIR_NAME);
39
- },
40
- getDefaultOutputPath: ({ cwd }) => {
41
- return path.default.join(cwd, HOT_UPDATE_DIR_NAME, HOT_UPDATE_OUTPUT_DIR_NAME);
42
- },
43
- getLogDirPath: ({ cwd }) => {
44
- return path.default.join(cwd, HOT_UPDATE_DIR_NAME, HOT_UPDATE_LOG_DIR_NAME);
45
- }
46
- };
47
-
48
- //#endregion
49
- //#region src/uuid.ts
50
- const NIL_UUID = "00000000-0000-0000-0000-000000000000";
51
-
52
- //#endregion
53
- exports.HotUpdateDirUtil = HotUpdateDirUtil;
54
- exports.NIL_UUID = NIL_UUID;
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_hotUpdateDirUtil = require("./hotUpdateDirUtil.cjs");
3
+ const require_rollout = require("./rollout.cjs");
4
+ const require_uuid = require("./uuid.cjs");
5
+ exports.DEFAULT_ROLLOUT_COHORT_COUNT = require_rollout.DEFAULT_ROLLOUT_COHORT_COUNT;
6
+ exports.HotUpdateDirUtil = require_hotUpdateDirUtil.HotUpdateDirUtil;
7
+ exports.INVALID_COHORT_ERROR_MESSAGE = require_rollout.INVALID_COHORT_ERROR_MESSAGE;
8
+ exports.MAX_COHORT_LENGTH = require_rollout.MAX_COHORT_LENGTH;
9
+ exports.NIL_UUID = require_uuid.NIL_UUID;
10
+ exports.NUMERIC_COHORT_SIZE = require_rollout.NUMERIC_COHORT_SIZE;
11
+ exports.getDefaultNumericCohort = require_rollout.getDefaultNumericCohort;
12
+ exports.getNumericCohortRolloutPosition = require_rollout.getNumericCohortRolloutPosition;
13
+ exports.getNumericCohortValue = require_rollout.getNumericCohortValue;
14
+ exports.getRolledOutNumericCohorts = require_rollout.getRolledOutNumericCohorts;
15
+ exports.isCohortEligibleForUpdate = require_rollout.isCohortEligibleForUpdate;
16
+ exports.isCustomCohort = require_rollout.isCustomCohort;
17
+ exports.isNumericCohort = require_rollout.isNumericCohort;
18
+ exports.isValidCohort = require_rollout.isValidCohort;
19
+ exports.normalizeCohortValue = require_rollout.normalizeCohortValue;
20
+ exports.normalizeRolloutCohortCount = require_rollout.normalizeRolloutCohortCount;
package/dist/index.d.cts CHANGED
@@ -1,198 +1,5 @@
1
- //#region src/hotUpdateDirUtil.d.ts
2
- declare const HotUpdateDirUtil: {
3
- readonly dirName: ".hot-updater";
4
- readonly outputDirName: "output";
5
- readonly logDirName: "log";
6
- readonly outputGitignorePath: ".hot-updater/output";
7
- readonly logGitignorePath: ".hot-updater/log";
8
- readonly getDirPath: ({
9
- cwd
10
- }: {
11
- cwd: string;
12
- }) => string;
13
- readonly getDefaultOutputPath: ({
14
- cwd
15
- }: {
16
- cwd: string;
17
- }) => string;
18
- readonly getLogDirPath: ({
19
- cwd
20
- }: {
21
- cwd: string;
22
- }) => string;
23
- };
24
- //#endregion
25
- //#region src/types.d.ts
26
- type Platform = "ios" | "android";
27
- type BundleMetadata = {
28
- app_version?: string;
29
- };
30
- interface Bundle {
31
- /**
32
- * The unique identifier for the bundle. uuidv7
33
- */
34
- id: string;
35
- /**
36
- * The platform the bundle is for.
37
- */
38
- platform: Platform;
39
- /**
40
- * Whether the bundle should force an update.
41
- */
42
- shouldForceUpdate: boolean;
43
- /**
44
- * Whether the bundle is enabled.
45
- */
46
- enabled: boolean;
47
- /**
48
- * The hash of the bundle.
49
- */
50
- fileHash: string;
51
- /**
52
- * The storage key of the bundle.
53
- * @example "s3://my-bucket/my-app/00000000-0000-0000-0000-000000000000/bundle.zip"
54
- * @example "r2://my-bucket/my-app/00000000-0000-0000-0000-000000000000/bundle.zip"
55
- * @example "firebase-storage://my-bucket/my-app/00000000-0000-0000-0000-000000000000/bundle.zip"
56
- * @example "storage://my-app/00000000-0000-0000-0000-000000000000/bundle.zip"
57
- */
58
- storageUri: string;
59
- /**
60
- * The git commit hash of the bundle.
61
- */
62
- gitCommitHash: string | null;
63
- /**
64
- * The message of the bundle.
65
- */
66
- message: string | null;
67
- /**
68
- * The name of the channel where the bundle is deployed.
69
- *
70
- * Examples:
71
- * - production: Production channel for end users
72
- * - development: Development channel for testing
73
- * - staging: Staging channel for quality assurance before production
74
- * - app-name: Channel for specific app instances (e.g., my-app, app-test)
75
- *
76
- * Different channel values can be used based on each app's requirements.
77
- */
78
- channel: string;
79
- /**
80
- * The target app version of the bundle.
81
- */
82
- targetAppVersion: string | null;
83
- /**
84
- * The fingerprint hash of the bundle.
85
- */
86
- fingerprintHash: string | null;
87
- /**
88
- * The metadata of the bundle.
89
- */
90
- metadata?: BundleMetadata;
91
- }
92
- type SnakeCase<S extends string> = S extends `${infer T}${infer U}` ? `${T extends Capitalize<T> ? "_" : ""}${Lowercase<T>}${SnakeCase<U>}` : S;
93
- type SnakeKeyObject<T$1> = T$1 extends Record<string, any> ? { [K in keyof T$1 as SnakeCase<Extract<K, string>>]: T$1[K] extends object ? SnakeKeyObject<T$1[K]> : T$1[K] } : T$1;
94
- type SnakeCaseBundle = SnakeKeyObject<Bundle>;
95
- type UpdateStatus = "ROLLBACK" | "UPDATE";
96
- /**
97
- * The update info for the database layer.
98
- * This is the update info that is used by the database.
99
- */
100
- interface UpdateInfo {
101
- id: string;
102
- shouldForceUpdate: boolean;
103
- message: string | null;
104
- status: UpdateStatus;
105
- storageUri: string | null;
106
- fileHash: string | null;
107
- }
108
- /**
109
- * The update info for the app layer.
110
- * This is the update info that is used by the app.
111
- */
112
- interface AppUpdateInfo extends Omit<UpdateInfo, "storageUri"> {
113
- fileUrl: string | null;
114
- /**
115
- * SHA256 hash of the bundle file, optionally with embedded signature.
116
- * Format when signed: "sig:<base64_signature>"
117
- * Format when unsigned: "<hex_hash>" (64-character lowercase hex)
118
- * The client parses this to extract signature for native verification.
119
- */
120
- fileHash: string | null;
121
- }
122
- type UpdateStrategy = "fingerprint" | "appVersion";
123
- type FingerprintGetBundlesArgs = {
124
- _updateStrategy: "fingerprint";
125
- platform: Platform;
126
- /**
127
- * The current bundle id of the app.
128
- */
129
- bundleId: string;
130
- /**
131
- * Minimum bundle id that should be used.
132
- * This value is generated at build time via getMinBundleId().
133
- *
134
- * @default "00000000-0000-0000-0000-000000000000"
135
- */
136
- minBundleId?: string;
137
- /**
138
- * The name of the channel where the bundle is deployed.
139
- *
140
- * @default "production"
141
- *
142
- * Examples:
143
- * - production: Production channel for end users
144
- * - development: Development channel for testing
145
- * - staging: Staging channel for quality assurance before production
146
- * - app-name: Channel for specific app instances (e.g., my-app, app-test)
147
- */
148
- channel?: string;
149
- /**
150
- * The fingerprint hash of the bundle.
151
- */
152
- fingerprintHash: string;
153
- };
154
- type AppVersionGetBundlesArgs = {
155
- _updateStrategy: "appVersion";
156
- platform: Platform;
157
- /**
158
- * The current bundle id of the app.
159
- */
160
- bundleId: string;
161
- /**
162
- * Minimum bundle id that should be used.
163
- * This value is generated at build time via getMinBundleId().
164
- *
165
- * @default "00000000-0000-0000-0000-000000000000"
166
- */
167
- minBundleId?: string;
168
- /**
169
- * The name of the channel where the bundle is deployed.
170
- *
171
- * @default "production"
172
- *
173
- * Examples:
174
- * - production: Production channel for end users
175
- * - development: Development channel for testing
176
- * - staging: Staging channel for quality assurance before production
177
- * - app-name: Channel for specific app instances (e.g., my-app, app-test)
178
- */
179
- channel?: string;
180
- /**
181
- * The current app version.
182
- */
183
- appVersion: string;
184
- };
185
- type GetBundlesArgs = FingerprintGetBundlesArgs | AppVersionGetBundlesArgs;
186
- type UpdateBundleParams = {
187
- platform: Platform;
188
- bundleId: string;
189
- minBundleId: string;
190
- channel: string;
191
- appVersion: string;
192
- fingerprintHash: string | null;
193
- };
194
- //#endregion
195
- //#region src/uuid.d.ts
196
- declare const NIL_UUID = "00000000-0000-0000-0000-000000000000";
197
- //#endregion
198
- export { AppUpdateInfo, AppVersionGetBundlesArgs, Bundle, BundleMetadata, FingerprintGetBundlesArgs, GetBundlesArgs, HotUpdateDirUtil, NIL_UUID, Platform, SnakeCaseBundle, UpdateBundleParams, UpdateInfo, UpdateStatus, UpdateStrategy };
1
+ import { HotUpdateDirUtil } from "./hotUpdateDirUtil.cjs";
2
+ import { DEFAULT_ROLLOUT_COHORT_COUNT, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NUMERIC_COHORT_SIZE, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount } from "./rollout.cjs";
3
+ import { AppUpdateInfo, AppVersionGetBundlesArgs, Bundle, BundleMetadata, FingerprintGetBundlesArgs, GetBundlesArgs, Platform, SnakeCaseBundle, UpdateBundleParams, UpdateInfo, UpdateStatus, UpdateStrategy } from "./types.cjs";
4
+ import { NIL_UUID } from "./uuid.cjs";
5
+ export { AppUpdateInfo, AppVersionGetBundlesArgs, Bundle, BundleMetadata, DEFAULT_ROLLOUT_COHORT_COUNT, FingerprintGetBundlesArgs, GetBundlesArgs, HotUpdateDirUtil, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NIL_UUID, NUMERIC_COHORT_SIZE, Platform, SnakeCaseBundle, UpdateBundleParams, UpdateInfo, UpdateStatus, UpdateStrategy, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount };
@@ -0,0 +1,5 @@
1
+ import { HotUpdateDirUtil } from "./hotUpdateDirUtil.mjs";
2
+ import { DEFAULT_ROLLOUT_COHORT_COUNT, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NUMERIC_COHORT_SIZE, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount } from "./rollout.mjs";
3
+ import { AppUpdateInfo, AppVersionGetBundlesArgs, Bundle, BundleMetadata, FingerprintGetBundlesArgs, GetBundlesArgs, Platform, SnakeCaseBundle, UpdateBundleParams, UpdateInfo, UpdateStatus, UpdateStrategy } from "./types.mjs";
4
+ import { NIL_UUID } from "./uuid.mjs";
5
+ export { AppUpdateInfo, AppVersionGetBundlesArgs, Bundle, BundleMetadata, DEFAULT_ROLLOUT_COHORT_COUNT, FingerprintGetBundlesArgs, GetBundlesArgs, HotUpdateDirUtil, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NIL_UUID, NUMERIC_COHORT_SIZE, Platform, SnakeCaseBundle, UpdateBundleParams, UpdateInfo, UpdateStatus, UpdateStrategy, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount };
package/dist/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import { HotUpdateDirUtil } from "./hotUpdateDirUtil.mjs";
2
+ import { DEFAULT_ROLLOUT_COHORT_COUNT, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NUMERIC_COHORT_SIZE, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount } from "./rollout.mjs";
3
+ import { NIL_UUID } from "./uuid.mjs";
4
+ export { DEFAULT_ROLLOUT_COHORT_COUNT, HotUpdateDirUtil, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NIL_UUID, NUMERIC_COHORT_SIZE, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount };
@@ -0,0 +1,18 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_rollout = require("./rollout.cjs");
3
+ const require_uuid = require("./uuid.cjs");
4
+ exports.DEFAULT_ROLLOUT_COHORT_COUNT = require_rollout.DEFAULT_ROLLOUT_COHORT_COUNT;
5
+ exports.INVALID_COHORT_ERROR_MESSAGE = require_rollout.INVALID_COHORT_ERROR_MESSAGE;
6
+ exports.MAX_COHORT_LENGTH = require_rollout.MAX_COHORT_LENGTH;
7
+ exports.NIL_UUID = require_uuid.NIL_UUID;
8
+ exports.NUMERIC_COHORT_SIZE = require_rollout.NUMERIC_COHORT_SIZE;
9
+ exports.getDefaultNumericCohort = require_rollout.getDefaultNumericCohort;
10
+ exports.getNumericCohortRolloutPosition = require_rollout.getNumericCohortRolloutPosition;
11
+ exports.getNumericCohortValue = require_rollout.getNumericCohortValue;
12
+ exports.getRolledOutNumericCohorts = require_rollout.getRolledOutNumericCohorts;
13
+ exports.isCohortEligibleForUpdate = require_rollout.isCohortEligibleForUpdate;
14
+ exports.isCustomCohort = require_rollout.isCustomCohort;
15
+ exports.isNumericCohort = require_rollout.isNumericCohort;
16
+ exports.isValidCohort = require_rollout.isValidCohort;
17
+ exports.normalizeCohortValue = require_rollout.normalizeCohortValue;
18
+ exports.normalizeRolloutCohortCount = require_rollout.normalizeRolloutCohortCount;
@@ -0,0 +1,4 @@
1
+ import { DEFAULT_ROLLOUT_COHORT_COUNT, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NUMERIC_COHORT_SIZE, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount } from "./rollout.cjs";
2
+ import { AppUpdateInfo, AppVersionGetBundlesArgs, Bundle, BundleMetadata, FingerprintGetBundlesArgs, GetBundlesArgs, Platform, SnakeCaseBundle, UpdateBundleParams, UpdateInfo, UpdateStatus, UpdateStrategy } from "./types.cjs";
3
+ import { NIL_UUID } from "./uuid.cjs";
4
+ export { AppUpdateInfo, AppVersionGetBundlesArgs, Bundle, BundleMetadata, DEFAULT_ROLLOUT_COHORT_COUNT, FingerprintGetBundlesArgs, GetBundlesArgs, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NIL_UUID, NUMERIC_COHORT_SIZE, Platform, SnakeCaseBundle, UpdateBundleParams, UpdateInfo, UpdateStatus, UpdateStrategy, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount };
@@ -0,0 +1,4 @@
1
+ import { DEFAULT_ROLLOUT_COHORT_COUNT, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NUMERIC_COHORT_SIZE, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount } from "./rollout.mjs";
2
+ import { AppUpdateInfo, AppVersionGetBundlesArgs, Bundle, BundleMetadata, FingerprintGetBundlesArgs, GetBundlesArgs, Platform, SnakeCaseBundle, UpdateBundleParams, UpdateInfo, UpdateStatus, UpdateStrategy } from "./types.mjs";
3
+ import { NIL_UUID } from "./uuid.mjs";
4
+ export { AppUpdateInfo, AppVersionGetBundlesArgs, Bundle, BundleMetadata, DEFAULT_ROLLOUT_COHORT_COUNT, FingerprintGetBundlesArgs, GetBundlesArgs, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NIL_UUID, NUMERIC_COHORT_SIZE, Platform, SnakeCaseBundle, UpdateBundleParams, UpdateInfo, UpdateStatus, UpdateStrategy, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount };
@@ -0,0 +1,3 @@
1
+ import { DEFAULT_ROLLOUT_COHORT_COUNT, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NUMERIC_COHORT_SIZE, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount } from "./rollout.mjs";
2
+ import { NIL_UUID } from "./uuid.mjs";
3
+ export { DEFAULT_ROLLOUT_COHORT_COUNT, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NIL_UUID, NUMERIC_COHORT_SIZE, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount };
@@ -0,0 +1,132 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/rollout.ts
3
+ const NUMERIC_COHORT_SIZE = 1e3;
4
+ const DEFAULT_ROLLOUT_COHORT_COUNT = NUMERIC_COHORT_SIZE;
5
+ const MAX_COHORT_LENGTH = 64;
6
+ const INVALID_COHORT_ERROR_MESSAGE = `Invalid cohort. Use 1-1000 or a lowercase slug without spaces, up to 64 characters.`;
7
+ const CUSTOM_COHORT_PATTERN = /^[a-z0-9-]+$/;
8
+ function parseNumericCohortValue(cohort) {
9
+ if (!/^\d+$/.test(cohort)) return null;
10
+ const parsed = Number.parseInt(cohort, 10);
11
+ if (Number.isNaN(parsed) || parsed < 1 || parsed > 1e3) return null;
12
+ return parsed;
13
+ }
14
+ function positiveMod(value, modulus) {
15
+ return (value % modulus + modulus) % modulus;
16
+ }
17
+ function hashString(value) {
18
+ let hash = 0;
19
+ for (let i = 0; i < value.length; i++) {
20
+ const char = value.charCodeAt(i);
21
+ hash = (hash << 5) - hash + char;
22
+ hash |= 0;
23
+ }
24
+ return hash;
25
+ }
26
+ function gcd(a, b) {
27
+ let x = Math.abs(a);
28
+ let y = Math.abs(b);
29
+ while (y !== 0) {
30
+ const next = x % y;
31
+ x = y;
32
+ y = next;
33
+ }
34
+ return x;
35
+ }
36
+ function modularInverse(value, modulus) {
37
+ let t = 0;
38
+ let newT = 1;
39
+ let r = modulus;
40
+ let newR = positiveMod(value, modulus);
41
+ while (newR !== 0) {
42
+ const quotient = Math.floor(r / newR);
43
+ [t, newT] = [newT, t - quotient * newT];
44
+ [r, newR] = [newR, r - quotient * newR];
45
+ }
46
+ if (r > 1) throw new Error(`No modular inverse for ${value} mod ${modulus}`);
47
+ return positiveMod(t, modulus);
48
+ }
49
+ function getRolloutShuffleParameters(bundleId) {
50
+ let multiplier = positiveMod(hashString(`${bundleId}:multiplier`), 997);
51
+ if (multiplier === 0) multiplier = 1;
52
+ while (gcd(multiplier, NUMERIC_COHORT_SIZE) !== 1) {
53
+ multiplier = positiveMod(multiplier + 1, NUMERIC_COHORT_SIZE);
54
+ if (multiplier === 0) multiplier = 1;
55
+ }
56
+ const offset = positiveMod(hashString(`${bundleId}:offset`), NUMERIC_COHORT_SIZE);
57
+ return {
58
+ multiplier,
59
+ offset,
60
+ inverseMultiplier: modularInverse(multiplier, NUMERIC_COHORT_SIZE)
61
+ };
62
+ }
63
+ function normalizeRolloutCohortCount(rolloutCohortCount) {
64
+ if (rolloutCohortCount === null || rolloutCohortCount === void 0) return DEFAULT_ROLLOUT_COHORT_COUNT;
65
+ if (rolloutCohortCount <= 0) return 0;
66
+ if (rolloutCohortCount >= 1e3) return NUMERIC_COHORT_SIZE;
67
+ return Math.floor(rolloutCohortCount);
68
+ }
69
+ function normalizeCohortValue(cohort) {
70
+ const normalized = cohort.trim().toLowerCase();
71
+ const numericCohort = parseNumericCohortValue(normalized);
72
+ if (numericCohort !== null) return String(numericCohort);
73
+ return normalized;
74
+ }
75
+ function getNumericCohortValue(cohort) {
76
+ return parseNumericCohortValue(normalizeCohortValue(cohort));
77
+ }
78
+ function isNumericCohort(cohort) {
79
+ return getNumericCohortValue(cohort) !== null;
80
+ }
81
+ function isCustomCohort(cohort) {
82
+ const normalized = normalizeCohortValue(cohort);
83
+ return normalized.length > 0 && normalized.length <= 64 && !/^\d+$/.test(normalized) && CUSTOM_COHORT_PATTERN.test(normalized);
84
+ }
85
+ function isValidCohort(cohort) {
86
+ const normalized = normalizeCohortValue(cohort);
87
+ return isNumericCohort(normalized) || isCustomCohort(normalized);
88
+ }
89
+ function getDefaultNumericCohort(identifier) {
90
+ const cohortValue = positiveMod(hashString(identifier), NUMERIC_COHORT_SIZE) + 1;
91
+ return String(cohortValue);
92
+ }
93
+ function getNumericCohortRolloutPosition(bundleId, cohortValue) {
94
+ if (cohortValue < 1 || cohortValue > 1e3) throw new Error(`Invalid numeric cohort: ${cohortValue}`);
95
+ const { offset, inverseMultiplier } = getRolloutShuffleParameters(bundleId);
96
+ return positiveMod(inverseMultiplier * (cohortValue - 1 - offset), NUMERIC_COHORT_SIZE);
97
+ }
98
+ function getRolledOutNumericCohorts(bundleId, rolloutCohortCount) {
99
+ const normalizedRolloutCount = normalizeRolloutCohortCount(rolloutCohortCount);
100
+ if (normalizedRolloutCount <= 0) return [];
101
+ return Array.from({ length: NUMERIC_COHORT_SIZE }, (_, index) => index + 1).filter((cohortValue) => {
102
+ if (normalizedRolloutCount >= 1e3) return true;
103
+ return getNumericCohortRolloutPosition(bundleId, cohortValue) < normalizedRolloutCount;
104
+ });
105
+ }
106
+ function isCohortEligibleForUpdate(bundleId, cohort, rolloutCohortCount, targetCohorts) {
107
+ const normalizedCohort = cohort === null || cohort === void 0 ? void 0 : normalizeCohortValue(cohort);
108
+ const normalizedTargetCohorts = targetCohorts?.map((targetCohort) => normalizeCohortValue(targetCohort)) ?? [];
109
+ if (normalizedTargetCohorts.length > 0) return normalizedCohort !== void 0 && normalizedTargetCohorts.includes(normalizedCohort);
110
+ const normalizedRolloutCount = normalizeRolloutCohortCount(rolloutCohortCount);
111
+ if (normalizedRolloutCount <= 0) return false;
112
+ if (normalizedCohort === void 0) return normalizedRolloutCount >= NUMERIC_COHORT_SIZE;
113
+ const numericCohort = getNumericCohortValue(normalizedCohort);
114
+ if (numericCohort === null) return false;
115
+ if (normalizedRolloutCount >= 1e3) return true;
116
+ return getNumericCohortRolloutPosition(bundleId, numericCohort) < normalizedRolloutCount;
117
+ }
118
+ //#endregion
119
+ exports.DEFAULT_ROLLOUT_COHORT_COUNT = DEFAULT_ROLLOUT_COHORT_COUNT;
120
+ exports.INVALID_COHORT_ERROR_MESSAGE = INVALID_COHORT_ERROR_MESSAGE;
121
+ exports.MAX_COHORT_LENGTH = MAX_COHORT_LENGTH;
122
+ exports.NUMERIC_COHORT_SIZE = NUMERIC_COHORT_SIZE;
123
+ exports.getDefaultNumericCohort = getDefaultNumericCohort;
124
+ exports.getNumericCohortRolloutPosition = getNumericCohortRolloutPosition;
125
+ exports.getNumericCohortValue = getNumericCohortValue;
126
+ exports.getRolledOutNumericCohorts = getRolledOutNumericCohorts;
127
+ exports.isCohortEligibleForUpdate = isCohortEligibleForUpdate;
128
+ exports.isCustomCohort = isCustomCohort;
129
+ exports.isNumericCohort = isNumericCohort;
130
+ exports.isValidCohort = isValidCohort;
131
+ exports.normalizeCohortValue = normalizeCohortValue;
132
+ exports.normalizeRolloutCohortCount = normalizeRolloutCohortCount;
@@ -0,0 +1,17 @@
1
+ //#region src/rollout.d.ts
2
+ declare const NUMERIC_COHORT_SIZE = 1000;
3
+ declare const DEFAULT_ROLLOUT_COHORT_COUNT = 1000;
4
+ declare const MAX_COHORT_LENGTH = 64;
5
+ declare const INVALID_COHORT_ERROR_MESSAGE = "Invalid cohort. Use 1-1000 or a lowercase slug without spaces, up to 64 characters.";
6
+ declare function normalizeRolloutCohortCount(rolloutCohortCount: number | null | undefined): number;
7
+ declare function normalizeCohortValue(cohort: string): string;
8
+ declare function getNumericCohortValue(cohort: string): number | null;
9
+ declare function isNumericCohort(cohort: string): boolean;
10
+ declare function isCustomCohort(cohort: string): boolean;
11
+ declare function isValidCohort(cohort: string): boolean;
12
+ declare function getDefaultNumericCohort(identifier: string): string;
13
+ declare function getNumericCohortRolloutPosition(bundleId: string, cohortValue: number): number;
14
+ declare function getRolledOutNumericCohorts(bundleId: string, rolloutCohortCount: number | null | undefined): number[];
15
+ declare function isCohortEligibleForUpdate(bundleId: string, cohort: string | null | undefined, rolloutCohortCount: number | null | undefined, targetCohorts: readonly string[] | null | undefined): boolean;
16
+ //#endregion
17
+ export { DEFAULT_ROLLOUT_COHORT_COUNT, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NUMERIC_COHORT_SIZE, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount };
@@ -0,0 +1,17 @@
1
+ //#region src/rollout.d.ts
2
+ declare const NUMERIC_COHORT_SIZE = 1000;
3
+ declare const DEFAULT_ROLLOUT_COHORT_COUNT = 1000;
4
+ declare const MAX_COHORT_LENGTH = 64;
5
+ declare const INVALID_COHORT_ERROR_MESSAGE = "Invalid cohort. Use 1-1000 or a lowercase slug without spaces, up to 64 characters.";
6
+ declare function normalizeRolloutCohortCount(rolloutCohortCount: number | null | undefined): number;
7
+ declare function normalizeCohortValue(cohort: string): string;
8
+ declare function getNumericCohortValue(cohort: string): number | null;
9
+ declare function isNumericCohort(cohort: string): boolean;
10
+ declare function isCustomCohort(cohort: string): boolean;
11
+ declare function isValidCohort(cohort: string): boolean;
12
+ declare function getDefaultNumericCohort(identifier: string): string;
13
+ declare function getNumericCohortRolloutPosition(bundleId: string, cohortValue: number): number;
14
+ declare function getRolledOutNumericCohorts(bundleId: string, rolloutCohortCount: number | null | undefined): number[];
15
+ declare function isCohortEligibleForUpdate(bundleId: string, cohort: string | null | undefined, rolloutCohortCount: number | null | undefined, targetCohorts: readonly string[] | null | undefined): boolean;
16
+ //#endregion
17
+ export { DEFAULT_ROLLOUT_COHORT_COUNT, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NUMERIC_COHORT_SIZE, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount };
@@ -0,0 +1,118 @@
1
+ //#region src/rollout.ts
2
+ const NUMERIC_COHORT_SIZE = 1e3;
3
+ const DEFAULT_ROLLOUT_COHORT_COUNT = NUMERIC_COHORT_SIZE;
4
+ const MAX_COHORT_LENGTH = 64;
5
+ const INVALID_COHORT_ERROR_MESSAGE = `Invalid cohort. Use 1-1000 or a lowercase slug without spaces, up to 64 characters.`;
6
+ const CUSTOM_COHORT_PATTERN = /^[a-z0-9-]+$/;
7
+ function parseNumericCohortValue(cohort) {
8
+ if (!/^\d+$/.test(cohort)) return null;
9
+ const parsed = Number.parseInt(cohort, 10);
10
+ if (Number.isNaN(parsed) || parsed < 1 || parsed > 1e3) return null;
11
+ return parsed;
12
+ }
13
+ function positiveMod(value, modulus) {
14
+ return (value % modulus + modulus) % modulus;
15
+ }
16
+ function hashString(value) {
17
+ let hash = 0;
18
+ for (let i = 0; i < value.length; i++) {
19
+ const char = value.charCodeAt(i);
20
+ hash = (hash << 5) - hash + char;
21
+ hash |= 0;
22
+ }
23
+ return hash;
24
+ }
25
+ function gcd(a, b) {
26
+ let x = Math.abs(a);
27
+ let y = Math.abs(b);
28
+ while (y !== 0) {
29
+ const next = x % y;
30
+ x = y;
31
+ y = next;
32
+ }
33
+ return x;
34
+ }
35
+ function modularInverse(value, modulus) {
36
+ let t = 0;
37
+ let newT = 1;
38
+ let r = modulus;
39
+ let newR = positiveMod(value, modulus);
40
+ while (newR !== 0) {
41
+ const quotient = Math.floor(r / newR);
42
+ [t, newT] = [newT, t - quotient * newT];
43
+ [r, newR] = [newR, r - quotient * newR];
44
+ }
45
+ if (r > 1) throw new Error(`No modular inverse for ${value} mod ${modulus}`);
46
+ return positiveMod(t, modulus);
47
+ }
48
+ function getRolloutShuffleParameters(bundleId) {
49
+ let multiplier = positiveMod(hashString(`${bundleId}:multiplier`), 997);
50
+ if (multiplier === 0) multiplier = 1;
51
+ while (gcd(multiplier, NUMERIC_COHORT_SIZE) !== 1) {
52
+ multiplier = positiveMod(multiplier + 1, NUMERIC_COHORT_SIZE);
53
+ if (multiplier === 0) multiplier = 1;
54
+ }
55
+ const offset = positiveMod(hashString(`${bundleId}:offset`), NUMERIC_COHORT_SIZE);
56
+ return {
57
+ multiplier,
58
+ offset,
59
+ inverseMultiplier: modularInverse(multiplier, NUMERIC_COHORT_SIZE)
60
+ };
61
+ }
62
+ function normalizeRolloutCohortCount(rolloutCohortCount) {
63
+ if (rolloutCohortCount === null || rolloutCohortCount === void 0) return DEFAULT_ROLLOUT_COHORT_COUNT;
64
+ if (rolloutCohortCount <= 0) return 0;
65
+ if (rolloutCohortCount >= 1e3) return NUMERIC_COHORT_SIZE;
66
+ return Math.floor(rolloutCohortCount);
67
+ }
68
+ function normalizeCohortValue(cohort) {
69
+ const normalized = cohort.trim().toLowerCase();
70
+ const numericCohort = parseNumericCohortValue(normalized);
71
+ if (numericCohort !== null) return String(numericCohort);
72
+ return normalized;
73
+ }
74
+ function getNumericCohortValue(cohort) {
75
+ return parseNumericCohortValue(normalizeCohortValue(cohort));
76
+ }
77
+ function isNumericCohort(cohort) {
78
+ return getNumericCohortValue(cohort) !== null;
79
+ }
80
+ function isCustomCohort(cohort) {
81
+ const normalized = normalizeCohortValue(cohort);
82
+ return normalized.length > 0 && normalized.length <= 64 && !/^\d+$/.test(normalized) && CUSTOM_COHORT_PATTERN.test(normalized);
83
+ }
84
+ function isValidCohort(cohort) {
85
+ const normalized = normalizeCohortValue(cohort);
86
+ return isNumericCohort(normalized) || isCustomCohort(normalized);
87
+ }
88
+ function getDefaultNumericCohort(identifier) {
89
+ const cohortValue = positiveMod(hashString(identifier), NUMERIC_COHORT_SIZE) + 1;
90
+ return String(cohortValue);
91
+ }
92
+ function getNumericCohortRolloutPosition(bundleId, cohortValue) {
93
+ if (cohortValue < 1 || cohortValue > 1e3) throw new Error(`Invalid numeric cohort: ${cohortValue}`);
94
+ const { offset, inverseMultiplier } = getRolloutShuffleParameters(bundleId);
95
+ return positiveMod(inverseMultiplier * (cohortValue - 1 - offset), NUMERIC_COHORT_SIZE);
96
+ }
97
+ function getRolledOutNumericCohorts(bundleId, rolloutCohortCount) {
98
+ const normalizedRolloutCount = normalizeRolloutCohortCount(rolloutCohortCount);
99
+ if (normalizedRolloutCount <= 0) return [];
100
+ return Array.from({ length: NUMERIC_COHORT_SIZE }, (_, index) => index + 1).filter((cohortValue) => {
101
+ if (normalizedRolloutCount >= 1e3) return true;
102
+ return getNumericCohortRolloutPosition(bundleId, cohortValue) < normalizedRolloutCount;
103
+ });
104
+ }
105
+ function isCohortEligibleForUpdate(bundleId, cohort, rolloutCohortCount, targetCohorts) {
106
+ const normalizedCohort = cohort === null || cohort === void 0 ? void 0 : normalizeCohortValue(cohort);
107
+ const normalizedTargetCohorts = targetCohorts?.map((targetCohort) => normalizeCohortValue(targetCohort)) ?? [];
108
+ if (normalizedTargetCohorts.length > 0) return normalizedCohort !== void 0 && normalizedTargetCohorts.includes(normalizedCohort);
109
+ const normalizedRolloutCount = normalizeRolloutCohortCount(rolloutCohortCount);
110
+ if (normalizedRolloutCount <= 0) return false;
111
+ if (normalizedCohort === void 0) return normalizedRolloutCount >= NUMERIC_COHORT_SIZE;
112
+ const numericCohort = getNumericCohortValue(normalizedCohort);
113
+ if (numericCohort === null) return false;
114
+ if (normalizedRolloutCount >= 1e3) return true;
115
+ return getNumericCohortRolloutPosition(bundleId, numericCohort) < normalizedRolloutCount;
116
+ }
117
+ //#endregion
118
+ export { DEFAULT_ROLLOUT_COHORT_COUNT, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, NUMERIC_COHORT_SIZE, getDefaultNumericCohort, getNumericCohortRolloutPosition, getNumericCohortValue, getRolledOutNumericCohorts, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isValidCohort, normalizeCohortValue, normalizeRolloutCohortCount };
package/dist/types.cjs ADDED
File without changes
@@ -1,27 +1,3 @@
1
- //#region src/hotUpdateDirUtil.d.ts
2
- declare const HotUpdateDirUtil: {
3
- readonly dirName: ".hot-updater";
4
- readonly outputDirName: "output";
5
- readonly logDirName: "log";
6
- readonly outputGitignorePath: ".hot-updater/output";
7
- readonly logGitignorePath: ".hot-updater/log";
8
- readonly getDirPath: ({
9
- cwd
10
- }: {
11
- cwd: string;
12
- }) => string;
13
- readonly getDefaultOutputPath: ({
14
- cwd
15
- }: {
16
- cwd: string;
17
- }) => string;
18
- readonly getLogDirPath: ({
19
- cwd
20
- }: {
21
- cwd: string;
22
- }) => string;
23
- };
24
- //#endregion
25
1
  //#region src/types.d.ts
26
2
  type Platform = "ios" | "android";
27
3
  type BundleMetadata = {
@@ -88,9 +64,28 @@ interface Bundle {
88
64
  * The metadata of the bundle.
89
65
  */
90
66
  metadata?: BundleMetadata;
67
+ /**
68
+ * Rollout cohort count (0-1000). Controls gradual rollout to numeric cohorts.
69
+ * - 0: No cohorts receive this update
70
+ * - 250: 25.0% of numeric cohorts receive this update
71
+ * - 1000 or null: All numeric cohorts receive this update (full rollout)
72
+ *
73
+ * @default 1000
74
+ */
75
+ rolloutCohortCount?: number | null;
76
+ /**
77
+ * Target specific cohorts for this update.
78
+ * If provided, only these cohorts will receive the update.
79
+ * If empty/null, rolloutCohortCount-based rollout is used.
80
+ *
81
+ * NOTE: This field is stored in database but should NOT be returned to
82
+ * update-check clients for security reasons. Server uses it for rollout
83
+ * decisions only.
84
+ */
85
+ targetCohorts?: string[] | null;
91
86
  }
92
- type SnakeCase<S extends string> = S extends `${infer T}${infer U}` ? `${T extends Capitalize<T> ? "_" : ""}${Lowercase<T>}${SnakeCase<U>}` : S;
93
- type SnakeKeyObject<T$1> = T$1 extends Record<string, any> ? { [K in keyof T$1 as SnakeCase<Extract<K, string>>]: T$1[K] extends object ? SnakeKeyObject<T$1[K]> : T$1[K] } : T$1;
87
+ type SnakeCase<S extends string> = S extends `${infer T}${infer U}` ? T extends "_" ? `_${SnakeCase<U>}` : T extends "-" ? `-${SnakeCase<U>}` : T extends Lowercase<T> ? `${T}${SnakeCase<U>}` : `_${Lowercase<T>}${SnakeCase<U>}` : S;
88
+ type SnakeKeyObject<T> = T extends readonly (infer U)[] ? SnakeKeyObject<U>[] : T extends Record<string, any> ? { [K in keyof T as SnakeCase<Extract<K, string>>]: SnakeKeyObject<T[K]> } : T;
94
89
  type SnakeCaseBundle = SnakeKeyObject<Bundle>;
95
90
  type UpdateStatus = "ROLLBACK" | "UPDATE";
96
91
  /**
@@ -104,6 +99,15 @@ interface UpdateInfo {
104
99
  status: UpdateStatus;
105
100
  storageUri: string | null;
106
101
  fileHash: string | null;
102
+ /**
103
+ * Rollout cohort count (0-1000). Controls gradual rollout to numeric cohorts.
104
+ */
105
+ rolloutCohortCount?: number | null;
106
+ /**
107
+ * Target specific cohorts for this update.
108
+ * Used internally for rollout decisions.
109
+ */
110
+ targetCohorts?: string[] | null;
107
111
  }
108
112
  /**
109
113
  * The update info for the app layer.
@@ -146,6 +150,10 @@ type FingerprintGetBundlesArgs = {
146
150
  * - app-name: Channel for specific app instances (e.g., my-app, app-test)
147
151
  */
148
152
  channel?: string;
153
+ /**
154
+ * Cohort identifier used for server-side rollout decisions.
155
+ */
156
+ cohort?: string;
149
157
  /**
150
158
  * The fingerprint hash of the bundle.
151
159
  */
@@ -177,6 +185,10 @@ type AppVersionGetBundlesArgs = {
177
185
  * - app-name: Channel for specific app instances (e.g., my-app, app-test)
178
186
  */
179
187
  channel?: string;
188
+ /**
189
+ * Cohort identifier used for server-side rollout decisions.
190
+ */
191
+ cohort?: string;
180
192
  /**
181
193
  * The current app version.
182
194
  */
@@ -192,7 +204,4 @@ type UpdateBundleParams = {
192
204
  fingerprintHash: string | null;
193
205
  };
194
206
  //#endregion
195
- //#region src/uuid.d.ts
196
- declare const NIL_UUID = "00000000-0000-0000-0000-000000000000";
197
- //#endregion
198
- export { AppUpdateInfo, AppVersionGetBundlesArgs, Bundle, BundleMetadata, FingerprintGetBundlesArgs, GetBundlesArgs, HotUpdateDirUtil, NIL_UUID, Platform, SnakeCaseBundle, UpdateBundleParams, UpdateInfo, UpdateStatus, UpdateStrategy };
207
+ export { AppUpdateInfo, AppVersionGetBundlesArgs, Bundle, BundleMetadata, FingerprintGetBundlesArgs, GetBundlesArgs, Platform, SnakeCaseBundle, UpdateBundleParams, UpdateInfo, UpdateStatus, UpdateStrategy };
@@ -0,0 +1,207 @@
1
+ //#region src/types.d.ts
2
+ type Platform = "ios" | "android";
3
+ type BundleMetadata = {
4
+ app_version?: string;
5
+ };
6
+ interface Bundle {
7
+ /**
8
+ * The unique identifier for the bundle. uuidv7
9
+ */
10
+ id: string;
11
+ /**
12
+ * The platform the bundle is for.
13
+ */
14
+ platform: Platform;
15
+ /**
16
+ * Whether the bundle should force an update.
17
+ */
18
+ shouldForceUpdate: boolean;
19
+ /**
20
+ * Whether the bundle is enabled.
21
+ */
22
+ enabled: boolean;
23
+ /**
24
+ * The hash of the bundle.
25
+ */
26
+ fileHash: string;
27
+ /**
28
+ * The storage key of the bundle.
29
+ * @example "s3://my-bucket/my-app/00000000-0000-0000-0000-000000000000/bundle.zip"
30
+ * @example "r2://my-bucket/my-app/00000000-0000-0000-0000-000000000000/bundle.zip"
31
+ * @example "firebase-storage://my-bucket/my-app/00000000-0000-0000-0000-000000000000/bundle.zip"
32
+ * @example "storage://my-app/00000000-0000-0000-0000-000000000000/bundle.zip"
33
+ */
34
+ storageUri: string;
35
+ /**
36
+ * The git commit hash of the bundle.
37
+ */
38
+ gitCommitHash: string | null;
39
+ /**
40
+ * The message of the bundle.
41
+ */
42
+ message: string | null;
43
+ /**
44
+ * The name of the channel where the bundle is deployed.
45
+ *
46
+ * Examples:
47
+ * - production: Production channel for end users
48
+ * - development: Development channel for testing
49
+ * - staging: Staging channel for quality assurance before production
50
+ * - app-name: Channel for specific app instances (e.g., my-app, app-test)
51
+ *
52
+ * Different channel values can be used based on each app's requirements.
53
+ */
54
+ channel: string;
55
+ /**
56
+ * The target app version of the bundle.
57
+ */
58
+ targetAppVersion: string | null;
59
+ /**
60
+ * The fingerprint hash of the bundle.
61
+ */
62
+ fingerprintHash: string | null;
63
+ /**
64
+ * The metadata of the bundle.
65
+ */
66
+ metadata?: BundleMetadata;
67
+ /**
68
+ * Rollout cohort count (0-1000). Controls gradual rollout to numeric cohorts.
69
+ * - 0: No cohorts receive this update
70
+ * - 250: 25.0% of numeric cohorts receive this update
71
+ * - 1000 or null: All numeric cohorts receive this update (full rollout)
72
+ *
73
+ * @default 1000
74
+ */
75
+ rolloutCohortCount?: number | null;
76
+ /**
77
+ * Target specific cohorts for this update.
78
+ * If provided, only these cohorts will receive the update.
79
+ * If empty/null, rolloutCohortCount-based rollout is used.
80
+ *
81
+ * NOTE: This field is stored in database but should NOT be returned to
82
+ * update-check clients for security reasons. Server uses it for rollout
83
+ * decisions only.
84
+ */
85
+ targetCohorts?: string[] | null;
86
+ }
87
+ type SnakeCase<S extends string> = S extends `${infer T}${infer U}` ? T extends "_" ? `_${SnakeCase<U>}` : T extends "-" ? `-${SnakeCase<U>}` : T extends Lowercase<T> ? `${T}${SnakeCase<U>}` : `_${Lowercase<T>}${SnakeCase<U>}` : S;
88
+ type SnakeKeyObject<T> = T extends readonly (infer U)[] ? SnakeKeyObject<U>[] : T extends Record<string, any> ? { [K in keyof T as SnakeCase<Extract<K, string>>]: SnakeKeyObject<T[K]> } : T;
89
+ type SnakeCaseBundle = SnakeKeyObject<Bundle>;
90
+ type UpdateStatus = "ROLLBACK" | "UPDATE";
91
+ /**
92
+ * The update info for the database layer.
93
+ * This is the update info that is used by the database.
94
+ */
95
+ interface UpdateInfo {
96
+ id: string;
97
+ shouldForceUpdate: boolean;
98
+ message: string | null;
99
+ status: UpdateStatus;
100
+ storageUri: string | null;
101
+ fileHash: string | null;
102
+ /**
103
+ * Rollout cohort count (0-1000). Controls gradual rollout to numeric cohorts.
104
+ */
105
+ rolloutCohortCount?: number | null;
106
+ /**
107
+ * Target specific cohorts for this update.
108
+ * Used internally for rollout decisions.
109
+ */
110
+ targetCohorts?: string[] | null;
111
+ }
112
+ /**
113
+ * The update info for the app layer.
114
+ * This is the update info that is used by the app.
115
+ */
116
+ interface AppUpdateInfo extends Omit<UpdateInfo, "storageUri"> {
117
+ fileUrl: string | null;
118
+ /**
119
+ * SHA256 hash of the bundle file, optionally with embedded signature.
120
+ * Format when signed: "sig:<base64_signature>"
121
+ * Format when unsigned: "<hex_hash>" (64-character lowercase hex)
122
+ * The client parses this to extract signature for native verification.
123
+ */
124
+ fileHash: string | null;
125
+ }
126
+ type UpdateStrategy = "fingerprint" | "appVersion";
127
+ type FingerprintGetBundlesArgs = {
128
+ _updateStrategy: "fingerprint";
129
+ platform: Platform;
130
+ /**
131
+ * The current bundle id of the app.
132
+ */
133
+ bundleId: string;
134
+ /**
135
+ * Minimum bundle id that should be used.
136
+ * This value is generated at build time via getMinBundleId().
137
+ *
138
+ * @default "00000000-0000-0000-0000-000000000000"
139
+ */
140
+ minBundleId?: string;
141
+ /**
142
+ * The name of the channel where the bundle is deployed.
143
+ *
144
+ * @default "production"
145
+ *
146
+ * Examples:
147
+ * - production: Production channel for end users
148
+ * - development: Development channel for testing
149
+ * - staging: Staging channel for quality assurance before production
150
+ * - app-name: Channel for specific app instances (e.g., my-app, app-test)
151
+ */
152
+ channel?: string;
153
+ /**
154
+ * Cohort identifier used for server-side rollout decisions.
155
+ */
156
+ cohort?: string;
157
+ /**
158
+ * The fingerprint hash of the bundle.
159
+ */
160
+ fingerprintHash: string;
161
+ };
162
+ type AppVersionGetBundlesArgs = {
163
+ _updateStrategy: "appVersion";
164
+ platform: Platform;
165
+ /**
166
+ * The current bundle id of the app.
167
+ */
168
+ bundleId: string;
169
+ /**
170
+ * Minimum bundle id that should be used.
171
+ * This value is generated at build time via getMinBundleId().
172
+ *
173
+ * @default "00000000-0000-0000-0000-000000000000"
174
+ */
175
+ minBundleId?: string;
176
+ /**
177
+ * The name of the channel where the bundle is deployed.
178
+ *
179
+ * @default "production"
180
+ *
181
+ * Examples:
182
+ * - production: Production channel for end users
183
+ * - development: Development channel for testing
184
+ * - staging: Staging channel for quality assurance before production
185
+ * - app-name: Channel for specific app instances (e.g., my-app, app-test)
186
+ */
187
+ channel?: string;
188
+ /**
189
+ * Cohort identifier used for server-side rollout decisions.
190
+ */
191
+ cohort?: string;
192
+ /**
193
+ * The current app version.
194
+ */
195
+ appVersion: string;
196
+ };
197
+ type GetBundlesArgs = FingerprintGetBundlesArgs | AppVersionGetBundlesArgs;
198
+ type UpdateBundleParams = {
199
+ platform: Platform;
200
+ bundleId: string;
201
+ minBundleId: string;
202
+ channel: string;
203
+ appVersion: string;
204
+ fingerprintHash: string | null;
205
+ };
206
+ //#endregion
207
+ export { AppUpdateInfo, AppVersionGetBundlesArgs, Bundle, BundleMetadata, FingerprintGetBundlesArgs, GetBundlesArgs, Platform, SnakeCaseBundle, UpdateBundleParams, UpdateInfo, UpdateStatus, UpdateStrategy };
package/dist/types.mjs ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/uuid.cjs ADDED
@@ -0,0 +1,5 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/uuid.ts
3
+ const NIL_UUID = "00000000-0000-0000-0000-000000000000";
4
+ //#endregion
5
+ exports.NIL_UUID = NIL_UUID;
@@ -0,0 +1,4 @@
1
+ //#region src/uuid.d.ts
2
+ declare const NIL_UUID = "00000000-0000-0000-0000-000000000000";
3
+ //#endregion
4
+ export { NIL_UUID };
@@ -0,0 +1,4 @@
1
+ //#region src/uuid.d.ts
2
+ declare const NIL_UUID = "00000000-0000-0000-0000-000000000000";
3
+ //#endregion
4
+ export { NIL_UUID };
package/dist/uuid.mjs ADDED
@@ -0,0 +1,4 @@
1
+ //#region src/uuid.ts
2
+ const NIL_UUID = "00000000-0000-0000-0000-000000000000";
3
+ //#endregion
4
+ export { NIL_UUID };
package/package.json CHANGED
@@ -1,17 +1,38 @@
1
1
  {
2
2
  "name": "@hot-updater/core",
3
- "version": "0.27.1",
3
+ "version": "0.29.0",
4
4
  "type": "module",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "sideEffects": false,
7
7
  "main": "./dist/index.cjs",
8
- "module": "./dist/index.js",
8
+ "module": "./dist/index.mjs",
9
+ "react-native": "./dist/react-native.mjs",
9
10
  "types": "./dist/index.d.cts",
10
11
  "exports": {
11
12
  ".": {
12
- "import": "./dist/index.js",
13
+ "import": "./dist/index.mjs",
13
14
  "require": "./dist/index.cjs"
14
15
  },
16
+ "./hotUpdateDirUtil": {
17
+ "import": "./dist/hotUpdateDirUtil.mjs",
18
+ "require": "./dist/hotUpdateDirUtil.cjs"
19
+ },
20
+ "./react-native": {
21
+ "import": "./dist/react-native.mjs",
22
+ "require": "./dist/react-native.cjs"
23
+ },
24
+ "./rollout": {
25
+ "import": "./dist/rollout.mjs",
26
+ "require": "./dist/rollout.cjs"
27
+ },
28
+ "./types": {
29
+ "import": "./dist/types.mjs",
30
+ "require": "./dist/types.cjs"
31
+ },
32
+ "./uuid": {
33
+ "import": "./dist/uuid.mjs",
34
+ "require": "./dist/uuid.cjs"
35
+ },
15
36
  "./package.json": "./package.json"
16
37
  },
17
38
  "files": [