@hot-updater/protocol 1.0.0-rc.39 → 1.0.0-rc.41
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.cjs +32 -12
- package/dist/index.d.cts +40 -16
- package/dist/index.d.mts +40 -16
- package/dist/index.mjs +32 -12
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -170,7 +170,7 @@ const MAX_STORAGE_VALUE_LENGTH = 65536;
|
|
|
170
170
|
const pluginStorageKey = (pluginId, key) => `plugins/${encodeURIComponent(pluginId)}/${key}`;
|
|
171
171
|
const createPluginHost = (environment) => {
|
|
172
172
|
let config = null;
|
|
173
|
-
/** Hooks by plugin, so a plugin configured again is not set up twice. */
|
|
173
|
+
/** Hooks and API by plugin, so a plugin configured again is not set up twice. */
|
|
174
174
|
const setUpPlugins = /* @__PURE__ */ new WeakMap();
|
|
175
175
|
let activePlugins = [];
|
|
176
176
|
/** What plugins read and wrote, so a read follows a write before native does. */
|
|
@@ -186,7 +186,7 @@ const createPluginHost = (environment) => {
|
|
|
186
186
|
console.warn(report.message, error);
|
|
187
187
|
};
|
|
188
188
|
const fetchFromServer = async (path, init = {}) => {
|
|
189
|
-
if (config === null) throw new Error("[HotUpdater] Plugins can fetch only after init
|
|
189
|
+
if (config === null) throw new Error("[HotUpdater] Plugins can fetch only after HotUpdater.init.");
|
|
190
190
|
if (/^[a-z][a-z\d+.-]*:/i.test(path) || path.startsWith("//")) throw new TypeError("[HotUpdater] Plugin fetch takes a path relative to baseURL.");
|
|
191
191
|
const { requestHeaders, requestTimeout } = config;
|
|
192
192
|
const url = `${await resolveBaseURL(config.baseURL)}/${path.replace(/^\/+/, "")}`;
|
|
@@ -264,6 +264,28 @@ const createPluginHost = (environment) => {
|
|
|
264
264
|
storage: createStorage(pluginId),
|
|
265
265
|
now: () => environment.now()
|
|
266
266
|
});
|
|
267
|
+
const setUpPlugin = (plugin) => {
|
|
268
|
+
let result;
|
|
269
|
+
try {
|
|
270
|
+
result = plugin.setup(createContext(plugin.id));
|
|
271
|
+
} catch (error) {
|
|
272
|
+
reportHookError(`Plugin "${plugin.id}" failed in setup`, error);
|
|
273
|
+
return {
|
|
274
|
+
hooks: {},
|
|
275
|
+
api: void 0
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (result === void 0 || result === null) return {
|
|
279
|
+
hooks: {},
|
|
280
|
+
api: void 0
|
|
281
|
+
};
|
|
282
|
+
const unknownKeys = Object.keys(result).filter((key) => key !== "hooks" && key !== "api");
|
|
283
|
+
if (unknownKeys.length > 0) reportHookError(`Plugin "${plugin.id}" setup returned ${unknownKeys.map((key) => `"${key}"`).join(", ")}; setup returns { hooks, api }`, /* @__PURE__ */ new TypeError("Unexpected setup result"));
|
|
284
|
+
return {
|
|
285
|
+
hooks: result.hooks ?? {},
|
|
286
|
+
api: result.api
|
|
287
|
+
};
|
|
288
|
+
};
|
|
267
289
|
const configurePlugins = (plugins, nextConfig) => {
|
|
268
290
|
const ids = /* @__PURE__ */ new Set();
|
|
269
291
|
for (const plugin of plugins ?? []) {
|
|
@@ -272,22 +294,20 @@ const createPluginHost = (environment) => {
|
|
|
272
294
|
ids.add(plugin.id);
|
|
273
295
|
}
|
|
274
296
|
config = nextConfig;
|
|
297
|
+
const apis = [];
|
|
275
298
|
activePlugins = (plugins ?? []).map((plugin) => {
|
|
276
|
-
let
|
|
277
|
-
if (
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
} catch (error) {
|
|
281
|
-
reportHookError(`Plugin "${plugin.id}" failed in setup`, error);
|
|
282
|
-
hooks = {};
|
|
283
|
-
}
|
|
284
|
-
setUpPlugins.set(plugin, hooks);
|
|
299
|
+
let setUp = setUpPlugins.get(plugin);
|
|
300
|
+
if (setUp === void 0) {
|
|
301
|
+
setUp = setUpPlugin(plugin);
|
|
302
|
+
setUpPlugins.set(plugin, setUp);
|
|
285
303
|
}
|
|
304
|
+
if (setUp.api !== void 0) apis.push([plugin.id, setUp.api]);
|
|
286
305
|
return {
|
|
287
306
|
plugin,
|
|
288
|
-
hooks
|
|
307
|
+
hooks: setUp.hooks
|
|
289
308
|
};
|
|
290
309
|
});
|
|
310
|
+
return Object.freeze(Object.fromEntries(apis));
|
|
291
311
|
};
|
|
292
312
|
const hasPluginHook = (name) => activePlugins.some(({ hooks }) => typeof hooks[name] === "function");
|
|
293
313
|
const buildPluginEvent = (name, createPayload) => {
|
package/dist/index.d.cts
CHANGED
|
@@ -247,10 +247,10 @@ interface UpdateHttpResponse {
|
|
|
247
247
|
readonly bodyTruncated: boolean;
|
|
248
248
|
}
|
|
249
249
|
/**
|
|
250
|
-
* Lifecycle hooks a plugin returns from `setup`. Hooks observe:
|
|
251
|
-
* never waits for one and ignores what it returns, and a hook that
|
|
252
|
-
* rejects is reported through `onError`, or a console warning
|
|
253
|
-
* so a plugin can never delay or change an update.
|
|
250
|
+
* Lifecycle hooks a plugin returns from `setup` as `hooks`. Hooks observe:
|
|
251
|
+
* the SDK never waits for one and ignores what it returns, and a hook that
|
|
252
|
+
* throws or rejects is reported through `onError`, or a console warning
|
|
253
|
+
* without one, so a plugin can never delay or change an update.
|
|
254
254
|
*/
|
|
255
255
|
interface HotUpdaterClientHooks {
|
|
256
256
|
/** Called once per JavaScript runtime, after native reports the launch. */
|
|
@@ -303,22 +303,46 @@ interface HotUpdaterClientContext {
|
|
|
303
303
|
/** The current time in milliseconds since the epoch. */
|
|
304
304
|
now(): number;
|
|
305
305
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
306
|
+
/**
|
|
307
|
+
* What a plugin's `setup` returns: the hooks it observes the SDK with, and
|
|
308
|
+
* the API `HotUpdater.init` puts on the instance it returns, under the
|
|
309
|
+
* plugin's id, as `hotUpdater.<id>`.
|
|
310
|
+
*/
|
|
311
|
+
interface HotUpdaterClientSetup<TApi = unknown> {
|
|
312
|
+
readonly hooks?: HotUpdaterClientHooks;
|
|
313
|
+
readonly api?: TApi;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* A client plugin with the id `TId` and the API `TApi` it adds to the
|
|
317
|
+
* instance `HotUpdater.init` returns.
|
|
318
|
+
*/
|
|
319
|
+
interface HotUpdaterClientPlugin<TId extends string = string, TApi = unknown> {
|
|
320
|
+
/**
|
|
321
|
+
* Unique among an app's plugins; two plugins with one id throw at init.
|
|
322
|
+
* It names the plugin's API on the instance, so it cannot be the name of
|
|
323
|
+
* one of the instance's own methods.
|
|
324
|
+
*/
|
|
325
|
+
readonly id: TId;
|
|
309
326
|
/**
|
|
310
|
-
* Called once
|
|
311
|
-
*
|
|
327
|
+
* Called once for each plugin host it is configured in. The React Native
|
|
328
|
+
* SDK creates a host for each instance `HotUpdater.init` returns.
|
|
312
329
|
*/
|
|
313
|
-
setup(context: HotUpdaterClientContext):
|
|
330
|
+
setup(context: HotUpdaterClientContext): HotUpdaterClientSetup<TApi> | void;
|
|
314
331
|
}
|
|
332
|
+
type SetupResult<P> = P extends {
|
|
333
|
+
setup(context: HotUpdaterClientContext): infer R;
|
|
334
|
+
} ? Exclude<R, void | undefined> : never;
|
|
335
|
+
/** The API a plugin's `setup` returns, or `never` for a plugin without one. */
|
|
336
|
+
type ClientPluginApi<P> = "api" extends keyof SetupResult<P> ? Exclude<SetupResult<P>["api" & keyof SetupResult<P>], undefined> : never;
|
|
337
|
+
/** The plugins' APIs by plugin id, as the instance `HotUpdater.init` returns has them. */
|
|
338
|
+
type ClientPluginApis<TPlugins extends readonly HotUpdaterClientPlugin[]> = { readonly [P in TPlugins[number] as [ClientPluginApi<P>] extends [never] ? never : P["id"]]: ClientPluginApi<P>; };
|
|
315
339
|
/** Declares a client plugin; built-in and third-party plugins use the same contract. */
|
|
316
340
|
declare const defineClientPlugin: <const P extends HotUpdaterClientPlugin>(plugin: P) => P;
|
|
317
341
|
//#endregion
|
|
318
342
|
//#region src/createPluginHost.d.ts
|
|
319
343
|
type PluginHookName = keyof HotUpdaterClientHooks;
|
|
320
344
|
type PluginHookPayload<K extends PluginHookName> = Parameters<NonNullable<HotUpdaterClientHooks[K]>>[0];
|
|
321
|
-
/** The init
|
|
345
|
+
/** The init settings plugins see through their context. */
|
|
322
346
|
interface PluginHostConfig {
|
|
323
347
|
readonly baseURL: HotUpdaterBaseURL;
|
|
324
348
|
readonly requestHeaders?: Record<string, string>;
|
|
@@ -348,11 +372,11 @@ interface PluginHostEnvironment {
|
|
|
348
372
|
}
|
|
349
373
|
interface PluginHost {
|
|
350
374
|
/**
|
|
351
|
-
* Sets up the plugins of an init
|
|
352
|
-
* earlier call keeps its hooks
|
|
353
|
-
* events.
|
|
375
|
+
* Sets up the plugins of an init call and returns their APIs by plugin
|
|
376
|
+
* id. A plugin passed to an earlier call keeps its hooks and its API, and
|
|
377
|
+
* a plugin left out stops receiving events.
|
|
354
378
|
*/
|
|
355
|
-
configurePlugins(plugins: readonly HotUpdaterClientPlugin[] | undefined, config: PluginHostConfig):
|
|
379
|
+
configurePlugins(plugins: readonly HotUpdaterClientPlugin[] | undefined, config: PluginHostConfig): Readonly<Record<string, unknown>>;
|
|
356
380
|
/** Whether any plugin listens to this hook, so callers build events only when one does. */
|
|
357
381
|
hasPluginHook(name: PluginHookName): boolean;
|
|
358
382
|
/**
|
|
@@ -552,4 +576,4 @@ declare function isCohortEligibleForUpdate(bundleId: string, cohort: string | nu
|
|
|
552
576
|
declare const NIL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
553
577
|
declare const isUUIDv7: (value: unknown) => value is string;
|
|
554
578
|
//#endregion
|
|
555
|
-
export { ARTIFACT_PROTOCOL_VERSION, AppReadyResult, ArtifactAsset, ArtifactAssetFile, ArtifactInfo, Bundle, BundleDownloadedInfo, BundleMetadata, BundlePatchArtifact, CatalogAcceptance, CatalogHighWater, ChangedAssetPatch, DEFAULT_ROLLOUT_COHORT_COUNT, DesiredRelease, ExpectedReleaseCatalogScope, HotUpdaterBaseURL, HotUpdaterClientContext, HotUpdaterClientHooks, HotUpdaterClientPlugin, HotUpdaterClientStorage, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, MAX_COMPILED_CATALOG_BYTES, MAX_CRASHED_BUNDLES, MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE, MAX_RELEASE_CATALOG_WIRE_BYTES, MAX_TARGET_COHORTS_PER_RELEASE, ManifestArchive, NIL_UUID, NUMERIC_COHORT_SIZE, PersistedSelectionReceipt, Platform, PluginHookName, PluginHookPayload, PluginHost, PluginHostConfig, PluginHostEnvironment, RELEASE_CATALOG_FALLBACK_POLICY, RELEASE_CATALOG_SCHEMA_VERSION, Release, ReleaseCatalog, ReleaseCatalogDescriptor, ReleaseCatalogScopeKeyInput, ReleaseKind, ReleaseOperation, ReleaseSelectionContextInput, ReleaseSelectionInput, ReleaseStrategy, ReleaseTransitionAuthorization, ReleaseTransitionKind, SelectionKind, UpdateCheckResult, UpdateError, UpdateErrorReason, UpdateErrorResource, UpdateErrorStage, UpdateErrorTransport, UpdateHttpResponse, UpdateStatus, UpdateStrategy, assertCanonicalChannelName, assessCatalogAcceptance, authorizeReleaseTransition, canonicalizeAppVersion, createPluginHost, createReleaseCatalogScopeKey, createReleaseSelectionContextHash, decodeChannelKey, defineClientPlugin, encodeChannelKey, getAssetBaseStorageUri, getBundlePatch, getBundlePatches, getDefaultNumericCohort, getManifestFileHash, getManifestStorageUri, getNumericCohortRolloutPosition, getNumericCohortValue, getPatchBaseBundleId, getPatchBaseFileHash, getPatchFileHash, getPatchStorageUri, getRolledOutNumericCohorts, getUtf8ByteLength, hasExpectedReleaseCatalogScope, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isReleaseEligibleForCohort, isUUIDv7, isValidCohort, normalizeChannelName, normalizeCohortValue, normalizeRolloutCohortCount, parseReleaseCatalog, parseReleaseCatalogScopeKey, pluginStorageKey, resolveBaseURL, selectDesiredRelease, stripBundleArtifactMetadata };
|
|
579
|
+
export { ARTIFACT_PROTOCOL_VERSION, AppReadyResult, ArtifactAsset, ArtifactAssetFile, ArtifactInfo, Bundle, BundleDownloadedInfo, BundleMetadata, BundlePatchArtifact, CatalogAcceptance, CatalogHighWater, ChangedAssetPatch, ClientPluginApi, ClientPluginApis, DEFAULT_ROLLOUT_COHORT_COUNT, DesiredRelease, ExpectedReleaseCatalogScope, HotUpdaterBaseURL, HotUpdaterClientContext, HotUpdaterClientHooks, HotUpdaterClientPlugin, HotUpdaterClientSetup, HotUpdaterClientStorage, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, MAX_COMPILED_CATALOG_BYTES, MAX_CRASHED_BUNDLES, MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE, MAX_RELEASE_CATALOG_WIRE_BYTES, MAX_TARGET_COHORTS_PER_RELEASE, ManifestArchive, NIL_UUID, NUMERIC_COHORT_SIZE, PersistedSelectionReceipt, Platform, PluginHookName, PluginHookPayload, PluginHost, PluginHostConfig, PluginHostEnvironment, RELEASE_CATALOG_FALLBACK_POLICY, RELEASE_CATALOG_SCHEMA_VERSION, Release, ReleaseCatalog, ReleaseCatalogDescriptor, ReleaseCatalogScopeKeyInput, ReleaseKind, ReleaseOperation, ReleaseSelectionContextInput, ReleaseSelectionInput, ReleaseStrategy, ReleaseTransitionAuthorization, ReleaseTransitionKind, SelectionKind, UpdateCheckResult, UpdateError, UpdateErrorReason, UpdateErrorResource, UpdateErrorStage, UpdateErrorTransport, UpdateHttpResponse, UpdateStatus, UpdateStrategy, assertCanonicalChannelName, assessCatalogAcceptance, authorizeReleaseTransition, canonicalizeAppVersion, createPluginHost, createReleaseCatalogScopeKey, createReleaseSelectionContextHash, decodeChannelKey, defineClientPlugin, encodeChannelKey, getAssetBaseStorageUri, getBundlePatch, getBundlePatches, getDefaultNumericCohort, getManifestFileHash, getManifestStorageUri, getNumericCohortRolloutPosition, getNumericCohortValue, getPatchBaseBundleId, getPatchBaseFileHash, getPatchFileHash, getPatchStorageUri, getRolledOutNumericCohorts, getUtf8ByteLength, hasExpectedReleaseCatalogScope, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isReleaseEligibleForCohort, isUUIDv7, isValidCohort, normalizeChannelName, normalizeCohortValue, normalizeRolloutCohortCount, parseReleaseCatalog, parseReleaseCatalogScopeKey, pluginStorageKey, resolveBaseURL, selectDesiredRelease, stripBundleArtifactMetadata };
|
package/dist/index.d.mts
CHANGED
|
@@ -247,10 +247,10 @@ interface UpdateHttpResponse {
|
|
|
247
247
|
readonly bodyTruncated: boolean;
|
|
248
248
|
}
|
|
249
249
|
/**
|
|
250
|
-
* Lifecycle hooks a plugin returns from `setup`. Hooks observe:
|
|
251
|
-
* never waits for one and ignores what it returns, and a hook that
|
|
252
|
-
* rejects is reported through `onError`, or a console warning
|
|
253
|
-
* so a plugin can never delay or change an update.
|
|
250
|
+
* Lifecycle hooks a plugin returns from `setup` as `hooks`. Hooks observe:
|
|
251
|
+
* the SDK never waits for one and ignores what it returns, and a hook that
|
|
252
|
+
* throws or rejects is reported through `onError`, or a console warning
|
|
253
|
+
* without one, so a plugin can never delay or change an update.
|
|
254
254
|
*/
|
|
255
255
|
interface HotUpdaterClientHooks {
|
|
256
256
|
/** Called once per JavaScript runtime, after native reports the launch. */
|
|
@@ -303,22 +303,46 @@ interface HotUpdaterClientContext {
|
|
|
303
303
|
/** The current time in milliseconds since the epoch. */
|
|
304
304
|
now(): number;
|
|
305
305
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
306
|
+
/**
|
|
307
|
+
* What a plugin's `setup` returns: the hooks it observes the SDK with, and
|
|
308
|
+
* the API `HotUpdater.init` puts on the instance it returns, under the
|
|
309
|
+
* plugin's id, as `hotUpdater.<id>`.
|
|
310
|
+
*/
|
|
311
|
+
interface HotUpdaterClientSetup<TApi = unknown> {
|
|
312
|
+
readonly hooks?: HotUpdaterClientHooks;
|
|
313
|
+
readonly api?: TApi;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* A client plugin with the id `TId` and the API `TApi` it adds to the
|
|
317
|
+
* instance `HotUpdater.init` returns.
|
|
318
|
+
*/
|
|
319
|
+
interface HotUpdaterClientPlugin<TId extends string = string, TApi = unknown> {
|
|
320
|
+
/**
|
|
321
|
+
* Unique among an app's plugins; two plugins with one id throw at init.
|
|
322
|
+
* It names the plugin's API on the instance, so it cannot be the name of
|
|
323
|
+
* one of the instance's own methods.
|
|
324
|
+
*/
|
|
325
|
+
readonly id: TId;
|
|
309
326
|
/**
|
|
310
|
-
* Called once
|
|
311
|
-
*
|
|
327
|
+
* Called once for each plugin host it is configured in. The React Native
|
|
328
|
+
* SDK creates a host for each instance `HotUpdater.init` returns.
|
|
312
329
|
*/
|
|
313
|
-
setup(context: HotUpdaterClientContext):
|
|
330
|
+
setup(context: HotUpdaterClientContext): HotUpdaterClientSetup<TApi> | void;
|
|
314
331
|
}
|
|
332
|
+
type SetupResult<P> = P extends {
|
|
333
|
+
setup(context: HotUpdaterClientContext): infer R;
|
|
334
|
+
} ? Exclude<R, void | undefined> : never;
|
|
335
|
+
/** The API a plugin's `setup` returns, or `never` for a plugin without one. */
|
|
336
|
+
type ClientPluginApi<P> = "api" extends keyof SetupResult<P> ? Exclude<SetupResult<P>["api" & keyof SetupResult<P>], undefined> : never;
|
|
337
|
+
/** The plugins' APIs by plugin id, as the instance `HotUpdater.init` returns has them. */
|
|
338
|
+
type ClientPluginApis<TPlugins extends readonly HotUpdaterClientPlugin[]> = { readonly [P in TPlugins[number] as [ClientPluginApi<P>] extends [never] ? never : P["id"]]: ClientPluginApi<P>; };
|
|
315
339
|
/** Declares a client plugin; built-in and third-party plugins use the same contract. */
|
|
316
340
|
declare const defineClientPlugin: <const P extends HotUpdaterClientPlugin>(plugin: P) => P;
|
|
317
341
|
//#endregion
|
|
318
342
|
//#region src/createPluginHost.d.ts
|
|
319
343
|
type PluginHookName = keyof HotUpdaterClientHooks;
|
|
320
344
|
type PluginHookPayload<K extends PluginHookName> = Parameters<NonNullable<HotUpdaterClientHooks[K]>>[0];
|
|
321
|
-
/** The init
|
|
345
|
+
/** The init settings plugins see through their context. */
|
|
322
346
|
interface PluginHostConfig {
|
|
323
347
|
readonly baseURL: HotUpdaterBaseURL;
|
|
324
348
|
readonly requestHeaders?: Record<string, string>;
|
|
@@ -348,11 +372,11 @@ interface PluginHostEnvironment {
|
|
|
348
372
|
}
|
|
349
373
|
interface PluginHost {
|
|
350
374
|
/**
|
|
351
|
-
* Sets up the plugins of an init
|
|
352
|
-
* earlier call keeps its hooks
|
|
353
|
-
* events.
|
|
375
|
+
* Sets up the plugins of an init call and returns their APIs by plugin
|
|
376
|
+
* id. A plugin passed to an earlier call keeps its hooks and its API, and
|
|
377
|
+
* a plugin left out stops receiving events.
|
|
354
378
|
*/
|
|
355
|
-
configurePlugins(plugins: readonly HotUpdaterClientPlugin[] | undefined, config: PluginHostConfig):
|
|
379
|
+
configurePlugins(plugins: readonly HotUpdaterClientPlugin[] | undefined, config: PluginHostConfig): Readonly<Record<string, unknown>>;
|
|
356
380
|
/** Whether any plugin listens to this hook, so callers build events only when one does. */
|
|
357
381
|
hasPluginHook(name: PluginHookName): boolean;
|
|
358
382
|
/**
|
|
@@ -552,4 +576,4 @@ declare function isCohortEligibleForUpdate(bundleId: string, cohort: string | nu
|
|
|
552
576
|
declare const NIL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
553
577
|
declare const isUUIDv7: (value: unknown) => value is string;
|
|
554
578
|
//#endregion
|
|
555
|
-
export { ARTIFACT_PROTOCOL_VERSION, AppReadyResult, ArtifactAsset, ArtifactAssetFile, ArtifactInfo, Bundle, BundleDownloadedInfo, BundleMetadata, BundlePatchArtifact, CatalogAcceptance, CatalogHighWater, ChangedAssetPatch, DEFAULT_ROLLOUT_COHORT_COUNT, DesiredRelease, ExpectedReleaseCatalogScope, HotUpdaterBaseURL, HotUpdaterClientContext, HotUpdaterClientHooks, HotUpdaterClientPlugin, HotUpdaterClientStorage, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, MAX_COMPILED_CATALOG_BYTES, MAX_CRASHED_BUNDLES, MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE, MAX_RELEASE_CATALOG_WIRE_BYTES, MAX_TARGET_COHORTS_PER_RELEASE, ManifestArchive, NIL_UUID, NUMERIC_COHORT_SIZE, PersistedSelectionReceipt, Platform, PluginHookName, PluginHookPayload, PluginHost, PluginHostConfig, PluginHostEnvironment, RELEASE_CATALOG_FALLBACK_POLICY, RELEASE_CATALOG_SCHEMA_VERSION, Release, ReleaseCatalog, ReleaseCatalogDescriptor, ReleaseCatalogScopeKeyInput, ReleaseKind, ReleaseOperation, ReleaseSelectionContextInput, ReleaseSelectionInput, ReleaseStrategy, ReleaseTransitionAuthorization, ReleaseTransitionKind, SelectionKind, UpdateCheckResult, UpdateError, UpdateErrorReason, UpdateErrorResource, UpdateErrorStage, UpdateErrorTransport, UpdateHttpResponse, UpdateStatus, UpdateStrategy, assertCanonicalChannelName, assessCatalogAcceptance, authorizeReleaseTransition, canonicalizeAppVersion, createPluginHost, createReleaseCatalogScopeKey, createReleaseSelectionContextHash, decodeChannelKey, defineClientPlugin, encodeChannelKey, getAssetBaseStorageUri, getBundlePatch, getBundlePatches, getDefaultNumericCohort, getManifestFileHash, getManifestStorageUri, getNumericCohortRolloutPosition, getNumericCohortValue, getPatchBaseBundleId, getPatchBaseFileHash, getPatchFileHash, getPatchStorageUri, getRolledOutNumericCohorts, getUtf8ByteLength, hasExpectedReleaseCatalogScope, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isReleaseEligibleForCohort, isUUIDv7, isValidCohort, normalizeChannelName, normalizeCohortValue, normalizeRolloutCohortCount, parseReleaseCatalog, parseReleaseCatalogScopeKey, pluginStorageKey, resolveBaseURL, selectDesiredRelease, stripBundleArtifactMetadata };
|
|
579
|
+
export { ARTIFACT_PROTOCOL_VERSION, AppReadyResult, ArtifactAsset, ArtifactAssetFile, ArtifactInfo, Bundle, BundleDownloadedInfo, BundleMetadata, BundlePatchArtifact, CatalogAcceptance, CatalogHighWater, ChangedAssetPatch, ClientPluginApi, ClientPluginApis, DEFAULT_ROLLOUT_COHORT_COUNT, DesiredRelease, ExpectedReleaseCatalogScope, HotUpdaterBaseURL, HotUpdaterClientContext, HotUpdaterClientHooks, HotUpdaterClientPlugin, HotUpdaterClientSetup, HotUpdaterClientStorage, INVALID_COHORT_ERROR_MESSAGE, MAX_COHORT_LENGTH, MAX_COMPILED_CATALOG_BYTES, MAX_CRASHED_BUNDLES, MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE, MAX_RELEASE_CATALOG_WIRE_BYTES, MAX_TARGET_COHORTS_PER_RELEASE, ManifestArchive, NIL_UUID, NUMERIC_COHORT_SIZE, PersistedSelectionReceipt, Platform, PluginHookName, PluginHookPayload, PluginHost, PluginHostConfig, PluginHostEnvironment, RELEASE_CATALOG_FALLBACK_POLICY, RELEASE_CATALOG_SCHEMA_VERSION, Release, ReleaseCatalog, ReleaseCatalogDescriptor, ReleaseCatalogScopeKeyInput, ReleaseKind, ReleaseOperation, ReleaseSelectionContextInput, ReleaseSelectionInput, ReleaseStrategy, ReleaseTransitionAuthorization, ReleaseTransitionKind, SelectionKind, UpdateCheckResult, UpdateError, UpdateErrorReason, UpdateErrorResource, UpdateErrorStage, UpdateErrorTransport, UpdateHttpResponse, UpdateStatus, UpdateStrategy, assertCanonicalChannelName, assessCatalogAcceptance, authorizeReleaseTransition, canonicalizeAppVersion, createPluginHost, createReleaseCatalogScopeKey, createReleaseSelectionContextHash, decodeChannelKey, defineClientPlugin, encodeChannelKey, getAssetBaseStorageUri, getBundlePatch, getBundlePatches, getDefaultNumericCohort, getManifestFileHash, getManifestStorageUri, getNumericCohortRolloutPosition, getNumericCohortValue, getPatchBaseBundleId, getPatchBaseFileHash, getPatchFileHash, getPatchStorageUri, getRolledOutNumericCohorts, getUtf8ByteLength, hasExpectedReleaseCatalogScope, isCohortEligibleForUpdate, isCustomCohort, isNumericCohort, isReleaseEligibleForCohort, isUUIDv7, isValidCohort, normalizeChannelName, normalizeCohortValue, normalizeRolloutCohortCount, parseReleaseCatalog, parseReleaseCatalogScopeKey, pluginStorageKey, resolveBaseURL, selectDesiredRelease, stripBundleArtifactMetadata };
|
package/dist/index.mjs
CHANGED
|
@@ -169,7 +169,7 @@ const MAX_STORAGE_VALUE_LENGTH = 65536;
|
|
|
169
169
|
const pluginStorageKey = (pluginId, key) => `plugins/${encodeURIComponent(pluginId)}/${key}`;
|
|
170
170
|
const createPluginHost = (environment) => {
|
|
171
171
|
let config = null;
|
|
172
|
-
/** Hooks by plugin, so a plugin configured again is not set up twice. */
|
|
172
|
+
/** Hooks and API by plugin, so a plugin configured again is not set up twice. */
|
|
173
173
|
const setUpPlugins = /* @__PURE__ */ new WeakMap();
|
|
174
174
|
let activePlugins = [];
|
|
175
175
|
/** What plugins read and wrote, so a read follows a write before native does. */
|
|
@@ -185,7 +185,7 @@ const createPluginHost = (environment) => {
|
|
|
185
185
|
console.warn(report.message, error);
|
|
186
186
|
};
|
|
187
187
|
const fetchFromServer = async (path, init = {}) => {
|
|
188
|
-
if (config === null) throw new Error("[HotUpdater] Plugins can fetch only after init
|
|
188
|
+
if (config === null) throw new Error("[HotUpdater] Plugins can fetch only after HotUpdater.init.");
|
|
189
189
|
if (/^[a-z][a-z\d+.-]*:/i.test(path) || path.startsWith("//")) throw new TypeError("[HotUpdater] Plugin fetch takes a path relative to baseURL.");
|
|
190
190
|
const { requestHeaders, requestTimeout } = config;
|
|
191
191
|
const url = `${await resolveBaseURL(config.baseURL)}/${path.replace(/^\/+/, "")}`;
|
|
@@ -263,6 +263,28 @@ const createPluginHost = (environment) => {
|
|
|
263
263
|
storage: createStorage(pluginId),
|
|
264
264
|
now: () => environment.now()
|
|
265
265
|
});
|
|
266
|
+
const setUpPlugin = (plugin) => {
|
|
267
|
+
let result;
|
|
268
|
+
try {
|
|
269
|
+
result = plugin.setup(createContext(plugin.id));
|
|
270
|
+
} catch (error) {
|
|
271
|
+
reportHookError(`Plugin "${plugin.id}" failed in setup`, error);
|
|
272
|
+
return {
|
|
273
|
+
hooks: {},
|
|
274
|
+
api: void 0
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
if (result === void 0 || result === null) return {
|
|
278
|
+
hooks: {},
|
|
279
|
+
api: void 0
|
|
280
|
+
};
|
|
281
|
+
const unknownKeys = Object.keys(result).filter((key) => key !== "hooks" && key !== "api");
|
|
282
|
+
if (unknownKeys.length > 0) reportHookError(`Plugin "${plugin.id}" setup returned ${unknownKeys.map((key) => `"${key}"`).join(", ")}; setup returns { hooks, api }`, /* @__PURE__ */ new TypeError("Unexpected setup result"));
|
|
283
|
+
return {
|
|
284
|
+
hooks: result.hooks ?? {},
|
|
285
|
+
api: result.api
|
|
286
|
+
};
|
|
287
|
+
};
|
|
266
288
|
const configurePlugins = (plugins, nextConfig) => {
|
|
267
289
|
const ids = /* @__PURE__ */ new Set();
|
|
268
290
|
for (const plugin of plugins ?? []) {
|
|
@@ -271,22 +293,20 @@ const createPluginHost = (environment) => {
|
|
|
271
293
|
ids.add(plugin.id);
|
|
272
294
|
}
|
|
273
295
|
config = nextConfig;
|
|
296
|
+
const apis = [];
|
|
274
297
|
activePlugins = (plugins ?? []).map((plugin) => {
|
|
275
|
-
let
|
|
276
|
-
if (
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
} catch (error) {
|
|
280
|
-
reportHookError(`Plugin "${plugin.id}" failed in setup`, error);
|
|
281
|
-
hooks = {};
|
|
282
|
-
}
|
|
283
|
-
setUpPlugins.set(plugin, hooks);
|
|
298
|
+
let setUp = setUpPlugins.get(plugin);
|
|
299
|
+
if (setUp === void 0) {
|
|
300
|
+
setUp = setUpPlugin(plugin);
|
|
301
|
+
setUpPlugins.set(plugin, setUp);
|
|
284
302
|
}
|
|
303
|
+
if (setUp.api !== void 0) apis.push([plugin.id, setUp.api]);
|
|
285
304
|
return {
|
|
286
305
|
plugin,
|
|
287
|
-
hooks
|
|
306
|
+
hooks: setUp.hooks
|
|
288
307
|
};
|
|
289
308
|
});
|
|
309
|
+
return Object.freeze(Object.fromEntries(apis));
|
|
290
310
|
};
|
|
291
311
|
const hasPluginHook = (name) => activePlugins.some(({ hooks }) => typeof hooks[name] === "function");
|
|
292
312
|
const buildPluginEvent = (name, createPayload) => {
|