@hot-updater/cloudflare 1.0.0-rc.40 → 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 +8 -3
- package/dist/index.d.cts +239 -3
- package/dist/index.d.mts +239 -3
- package/dist/index.mjs +8 -3
- package/dist/init/index.cjs +8 -3
- package/dist/init/index.mjs +8 -3
- package/dist/worker/index.cjs +8 -3
- package/dist/worker/index.d.cts +239 -3
- package/dist/worker/index.d.mts +239 -3
- package/dist/worker/index.mjs +8 -3
- package/package.json +6 -6
- package/sql/bundles.sql +6 -0
- package/src/plugins.ts +4 -3
- package/worker/dist/README.md +1 -1
- package/worker/dist/index.js +901 -31
- package/worker/dist/index.js.map +4 -4
- package/worker/migrations/0001_hot-updater_1.0.0.sql +6 -0
package/dist/index.cjs
CHANGED
|
@@ -28,6 +28,7 @@ let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
|
|
|
28
28
|
let _aws_sdk_lib_storage = require("@aws-sdk/lib-storage");
|
|
29
29
|
let _hot_updater_server_plugins_api_keys = require("@hot-updater/server/plugins/api-keys");
|
|
30
30
|
let _hot_updater_server_plugins_insights = require("@hot-updater/server/plugins/insights");
|
|
31
|
+
let _hot_updater_server_plugins_remote_config = require("@hot-updater/server/plugins/remote-config");
|
|
31
32
|
//#region src/d1Executor.ts
|
|
32
33
|
var D1ExecutionError = class extends Error {
|
|
33
34
|
name = "D1ExecutionError";
|
|
@@ -227,10 +228,14 @@ const r2Storage = (config) => {
|
|
|
227
228
|
//#endregion
|
|
228
229
|
//#region src/plugins.ts
|
|
229
230
|
/**
|
|
230
|
-
* The plugins the managed Cloudflare server runs: Insights,
|
|
231
|
-
* client route. Its Worker imports this list.
|
|
231
|
+
* The plugins the managed Cloudflare server runs: Insights, API keys on every
|
|
232
|
+
* client route, and Remote Config. Its Worker imports this list.
|
|
232
233
|
*/
|
|
233
|
-
const plugins = [
|
|
234
|
+
const plugins = [
|
|
235
|
+
(0, _hot_updater_server_plugins_insights.insights)(),
|
|
236
|
+
(0, _hot_updater_server_plugins_api_keys.apiKeys)(),
|
|
237
|
+
(0, _hot_updater_server_plugins_remote_config.remoteConfig)()
|
|
238
|
+
];
|
|
234
239
|
//#endregion
|
|
235
240
|
exports.D1ExecutionError = D1ExecutionError;
|
|
236
241
|
exports.d1Database = d1Database;
|
package/dist/index.d.cts
CHANGED
|
@@ -366,10 +366,198 @@ interface CreatedApiKey {
|
|
|
366
366
|
readonly record: ApiKeyMetadata;
|
|
367
367
|
}
|
|
368
368
|
//#endregion
|
|
369
|
+
//#region ../remote-config/dist/shared/wire.d.mts
|
|
370
|
+
//#region src/shared/wire.d.ts
|
|
371
|
+
/** What a device tells the server, so the server can evaluate conditions. */
|
|
372
|
+
interface RemoteConfigDeviceContext {
|
|
373
|
+
readonly platform: "ios" | "android";
|
|
374
|
+
/** The native app version, or null when the build has none. */
|
|
375
|
+
readonly appVersion: string | null;
|
|
376
|
+
readonly channel: string;
|
|
377
|
+
/** A numeric cohort from 1 to 1000, or a custom cohort's slug. */
|
|
378
|
+
readonly cohort: string;
|
|
379
|
+
/** The native build's fingerprint, or null without one. */
|
|
380
|
+
readonly fingerprintHash: string | null;
|
|
381
|
+
}
|
|
382
|
+
/** The body of a `200` from `GET /remote-config`. */
|
|
383
|
+
interface RemoteConfigFetchResponse {
|
|
384
|
+
/** The published template version the values come from; 0 before the first publish. */
|
|
385
|
+
readonly version: number;
|
|
386
|
+
/**
|
|
387
|
+
* Each parameter's value for this device, as text. A parameter whose
|
|
388
|
+
* value is the app's own default is left out.
|
|
389
|
+
*/
|
|
390
|
+
readonly values: Readonly<Record<string, string>>;
|
|
391
|
+
}
|
|
392
|
+
//#endregion
|
|
393
|
+
//#region ../remote-config/dist/server/template.d.mts
|
|
394
|
+
//#region src/server/template.d.ts
|
|
395
|
+
/**
|
|
396
|
+
* How the Console edits a parameter's value and checks it before a publish.
|
|
397
|
+
* Devices receive every value as text.
|
|
398
|
+
*/
|
|
399
|
+
type RemoteConfigValueType = "STRING" | "NUMBER" | "BOOLEAN" | "JSON";
|
|
400
|
+
/** A value a parameter takes: text, or the app's own default. */
|
|
401
|
+
type RemoteConfigParameterValue = {
|
|
402
|
+
readonly value: string;
|
|
403
|
+
} | {
|
|
404
|
+
readonly useInAppDefault: true;
|
|
405
|
+
};
|
|
406
|
+
interface RemoteConfigParameter {
|
|
407
|
+
readonly valueType: RemoteConfigValueType;
|
|
408
|
+
/** The value when none of the parameter's conditions match. */
|
|
409
|
+
readonly defaultValue: RemoteConfigParameterValue;
|
|
410
|
+
/**
|
|
411
|
+
* Values by condition name. When several of these conditions match, the
|
|
412
|
+
* one listed first in the template's `conditions` wins.
|
|
413
|
+
*/
|
|
414
|
+
readonly conditionalValues?: Readonly<Record<string, RemoteConfigParameterValue>>;
|
|
415
|
+
readonly description?: string;
|
|
416
|
+
}
|
|
417
|
+
/** A test on a device's context; a condition matches when all of its rules do. */
|
|
418
|
+
type RemoteConfigRule = {
|
|
419
|
+
readonly type: "platform";
|
|
420
|
+
readonly platforms: readonly ("ios" | "android")[];
|
|
421
|
+
} | {
|
|
422
|
+
readonly type: "channel";
|
|
423
|
+
readonly channels: readonly string[];
|
|
424
|
+
} |
|
|
425
|
+
/** A semver range, such as `>=1.4.0` or `2.x`, as release targets use. */
|
|
426
|
+
{
|
|
427
|
+
readonly type: "appVersion";
|
|
428
|
+
readonly range: string;
|
|
429
|
+
} |
|
|
430
|
+
/** Devices whose cohort is one of these: numbers from 1 to 1000 or slugs. */
|
|
431
|
+
{
|
|
432
|
+
readonly type: "cohort";
|
|
433
|
+
readonly cohorts: readonly string[];
|
|
434
|
+
} |
|
|
435
|
+
/**
|
|
436
|
+
* A share of numeric cohorts: the devices whose cohort lands in
|
|
437
|
+
* `[from, to)` percent once `seed` shuffles the 1,000 cohorts. Rules with
|
|
438
|
+
* one seed pick from one order, so `0–10` and `10–20` never overlap; a new
|
|
439
|
+
* seed picks other devices. Custom cohorts never match.
|
|
440
|
+
*/
|
|
441
|
+
{
|
|
442
|
+
readonly type: "percent";
|
|
443
|
+
readonly seed: string;
|
|
444
|
+
readonly from: number;
|
|
445
|
+
readonly to: number;
|
|
446
|
+
} | {
|
|
447
|
+
readonly type: "fingerprint";
|
|
448
|
+
readonly hashes: readonly string[];
|
|
449
|
+
} |
|
|
450
|
+
/**
|
|
451
|
+
* While the server's clock is in `[from, to)`: ISO 8601 date-times in UTC,
|
|
452
|
+
* either of them open. A device gets the change at its next fetch.
|
|
453
|
+
*/
|
|
454
|
+
{
|
|
455
|
+
readonly type: "dateTime";
|
|
456
|
+
readonly from?: string;
|
|
457
|
+
readonly to?: string;
|
|
458
|
+
};
|
|
459
|
+
interface RemoteConfigCondition {
|
|
460
|
+
/** Unique in the template; parameters name it in `conditionalValues`. */
|
|
461
|
+
readonly name: string;
|
|
462
|
+
/** Every rule must match. */
|
|
463
|
+
readonly rules: readonly RemoteConfigRule[];
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Everything Remote Config serves, published as a whole and versioned.
|
|
467
|
+
*/
|
|
468
|
+
interface RemoteConfigTemplate {
|
|
469
|
+
/** In priority order: the first one that matches decides a parameter's value. */
|
|
470
|
+
readonly conditions: readonly RemoteConfigCondition[];
|
|
471
|
+
readonly parameters: Readonly<Record<string, RemoteConfigParameter>>;
|
|
472
|
+
}
|
|
473
|
+
/** What the server knows of a device; a rule on a missing field never matches. */
|
|
474
|
+
type RemoteConfigEvaluationContext = Partial<Readonly<{ [K in keyof RemoteConfigDeviceContext]: RemoteConfigDeviceContext[K] | null; }>> & {
|
|
475
|
+
/**
|
|
476
|
+
* When to evaluate, in ms since the epoch. The server uses its own clock;
|
|
477
|
+
* a `dateTime` rule never matches without it.
|
|
478
|
+
*/
|
|
479
|
+
readonly now?: number;
|
|
480
|
+
};
|
|
481
|
+
//#endregion
|
|
482
|
+
//#region ../remote-config/dist/server/api.d.mts
|
|
483
|
+
//#region src/server/api.d.ts
|
|
484
|
+
/** A published version's record, without its template. */
|
|
485
|
+
interface RemoteConfigVersion {
|
|
486
|
+
readonly version: number;
|
|
487
|
+
readonly description: string | null;
|
|
488
|
+
/** `ROLLBACK` when the version copies `rollbackSource`. */
|
|
489
|
+
readonly updateType: "PUBLISH" | "ROLLBACK";
|
|
490
|
+
readonly rollbackSource: number | null;
|
|
491
|
+
readonly createdAtMs: number;
|
|
492
|
+
}
|
|
493
|
+
interface RemoteConfigVersionDetail extends RemoteConfigVersion {
|
|
494
|
+
readonly template: RemoteConfigTemplate;
|
|
495
|
+
}
|
|
496
|
+
/** The template devices receive now. */
|
|
497
|
+
interface RemoteConfigActive {
|
|
498
|
+
/** 0, with an empty template, before the first publish. */
|
|
499
|
+
readonly version: number;
|
|
500
|
+
readonly template: RemoteConfigTemplate;
|
|
501
|
+
readonly updatedAtMs: number | null;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* How a publish or a rollback ended: `conflict` when another publish moved
|
|
505
|
+
* the active version past `baseVersion` first, so a stale edit never
|
|
506
|
+
* replaces a newer template.
|
|
507
|
+
*/
|
|
508
|
+
type RemoteConfigPublishResult = {
|
|
509
|
+
readonly status: "published";
|
|
510
|
+
readonly version: RemoteConfigVersion;
|
|
511
|
+
} | {
|
|
512
|
+
readonly status: "conflict";
|
|
513
|
+
readonly currentVersion: number;
|
|
514
|
+
};
|
|
515
|
+
interface RemoteConfigVersionsPage {
|
|
516
|
+
/** Newest first. */
|
|
517
|
+
readonly versions: readonly RemoteConfigVersion[];
|
|
518
|
+
/** Pass as `cursor` for the next page; absent on the last one. */
|
|
519
|
+
readonly next?: string;
|
|
520
|
+
}
|
|
521
|
+
/** `hotUpdater.api.remoteConfig`. */
|
|
522
|
+
interface RemoteConfigApi {
|
|
523
|
+
/** The active template and its version. */
|
|
524
|
+
getActive(): Promise<RemoteConfigActive>;
|
|
525
|
+
/**
|
|
526
|
+
* Validates and publishes a template as the next version. Throws
|
|
527
|
+
* `RemoteConfigValidationError` for an invalid template.
|
|
528
|
+
*/
|
|
529
|
+
publish(input: {
|
|
530
|
+
readonly template: unknown;
|
|
531
|
+
/** The version the edit started from; 0 before the first publish. */
|
|
532
|
+
readonly baseVersion: number;
|
|
533
|
+
readonly description?: string;
|
|
534
|
+
}): Promise<RemoteConfigPublishResult>;
|
|
535
|
+
/** Publishes a copy of an earlier version; `not_found` when it does not exist. */
|
|
536
|
+
rollback(input: {
|
|
537
|
+
readonly version: number;
|
|
538
|
+
readonly baseVersion: number;
|
|
539
|
+
readonly description?: string;
|
|
540
|
+
}): Promise<RemoteConfigPublishResult | {
|
|
541
|
+
readonly status: "not_found";
|
|
542
|
+
}>;
|
|
543
|
+
/** One page of published versions, newest first. */
|
|
544
|
+
listVersions(input?: {
|
|
545
|
+
readonly limit?: number;
|
|
546
|
+
readonly cursor?: string;
|
|
547
|
+
}): Promise<RemoteConfigVersionsPage>;
|
|
548
|
+
/** One version with its template, or null. */
|
|
549
|
+
getVersion(version: number): Promise<RemoteConfigVersionDetail | null>;
|
|
550
|
+
/**
|
|
551
|
+
* What a device with this context receives from `GET /remote-config`, at
|
|
552
|
+
* `context.now` or, without it, now.
|
|
553
|
+
*/
|
|
554
|
+
resolve(context: RemoteConfigEvaluationContext): Promise<RemoteConfigFetchResponse>;
|
|
555
|
+
}
|
|
556
|
+
//#endregion
|
|
369
557
|
//#region src/plugins.d.ts
|
|
370
558
|
/**
|
|
371
|
-
* The plugins the managed Cloudflare server runs: Insights,
|
|
372
|
-
* client route. Its Worker imports this list.
|
|
559
|
+
* The plugins the managed Cloudflare server runs: Insights, API keys on every
|
|
560
|
+
* client route, and Remote Config. Its Worker imports this list.
|
|
373
561
|
*/
|
|
374
562
|
declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPlugin<"insights", {
|
|
375
563
|
readonly bundle_events: import("@hot-updater/plugin-core").TableDefinition<{
|
|
@@ -811,6 +999,54 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
|
|
|
811
999
|
};
|
|
812
1000
|
}, {
|
|
813
1001
|
readonly clientAuth: true;
|
|
814
|
-
}
|
|
1002
|
+
}>, import("@hot-updater/plugin-core").HotUpdaterPlugin<"remoteConfig", {
|
|
1003
|
+
readonly remote_config_active: import("@hot-updater/plugin-core").TableDefinition<{
|
|
1004
|
+
readonly id: {
|
|
1005
|
+
readonly type: "string";
|
|
1006
|
+
readonly maxLength: 16;
|
|
1007
|
+
};
|
|
1008
|
+
readonly version: {
|
|
1009
|
+
readonly type: "integer";
|
|
1010
|
+
};
|
|
1011
|
+
readonly template: {
|
|
1012
|
+
readonly type: "json";
|
|
1013
|
+
};
|
|
1014
|
+
readonly updated_at_ms: {
|
|
1015
|
+
readonly type: "integer";
|
|
1016
|
+
};
|
|
1017
|
+
}, {}, {}, readonly ["id"]>;
|
|
1018
|
+
readonly remote_config_versions: import("@hot-updater/plugin-core").TableDefinition<{
|
|
1019
|
+
readonly version: {
|
|
1020
|
+
readonly type: "integer";
|
|
1021
|
+
};
|
|
1022
|
+
readonly template: {
|
|
1023
|
+
readonly type: "json";
|
|
1024
|
+
};
|
|
1025
|
+
readonly description: {
|
|
1026
|
+
readonly type: "string";
|
|
1027
|
+
readonly maxLength: 256;
|
|
1028
|
+
readonly required: false;
|
|
1029
|
+
};
|
|
1030
|
+
readonly update_type: {
|
|
1031
|
+
readonly type: "string";
|
|
1032
|
+
readonly maxLength: 16;
|
|
1033
|
+
};
|
|
1034
|
+
readonly rollback_source: {
|
|
1035
|
+
readonly type: "integer";
|
|
1036
|
+
readonly required: false;
|
|
1037
|
+
};
|
|
1038
|
+
readonly created_at_ms: {
|
|
1039
|
+
readonly type: "integer";
|
|
1040
|
+
};
|
|
1041
|
+
}, {}, {
|
|
1042
|
+
readonly byVersion: {
|
|
1043
|
+
readonly eq: readonly [];
|
|
1044
|
+
readonly sort: readonly ["version"];
|
|
1045
|
+
};
|
|
1046
|
+
}, readonly ["version"]>;
|
|
1047
|
+
}, {
|
|
1048
|
+
api: RemoteConfigApi;
|
|
1049
|
+
endpoints: readonly import("@hot-updater/plugin-core").PluginEndpoint[];
|
|
1050
|
+
}, {}>];
|
|
815
1051
|
//#endregion
|
|
816
1052
|
export { D1DatabaseConfig, D1ExecutionError, type R2S3StorageConfig, R2StorageConfig, d1Database, plugins, r2Storage };
|
package/dist/index.d.mts
CHANGED
|
@@ -366,10 +366,198 @@ interface CreatedApiKey {
|
|
|
366
366
|
readonly record: ApiKeyMetadata;
|
|
367
367
|
}
|
|
368
368
|
//#endregion
|
|
369
|
+
//#region ../remote-config/dist/shared/wire.d.mts
|
|
370
|
+
//#region src/shared/wire.d.ts
|
|
371
|
+
/** What a device tells the server, so the server can evaluate conditions. */
|
|
372
|
+
interface RemoteConfigDeviceContext {
|
|
373
|
+
readonly platform: "ios" | "android";
|
|
374
|
+
/** The native app version, or null when the build has none. */
|
|
375
|
+
readonly appVersion: string | null;
|
|
376
|
+
readonly channel: string;
|
|
377
|
+
/** A numeric cohort from 1 to 1000, or a custom cohort's slug. */
|
|
378
|
+
readonly cohort: string;
|
|
379
|
+
/** The native build's fingerprint, or null without one. */
|
|
380
|
+
readonly fingerprintHash: string | null;
|
|
381
|
+
}
|
|
382
|
+
/** The body of a `200` from `GET /remote-config`. */
|
|
383
|
+
interface RemoteConfigFetchResponse {
|
|
384
|
+
/** The published template version the values come from; 0 before the first publish. */
|
|
385
|
+
readonly version: number;
|
|
386
|
+
/**
|
|
387
|
+
* Each parameter's value for this device, as text. A parameter whose
|
|
388
|
+
* value is the app's own default is left out.
|
|
389
|
+
*/
|
|
390
|
+
readonly values: Readonly<Record<string, string>>;
|
|
391
|
+
}
|
|
392
|
+
//#endregion
|
|
393
|
+
//#region ../remote-config/dist/server/template.d.mts
|
|
394
|
+
//#region src/server/template.d.ts
|
|
395
|
+
/**
|
|
396
|
+
* How the Console edits a parameter's value and checks it before a publish.
|
|
397
|
+
* Devices receive every value as text.
|
|
398
|
+
*/
|
|
399
|
+
type RemoteConfigValueType = "STRING" | "NUMBER" | "BOOLEAN" | "JSON";
|
|
400
|
+
/** A value a parameter takes: text, or the app's own default. */
|
|
401
|
+
type RemoteConfigParameterValue = {
|
|
402
|
+
readonly value: string;
|
|
403
|
+
} | {
|
|
404
|
+
readonly useInAppDefault: true;
|
|
405
|
+
};
|
|
406
|
+
interface RemoteConfigParameter {
|
|
407
|
+
readonly valueType: RemoteConfigValueType;
|
|
408
|
+
/** The value when none of the parameter's conditions match. */
|
|
409
|
+
readonly defaultValue: RemoteConfigParameterValue;
|
|
410
|
+
/**
|
|
411
|
+
* Values by condition name. When several of these conditions match, the
|
|
412
|
+
* one listed first in the template's `conditions` wins.
|
|
413
|
+
*/
|
|
414
|
+
readonly conditionalValues?: Readonly<Record<string, RemoteConfigParameterValue>>;
|
|
415
|
+
readonly description?: string;
|
|
416
|
+
}
|
|
417
|
+
/** A test on a device's context; a condition matches when all of its rules do. */
|
|
418
|
+
type RemoteConfigRule = {
|
|
419
|
+
readonly type: "platform";
|
|
420
|
+
readonly platforms: readonly ("ios" | "android")[];
|
|
421
|
+
} | {
|
|
422
|
+
readonly type: "channel";
|
|
423
|
+
readonly channels: readonly string[];
|
|
424
|
+
} |
|
|
425
|
+
/** A semver range, such as `>=1.4.0` or `2.x`, as release targets use. */
|
|
426
|
+
{
|
|
427
|
+
readonly type: "appVersion";
|
|
428
|
+
readonly range: string;
|
|
429
|
+
} |
|
|
430
|
+
/** Devices whose cohort is one of these: numbers from 1 to 1000 or slugs. */
|
|
431
|
+
{
|
|
432
|
+
readonly type: "cohort";
|
|
433
|
+
readonly cohorts: readonly string[];
|
|
434
|
+
} |
|
|
435
|
+
/**
|
|
436
|
+
* A share of numeric cohorts: the devices whose cohort lands in
|
|
437
|
+
* `[from, to)` percent once `seed` shuffles the 1,000 cohorts. Rules with
|
|
438
|
+
* one seed pick from one order, so `0–10` and `10–20` never overlap; a new
|
|
439
|
+
* seed picks other devices. Custom cohorts never match.
|
|
440
|
+
*/
|
|
441
|
+
{
|
|
442
|
+
readonly type: "percent";
|
|
443
|
+
readonly seed: string;
|
|
444
|
+
readonly from: number;
|
|
445
|
+
readonly to: number;
|
|
446
|
+
} | {
|
|
447
|
+
readonly type: "fingerprint";
|
|
448
|
+
readonly hashes: readonly string[];
|
|
449
|
+
} |
|
|
450
|
+
/**
|
|
451
|
+
* While the server's clock is in `[from, to)`: ISO 8601 date-times in UTC,
|
|
452
|
+
* either of them open. A device gets the change at its next fetch.
|
|
453
|
+
*/
|
|
454
|
+
{
|
|
455
|
+
readonly type: "dateTime";
|
|
456
|
+
readonly from?: string;
|
|
457
|
+
readonly to?: string;
|
|
458
|
+
};
|
|
459
|
+
interface RemoteConfigCondition {
|
|
460
|
+
/** Unique in the template; parameters name it in `conditionalValues`. */
|
|
461
|
+
readonly name: string;
|
|
462
|
+
/** Every rule must match. */
|
|
463
|
+
readonly rules: readonly RemoteConfigRule[];
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Everything Remote Config serves, published as a whole and versioned.
|
|
467
|
+
*/
|
|
468
|
+
interface RemoteConfigTemplate {
|
|
469
|
+
/** In priority order: the first one that matches decides a parameter's value. */
|
|
470
|
+
readonly conditions: readonly RemoteConfigCondition[];
|
|
471
|
+
readonly parameters: Readonly<Record<string, RemoteConfigParameter>>;
|
|
472
|
+
}
|
|
473
|
+
/** What the server knows of a device; a rule on a missing field never matches. */
|
|
474
|
+
type RemoteConfigEvaluationContext = Partial<Readonly<{ [K in keyof RemoteConfigDeviceContext]: RemoteConfigDeviceContext[K] | null; }>> & {
|
|
475
|
+
/**
|
|
476
|
+
* When to evaluate, in ms since the epoch. The server uses its own clock;
|
|
477
|
+
* a `dateTime` rule never matches without it.
|
|
478
|
+
*/
|
|
479
|
+
readonly now?: number;
|
|
480
|
+
};
|
|
481
|
+
//#endregion
|
|
482
|
+
//#region ../remote-config/dist/server/api.d.mts
|
|
483
|
+
//#region src/server/api.d.ts
|
|
484
|
+
/** A published version's record, without its template. */
|
|
485
|
+
interface RemoteConfigVersion {
|
|
486
|
+
readonly version: number;
|
|
487
|
+
readonly description: string | null;
|
|
488
|
+
/** `ROLLBACK` when the version copies `rollbackSource`. */
|
|
489
|
+
readonly updateType: "PUBLISH" | "ROLLBACK";
|
|
490
|
+
readonly rollbackSource: number | null;
|
|
491
|
+
readonly createdAtMs: number;
|
|
492
|
+
}
|
|
493
|
+
interface RemoteConfigVersionDetail extends RemoteConfigVersion {
|
|
494
|
+
readonly template: RemoteConfigTemplate;
|
|
495
|
+
}
|
|
496
|
+
/** The template devices receive now. */
|
|
497
|
+
interface RemoteConfigActive {
|
|
498
|
+
/** 0, with an empty template, before the first publish. */
|
|
499
|
+
readonly version: number;
|
|
500
|
+
readonly template: RemoteConfigTemplate;
|
|
501
|
+
readonly updatedAtMs: number | null;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* How a publish or a rollback ended: `conflict` when another publish moved
|
|
505
|
+
* the active version past `baseVersion` first, so a stale edit never
|
|
506
|
+
* replaces a newer template.
|
|
507
|
+
*/
|
|
508
|
+
type RemoteConfigPublishResult = {
|
|
509
|
+
readonly status: "published";
|
|
510
|
+
readonly version: RemoteConfigVersion;
|
|
511
|
+
} | {
|
|
512
|
+
readonly status: "conflict";
|
|
513
|
+
readonly currentVersion: number;
|
|
514
|
+
};
|
|
515
|
+
interface RemoteConfigVersionsPage {
|
|
516
|
+
/** Newest first. */
|
|
517
|
+
readonly versions: readonly RemoteConfigVersion[];
|
|
518
|
+
/** Pass as `cursor` for the next page; absent on the last one. */
|
|
519
|
+
readonly next?: string;
|
|
520
|
+
}
|
|
521
|
+
/** `hotUpdater.api.remoteConfig`. */
|
|
522
|
+
interface RemoteConfigApi {
|
|
523
|
+
/** The active template and its version. */
|
|
524
|
+
getActive(): Promise<RemoteConfigActive>;
|
|
525
|
+
/**
|
|
526
|
+
* Validates and publishes a template as the next version. Throws
|
|
527
|
+
* `RemoteConfigValidationError` for an invalid template.
|
|
528
|
+
*/
|
|
529
|
+
publish(input: {
|
|
530
|
+
readonly template: unknown;
|
|
531
|
+
/** The version the edit started from; 0 before the first publish. */
|
|
532
|
+
readonly baseVersion: number;
|
|
533
|
+
readonly description?: string;
|
|
534
|
+
}): Promise<RemoteConfigPublishResult>;
|
|
535
|
+
/** Publishes a copy of an earlier version; `not_found` when it does not exist. */
|
|
536
|
+
rollback(input: {
|
|
537
|
+
readonly version: number;
|
|
538
|
+
readonly baseVersion: number;
|
|
539
|
+
readonly description?: string;
|
|
540
|
+
}): Promise<RemoteConfigPublishResult | {
|
|
541
|
+
readonly status: "not_found";
|
|
542
|
+
}>;
|
|
543
|
+
/** One page of published versions, newest first. */
|
|
544
|
+
listVersions(input?: {
|
|
545
|
+
readonly limit?: number;
|
|
546
|
+
readonly cursor?: string;
|
|
547
|
+
}): Promise<RemoteConfigVersionsPage>;
|
|
548
|
+
/** One version with its template, or null. */
|
|
549
|
+
getVersion(version: number): Promise<RemoteConfigVersionDetail | null>;
|
|
550
|
+
/**
|
|
551
|
+
* What a device with this context receives from `GET /remote-config`, at
|
|
552
|
+
* `context.now` or, without it, now.
|
|
553
|
+
*/
|
|
554
|
+
resolve(context: RemoteConfigEvaluationContext): Promise<RemoteConfigFetchResponse>;
|
|
555
|
+
}
|
|
556
|
+
//#endregion
|
|
369
557
|
//#region src/plugins.d.ts
|
|
370
558
|
/**
|
|
371
|
-
* The plugins the managed Cloudflare server runs: Insights,
|
|
372
|
-
* client route. Its Worker imports this list.
|
|
559
|
+
* The plugins the managed Cloudflare server runs: Insights, API keys on every
|
|
560
|
+
* client route, and Remote Config. Its Worker imports this list.
|
|
373
561
|
*/
|
|
374
562
|
declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPlugin<"insights", {
|
|
375
563
|
readonly bundle_events: import("@hot-updater/plugin-core").TableDefinition<{
|
|
@@ -811,6 +999,54 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
|
|
|
811
999
|
};
|
|
812
1000
|
}, {
|
|
813
1001
|
readonly clientAuth: true;
|
|
814
|
-
}
|
|
1002
|
+
}>, import("@hot-updater/plugin-core").HotUpdaterPlugin<"remoteConfig", {
|
|
1003
|
+
readonly remote_config_active: import("@hot-updater/plugin-core").TableDefinition<{
|
|
1004
|
+
readonly id: {
|
|
1005
|
+
readonly type: "string";
|
|
1006
|
+
readonly maxLength: 16;
|
|
1007
|
+
};
|
|
1008
|
+
readonly version: {
|
|
1009
|
+
readonly type: "integer";
|
|
1010
|
+
};
|
|
1011
|
+
readonly template: {
|
|
1012
|
+
readonly type: "json";
|
|
1013
|
+
};
|
|
1014
|
+
readonly updated_at_ms: {
|
|
1015
|
+
readonly type: "integer";
|
|
1016
|
+
};
|
|
1017
|
+
}, {}, {}, readonly ["id"]>;
|
|
1018
|
+
readonly remote_config_versions: import("@hot-updater/plugin-core").TableDefinition<{
|
|
1019
|
+
readonly version: {
|
|
1020
|
+
readonly type: "integer";
|
|
1021
|
+
};
|
|
1022
|
+
readonly template: {
|
|
1023
|
+
readonly type: "json";
|
|
1024
|
+
};
|
|
1025
|
+
readonly description: {
|
|
1026
|
+
readonly type: "string";
|
|
1027
|
+
readonly maxLength: 256;
|
|
1028
|
+
readonly required: false;
|
|
1029
|
+
};
|
|
1030
|
+
readonly update_type: {
|
|
1031
|
+
readonly type: "string";
|
|
1032
|
+
readonly maxLength: 16;
|
|
1033
|
+
};
|
|
1034
|
+
readonly rollback_source: {
|
|
1035
|
+
readonly type: "integer";
|
|
1036
|
+
readonly required: false;
|
|
1037
|
+
};
|
|
1038
|
+
readonly created_at_ms: {
|
|
1039
|
+
readonly type: "integer";
|
|
1040
|
+
};
|
|
1041
|
+
}, {}, {
|
|
1042
|
+
readonly byVersion: {
|
|
1043
|
+
readonly eq: readonly [];
|
|
1044
|
+
readonly sort: readonly ["version"];
|
|
1045
|
+
};
|
|
1046
|
+
}, readonly ["version"]>;
|
|
1047
|
+
}, {
|
|
1048
|
+
api: RemoteConfigApi;
|
|
1049
|
+
endpoints: readonly import("@hot-updater/plugin-core").PluginEndpoint[];
|
|
1050
|
+
}, {}>];
|
|
815
1051
|
//#endregion
|
|
816
1052
|
export { D1DatabaseConfig, D1ExecutionError, type R2S3StorageConfig, R2StorageConfig, d1Database, plugins, r2Storage };
|
package/dist/index.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, S3Client } fr
|
|
|
4
4
|
import { Upload } from "@aws-sdk/lib-storage";
|
|
5
5
|
import { apiKeys } from "@hot-updater/server/plugins/api-keys";
|
|
6
6
|
import { insights } from "@hot-updater/server/plugins/insights";
|
|
7
|
+
import { remoteConfig } from "@hot-updater/server/plugins/remote-config";
|
|
7
8
|
//#region src/d1Executor.ts
|
|
8
9
|
var D1ExecutionError = class extends Error {
|
|
9
10
|
name = "D1ExecutionError";
|
|
@@ -203,9 +204,13 @@ const r2Storage = (config) => {
|
|
|
203
204
|
//#endregion
|
|
204
205
|
//#region src/plugins.ts
|
|
205
206
|
/**
|
|
206
|
-
* The plugins the managed Cloudflare server runs: Insights,
|
|
207
|
-
* client route. Its Worker imports this list.
|
|
207
|
+
* The plugins the managed Cloudflare server runs: Insights, API keys on every
|
|
208
|
+
* client route, and Remote Config. Its Worker imports this list.
|
|
208
209
|
*/
|
|
209
|
-
const plugins = [
|
|
210
|
+
const plugins = [
|
|
211
|
+
insights(),
|
|
212
|
+
apiKeys(),
|
|
213
|
+
remoteConfig()
|
|
214
|
+
];
|
|
210
215
|
//#endregion
|
|
211
216
|
export { D1ExecutionError, d1Database, plugins, r2Storage };
|
package/dist/init/index.cjs
CHANGED
|
@@ -32,6 +32,7 @@ cloudflare = __toESM(cloudflare, 1);
|
|
|
32
32
|
let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
33
33
|
let _hot_updater_server_plugins_api_keys = require("@hot-updater/server/plugins/api-keys");
|
|
34
34
|
let _hot_updater_server_plugins_insights = require("@hot-updater/server/plugins/insights");
|
|
35
|
+
let _hot_updater_server_plugins_remote_config = require("@hot-updater/server/plugins/remote-config");
|
|
35
36
|
let node_url = require("node:url");
|
|
36
37
|
let node_child_process = require("node:child_process");
|
|
37
38
|
let node_string_decoder = require("node:string_decoder");
|
|
@@ -163,10 +164,14 @@ const d1Database = (config) => {
|
|
|
163
164
|
//#endregion
|
|
164
165
|
//#region src/plugins.ts
|
|
165
166
|
/**
|
|
166
|
-
* The plugins the managed Cloudflare server runs: Insights,
|
|
167
|
-
* client route. Its Worker imports this list.
|
|
167
|
+
* The plugins the managed Cloudflare server runs: Insights, API keys on every
|
|
168
|
+
* client route, and Remote Config. Its Worker imports this list.
|
|
168
169
|
*/
|
|
169
|
-
const plugins = [
|
|
170
|
+
const plugins = [
|
|
171
|
+
(0, _hot_updater_server_plugins_insights.insights)(),
|
|
172
|
+
(0, _hot_updater_server_plugins_api_keys.apiKeys)(),
|
|
173
|
+
(0, _hot_updater_server_plugins_remote_config.remoteConfig)()
|
|
174
|
+
];
|
|
170
175
|
//#endregion
|
|
171
176
|
//#region ../../node_modules/.pnpm/is-plain-obj@4.1.0/node_modules/is-plain-obj/index.js
|
|
172
177
|
function isPlainObject(value) {
|
package/dist/init/index.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import Cloudflare$1, { Cloudflare } from "cloudflare";
|
|
|
6
6
|
import { WRITE_GUARD_TABLE, coreSettings, coreTarget, createEngineDatabase, createSqlAdapter, createTableStatements, generateEngineSql } from "@hot-updater/plugin-core";
|
|
7
7
|
import { apiKeys } from "@hot-updater/server/plugins/api-keys";
|
|
8
8
|
import { insights } from "@hot-updater/server/plugins/insights";
|
|
9
|
+
import { remoteConfig } from "@hot-updater/server/plugins/remote-config";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
10
11
|
import { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
|
|
11
12
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -155,10 +156,14 @@ const d1Database = (config) => {
|
|
|
155
156
|
//#endregion
|
|
156
157
|
//#region src/plugins.ts
|
|
157
158
|
/**
|
|
158
|
-
* The plugins the managed Cloudflare server runs: Insights,
|
|
159
|
-
* client route. Its Worker imports this list.
|
|
159
|
+
* The plugins the managed Cloudflare server runs: Insights, API keys on every
|
|
160
|
+
* client route, and Remote Config. Its Worker imports this list.
|
|
160
161
|
*/
|
|
161
|
-
const plugins = [
|
|
162
|
+
const plugins = [
|
|
163
|
+
insights(),
|
|
164
|
+
apiKeys(),
|
|
165
|
+
remoteConfig()
|
|
166
|
+
];
|
|
162
167
|
//#endregion
|
|
163
168
|
//#region ../../node_modules/.pnpm/is-plain-obj@4.1.0/node_modules/is-plain-obj/index.js
|
|
164
169
|
function isPlainObject(value) {
|
package/dist/worker/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
3
3
|
let _hot_updater_server_plugins_api_keys = require("@hot-updater/server/plugins/api-keys");
|
|
4
4
|
let _hot_updater_server_plugins_insights = require("@hot-updater/server/plugins/insights");
|
|
5
|
+
let _hot_updater_server_plugins_remote_config = require("@hot-updater/server/plugins/remote-config");
|
|
5
6
|
//#region src/d1Executor.ts
|
|
6
7
|
var D1ExecutionError = class extends Error {
|
|
7
8
|
name = "D1ExecutionError";
|
|
@@ -63,10 +64,14 @@ const d1Database = (database) => createD1Database({
|
|
|
63
64
|
//#endregion
|
|
64
65
|
//#region src/plugins.ts
|
|
65
66
|
/**
|
|
66
|
-
* The plugins the managed Cloudflare server runs: Insights,
|
|
67
|
-
* client route. Its Worker imports this list.
|
|
67
|
+
* The plugins the managed Cloudflare server runs: Insights, API keys on every
|
|
68
|
+
* client route, and Remote Config. Its Worker imports this list.
|
|
68
69
|
*/
|
|
69
|
-
const plugins = [
|
|
70
|
+
const plugins = [
|
|
71
|
+
(0, _hot_updater_server_plugins_insights.insights)(),
|
|
72
|
+
(0, _hot_updater_server_plugins_api_keys.apiKeys)(),
|
|
73
|
+
(0, _hot_updater_server_plugins_remote_config.remoteConfig)()
|
|
74
|
+
];
|
|
70
75
|
//#endregion
|
|
71
76
|
//#region src/r2WorkerStorage.ts
|
|
72
77
|
const r2WorkerStorage = (config) => {
|