@hot-updater/plugin-remote-config 0.0.0-stage → 1.0.0-rc.42

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,85 @@
1
+ import { RemoteConfigFetchResponse } from "../shared/wire.cjs";
2
+ import { RemoteConfigEvaluationContext, RemoteConfigTemplate } from "./template.cjs";
3
+ import "@hot-updater/plugin-core";
4
+ //#region src/server/api.d.ts
5
+ /** A published version's record, without its template. */
6
+ interface RemoteConfigVersion {
7
+ readonly version: number;
8
+ readonly description: string | null;
9
+ /** `ROLLBACK` when the version copies `rollbackSource`. */
10
+ readonly updateType: "PUBLISH" | "ROLLBACK";
11
+ readonly rollbackSource: number | null;
12
+ readonly createdAtMs: number;
13
+ }
14
+ interface RemoteConfigVersionDetail extends RemoteConfigVersion {
15
+ readonly template: RemoteConfigTemplate;
16
+ }
17
+ /** The template devices receive now. */
18
+ interface RemoteConfigActive {
19
+ /** 0, with an empty template, before the first publish. */
20
+ readonly version: number;
21
+ readonly template: RemoteConfigTemplate;
22
+ readonly updatedAtMs: number | null;
23
+ }
24
+ /**
25
+ * How a publish or a rollback ended: `conflict` when another publish moved
26
+ * the active version past `baseVersion` first, so a stale edit never
27
+ * replaces a newer template.
28
+ */
29
+ type RemoteConfigPublishResult = {
30
+ readonly status: "published";
31
+ readonly version: RemoteConfigVersion;
32
+ } | {
33
+ readonly status: "conflict";
34
+ readonly currentVersion: number;
35
+ };
36
+ interface RemoteConfigVersionsPage {
37
+ /** Newest first. */
38
+ readonly versions: readonly RemoteConfigVersion[];
39
+ /** Pass as `cursor` for the next page; absent on the last one. */
40
+ readonly next?: string;
41
+ }
42
+ /** `hotUpdater.api.remoteConfig`. */
43
+ interface RemoteConfigApi {
44
+ /** The active template and its version. */
45
+ getActive(): Promise<RemoteConfigActive>;
46
+ /**
47
+ * Validates and publishes a template as the next version. Throws
48
+ * `RemoteConfigValidationError` for an invalid template.
49
+ */
50
+ publish(input: {
51
+ readonly template: unknown;
52
+ /** The version the edit started from; 0 before the first publish. */
53
+ readonly baseVersion: number;
54
+ readonly description?: string;
55
+ }): Promise<RemoteConfigPublishResult>;
56
+ /** Publishes a copy of an earlier version; `not_found` when it does not exist. */
57
+ rollback(input: {
58
+ readonly version: number;
59
+ readonly baseVersion: number;
60
+ readonly description?: string;
61
+ }): Promise<RemoteConfigPublishResult | {
62
+ readonly status: "not_found";
63
+ }>;
64
+ /** One page of published versions, newest first. */
65
+ listVersions(input?: {
66
+ readonly limit?: number;
67
+ readonly cursor?: string;
68
+ }): Promise<RemoteConfigVersionsPage>;
69
+ /** One version with its template, or null. */
70
+ getVersion(version: number): Promise<RemoteConfigVersionDetail | null>;
71
+ /**
72
+ * What a device with this context receives from `GET /remote-config`, at
73
+ * `context.now` or, without it, now.
74
+ */
75
+ resolve(context: RemoteConfigEvaluationContext): Promise<RemoteConfigFetchResponse>;
76
+ }
77
+ /**
78
+ * An argument the API refuses, such as a negative `baseVersion`; the admin
79
+ * routes answer it with `400`.
80
+ */
81
+ declare class RemoteConfigInputError extends TypeError {
82
+ readonly name = "RemoteConfigInputError";
83
+ }
84
+ //#endregion
85
+ export { RemoteConfigActive, RemoteConfigApi, RemoteConfigInputError, RemoteConfigPublishResult, RemoteConfigVersion, RemoteConfigVersionDetail, RemoteConfigVersionsPage };
@@ -0,0 +1,85 @@
1
+ import { RemoteConfigFetchResponse } from "../shared/wire.mjs";
2
+ import { RemoteConfigEvaluationContext, RemoteConfigTemplate } from "./template.mjs";
3
+ import "@hot-updater/plugin-core";
4
+ //#region src/server/api.d.ts
5
+ /** A published version's record, without its template. */
6
+ interface RemoteConfigVersion {
7
+ readonly version: number;
8
+ readonly description: string | null;
9
+ /** `ROLLBACK` when the version copies `rollbackSource`. */
10
+ readonly updateType: "PUBLISH" | "ROLLBACK";
11
+ readonly rollbackSource: number | null;
12
+ readonly createdAtMs: number;
13
+ }
14
+ interface RemoteConfigVersionDetail extends RemoteConfigVersion {
15
+ readonly template: RemoteConfigTemplate;
16
+ }
17
+ /** The template devices receive now. */
18
+ interface RemoteConfigActive {
19
+ /** 0, with an empty template, before the first publish. */
20
+ readonly version: number;
21
+ readonly template: RemoteConfigTemplate;
22
+ readonly updatedAtMs: number | null;
23
+ }
24
+ /**
25
+ * How a publish or a rollback ended: `conflict` when another publish moved
26
+ * the active version past `baseVersion` first, so a stale edit never
27
+ * replaces a newer template.
28
+ */
29
+ type RemoteConfigPublishResult = {
30
+ readonly status: "published";
31
+ readonly version: RemoteConfigVersion;
32
+ } | {
33
+ readonly status: "conflict";
34
+ readonly currentVersion: number;
35
+ };
36
+ interface RemoteConfigVersionsPage {
37
+ /** Newest first. */
38
+ readonly versions: readonly RemoteConfigVersion[];
39
+ /** Pass as `cursor` for the next page; absent on the last one. */
40
+ readonly next?: string;
41
+ }
42
+ /** `hotUpdater.api.remoteConfig`. */
43
+ interface RemoteConfigApi {
44
+ /** The active template and its version. */
45
+ getActive(): Promise<RemoteConfigActive>;
46
+ /**
47
+ * Validates and publishes a template as the next version. Throws
48
+ * `RemoteConfigValidationError` for an invalid template.
49
+ */
50
+ publish(input: {
51
+ readonly template: unknown;
52
+ /** The version the edit started from; 0 before the first publish. */
53
+ readonly baseVersion: number;
54
+ readonly description?: string;
55
+ }): Promise<RemoteConfigPublishResult>;
56
+ /** Publishes a copy of an earlier version; `not_found` when it does not exist. */
57
+ rollback(input: {
58
+ readonly version: number;
59
+ readonly baseVersion: number;
60
+ readonly description?: string;
61
+ }): Promise<RemoteConfigPublishResult | {
62
+ readonly status: "not_found";
63
+ }>;
64
+ /** One page of published versions, newest first. */
65
+ listVersions(input?: {
66
+ readonly limit?: number;
67
+ readonly cursor?: string;
68
+ }): Promise<RemoteConfigVersionsPage>;
69
+ /** One version with its template, or null. */
70
+ getVersion(version: number): Promise<RemoteConfigVersionDetail | null>;
71
+ /**
72
+ * What a device with this context receives from `GET /remote-config`, at
73
+ * `context.now` or, without it, now.
74
+ */
75
+ resolve(context: RemoteConfigEvaluationContext): Promise<RemoteConfigFetchResponse>;
76
+ }
77
+ /**
78
+ * An argument the API refuses, such as a negative `baseVersion`; the admin
79
+ * routes answer it with `400`.
80
+ */
81
+ declare class RemoteConfigInputError extends TypeError {
82
+ readonly name = "RemoteConfigInputError";
83
+ }
84
+ //#endregion
85
+ export { RemoteConfigActive, RemoteConfigApi, RemoteConfigInputError, RemoteConfigPublishResult, RemoteConfigVersion, RemoteConfigVersionDetail, RemoteConfigVersionsPage };
@@ -0,0 +1,174 @@
1
+ import { EMPTY_REMOTE_CONFIG_TEMPLATE, resolveRemoteConfigValues, validateRemoteConfigTemplate } from "./template.mjs";
2
+ import { DatabaseConstraintError } from "@hot-updater/plugin-core";
3
+ //#region src/server/api.ts
4
+ const ACTIVE_ID = "active";
5
+ /** How long a server answers fetches from the active template it read last. */
6
+ const ACTIVE_CACHE_TTL_MS = 5e3;
7
+ const MAX_DESCRIPTION_LENGTH = 256;
8
+ const MAX_VERSIONS_PAGE = 100;
9
+ /**
10
+ * An argument the API refuses, such as a negative `baseVersion`; the admin
11
+ * routes answer it with `400`.
12
+ */
13
+ var RemoteConfigInputError = class extends TypeError {
14
+ name = "RemoteConfigInputError";
15
+ };
16
+ const normalizeDescription = (description) => {
17
+ if (description === void 0 || description === null) return null;
18
+ if (typeof description !== "string") throw new RemoteConfigInputError("A version description is text.");
19
+ const trimmed = description.trim();
20
+ if (trimmed.length > MAX_DESCRIPTION_LENGTH) throw new RemoteConfigInputError(`A version description holds up to ${MAX_DESCRIPTION_LENGTH} characters.`);
21
+ return trimmed.length === 0 ? null : trimmed;
22
+ };
23
+ const requireVersionNumber = (value, name) => {
24
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new RemoteConfigInputError(`${name} must be a non-negative integer.`);
25
+ return value;
26
+ };
27
+ /** A template as its `json` column stores it; validation made it plain JSON. */
28
+ const toColumn = (template) => template;
29
+ const fromColumn = (value) => value;
30
+ const toVersion = (row) => Object.freeze({
31
+ version: row.version,
32
+ description: row.description,
33
+ updateType: row.update_type === "ROLLBACK" ? "ROLLBACK" : "PUBLISH",
34
+ rollbackSource: row.rollback_source,
35
+ createdAtMs: row.created_at_ms
36
+ });
37
+ /** Remote Config's API over its tables. */
38
+ const createRemoteConfigApi = ({ db, now }) => {
39
+ let cached = null;
40
+ let loading = null;
41
+ let generation = 0;
42
+ const readActive = async () => {
43
+ const row = await db.findOne("remote_config_active", { id: ACTIVE_ID });
44
+ return row === null ? {
45
+ version: 0,
46
+ template: EMPTY_REMOTE_CONFIG_TEMPLATE,
47
+ updatedAtMs: null
48
+ } : {
49
+ version: row.version,
50
+ template: fromColumn(row.template),
51
+ updatedAtMs: row.updated_at_ms
52
+ };
53
+ };
54
+ /** The active template for fetches: one read per server every few seconds at most. */
55
+ const servedActive = () => {
56
+ if (cached !== null && cached.expiresAt > now()) return Promise.resolve(cached.active);
57
+ if (loading !== null) return loading;
58
+ const started = generation;
59
+ const read = readActive().then((active) => {
60
+ if (generation === started) cached = {
61
+ active,
62
+ expiresAt: now() + ACTIVE_CACHE_TTL_MS
63
+ };
64
+ return active;
65
+ }).finally(() => {
66
+ if (loading === read) loading = null;
67
+ });
68
+ loading = read;
69
+ return read;
70
+ };
71
+ const commit = async (input) => {
72
+ const attempt = () => db.transaction(async (tx) => {
73
+ const active = await tx.findOne("remote_config_active", { id: ACTIVE_ID });
74
+ const currentVersion = active?.version ?? 0;
75
+ if (currentVersion !== input.baseVersion) return {
76
+ status: "conflict",
77
+ currentVersion
78
+ };
79
+ const version = currentVersion + 1;
80
+ const at = now();
81
+ const row = {
82
+ version,
83
+ template: toColumn(input.template),
84
+ description: input.description,
85
+ update_type: input.rollbackSource === null ? "PUBLISH" : "ROLLBACK",
86
+ rollback_source: input.rollbackSource,
87
+ created_at_ms: at
88
+ };
89
+ tx.create("remote_config_versions", row);
90
+ if (active === null) tx.create("remote_config_active", {
91
+ id: ACTIVE_ID,
92
+ version,
93
+ template: toColumn(input.template),
94
+ updated_at_ms: at
95
+ });
96
+ else tx.update("remote_config_active", active, {
97
+ version,
98
+ template: toColumn(input.template),
99
+ updated_at_ms: at
100
+ });
101
+ return {
102
+ status: "published",
103
+ version: toVersion(row)
104
+ };
105
+ });
106
+ try {
107
+ const result = await attempt();
108
+ if (result.status === "published") {
109
+ generation += 1;
110
+ cached = null;
111
+ loading = null;
112
+ }
113
+ return result;
114
+ } catch (error) {
115
+ if (error instanceof DatabaseConstraintError && (error.reason === "exists" || error.reason === "unique")) return {
116
+ status: "conflict",
117
+ currentVersion: (await readActive()).version
118
+ };
119
+ throw error;
120
+ }
121
+ };
122
+ return Object.freeze({
123
+ getActive: readActive,
124
+ publish: async ({ template, baseVersion, description }) => commit({
125
+ template: validateRemoteConfigTemplate(template),
126
+ baseVersion: requireVersionNumber(baseVersion, "baseVersion"),
127
+ description: normalizeDescription(description),
128
+ rollbackSource: null
129
+ }),
130
+ rollback: async ({ version, baseVersion, description }) => {
131
+ const source = await db.findOne("remote_config_versions", { version: requireVersionNumber(version, "version") });
132
+ if (source === null) return { status: "not_found" };
133
+ return commit({
134
+ template: fromColumn(source.template),
135
+ baseVersion: requireVersionNumber(baseVersion, "baseVersion"),
136
+ description: normalizeDescription(description) ?? `Rollback to version ${source.version}`,
137
+ rollbackSource: source.version
138
+ });
139
+ },
140
+ listVersions: async ({ limit = 20, cursor } = {}) => {
141
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_VERSIONS_PAGE) throw new RemoteConfigInputError(`limit must be an integer from 1 to ${MAX_VERSIONS_PAGE}.`);
142
+ const page = await db.findMany("remote_config_versions", {
143
+ index: "byVersion",
144
+ where: {},
145
+ order: "desc",
146
+ limit,
147
+ ...cursor === void 0 ? {} : { cursor }
148
+ });
149
+ return Object.freeze({
150
+ versions: Object.freeze(page.rows.map(toVersion)),
151
+ ...page.next === void 0 ? {} : { next: page.next }
152
+ });
153
+ },
154
+ getVersion: async (version) => {
155
+ const row = await db.findOne("remote_config_versions", { version: requireVersionNumber(version, "version") });
156
+ return row === null ? null : Object.freeze({
157
+ ...toVersion(row),
158
+ template: fromColumn(row.template)
159
+ });
160
+ },
161
+ resolve: async (context) => {
162
+ const active = await servedActive();
163
+ return {
164
+ version: active.version,
165
+ values: resolveRemoteConfigValues(active.template, {
166
+ ...context,
167
+ now: context.now ?? now()
168
+ })
169
+ };
170
+ }
171
+ });
172
+ };
173
+ //#endregion
174
+ export { RemoteConfigInputError, createRemoteConfigApi };
@@ -0,0 +1,50 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_template = require("./template.cjs");
3
+ const require_api = require("./api.cjs");
4
+ const require_official = require("./official.cjs");
5
+ const require_routes = require("./routes.cjs");
6
+ const require_schema = require("./schema.cjs");
7
+ const require_admin = require("./admin.cjs");
8
+ let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
9
+ //#region src/server/index.ts
10
+ /**
11
+ * Remote Config: parameters whose values the server picks per device by
12
+ * conditions, published as versioned templates. Devices fetch their values
13
+ * from `GET /remote-config` with the `remoteConfig()` client plugin; the
14
+ * Console and `hotUpdater.api.remoteConfig` edit, publish, and roll back
15
+ * templates.
16
+ */
17
+ const remoteConfig = () => {
18
+ const plugin = (0, _hot_updater_plugin_core.definePlugin)({
19
+ id: "remoteConfig",
20
+ namespace: false,
21
+ schemaVersion: "1.0.0",
22
+ schema: require_schema.remoteConfigSchema,
23
+ init: ({ db, now }) => {
24
+ const api = require_api.createRemoteConfigApi({
25
+ db,
26
+ now
27
+ });
28
+ return {
29
+ api,
30
+ endpoints: require_routes.createRemoteConfigEndpoints(api)
31
+ };
32
+ }
33
+ });
34
+ return require_official.markOfficial(plugin);
35
+ };
36
+ //#endregion
37
+ exports.EMPTY_REMOTE_CONFIG_TEMPLATE = require_template.EMPTY_REMOTE_CONFIG_TEMPLATE;
38
+ exports.REMOTE_CONFIG_MAX_CONDITIONS = require_template.REMOTE_CONFIG_MAX_CONDITIONS;
39
+ exports.REMOTE_CONFIG_MAX_PARAMETERS = require_template.REMOTE_CONFIG_MAX_PARAMETERS;
40
+ exports.REMOTE_CONFIG_MAX_TEMPLATE_LENGTH = require_template.REMOTE_CONFIG_MAX_TEMPLATE_LENGTH;
41
+ exports.RemoteConfigInputError = require_api.RemoteConfigInputError;
42
+ exports.RemoteConfigValidationError = require_template.RemoteConfigValidationError;
43
+ exports.createRemoteConfigAdminApi = require_admin.createRemoteConfigAdminApi;
44
+ exports.evaluateRemoteConfig = require_template.evaluateRemoteConfig;
45
+ exports.matchesRemoteConfigCondition = require_template.matchesRemoteConfigCondition;
46
+ exports.matchesRemoteConfigRule = require_template.matchesRemoteConfigRule;
47
+ exports.remoteConfig = remoteConfig;
48
+ exports.remoteConfigSchema = require_schema.remoteConfigSchema;
49
+ exports.resolveRemoteConfigValues = require_template.resolveRemoteConfigValues;
50
+ exports.validateRemoteConfigTemplate = require_template.validateRemoteConfigTemplate;
@@ -0,0 +1,64 @@
1
+ import { RemoteConfigDeviceContext, RemoteConfigFetchResponse } from "../shared/wire.cjs";
2
+ import { RemoteConfigSchema, remoteConfigSchema } from "./schema.cjs";
3
+ import { EMPTY_REMOTE_CONFIG_TEMPLATE, REMOTE_CONFIG_MAX_CONDITIONS, REMOTE_CONFIG_MAX_PARAMETERS, REMOTE_CONFIG_MAX_TEMPLATE_LENGTH, RemoteConfigCondition, RemoteConfigEvaluatedParameter, RemoteConfigEvaluationContext, RemoteConfigParameter, RemoteConfigParameterValue, RemoteConfigRule, RemoteConfigRuleType, RemoteConfigTemplate, RemoteConfigTemplateIssue, RemoteConfigValidationError, RemoteConfigValueType, evaluateRemoteConfig, matchesRemoteConfigCondition, matchesRemoteConfigRule, resolveRemoteConfigValues, validateRemoteConfigTemplate } from "./template.cjs";
4
+ import { RemoteConfigActive, RemoteConfigApi, RemoteConfigInputError, RemoteConfigPublishResult, RemoteConfigVersion, RemoteConfigVersionDetail, RemoteConfigVersionsPage } from "./api.cjs";
5
+ import { RemoteConfigAdminApi, RemoteConfigAdminApiOptions, createRemoteConfigAdminApi } from "./admin.cjs";
6
+ //#region src/server/index.d.ts
7
+ /**
8
+ * Remote Config: parameters whose values the server picks per device by
9
+ * conditions, published as versioned templates. Devices fetch their values
10
+ * from `GET /remote-config` with the `remoteConfig()` client plugin; the
11
+ * Console and `hotUpdater.api.remoteConfig` edit, publish, and roll back
12
+ * templates.
13
+ */
14
+ declare const remoteConfig: () => import("@hot-updater/plugin-core").HotUpdaterPlugin<"remoteConfig", {
15
+ readonly remote_config_active: import("@hot-updater/plugin-core").TableDefinition<{
16
+ readonly id: {
17
+ readonly type: "string";
18
+ readonly maxLength: 16;
19
+ };
20
+ readonly version: {
21
+ readonly type: "integer";
22
+ };
23
+ readonly template: {
24
+ readonly type: "json";
25
+ };
26
+ readonly updated_at_ms: {
27
+ readonly type: "integer";
28
+ };
29
+ }, {}, {}, readonly ["id"]>;
30
+ readonly remote_config_versions: import("@hot-updater/plugin-core").TableDefinition<{
31
+ readonly version: {
32
+ readonly type: "integer";
33
+ };
34
+ readonly template: {
35
+ readonly type: "json";
36
+ };
37
+ readonly description: {
38
+ readonly type: "string";
39
+ readonly maxLength: 256;
40
+ readonly required: false;
41
+ };
42
+ readonly update_type: {
43
+ readonly type: "string";
44
+ readonly maxLength: 16;
45
+ };
46
+ readonly rollback_source: {
47
+ readonly type: "integer";
48
+ readonly required: false;
49
+ };
50
+ readonly created_at_ms: {
51
+ readonly type: "integer";
52
+ };
53
+ }, {}, {
54
+ readonly byVersion: {
55
+ readonly eq: readonly [];
56
+ readonly sort: readonly ["version"];
57
+ };
58
+ }, readonly ["version"]>;
59
+ }, {
60
+ api: RemoteConfigApi;
61
+ endpoints: readonly import("@hot-updater/plugin-core").PluginEndpoint[];
62
+ }, {}>;
63
+ //#endregion
64
+ export { EMPTY_REMOTE_CONFIG_TEMPLATE, REMOTE_CONFIG_MAX_CONDITIONS, REMOTE_CONFIG_MAX_PARAMETERS, REMOTE_CONFIG_MAX_TEMPLATE_LENGTH, type RemoteConfigActive, type RemoteConfigAdminApi, type RemoteConfigAdminApiOptions, type RemoteConfigApi, type RemoteConfigCondition, type RemoteConfigDeviceContext, type RemoteConfigEvaluatedParameter, type RemoteConfigEvaluationContext, type RemoteConfigFetchResponse, RemoteConfigInputError, type RemoteConfigParameter, type RemoteConfigParameterValue, type RemoteConfigPublishResult, type RemoteConfigRule, type RemoteConfigRuleType, type RemoteConfigSchema, type RemoteConfigTemplate, type RemoteConfigTemplateIssue, RemoteConfigValidationError, type RemoteConfigValueType, type RemoteConfigVersion, type RemoteConfigVersionDetail, type RemoteConfigVersionsPage, createRemoteConfigAdminApi, evaluateRemoteConfig, matchesRemoteConfigCondition, matchesRemoteConfigRule, remoteConfig, remoteConfigSchema, resolveRemoteConfigValues, validateRemoteConfigTemplate };
@@ -0,0 +1,64 @@
1
+ import { RemoteConfigDeviceContext, RemoteConfigFetchResponse } from "../shared/wire.mjs";
2
+ import { RemoteConfigSchema, remoteConfigSchema } from "./schema.mjs";
3
+ import { EMPTY_REMOTE_CONFIG_TEMPLATE, REMOTE_CONFIG_MAX_CONDITIONS, REMOTE_CONFIG_MAX_PARAMETERS, REMOTE_CONFIG_MAX_TEMPLATE_LENGTH, RemoteConfigCondition, RemoteConfigEvaluatedParameter, RemoteConfigEvaluationContext, RemoteConfigParameter, RemoteConfigParameterValue, RemoteConfigRule, RemoteConfigRuleType, RemoteConfigTemplate, RemoteConfigTemplateIssue, RemoteConfigValidationError, RemoteConfigValueType, evaluateRemoteConfig, matchesRemoteConfigCondition, matchesRemoteConfigRule, resolveRemoteConfigValues, validateRemoteConfigTemplate } from "./template.mjs";
4
+ import { RemoteConfigActive, RemoteConfigApi, RemoteConfigInputError, RemoteConfigPublishResult, RemoteConfigVersion, RemoteConfigVersionDetail, RemoteConfigVersionsPage } from "./api.mjs";
5
+ import { RemoteConfigAdminApi, RemoteConfigAdminApiOptions, createRemoteConfigAdminApi } from "./admin.mjs";
6
+ //#region src/server/index.d.ts
7
+ /**
8
+ * Remote Config: parameters whose values the server picks per device by
9
+ * conditions, published as versioned templates. Devices fetch their values
10
+ * from `GET /remote-config` with the `remoteConfig()` client plugin; the
11
+ * Console and `hotUpdater.api.remoteConfig` edit, publish, and roll back
12
+ * templates.
13
+ */
14
+ declare const remoteConfig: () => import("@hot-updater/plugin-core").HotUpdaterPlugin<"remoteConfig", {
15
+ readonly remote_config_active: import("@hot-updater/plugin-core").TableDefinition<{
16
+ readonly id: {
17
+ readonly type: "string";
18
+ readonly maxLength: 16;
19
+ };
20
+ readonly version: {
21
+ readonly type: "integer";
22
+ };
23
+ readonly template: {
24
+ readonly type: "json";
25
+ };
26
+ readonly updated_at_ms: {
27
+ readonly type: "integer";
28
+ };
29
+ }, {}, {}, readonly ["id"]>;
30
+ readonly remote_config_versions: import("@hot-updater/plugin-core").TableDefinition<{
31
+ readonly version: {
32
+ readonly type: "integer";
33
+ };
34
+ readonly template: {
35
+ readonly type: "json";
36
+ };
37
+ readonly description: {
38
+ readonly type: "string";
39
+ readonly maxLength: 256;
40
+ readonly required: false;
41
+ };
42
+ readonly update_type: {
43
+ readonly type: "string";
44
+ readonly maxLength: 16;
45
+ };
46
+ readonly rollback_source: {
47
+ readonly type: "integer";
48
+ readonly required: false;
49
+ };
50
+ readonly created_at_ms: {
51
+ readonly type: "integer";
52
+ };
53
+ }, {}, {
54
+ readonly byVersion: {
55
+ readonly eq: readonly [];
56
+ readonly sort: readonly ["version"];
57
+ };
58
+ }, readonly ["version"]>;
59
+ }, {
60
+ api: RemoteConfigApi;
61
+ endpoints: readonly import("@hot-updater/plugin-core").PluginEndpoint[];
62
+ }, {}>;
63
+ //#endregion
64
+ export { EMPTY_REMOTE_CONFIG_TEMPLATE, REMOTE_CONFIG_MAX_CONDITIONS, REMOTE_CONFIG_MAX_PARAMETERS, REMOTE_CONFIG_MAX_TEMPLATE_LENGTH, type RemoteConfigActive, type RemoteConfigAdminApi, type RemoteConfigAdminApiOptions, type RemoteConfigApi, type RemoteConfigCondition, type RemoteConfigDeviceContext, type RemoteConfigEvaluatedParameter, type RemoteConfigEvaluationContext, type RemoteConfigFetchResponse, RemoteConfigInputError, type RemoteConfigParameter, type RemoteConfigParameterValue, type RemoteConfigPublishResult, type RemoteConfigRule, type RemoteConfigRuleType, type RemoteConfigSchema, type RemoteConfigTemplate, type RemoteConfigTemplateIssue, RemoteConfigValidationError, type RemoteConfigValueType, type RemoteConfigVersion, type RemoteConfigVersionDetail, type RemoteConfigVersionsPage, createRemoteConfigAdminApi, evaluateRemoteConfig, matchesRemoteConfigCondition, matchesRemoteConfigRule, remoteConfig, remoteConfigSchema, resolveRemoteConfigValues, validateRemoteConfigTemplate };
@@ -0,0 +1,36 @@
1
+ import { EMPTY_REMOTE_CONFIG_TEMPLATE, REMOTE_CONFIG_MAX_CONDITIONS, REMOTE_CONFIG_MAX_PARAMETERS, REMOTE_CONFIG_MAX_TEMPLATE_LENGTH, RemoteConfigValidationError, evaluateRemoteConfig, matchesRemoteConfigCondition, matchesRemoteConfigRule, resolveRemoteConfigValues, validateRemoteConfigTemplate } from "./template.mjs";
2
+ import { RemoteConfigInputError, createRemoteConfigApi } from "./api.mjs";
3
+ import { markOfficial } from "./official.mjs";
4
+ import { createRemoteConfigEndpoints } from "./routes.mjs";
5
+ import { remoteConfigSchema } from "./schema.mjs";
6
+ import { createRemoteConfigAdminApi } from "./admin.mjs";
7
+ import { definePlugin } from "@hot-updater/plugin-core";
8
+ //#region src/server/index.ts
9
+ /**
10
+ * Remote Config: parameters whose values the server picks per device by
11
+ * conditions, published as versioned templates. Devices fetch their values
12
+ * from `GET /remote-config` with the `remoteConfig()` client plugin; the
13
+ * Console and `hotUpdater.api.remoteConfig` edit, publish, and roll back
14
+ * templates.
15
+ */
16
+ const remoteConfig = () => {
17
+ const plugin = definePlugin({
18
+ id: "remoteConfig",
19
+ namespace: false,
20
+ schemaVersion: "1.0.0",
21
+ schema: remoteConfigSchema,
22
+ init: ({ db, now }) => {
23
+ const api = createRemoteConfigApi({
24
+ db,
25
+ now
26
+ });
27
+ return {
28
+ api,
29
+ endpoints: createRemoteConfigEndpoints(api)
30
+ };
31
+ }
32
+ });
33
+ return markOfficial(plugin);
34
+ };
35
+ //#endregion
36
+ export { EMPTY_REMOTE_CONFIG_TEMPLATE, REMOTE_CONFIG_MAX_CONDITIONS, REMOTE_CONFIG_MAX_PARAMETERS, REMOTE_CONFIG_MAX_TEMPLATE_LENGTH, RemoteConfigInputError, RemoteConfigValidationError, createRemoteConfigAdminApi, evaluateRemoteConfig, matchesRemoteConfigCondition, matchesRemoteConfigRule, remoteConfig, remoteConfigSchema, resolveRemoteConfigValues, validateRemoteConfigTemplate };
@@ -0,0 +1,12 @@
1
+ //#region src/server/official.ts
2
+ /**
3
+ * The brand of Hot Updater's own plugins, a registered symbol that
4
+ * `@hot-updater/server` reads to reserve the id "remoteConfig" for this plugin.
5
+ * Each official plugin package keeps this copy, so none imports another's
6
+ * internals for it.
7
+ */
8
+ const OFFICIAL = Symbol.for("@hot-updater/server/official-plugin");
9
+ /** Marks a plugin one of Hot Updater's own factories made. */
10
+ const markOfficial = (plugin) => Object.defineProperty(plugin, OFFICIAL, { value: true });
11
+ //#endregion
12
+ exports.markOfficial = markOfficial;
@@ -0,0 +1,12 @@
1
+ //#region src/server/official.ts
2
+ /**
3
+ * The brand of Hot Updater's own plugins, a registered symbol that
4
+ * `@hot-updater/server` reads to reserve the id "remoteConfig" for this plugin.
5
+ * Each official plugin package keeps this copy, so none imports another's
6
+ * internals for it.
7
+ */
8
+ const OFFICIAL = Symbol.for("@hot-updater/server/official-plugin");
9
+ /** Marks a plugin one of Hot Updater's own factories made. */
10
+ const markOfficial = (plugin) => Object.defineProperty(plugin, OFFICIAL, { value: true });
11
+ //#endregion
12
+ export { markOfficial };