@hot-updater/console 1.0.0-rc.20 → 1.0.0-rc.21
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/.output/nitro.json +1 -1
- package/.output/public/assets/{ConsoleFeatureUnavailable-B9VYGA3N.js → ConsoleFeatureUnavailable-D0LPL1Gy.js} +1 -1
- package/.output/public/assets/{HashValueDisplay-D880zntR.js → HashValueDisplay-BmuB4FW-.js} +1 -1
- package/.output/public/assets/InsightsInfo--v4SC07c.js +1 -0
- package/.output/public/assets/InsightsPageHeader-dU83CAQY.js +1 -0
- package/.output/public/assets/{NotFoundPage-Dys8C0cW.js → NotFoundPage-B5mhGG2a.js} +1 -1
- package/.output/public/assets/api-D-vOVlQ4.js +1 -0
- package/.output/public/assets/api-keys-Csh8ARKU.js +1 -0
- package/.output/public/assets/api-keys-OMPUoA_A.js +1 -0
- package/.output/public/assets/arrow-left-Bwq9X1eS.js +1 -0
- package/.output/public/assets/arrow-up-right-BkOVn4if.js +1 -0
- package/.output/public/assets/{card-DzCPSRsh.js → card-C_7d6O9a.js} +1 -1
- package/.output/public/assets/console-features-api-C1jRZTGw.js +1 -0
- package/.output/public/assets/dialog-DE8wFN_P.js +1 -0
- package/.output/public/assets/{dist-Clb6cQ9l.js → dist-C7ENGzpS.js} +1 -1
- package/.output/public/assets/empty-CVd5NlQD.js +1 -0
- package/.output/public/assets/{field-Bou7shoO.js → field-C06q1iC9.js} +1 -1
- package/.output/public/assets/index-BeWlQtO6.js +11 -0
- package/.output/public/assets/{input-group-DVtWFUUF.js → input-group-Bi61biIA.js} +1 -1
- package/.output/public/assets/insights-BjnAtC0f.js +2 -0
- package/.output/public/assets/insights-D86m11yH.js +42 -0
- package/.output/public/assets/insights-DpYstMoY.js +1 -0
- package/.output/public/assets/{insights-recovery-rpc-wvdyM4LD.js → insights-recovery-rpc-BIm5_ALc.js} +1 -1
- package/.output/public/assets/{insights-usage-rpc-qUkNtrWr.js → insights-usage-rpc-VHj8vyBt.js} +1 -1
- package/.output/public/assets/insights_.distribution-ADcrX48c.js +2 -0
- package/.output/public/assets/insights_.distribution-CuuJUN6Y.js +1 -0
- package/.output/public/assets/{insights_.distribution-CItZw3-8.js → insights_.distribution-DJRjuXsO.js} +1 -1
- package/.output/public/assets/installations-BSBBzqWs.js +2 -0
- package/.output/public/assets/installations-DqIE6WcO.js +1 -0
- package/.output/public/assets/installations-UsELbDvw.js +1 -0
- package/.output/public/assets/{loader-circle-Ca7C9YY1.js → loader-circle-CnCBxzHp.js} +1 -1
- package/.output/public/assets/mutation-Csp7xUNh.js +1 -0
- package/.output/public/assets/preload-helper-B-cOmpLH.js +1 -0
- package/.output/public/assets/{refresh-cw-CVGqvy8Z.js → refresh-cw-FF1NtzlQ.js} +1 -1
- package/.output/public/assets/{routes-veKtDW39.js → routes-CoN9XHP_.js} +2 -2
- package/.output/public/assets/routes-DT1jB0Zi.js +1 -0
- package/.output/public/assets/sidebar-D8mJRF15.js +3 -0
- package/.output/public/assets/styles-Bwt7v6U5.css +2 -0
- package/.output/public/assets/table-WBgfvgXg.js +1 -0
- package/.output/public/assets/{useCompositeListItem-CF7vsfcA.js → useCompositeListItem-CloBmDwn.js} +1 -1
- package/.output/public/assets/useMutation-DO8cvsTx.js +1 -0
- package/.output/public/assets/{useOpenInteractionType-AnAMWYIY.js → useOpenInteractionType-ReGr9GzJ.js} +1 -1
- package/.output/public/assets/valueToPercent-BvPAi4bP.js +1 -0
- package/.output/server/{__23tanstack-start-server-fn-resolver-CUQLz7hs.mjs → __23tanstack-start-server-fn-resolver-DMRD4e4f.mjs} +34 -42
- package/.output/server/_libs/@tanstack/react-router+[...].mjs +3756 -4022
- package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +138 -95
- package/.output/server/_libs/tanstack__history.mjs +68 -41
- package/.output/server/_libs/tanstack__query-core.mjs +1618 -309
- package/.output/server/_libs/tanstack__react-query.mjs +255 -20
- package/.output/server/_ssr/{ConsoleFeatureUnavailable-DoxET2AT.mjs → ConsoleFeatureUnavailable-Du7jNPNS.mjs} +7 -7
- package/.output/server/_ssr/{HashValueDisplay-BplboHEb.mjs → HashValueDisplay-GQvFkXoi.mjs} +2 -2
- package/.output/server/_ssr/{InsightsInfo-o2YPd5Qc.mjs → InsightsInfo-Do7QSaFR.mjs} +2 -2
- package/.output/server/_ssr/{InsightsPageHeader-NExCJL1I.mjs → InsightsPageHeader-DSUWUWRc.mjs} +6 -6
- package/.output/server/_ssr/{NotFoundPage-Cd-A8ock.mjs → NotFoundPage-DsF4Vrhg.mjs} +2 -2
- package/.output/server/_ssr/{api-B1Ro0uys.mjs → api-BEiVs0H7.mjs} +9 -8
- package/.output/server/_ssr/{api-keys-Dvvy1sem.mjs → api-keys-BrvBWGRf.mjs} +2 -2
- package/.output/server/_ssr/{api-keys-CT3HG7KT.mjs → api-keys-K5v32nf7.mjs} +8 -8
- package/.output/server/_ssr/{api-keys-rpc-b1qTTFfG.mjs → api-keys-rpc-B96bBllm.mjs} +5 -5
- package/.output/server/_ssr/{api-rpc-Cbns2MGx.mjs → api-rpc-DcUe280B.mjs} +13 -55
- package/.output/server/_ssr/{auth-rpc-DChS7m9y.mjs → auth-rpc-C68W56DP.mjs} +6 -6
- package/.output/server/_ssr/{api-C0G9MTMI.mjs → auth.server-DM1J6NwR.mjs} +2815 -1308
- package/.output/server/_ssr/{bundleActivity-D17Pb_h5.mjs → bundleActivity-CpuY9CTP.mjs} +1 -1
- package/.output/server/_ssr/{card-DMbcvlPz.mjs → card-CPAEKx0w.mjs} +1 -1
- package/.output/server/_ssr/config-nsIcsHh8.mjs +15 -0
- package/.output/server/_ssr/config.server-DDCZk3Sq.mjs +36 -0
- package/.output/server/_ssr/{console-access-KpPv5lin.mjs → console-access-Da-2Qd7q.mjs} +3 -3
- package/.output/server/_ssr/{console-features-CsbyHYfK.mjs → console-features-Dzu2iUGD.mjs} +6 -6
- package/.output/server/_ssr/{console-features-api-B-KVuo4p.mjs → console-features-api-xAsSgFa_.mjs} +6 -6
- package/.output/server/_ssr/{console-features-rpc-Djyo5740.mjs → console-features-rpc-qARGpB24.mjs} +5 -5
- package/.output/server/_ssr/{createMiddleware-B_I3Ll4M.mjs → createMiddleware-BdBf_ezN.mjs} +41 -56
- package/.output/server/_ssr/{createServerRpc-DuNm5mmo.mjs → createServerRpc-B_y2lrwA.mjs} +2 -2
- package/.output/server/_ssr/databaseJsonValue-Bhxciy7Z.mjs +68 -0
- package/.output/server/_ssr/databaseRows-DwMAjaF8.mjs +74 -0
- package/.output/server/_ssr/{dialog-cOmLTUV2.mjs → dialog-q__BJ7mQ.mjs} +1 -1
- package/.output/server/_ssr/{dist-B-r4u3Lg.mjs → dist-Cxo9ghYv.mjs} +114 -3
- package/.output/server/_ssr/{downloadBundle-CexOFoZ4.mjs → downloadBundle-BW9I-pMQ.mjs} +6 -5
- package/.output/server/_ssr/{empty-BHoY_5jt.mjs → empty-DcBTAooP.mjs} +2 -2
- package/.output/server/_ssr/{empty-plugin-adapters-B5QvGpEl.mjs → empty-plugin-adapters-CYdinE-i.mjs} +1 -1
- package/.output/server/_ssr/{field-mjhcNEXD.mjs → field-QCDBVpXA.mjs} +2 -2
- package/.output/server/_ssr/{getBundleChildren-DbDXe9wB.mjs → getBundleChildren-DIAGCF4a.mjs} +2 -2
- package/.output/server/_ssr/{input-group-96H30HWC.mjs → input-group-hCk2nUoU.mjs} +1 -1
- package/.output/server/_ssr/{insights-DgFv1NsO.mjs → insights-BfBO0RFr.mjs} +18 -18
- package/.output/server/_ssr/{insights-D359GJFL.mjs → insights-DsAU3-TB.mjs} +5 -5
- package/.output/server/_ssr/{insights-D25VDVaz.mjs → insights-RIfg447W.mjs} +2 -2
- package/.output/server/_ssr/{insights-api-ChAKrbhG.mjs → insights-api-CDn-QQHU.mjs} +5 -5
- package/.output/server/_ssr/{insights-failures-WQ-ttjOx.mjs → insights-failures-BBGMhuUr.mjs} +2 -2
- package/.output/server/_ssr/{insights-failures-rpc-BFsqlltN.mjs → insights-failures-rpc-rA2dctK2.mjs} +8 -8
- package/.output/server/_ssr/{insights-recovery-DkL6bxMW.mjs → insights-recovery-Dpk6Mfb9.mjs} +1 -1
- package/.output/server/_ssr/{insights-recovery-rpc-2P5m8F62.mjs → insights-recovery-rpc-BCaF-xSd.mjs} +11 -11
- package/.output/server/_ssr/{insights-recovery-rpc-Tj8vNC19.mjs → insights-recovery-rpc-CcgYyUHk.mjs} +5 -5
- package/.output/server/_ssr/{insights-rpc-DrkVmV9o.mjs → insights-rpc-keycmwzw.mjs} +5 -5
- package/.output/server/_ssr/{insights-search-BsalnNw-.mjs → insights-search-BRg5VsNe.mjs} +1 -1
- package/.output/server/_ssr/{insights-usage-1nVTQrLy.mjs → insights-usage-cbw9n2Fq.mjs} +1 -1
- package/.output/server/_ssr/{insights-usage-rpc-Ct_4xHhZ.mjs → insights-usage-rpc-BKgV1QDf.mjs} +8 -8
- package/.output/server/_ssr/{insights-usage-rpc-DaYIRtFK.mjs → insights-usage-rpc-D-JYachy.mjs} +5 -5
- package/.output/server/_ssr/{insightsRecovery-CpGucWN1.mjs → insightsRecovery-B7oMc8d8.mjs} +2 -2
- package/.output/server/_ssr/{insightsUsage-JYpKZAF7.mjs → insightsUsage-Oa7kk5ek.mjs} +3 -3
- package/.output/server/_ssr/{insights_.distribution-jQZEF07J.mjs → insights_.distribution-BIqOqRW0.mjs} +13 -13
- package/.output/server/_ssr/{insights_.distribution-Bcu6uYs_.mjs → insights_.distribution-CuUrDa7P.mjs} +2 -2
- package/.output/server/_ssr/{insights_.distribution-D3Csuyjw.mjs → insights_.distribution-RT_6x_ud.mjs} +5 -5
- package/.output/server/_ssr/{installations-Cxikllmx.mjs → installations-BO5CjX2z.mjs} +11 -11
- package/.output/server/_ssr/{installations-BI5kelHv.mjs → installations-BjaHiIfh.mjs} +2 -2
- package/.output/server/_ssr/{installations-B8Vh54lH.mjs → installations-Dt5luA-N.mjs} +4 -4
- package/.output/server/_ssr/{listReleases-DqU9t1l3.mjs → listReleases-BitHzOYB.mjs} +1 -1
- package/.output/server/_ssr/{router-CRkm-wHD.mjs → router-DfVAixxe.mjs} +42 -39
- package/.output/server/_ssr/{routes-CNAi0Mmv.mjs → routes-C6LstNp8.mjs} +14 -14
- package/.output/server/_ssr/{routes-B3IJo_jL.mjs → routes-Q3Uhmwwz.mjs} +11 -11
- package/.output/server/_ssr/{runtime.server-NucbYjc7.mjs → runtime.server-fL0o6I2b.mjs} +500 -413
- package/.output/server/_ssr/schema-BKY9SqaY.mjs +25 -0
- package/.output/server/_ssr/{select-BkPAJFL7.mjs → select-CUlInmjk.mjs} +1 -1
- package/.output/server/_ssr/server-C43wq9ft.mjs +8330 -0
- package/.output/server/_ssr/{sidebar-Cw1VuC7d.mjs → sidebar-DKbx2ach.mjs} +3 -3
- package/.output/server/_ssr/ssr.mjs +1 -1
- package/.output/server/_ssr/start-BXUOv_n8.mjs +4 -0
- package/.output/server/_ssr/{table-q0P9wz7x.mjs → table-NGWV8B-S.mjs} +1 -1
- package/.output/server/_ssr/{updateFailures-DMU9f0pK.mjs → updateFailures-Jwe2Baab.mjs} +3 -3
- package/.output/server/_ssr/{uuidv7-BzSO-fr-.mjs → uuidv7-C8bwiZr8.mjs} +2 -2
- package/.output/server/_tanstack-start-manifest_v-WQ5qGn8n.mjs +124 -0
- package/.output/server/index.mjs +218 -218
- package/LICENSE +13 -14
- package/README.md +16 -12
- package/lib/index.d.mts +18 -8
- package/lib/vite-internal.mjs +15 -8
- package/lib/vite.d.mts +2 -0
- package/package.json +14 -14
- package/src/components/AppSidebar.tsx +9 -0
- package/src/components/ConsoleFeatureUnavailable.tsx +1 -3
- package/src/components/features/bundles/BundleMetadata.tsx +1 -1
- package/src/components/features/bundles/RolloutCohortsDialog.tsx +1 -1
- package/src/components/features/releases/ReleaseEditorSheet.tsx +6 -6
- package/src/index.ts +27 -15
- package/src/lib/api-rpc.ts +1 -54
- package/src/lib/api.ts +8 -89
- package/src/lib/console-features.ts +5 -5
- package/src/lib/scroll-restoration.ts +1 -1
- package/src/lib/server/config.server.ts +15 -52
- package/src/lib/server/console-runtime.server.ts +84 -6
- package/src/lib/server/downloadBundle.ts +10 -6
- package/src/lib/server/releaseReachability.ts +4 -4
- package/src/lib/server/runtime.server.ts +34 -46
- package/src/routeTree.gen.ts +31 -31
- package/src/routes/api/bundles/$bundleId/download.ts +2 -2
- package/src/vite-internal.ts +17 -8
- package/src/vite.ts +2 -0
- package/.output/public/assets/InsightsInfo-By_sPjTE.js +0 -1
- package/.output/public/assets/InsightsPageHeader-9_kx_LL7.js +0 -1
- package/.output/public/assets/api-CtarlV0l.js +0 -1
- package/.output/public/assets/api-keys-DFF_QqtN.js +0 -1
- package/.output/public/assets/api-keys-evfsQ4sN.js +0 -1
- package/.output/public/assets/arrow-left-BP9D-ta_.js +0 -1
- package/.output/public/assets/arrow-up-right-CDP9Tevw.js +0 -1
- package/.output/public/assets/console-features-api-yv7rm3DX.js +0 -1
- package/.output/public/assets/dialog-BDvCy_QO.js +0 -1
- package/.output/public/assets/empty-C5x9mn_s.js +0 -1
- package/.output/public/assets/index-0eg3l8aC.js +0 -10
- package/.output/public/assets/insights-2MbdiSNv.js +0 -1
- package/.output/public/assets/insights-C6tmCZHk.js +0 -2
- package/.output/public/assets/insights-CadS_aIA.js +0 -42
- package/.output/public/assets/insights_.distribution-BhBs6PNA.js +0 -1
- package/.output/public/assets/insights_.distribution-CJUH_wAS.js +0 -2
- package/.output/public/assets/installations-CH2H7JQt.js +0 -2
- package/.output/public/assets/installations-DMeMuwsW.js +0 -1
- package/.output/public/assets/installations-DnvxI9Dt.js +0 -1
- package/.output/public/assets/mutation-x9L28_CD.js +0 -1
- package/.output/public/assets/preload-helper-BN-UkjOE.js +0 -1
- package/.output/public/assets/routes-BhK8DuZA.js +0 -1
- package/.output/public/assets/sidebar-B45aMqRm.js +0 -3
- package/.output/public/assets/styles-Dn0pK1EX.css +0 -2
- package/.output/public/assets/table-CwLnN6KX.js +0 -1
- package/.output/public/assets/useMutation-CN1ygYEr.js +0 -1
- package/.output/public/assets/valueToPercent-C-t8FT0y.js +0 -1
- package/.output/server/_ssr/auth.server-Ccqz2iVv.mjs +0 -37
- package/.output/server/_ssr/config-DMEJdhoW.mjs +0 -13
- package/.output/server/_ssr/config.server-DyypN6cQ.mjs +0 -60
- package/.output/server/_ssr/databaseRows-CXxf_VIB.mjs +0 -132
- package/.output/server/_ssr/deleteBundle-Dcc21Lbd.mjs +0 -56
- package/.output/server/_ssr/server-DKJYsaCP.mjs +0 -7442
- package/.output/server/_ssr/start-DpViQGnt.mjs +0 -4
- package/.output/server/_tanstack-start-manifest_v-Bb0gKDbb.mjs +0 -124
- package/src/lib/server/deleteBundle.ts +0 -136
|
@@ -1,379 +1,9 @@
|
|
|
1
|
-
import { i as resolveConsoleFeatures, n as consoleFeatures, t as ConsoleFeatureUnavailableError } from "./console-features-
|
|
2
|
-
import { C as isUUIDv7 } from "./dist-
|
|
3
|
-
import {
|
|
4
|
-
import { t as createUUIDv7 } from "./uuidv7-
|
|
5
|
-
import {
|
|
6
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/runtime.server-
|
|
7
|
-
/** Whether a configured database is a self-hosted server's. */
|
|
8
|
-
var isRemoteDatabase = (database) => "core" in database && "fetchAdmin" in database;
|
|
9
|
-
/** The settings rows migrations write, last, and the fence reads. */
|
|
10
|
-
var SETTINGS_TABLE = {
|
|
11
|
-
name: "private_hot_updater_settings",
|
|
12
|
-
columns: [
|
|
13
|
-
{
|
|
14
|
-
name: "key",
|
|
15
|
-
type: "string",
|
|
16
|
-
nullable: false,
|
|
17
|
-
maxLength: 255
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
name: "value",
|
|
21
|
-
type: "string",
|
|
22
|
-
nullable: false,
|
|
23
|
-
maxLength: 255
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
name: "_v",
|
|
27
|
-
type: "integer",
|
|
28
|
-
nullable: false,
|
|
29
|
-
default: 0
|
|
30
|
-
}
|
|
31
|
-
],
|
|
32
|
-
key: ["key"],
|
|
33
|
-
indexes: []
|
|
34
|
-
};
|
|
35
|
-
/** The engine's storage-layout generation; it changes only with a migration. */
|
|
36
|
-
var ENGINE_SCHEMA_KEY = "schema.engine";
|
|
37
|
-
var settingMessage = (adapterName, { key, expected, found }) => `Hot Updater schema setting "${key}" for ${adapterName} is ${found === null ? "missing" : `"${found}"`}; expected "${expected}". ${key === "schema.engine" ? "Create a new empty database and run `hot-updater db migrate`; databases from before the storage engine are not converted." : "Run `hot-updater db migrate`."}`;
|
|
38
|
-
/** The database needs `hot-updater db migrate`, or a new database; the handler answers 503. */
|
|
39
|
-
var HotUpdaterSchemaMigrationRequiredError = class extends Error {
|
|
40
|
-
adapterName;
|
|
41
|
-
currentVersion;
|
|
42
|
-
setting;
|
|
43
|
-
constructor(adapterName, currentVersion, setting, options) {
|
|
44
|
-
super(setting !== void 0 ? settingMessage(adapterName, setting) : currentVersion === void 0 ? `Hot Updater database schema is not initialized for ${adapterName}. Run \`hot-updater db migrate\` before using this adapter.` : `Hot Updater v1 cannot migrate schema ${currentVersion} in place. Create a new empty database and run \`hot-updater db migrate\`.`, options);
|
|
45
|
-
this.adapterName = adapterName;
|
|
46
|
-
this.currentVersion = currentVersion;
|
|
47
|
-
this.setting = setting;
|
|
48
|
-
this.name = "HotUpdaterSchemaMigrationRequiredError";
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
/** Driver codes for a table or column the database lacks. */
|
|
52
|
-
var MISSING_SCHEMA_CODES = /* @__PURE__ */ new Set([
|
|
53
|
-
"42P01",
|
|
54
|
-
"42703",
|
|
55
|
-
"ER_NO_SUCH_TABLE",
|
|
56
|
-
"ER_BAD_FIELD_ERROR"
|
|
57
|
-
]);
|
|
58
|
-
var MISSING_SCHEMA_MESSAGE = /no such (?:table|column)|(?:relation|column) .+ does not exist|table .+ doesn't exist|unknown column/iu;
|
|
59
|
-
/**
|
|
60
|
-
* Whether a read failed because a table or column is missing, so the
|
|
61
|
-
* database needs migrations. ORMs may wrap the driver's error in `cause`.
|
|
62
|
-
*/
|
|
63
|
-
var isMissingSchemaError = (error) => {
|
|
64
|
-
for (let depth = 0; error instanceof Object && depth < 8; depth += 1) {
|
|
65
|
-
const { code, message, cause } = error;
|
|
66
|
-
if (MISSING_SCHEMA_CODES.has(String(code)) || typeof message === "string" && MISSING_SCHEMA_MESSAGE.test(message)) return true;
|
|
67
|
-
error = cause;
|
|
68
|
-
}
|
|
69
|
-
return false;
|
|
70
|
-
};
|
|
71
|
-
var readSettings = (adapter, keys) => adapter.get(SETTINGS_TABLE, keys.map((key) => [key]));
|
|
72
|
-
/** Throws unless every expected setting is stored with its value: one batch read. */
|
|
73
|
-
var checkSchemaFence = async (adapter, adapterName, expected) => {
|
|
74
|
-
const keys = Object.keys(expected);
|
|
75
|
-
let rows;
|
|
76
|
-
try {
|
|
77
|
-
rows = await readSettings(adapter, keys);
|
|
78
|
-
} catch (cause) {
|
|
79
|
-
if (!isMissingSchemaError(cause)) throw cause;
|
|
80
|
-
throw new HotUpdaterSchemaMigrationRequiredError(adapterName, void 0, {
|
|
81
|
-
key: ENGINE_SCHEMA_KEY,
|
|
82
|
-
expected: "1",
|
|
83
|
-
found: null
|
|
84
|
-
}, { cause });
|
|
85
|
-
}
|
|
86
|
-
keys.forEach((key, position) => {
|
|
87
|
-
const found = rows[position]?.value;
|
|
88
|
-
if (found !== expected[key]) throw new HotUpdaterSchemaMigrationRequiredError(adapterName, void 0, {
|
|
89
|
-
key,
|
|
90
|
-
expected: expected[key],
|
|
91
|
-
found: typeof found === "string" ? found : null
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
};
|
|
95
|
-
/** The database name a fenced adapter carries, so a server can fence its plugins' rows too. */
|
|
96
|
-
var FENCED = Symbol.for("@hot-updater/server/schema-fence");
|
|
97
|
-
/** The database name an adapter behind the schema fence reports under. */
|
|
98
|
-
var fencedName = (adapter) => adapter[FENCED];
|
|
99
|
-
/**
|
|
100
|
-
* The adapter behind the schema fence: a process's first read or write
|
|
101
|
-
* checks the settings first. Success is kept; a failure is checked again on
|
|
102
|
-
* the next call.
|
|
103
|
-
*/
|
|
104
|
-
var withSchemaFence = (adapter, adapterName, expected) => {
|
|
105
|
-
let ready;
|
|
106
|
-
const fence = () => {
|
|
107
|
-
ready ??= checkSchemaFence(adapter, adapterName, expected).catch((error) => {
|
|
108
|
-
ready = void 0;
|
|
109
|
-
throw error;
|
|
110
|
-
});
|
|
111
|
-
return ready;
|
|
112
|
-
};
|
|
113
|
-
return {
|
|
114
|
-
...adapter,
|
|
115
|
-
[FENCED]: adapterName,
|
|
116
|
-
get: (table, keys) => fence().then(() => adapter.get(table, keys)),
|
|
117
|
-
query: (table, request) => fence().then(() => adapter.query(table, request)),
|
|
118
|
-
write: (ops) => fence().then(() => adapter.write(ops)),
|
|
119
|
-
...adapter.prune && { prune: (table, before, limit) => fence().then(() => adapter.prune(table, before, limit)) }
|
|
120
|
-
};
|
|
121
|
-
};
|
|
122
|
-
/** A misconfigured `createHotUpdater` call, reported at startup. */
|
|
123
|
-
var HotUpdaterConfigError = class extends Error {
|
|
124
|
-
name = "HotUpdaterConfigError";
|
|
125
|
-
};
|
|
126
|
-
var EXPORT_NAME = /^[A-Za-z_$][\w$]*$/u;
|
|
127
|
-
/**
|
|
128
|
-
* Names app code cannot import as they are: reserved words, and the names
|
|
129
|
-
* the printed app code declares itself.
|
|
130
|
-
*/
|
|
131
|
-
var UNAVAILABLE_NAMES = /* @__PURE__ */ new Set([
|
|
132
|
-
"App",
|
|
133
|
-
"HotUpdater",
|
|
134
|
-
"arguments",
|
|
135
|
-
"await",
|
|
136
|
-
"break",
|
|
137
|
-
"case",
|
|
138
|
-
"catch",
|
|
139
|
-
"class",
|
|
140
|
-
"const",
|
|
141
|
-
"continue",
|
|
142
|
-
"debugger",
|
|
143
|
-
"default",
|
|
144
|
-
"delete",
|
|
145
|
-
"do",
|
|
146
|
-
"else",
|
|
147
|
-
"enum",
|
|
148
|
-
"eval",
|
|
149
|
-
"export",
|
|
150
|
-
"extends",
|
|
151
|
-
"false",
|
|
152
|
-
"finally",
|
|
153
|
-
"for",
|
|
154
|
-
"function",
|
|
155
|
-
"if",
|
|
156
|
-
"implements",
|
|
157
|
-
"import",
|
|
158
|
-
"in",
|
|
159
|
-
"instanceof",
|
|
160
|
-
"interface",
|
|
161
|
-
"let",
|
|
162
|
-
"new",
|
|
163
|
-
"null",
|
|
164
|
-
"package",
|
|
165
|
-
"private",
|
|
166
|
-
"protected",
|
|
167
|
-
"public",
|
|
168
|
-
"return",
|
|
169
|
-
"static",
|
|
170
|
-
"super",
|
|
171
|
-
"switch",
|
|
172
|
-
"this",
|
|
173
|
-
"throw",
|
|
174
|
-
"true",
|
|
175
|
-
"try",
|
|
176
|
-
"typeof",
|
|
177
|
-
"var",
|
|
178
|
-
"void",
|
|
179
|
-
"while",
|
|
180
|
-
"with",
|
|
181
|
-
"yield"
|
|
182
|
-
]);
|
|
183
|
-
var isRecord$5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
184
|
-
/**
|
|
185
|
-
* The client plugins an app adds for the plugins, each once, in plugin
|
|
186
|
-
* order: what init prints in `HotUpdater.init`'s `plugins`. A malformed one,
|
|
187
|
-
* or one name from two modules, throws `HotUpdaterConfigError`.
|
|
188
|
-
*/
|
|
189
|
-
var clientPluginsOf = (plugins) => {
|
|
190
|
-
if (!Array.isArray(plugins)) throw new HotUpdaterConfigError("plugins must be an array of plugins.");
|
|
191
|
-
const found = [];
|
|
192
|
-
for (const plugin of plugins) {
|
|
193
|
-
if (!isRecord$5(plugin) || !isRecord$5(plugin.cli)) continue;
|
|
194
|
-
const value = plugin.cli.clientPlugin;
|
|
195
|
-
if (value === void 0) continue;
|
|
196
|
-
const id = String(plugin.id);
|
|
197
|
-
if (!isRecord$5(value) || typeof value.module !== "string" || value.module === "" || typeof value.name !== "string" || !EXPORT_NAME.test(value.name) || UNAVAILABLE_NAMES.has(value.name)) throw new HotUpdaterConfigError(`Plugin "${id}" cli.clientPlugin needs a module and an export name the app can import: an identifier other than a reserved word, App, or HotUpdater.`);
|
|
198
|
-
const { module, name } = value;
|
|
199
|
-
const named = found.find((entry) => entry.name === name);
|
|
200
|
-
if (named === void 0) found.push({
|
|
201
|
-
module,
|
|
202
|
-
name
|
|
203
|
-
});
|
|
204
|
-
else if (named.module !== module) throw new HotUpdaterConfigError(`Plugin "${id}" names the client plugin "${name}" from ${module}, but another plugin names it from ${named.module}.`);
|
|
205
|
-
}
|
|
206
|
-
return found;
|
|
207
|
-
};
|
|
208
|
-
resolveSchema([coreModule]);
|
|
209
|
-
/** Each plugin's `schema.<id>` settings row. */
|
|
210
|
-
var pluginSettings = (plugins) => Object.fromEntries(plugins.map(({ id, schemaVersion }) => [`schema.${id}`, schemaVersion]));
|
|
211
|
-
/** A plugin's schema as the engine resolves it: prefixed by its id unless it keeps its names. */
|
|
212
|
-
var pluginModule = ({ id, schema, namespace }) => ({
|
|
213
|
-
id,
|
|
214
|
-
schema,
|
|
215
|
-
...namespace === false ? {} : { namespace: id }
|
|
216
|
-
});
|
|
217
|
-
var PLUGIN_KEYS = /* @__PURE__ */ new Set([
|
|
218
|
-
"id",
|
|
219
|
-
"provides",
|
|
220
|
-
"schemaVersion",
|
|
221
|
-
"schema",
|
|
222
|
-
"namespace",
|
|
223
|
-
"init",
|
|
224
|
-
"cli"
|
|
225
|
-
]);
|
|
226
|
-
var CLI_KEYS = /* @__PURE__ */ new Set([
|
|
227
|
-
"commands",
|
|
228
|
-
"clientCredential",
|
|
229
|
-
"clientPlugin"
|
|
230
|
-
]);
|
|
231
|
-
var INSTANCE_KEYS = /* @__PURE__ */ new Set([
|
|
232
|
-
"api",
|
|
233
|
-
"endpoints",
|
|
234
|
-
"clientAuth"
|
|
235
|
-
]);
|
|
236
|
-
var METHODS = /* @__PURE__ */ new Set([
|
|
237
|
-
"GET",
|
|
238
|
-
"POST",
|
|
239
|
-
"PUT",
|
|
240
|
-
"PATCH",
|
|
241
|
-
"DELETE"
|
|
242
|
-
]);
|
|
243
|
-
/** An id that prefixes table names is a SQL name; one that does not may use camelCase. */
|
|
244
|
-
var PREFIX_ID = /^[a-z][a-z0-9_]*$/u;
|
|
245
|
-
var UNPREFIXED_ID = /^[a-z][A-Za-z0-9_]*$/u;
|
|
246
|
-
var PATH = /^(\/(:?[A-Za-z0-9_.~-]+))+$/u;
|
|
247
|
-
var fail = (message) => {
|
|
248
|
-
throw new HotUpdaterConfigError(message);
|
|
249
|
-
};
|
|
250
|
-
var isRecord$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
251
|
-
var checkPlugin = (value, at) => {
|
|
252
|
-
if (!isRecord$4(value)) return fail(`${at} is not a plugin.`);
|
|
253
|
-
if ("kind" in value) fail(`${at} has a kind; core is built in and is never passed as a plugin.`);
|
|
254
|
-
const unknown = Object.keys(value).find((key) => !PLUGIN_KEYS.has(key));
|
|
255
|
-
if (unknown !== void 0) fail(`${at} has an unknown key "${unknown}".`);
|
|
256
|
-
const plugin = value;
|
|
257
|
-
if (plugin.namespace !== void 0 && plugin.namespace !== false) fail(`${at} may only set namespace: false.`);
|
|
258
|
-
const pattern = plugin.namespace === false ? UNPREFIXED_ID : PREFIX_ID;
|
|
259
|
-
if (typeof plugin.id !== "string" || !pattern.test(plugin.id)) fail(`${at} needs an id matching ${pattern.source}.`);
|
|
260
|
-
if (plugin.id === coreModule.id) fail(`${at} uses the id "core", which is core's own.`);
|
|
261
|
-
if (typeof plugin.schemaVersion !== "string" || !isRecord$4(plugin.schema) || typeof plugin.init !== "function") fail(`Plugin "${plugin.id}" needs a schemaVersion, a schema, and init.`);
|
|
262
|
-
const provides = plugin.provides;
|
|
263
|
-
if (provides !== void 0 && (!isRecord$4(provides) || Object.entries(provides).some(([key, flag]) => key !== "clientAuth" || flag !== true))) fail(`Plugin "${plugin.id}" may only declare provides: { clientAuth: true }.`);
|
|
264
|
-
const cli = value.cli;
|
|
265
|
-
if (cli !== void 0) {
|
|
266
|
-
if (!isRecord$4(cli) || Object.keys(cli).some((key) => !CLI_KEYS.has(key)) || cli.commands !== void 0 && !Array.isArray(cli.commands)) fail(`Plugin "${plugin.id}" cli may only hold commands (an array), clientCredential, and clientPlugin.`);
|
|
267
|
-
if (cli.clientCredential !== void 0 && plugin.provides?.clientAuth !== true) fail(`Plugin "${plugin.id}" adds cli.clientCredential without declaring provides: { clientAuth: true }.`);
|
|
268
|
-
}
|
|
269
|
-
return plugin;
|
|
270
|
-
};
|
|
271
|
-
var checkEndpoint = (id, value) => {
|
|
272
|
-
const endpoint = isRecord$4(value) ? value : {};
|
|
273
|
-
if (!METHODS.has(endpoint.method) || typeof endpoint.path !== "string" || !PATH.test(endpoint.path) || endpoint.access !== "client" && endpoint.access !== "admin" || typeof endpoint.handler !== "function") fail(`Plugin "${id}" has an endpoint that needs a method, a path like /name/:param, access "client" or "admin", and a handler.`);
|
|
274
|
-
return {
|
|
275
|
-
...endpoint,
|
|
276
|
-
plugin: id
|
|
277
|
-
};
|
|
278
|
-
};
|
|
279
|
-
var checkInstance = (plugin, instance) => {
|
|
280
|
-
const { id } = plugin;
|
|
281
|
-
if (!isRecord$4(instance)) return fail(`Plugin "${id}" init returned no instance.`);
|
|
282
|
-
if (typeof instance.then === "function") fail(`Plugin "${id}" init returned a promise; init must be synchronous.`);
|
|
283
|
-
const unknown = Object.keys(instance).find((key) => !INSTANCE_KEYS.has(key));
|
|
284
|
-
if (unknown !== void 0 || !("api" in instance)) fail(`Plugin "${id}" init must return { api, endpoints?, clientAuth? }${unknown === void 0 ? "" : `, not "${unknown}"`}.`);
|
|
285
|
-
const declared = plugin.provides?.clientAuth === true;
|
|
286
|
-
const clientAuth = instance.clientAuth;
|
|
287
|
-
if (declared !== (clientAuth !== void 0)) fail(declared ? `Plugin "${id}" declares provides: { clientAuth: true } but its instance has no clientAuth.` : `Plugin "${id}" returns clientAuth without declaring provides: { clientAuth: true }.`);
|
|
288
|
-
if (clientAuth !== void 0 && (typeof clientAuth.authenticate !== "function" || !Array.isArray(clientAuth.varyHeaders) || !clientAuth.varyHeaders.every((header) => typeof header === "string"))) fail(`Plugin "${id}" clientAuth needs varyHeaders and authenticate.`);
|
|
289
|
-
const endpoints = instance.endpoints ?? [];
|
|
290
|
-
if (!Array.isArray(endpoints)) fail(`Plugin "${id}" endpoints must be an array.`);
|
|
291
|
-
return {
|
|
292
|
-
api: instance.api,
|
|
293
|
-
clientAuth,
|
|
294
|
-
endpoints: endpoints.map((endpoint) => checkEndpoint(id, endpoint))
|
|
295
|
-
};
|
|
296
|
-
};
|
|
297
|
-
/**
|
|
298
|
-
* Checks every plugin, runs each `init` once against one engine over the
|
|
299
|
-
* database's storage adapter, and collects APIs, endpoints, and clientAuth.
|
|
300
|
-
* Core's reads run on the same engine, so plugins read core through `ctx.core`.
|
|
301
|
-
*/
|
|
302
|
-
var assemblePlugins = (value, adapter, { now = Date.now, storage = { resolveFileUrl: async () => null }, verify = false, onCachedRoutesChange, batching } = {}) => {
|
|
303
|
-
if (!Array.isArray(value)) return fail("plugins must be an array of plugins.");
|
|
304
|
-
const plugins = value.map((plugin, position) => checkPlugin(plugin, `plugins[${position}]`));
|
|
305
|
-
const ids = /* @__PURE__ */ new Set();
|
|
306
|
-
for (const { id } of plugins) {
|
|
307
|
-
if (ids.has(id)) fail(`Plugin "${id}" is registered twice.`);
|
|
308
|
-
ids.add(id);
|
|
309
|
-
}
|
|
310
|
-
clientPluginsOf(plugins);
|
|
311
|
-
const modules = plugins.map(pluginModule);
|
|
312
|
-
if (modules.length > 0) validateSchema([coreModule, ...modules]);
|
|
313
|
-
const name = fencedName(adapter);
|
|
314
|
-
const fenced = name === void 0 || plugins.length === 0 ? adapter : withSchemaFence(adapter, name, pluginSettings(plugins));
|
|
315
|
-
const schema = resolveSchema([
|
|
316
|
-
coreModule,
|
|
317
|
-
...modules,
|
|
318
|
-
...batching === void 0 ? [] : [aggregateBatchingModule]
|
|
319
|
-
]);
|
|
320
|
-
const engine = createDatabaseEngine({
|
|
321
|
-
adapter: verify ? fenced : pruneDuringWrites(fenced, schema.tables, {
|
|
322
|
-
leaseTable: SETTINGS_TABLE,
|
|
323
|
-
now
|
|
324
|
-
}),
|
|
325
|
-
schema,
|
|
326
|
-
verify,
|
|
327
|
-
...batching === void 0 ? {} : {
|
|
328
|
-
batching,
|
|
329
|
-
now
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
const coreDatabase = engine.database(coreModule);
|
|
333
|
-
const core = createCoreApi(coreDatabase, storage, {
|
|
334
|
-
now,
|
|
335
|
-
...onCachedRoutesChange === void 0 ? {} : { onCachedRoutesChange }
|
|
336
|
-
});
|
|
337
|
-
const reads = createCoreReads(coreDatabase, storage);
|
|
338
|
-
const api = {};
|
|
339
|
-
const endpoints = [];
|
|
340
|
-
let clientAuth;
|
|
341
|
-
plugins.forEach((plugin, position) => {
|
|
342
|
-
const instance = checkInstance(plugin, plugin.init({
|
|
343
|
-
db: engine.database(modules[position]),
|
|
344
|
-
core: reads,
|
|
345
|
-
now
|
|
346
|
-
}));
|
|
347
|
-
if (instance.clientAuth !== void 0) {
|
|
348
|
-
if (clientAuth !== void 0) fail(`Plugins "${clientAuth.plugin}" and "${plugin.id}" both provide clientAuth; keep one.`);
|
|
349
|
-
const provided = instance.clientAuth;
|
|
350
|
-
clientAuth = {
|
|
351
|
-
plugin: plugin.id,
|
|
352
|
-
varyHeaders: provided.varyHeaders,
|
|
353
|
-
authenticate: (headers) => provided.authenticate(headers)
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
endpoints.push(...instance.endpoints);
|
|
357
|
-
api[plugin.id] = instance.api;
|
|
358
|
-
});
|
|
359
|
-
return {
|
|
360
|
-
core,
|
|
361
|
-
api,
|
|
362
|
-
endpoints,
|
|
363
|
-
...clientAuth === void 0 ? {} : { clientAuth },
|
|
364
|
-
...verify ? { measureReads: engine.measureReads } : {},
|
|
365
|
-
flush: engine.flush
|
|
366
|
-
};
|
|
367
|
-
};
|
|
368
|
-
function createDatabasePluginApis(database, plugins, options = {}) {
|
|
369
|
-
const adapter = engineAdapterOf(database);
|
|
370
|
-
if (adapter === void 0) throw new Error(OFF_ENGINE_DATABASE);
|
|
371
|
-
const { aggregateBatching } = database;
|
|
372
|
-
return assemblePlugins(plugins, adapter, {
|
|
373
|
-
...options,
|
|
374
|
-
...aggregateBatching === void 0 ? {} : { batching: aggregateBatching }
|
|
375
|
-
}).api;
|
|
376
|
-
}
|
|
1
|
+
import { i as resolveConsoleFeatures, n as consoleFeatures, t as ConsoleFeatureUnavailableError } from "./console-features-Dzu2iUGD.mjs";
|
|
2
|
+
import { C as isUUIDv7 } from "./dist-Cxo9ghYv.mjs";
|
|
3
|
+
import { i as isDatabaseJsonObject, t as DatabaseAdapterInputError } from "./databaseJsonValue-Bhxciy7Z.mjs";
|
|
4
|
+
import { t as createUUIDv7 } from "./uuidv7-C8bwiZr8.mjs";
|
|
5
|
+
import { n as defineTable, r as isRemoteDatabase, t as defineAggregate } from "./schema-BKY9SqaY.mjs";
|
|
6
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/runtime.server-fL0o6I2b.js
|
|
377
7
|
var isOptional = (value, key, valid) => !Object.hasOwn(value, key) || valid(value[key]);
|
|
378
8
|
var isString = (value) => typeof value === "string";
|
|
379
9
|
/** An `UPDATE_FAILED` report's stored failure: a stage and a reason, and what else the client knew. */
|
|
@@ -411,10 +41,10 @@ var hasEventInvariants = (row) => (row.type === "UPDATE_DOWNLOADED" || row.type
|
|
|
411
41
|
* `invalid-data` for anything else.
|
|
412
42
|
*/
|
|
413
43
|
var validateBundleEventFields = (row) => {
|
|
414
|
-
if (!isRecord$3(row)) throw new
|
|
415
|
-
for (const field of Object.keys(row)) if (!Object.hasOwn(BUNDLE_EVENT_FIELDS, field)) throw new
|
|
416
|
-
for (const [field, valid] of Object.entries(BUNDLE_EVENT_FIELDS)) if (!Object.hasOwn(row, field) || !valid(row[field])) throw new
|
|
417
|
-
if (!hasEventInvariants(row)) throw new
|
|
44
|
+
if (!isRecord$3(row)) throw new DatabaseAdapterInputError("invalid-data");
|
|
45
|
+
for (const field of Object.keys(row)) if (!Object.hasOwn(BUNDLE_EVENT_FIELDS, field)) throw new DatabaseAdapterInputError("invalid-field");
|
|
46
|
+
for (const [field, valid] of Object.entries(BUNDLE_EVENT_FIELDS)) if (!Object.hasOwn(row, field) || !valid(row[field])) throw new DatabaseAdapterInputError("invalid-data");
|
|
47
|
+
if (!hasEventInvariants(row)) throw new DatabaseAdapterInputError("invalid-data");
|
|
418
48
|
};
|
|
419
49
|
var encoder = new TextEncoder();
|
|
420
50
|
/** Exact UTF-8 byte ordering, without case folding or Unicode normalization. */
|
|
@@ -456,9 +86,9 @@ var isEventFilter = (value) => {
|
|
|
456
86
|
var validateRow = (model, row, result = false) => {
|
|
457
87
|
try {
|
|
458
88
|
validateBundleEventFields(row);
|
|
459
|
-
if (!isRecord$3(row) || typeof row.id !== "string" || !isUUIDv7(row.id) || [...Object.values(row), ...isRecord$3(row.metadata) ? Object.values(row.metadata) : []].some((value) => typeof value === "string" && !isWellFormedText(value))) throw new
|
|
89
|
+
if (!isRecord$3(row) || typeof row.id !== "string" || !isUUIDv7(row.id) || [...Object.values(row), ...isRecord$3(row.metadata) ? Object.values(row.metadata) : []].some((value) => typeof value === "string" && !isWellFormedText(value))) throw new DatabaseAdapterInputError("invalid-data");
|
|
460
90
|
} catch (error) {
|
|
461
|
-
if (result) throw new
|
|
91
|
+
if (result) throw new DatabaseAdapterInputError("invalid-result");
|
|
462
92
|
throw error;
|
|
463
93
|
}
|
|
464
94
|
};
|
|
@@ -470,10 +100,10 @@ var matchesInsightsEventFilter = (event, filter) => {
|
|
|
470
100
|
return event.type === filter.type && event.platform === filter.platform && event.channel === filter.channel && (filter.type === "RECOVERED" ? event.from_bundle_id === filter.fromBundleId : event.to_bundle_id === filter.toBundleId && event.metadata.failure?.stage !== "check");
|
|
471
101
|
};
|
|
472
102
|
var invalidQuery = () => {
|
|
473
|
-
throw new
|
|
103
|
+
throw new DatabaseAdapterInputError("invalid-query");
|
|
474
104
|
};
|
|
475
105
|
var invalidResult = () => {
|
|
476
|
-
throw new
|
|
106
|
+
throw new DatabaseAdapterInputError("invalid-result");
|
|
477
107
|
};
|
|
478
108
|
var validateCount = (count) => isTimestamp(count) ? count : invalidResult();
|
|
479
109
|
var isTimeRange = (value) => isRecord$3(value) && hasOnlyKeys(value, ["start", "end"]) && isTimestamp(value.start) && isTimestamp(value.end) && value.start < value.end;
|
|
@@ -491,7 +121,7 @@ var validateAggregateResult = (value) => {
|
|
|
491
121
|
/** Validate custom and bundled providers at the same public boundary. */
|
|
492
122
|
var createValidatedInsightsModel = (model) => ({
|
|
493
123
|
async recordEvent(input) {
|
|
494
|
-
if (!isRecord$3(input) || !hasOnlyKeys(input, ["event"])) throw new
|
|
124
|
+
if (!isRecord$3(input) || !hasOnlyKeys(input, ["event"])) throw new DatabaseAdapterInputError("invalid-data");
|
|
495
125
|
validateRow("bundle_events", input.event);
|
|
496
126
|
await model.recordEvent(input);
|
|
497
127
|
},
|
|
@@ -611,6 +241,10 @@ var createInsightsModel = (api) => createValidatedInsightsModel({
|
|
|
611
241
|
getReleaseActivity: api.getReleaseActivity,
|
|
612
242
|
getAppUsage: api.getAppUsage
|
|
613
243
|
});
|
|
244
|
+
/**
|
|
245
|
+
* What Insights' API throws for an input it refuses, such as an unknown
|
|
246
|
+
* event field or a malformed cursor; its routes answer 400 for it.
|
|
247
|
+
*/
|
|
614
248
|
var InsightsBadRequestError = class extends Error {
|
|
615
249
|
name = "InsightsBadRequestError";
|
|
616
250
|
};
|
|
@@ -863,7 +497,7 @@ var MAX_EVENT_ID_LENGTH = 1024;
|
|
|
863
497
|
var MAX_IDENTITY_LENGTH = 255;
|
|
864
498
|
var MAX_CURSOR_LENGTH = 8192;
|
|
865
499
|
var HOUR_MS = 36e5;
|
|
866
|
-
var DAY_MS = 24 * HOUR_MS;
|
|
500
|
+
var DAY_MS$1 = 24 * HOUR_MS;
|
|
867
501
|
var WINDOW_MS = {
|
|
868
502
|
"24h": 864e5,
|
|
869
503
|
"7d": 6048e5,
|
|
@@ -1151,7 +785,7 @@ var createInsightsProvider = (model) => Object.freeze({
|
|
|
1151
785
|
const now = Date.now();
|
|
1152
786
|
const beforeReceivedAtMs = now - now % HOUR_MS + (now % HOUR_MS === 0 ? 0 : HOUR_MS);
|
|
1153
787
|
const reach = Math.max(0, beforeReceivedAtMs - WINDOW_MS[window]);
|
|
1154
|
-
const sinceMs = reach - reach % DAY_MS;
|
|
788
|
+
const sinceMs = reach - reach % DAY_MS$1;
|
|
1155
789
|
const measure = async (count) => {
|
|
1156
790
|
const value = await count;
|
|
1157
791
|
if (!Number.isSafeInteger(value) || value < 0) throw new Error("Insights database returned an invalid count.");
|
|
@@ -1222,6 +856,464 @@ var createInsightsProvider = (model) => Object.freeze({
|
|
|
1222
856
|
};
|
|
1223
857
|
}
|
|
1224
858
|
});
|
|
859
|
+
var DAY_MS = 864e5;
|
|
860
|
+
var DEFAULT_INSIGHTS_RETENTION = {
|
|
861
|
+
rawDays: 90,
|
|
862
|
+
dailyDays: 400
|
|
863
|
+
};
|
|
864
|
+
var bucketRetention = (days) => ({
|
|
865
|
+
field: "bucket_start_ms",
|
|
866
|
+
days
|
|
867
|
+
});
|
|
868
|
+
var MOVEMENTS = /* @__PURE__ */ new Set([
|
|
869
|
+
"UPDATE_DOWNLOADED",
|
|
870
|
+
"UPDATE_APPLIED",
|
|
871
|
+
"RECOVERED",
|
|
872
|
+
"UPDATE_FAILED"
|
|
873
|
+
]);
|
|
874
|
+
/** A failed update check: it names no target bundle, so no bundle list shows it. */
|
|
875
|
+
var isFailedCheck = (row) => row.type === "UPDATE_FAILED" && row.metadata?.failure?.stage === "check";
|
|
876
|
+
/**
|
|
877
|
+
* Downloads, applies, recoveries, and update failures, whole. An UNCHANGED
|
|
878
|
+
* report counts as a launch and moves its installation's head, but no list
|
|
879
|
+
* shows it, so no row keeps it.
|
|
880
|
+
*/
|
|
881
|
+
var bundleEvents = (days) => defineTable({
|
|
882
|
+
id: {
|
|
883
|
+
type: "string",
|
|
884
|
+
maxLength: 36
|
|
885
|
+
},
|
|
886
|
+
type: {
|
|
887
|
+
type: "string",
|
|
888
|
+
maxLength: 32
|
|
889
|
+
},
|
|
890
|
+
install_id: {
|
|
891
|
+
type: "string",
|
|
892
|
+
maxLength: 255
|
|
893
|
+
},
|
|
894
|
+
user_id: {
|
|
895
|
+
type: "string",
|
|
896
|
+
maxLength: 255,
|
|
897
|
+
required: false
|
|
898
|
+
},
|
|
899
|
+
from_release_id: {
|
|
900
|
+
type: "string",
|
|
901
|
+
maxLength: 36,
|
|
902
|
+
required: false
|
|
903
|
+
},
|
|
904
|
+
from_bundle_id: {
|
|
905
|
+
type: "string",
|
|
906
|
+
maxLength: 36,
|
|
907
|
+
required: false
|
|
908
|
+
},
|
|
909
|
+
to_release_id: {
|
|
910
|
+
type: "string",
|
|
911
|
+
maxLength: 36,
|
|
912
|
+
required: false
|
|
913
|
+
},
|
|
914
|
+
to_bundle_id: {
|
|
915
|
+
type: "string",
|
|
916
|
+
maxLength: 36
|
|
917
|
+
},
|
|
918
|
+
platform: {
|
|
919
|
+
type: "string",
|
|
920
|
+
maxLength: 16
|
|
921
|
+
},
|
|
922
|
+
app_version: { type: "string" },
|
|
923
|
+
channel: { type: "string" },
|
|
924
|
+
metadata: { type: "json" },
|
|
925
|
+
received_at_ms: { type: "integer" }
|
|
926
|
+
}, {
|
|
927
|
+
key: ["id"],
|
|
928
|
+
derived: {
|
|
929
|
+
day: {
|
|
930
|
+
type: "integer",
|
|
931
|
+
compute: (row) => row.received_at_ms - row.received_at_ms % DAY_MS
|
|
932
|
+
},
|
|
933
|
+
movement_install_id: {
|
|
934
|
+
type: "string",
|
|
935
|
+
compute: (row) => MOVEMENTS.has(row.type) ? row.install_id : null
|
|
936
|
+
},
|
|
937
|
+
bundle_ref: {
|
|
938
|
+
type: "string",
|
|
939
|
+
multi: true,
|
|
940
|
+
compute: (row) => isFailedCheck(row) ? [] : [row.type === "RECOVERED" && row.from_bundle_id !== null ? `from:${row.from_bundle_id}` : `to:${row.to_bundle_id}`]
|
|
941
|
+
}
|
|
942
|
+
},
|
|
943
|
+
indexes: {
|
|
944
|
+
recent: {
|
|
945
|
+
eq: [
|
|
946
|
+
"channel",
|
|
947
|
+
"platform",
|
|
948
|
+
"day"
|
|
949
|
+
],
|
|
950
|
+
sort: ["received_at_ms"]
|
|
951
|
+
},
|
|
952
|
+
movementsByInstall: {
|
|
953
|
+
eq: ["movement_install_id"],
|
|
954
|
+
sort: ["received_at_ms"]
|
|
955
|
+
},
|
|
956
|
+
byBundle: {
|
|
957
|
+
eq: [
|
|
958
|
+
"platform",
|
|
959
|
+
"channel",
|
|
960
|
+
"type",
|
|
961
|
+
"bundle_ref",
|
|
962
|
+
"day"
|
|
963
|
+
],
|
|
964
|
+
sort: ["received_at_ms"]
|
|
965
|
+
},
|
|
966
|
+
byDay: {
|
|
967
|
+
eq: ["day"],
|
|
968
|
+
sort: ["received_at_ms"]
|
|
969
|
+
}
|
|
970
|
+
},
|
|
971
|
+
retention: {
|
|
972
|
+
field: "day",
|
|
973
|
+
days
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
/**
|
|
977
|
+
* Each installation's latest event, whole, so reading it is one point read.
|
|
978
|
+
* Its gauges count it in the UTC day of that event. An update failure moves
|
|
979
|
+
* nothing, so no head holds one.
|
|
980
|
+
*/
|
|
981
|
+
var bundleEventHeads = (days) => defineTable({
|
|
982
|
+
install_id: {
|
|
983
|
+
type: "string",
|
|
984
|
+
maxLength: 255
|
|
985
|
+
},
|
|
986
|
+
id: {
|
|
987
|
+
type: "string",
|
|
988
|
+
maxLength: 36
|
|
989
|
+
},
|
|
990
|
+
type: {
|
|
991
|
+
type: "string",
|
|
992
|
+
maxLength: 32
|
|
993
|
+
},
|
|
994
|
+
user_id: {
|
|
995
|
+
type: "string",
|
|
996
|
+
maxLength: 255,
|
|
997
|
+
required: false
|
|
998
|
+
},
|
|
999
|
+
from_release_id: {
|
|
1000
|
+
type: "string",
|
|
1001
|
+
maxLength: 36,
|
|
1002
|
+
required: false
|
|
1003
|
+
},
|
|
1004
|
+
from_bundle_id: {
|
|
1005
|
+
type: "string",
|
|
1006
|
+
maxLength: 36,
|
|
1007
|
+
required: false
|
|
1008
|
+
},
|
|
1009
|
+
to_release_id: {
|
|
1010
|
+
type: "string",
|
|
1011
|
+
maxLength: 36,
|
|
1012
|
+
required: false
|
|
1013
|
+
},
|
|
1014
|
+
to_bundle_id: {
|
|
1015
|
+
type: "string",
|
|
1016
|
+
maxLength: 36
|
|
1017
|
+
},
|
|
1018
|
+
platform: {
|
|
1019
|
+
type: "string",
|
|
1020
|
+
maxLength: 16
|
|
1021
|
+
},
|
|
1022
|
+
app_version: { type: "string" },
|
|
1023
|
+
channel: { type: "string" },
|
|
1024
|
+
metadata: { type: "json" },
|
|
1025
|
+
received_at_ms: { type: "integer" }
|
|
1026
|
+
}, {
|
|
1027
|
+
key: ["install_id"],
|
|
1028
|
+
indexes: { byUser: {
|
|
1029
|
+
eq: ["user_id"],
|
|
1030
|
+
sort: ["install_id"]
|
|
1031
|
+
} },
|
|
1032
|
+
retention: {
|
|
1033
|
+
field: "received_at_ms",
|
|
1034
|
+
days
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
1037
|
+
/**
|
|
1038
|
+
* Shards are keyed by install id. Counters are blind increments and never
|
|
1039
|
+
* conflict, so they keep 8. Gauges and sketches are read, merged, and written
|
|
1040
|
+
* back. B3's rollout gate on PostgreSQL retried 2.4% of transactions at 8
|
|
1041
|
+
* shards and 1.3% at 16. DynamoDB's transactions take longer, so D8's gate
|
|
1042
|
+
* on DynamoDB Local retried 2–24% at 16, almost all on gauge rows, and 1–5%
|
|
1043
|
+
* with gauges at 32. Sketches stay at 16: each shard row carries 2 KB of
|
|
1044
|
+
* registers that every read merges.
|
|
1045
|
+
*
|
|
1046
|
+
* Every Insights aggregate is `batched`: on DynamoDB and Firestore, which
|
|
1047
|
+
* bill each write, an event's changes apply after it commits, merged with
|
|
1048
|
+
* other events' into one write per row, on one shard per writer.
|
|
1049
|
+
*/
|
|
1050
|
+
var COUNTER_SHARDS = 8;
|
|
1051
|
+
var GAUGE_SHARDS = 32;
|
|
1052
|
+
var SKETCH_SHARDS = 16;
|
|
1053
|
+
/** Rows keyed by a hashed identity (scope and period) and a bucket. */
|
|
1054
|
+
var identityFields = {
|
|
1055
|
+
identity: {
|
|
1056
|
+
type: "string",
|
|
1057
|
+
maxLength: 32
|
|
1058
|
+
},
|
|
1059
|
+
bucket_start_ms: { type: "integer" }
|
|
1060
|
+
};
|
|
1061
|
+
var window = {
|
|
1062
|
+
eq: ["identity"],
|
|
1063
|
+
sort: ["bucket_start_ms"]
|
|
1064
|
+
};
|
|
1065
|
+
/**
|
|
1066
|
+
* Counters of one period kind: hourly, daily, or lifetime at bucket 0. A
|
|
1067
|
+
* release or channel row counts downloads, launches, and failed launches,
|
|
1068
|
+
* and downloads a patch delivered and those that fell back from one. A
|
|
1069
|
+
* release's lifetime row counts its failed updates (`failed_updates`), since
|
|
1070
|
+
* no breakdown row outlives the raw period; windowed reads sum
|
|
1071
|
+
* `insights_failures` instead.
|
|
1072
|
+
*/
|
|
1073
|
+
var counters = (retention) => defineAggregate(identityFields, {
|
|
1074
|
+
key: ["identity", "bucket_start_ms"],
|
|
1075
|
+
counters: [
|
|
1076
|
+
"downloads",
|
|
1077
|
+
"launches",
|
|
1078
|
+
"failed_launches",
|
|
1079
|
+
"failed_updates",
|
|
1080
|
+
"patch_downloads",
|
|
1081
|
+
"patch_fallbacks"
|
|
1082
|
+
],
|
|
1083
|
+
shards: COUNTER_SHARDS,
|
|
1084
|
+
batched: true,
|
|
1085
|
+
indexes: { window },
|
|
1086
|
+
...retention === void 0 ? {} : { retention }
|
|
1087
|
+
});
|
|
1088
|
+
/**
|
|
1089
|
+
* Unique-installation sketches of one period kind. Rows of a failure
|
|
1090
|
+
* identity (`failure` and `check` scopes) keep only `failed_users`, so a
|
|
1091
|
+
* release's launch rows never grow by a failure's registers.
|
|
1092
|
+
*/
|
|
1093
|
+
var sketches = (retention) => defineAggregate(identityFields, {
|
|
1094
|
+
key: ["identity", "bucket_start_ms"],
|
|
1095
|
+
distinct: [
|
|
1096
|
+
"launch_users",
|
|
1097
|
+
"activity_users",
|
|
1098
|
+
"failed_users"
|
|
1099
|
+
],
|
|
1100
|
+
shards: SKETCH_SHARDS,
|
|
1101
|
+
batched: true,
|
|
1102
|
+
indexes: { window },
|
|
1103
|
+
retention
|
|
1104
|
+
});
|
|
1105
|
+
/** Installations whose update to a release failed, since its first failure: kept, at bucket 0. */
|
|
1106
|
+
var lifetimeSketches = () => defineAggregate(identityFields, {
|
|
1107
|
+
key: ["identity", "bucket_start_ms"],
|
|
1108
|
+
distinct: ["failed_users"],
|
|
1109
|
+
shards: SKETCH_SHARDS,
|
|
1110
|
+
batched: true,
|
|
1111
|
+
indexes: { window }
|
|
1112
|
+
});
|
|
1113
|
+
/**
|
|
1114
|
+
* Update failures by what failed, and recoveries by why the crashed process
|
|
1115
|
+
* exited, a row an hour. `release_id` is the failed update's target release,
|
|
1116
|
+
* or empty for a failed check or an unknown target, and the recovered
|
|
1117
|
+
* release for a recovery. A failure's `stage` and `reason` say where and why
|
|
1118
|
+
* it failed, and `detail` what else the client knew, as the JSON array
|
|
1119
|
+
* `[resource, httpStatus, originCode, transport]`. A recovery's stage is
|
|
1120
|
+
* `launch` and its reason the exit reason, with an empty detail. Every index
|
|
1121
|
+
* is in key order, so its counters stay blind increments.
|
|
1122
|
+
*/
|
|
1123
|
+
var insightsFailures = (retention) => defineAggregate({
|
|
1124
|
+
platform: {
|
|
1125
|
+
type: "string",
|
|
1126
|
+
maxLength: 16
|
|
1127
|
+
},
|
|
1128
|
+
channel: { type: "string" },
|
|
1129
|
+
bucket_start_ms: { type: "integer" },
|
|
1130
|
+
release_id: {
|
|
1131
|
+
type: "string",
|
|
1132
|
+
maxLength: 36
|
|
1133
|
+
},
|
|
1134
|
+
stage: {
|
|
1135
|
+
type: "string",
|
|
1136
|
+
maxLength: 16
|
|
1137
|
+
},
|
|
1138
|
+
reason: {
|
|
1139
|
+
type: "string",
|
|
1140
|
+
maxLength: 64
|
|
1141
|
+
},
|
|
1142
|
+
detail: {
|
|
1143
|
+
type: "string",
|
|
1144
|
+
maxLength: 160
|
|
1145
|
+
}
|
|
1146
|
+
}, {
|
|
1147
|
+
key: [
|
|
1148
|
+
"platform",
|
|
1149
|
+
"channel",
|
|
1150
|
+
"bucket_start_ms",
|
|
1151
|
+
"release_id",
|
|
1152
|
+
"stage",
|
|
1153
|
+
"reason",
|
|
1154
|
+
"detail"
|
|
1155
|
+
],
|
|
1156
|
+
counters: ["events"],
|
|
1157
|
+
shards: COUNTER_SHARDS,
|
|
1158
|
+
batched: true,
|
|
1159
|
+
indexes: { byScope: {
|
|
1160
|
+
eq: ["platform", "channel"],
|
|
1161
|
+
sort: ["bucket_start_ms"]
|
|
1162
|
+
} },
|
|
1163
|
+
retention
|
|
1164
|
+
});
|
|
1165
|
+
/** Installations counted in the UTC day of their latest event, so reads sum whole UTC days. */
|
|
1166
|
+
var insightsDistribution = (retention) => defineAggregate({
|
|
1167
|
+
channel: { type: "string" },
|
|
1168
|
+
platform: {
|
|
1169
|
+
type: "string",
|
|
1170
|
+
maxLength: 16
|
|
1171
|
+
},
|
|
1172
|
+
app_version: { type: "string" },
|
|
1173
|
+
release_id: {
|
|
1174
|
+
type: "string",
|
|
1175
|
+
maxLength: 36
|
|
1176
|
+
},
|
|
1177
|
+
bucket_start_ms: { type: "integer" }
|
|
1178
|
+
}, {
|
|
1179
|
+
key: [
|
|
1180
|
+
"channel",
|
|
1181
|
+
"platform",
|
|
1182
|
+
"app_version",
|
|
1183
|
+
"release_id",
|
|
1184
|
+
"bucket_start_ms"
|
|
1185
|
+
],
|
|
1186
|
+
gauges: ["latest_installations"],
|
|
1187
|
+
shards: GAUGE_SHARDS,
|
|
1188
|
+
batched: true,
|
|
1189
|
+
indexes: {
|
|
1190
|
+
byScope: {
|
|
1191
|
+
eq: ["channel", "platform"],
|
|
1192
|
+
sort: ["bucket_start_ms"]
|
|
1193
|
+
},
|
|
1194
|
+
byVersion: {
|
|
1195
|
+
eq: [
|
|
1196
|
+
"channel",
|
|
1197
|
+
"platform",
|
|
1198
|
+
"app_version"
|
|
1199
|
+
],
|
|
1200
|
+
sort: ["bucket_start_ms"]
|
|
1201
|
+
}
|
|
1202
|
+
},
|
|
1203
|
+
retention
|
|
1204
|
+
});
|
|
1205
|
+
/** Latest events by the bundle they came from or went to, bucketed like the distribution. */
|
|
1206
|
+
var insightsLatestByBundle = (retention) => defineAggregate({
|
|
1207
|
+
platform: {
|
|
1208
|
+
type: "string",
|
|
1209
|
+
maxLength: 16
|
|
1210
|
+
},
|
|
1211
|
+
channel: { type: "string" },
|
|
1212
|
+
bundle_field: {
|
|
1213
|
+
type: "string",
|
|
1214
|
+
maxLength: 16
|
|
1215
|
+
},
|
|
1216
|
+
bundle_id: {
|
|
1217
|
+
type: "string",
|
|
1218
|
+
maxLength: 36
|
|
1219
|
+
},
|
|
1220
|
+
type: {
|
|
1221
|
+
type: "string",
|
|
1222
|
+
maxLength: 32
|
|
1223
|
+
},
|
|
1224
|
+
bucket_start_ms: { type: "integer" }
|
|
1225
|
+
}, {
|
|
1226
|
+
key: [
|
|
1227
|
+
"platform",
|
|
1228
|
+
"channel",
|
|
1229
|
+
"bundle_field",
|
|
1230
|
+
"bundle_id",
|
|
1231
|
+
"type",
|
|
1232
|
+
"bucket_start_ms"
|
|
1233
|
+
],
|
|
1234
|
+
gauges: ["installations"],
|
|
1235
|
+
shards: GAUGE_SHARDS,
|
|
1236
|
+
batched: true,
|
|
1237
|
+
indexes: { byBundle: {
|
|
1238
|
+
eq: [
|
|
1239
|
+
"platform",
|
|
1240
|
+
"channel",
|
|
1241
|
+
"bundle_field",
|
|
1242
|
+
"bundle_id",
|
|
1243
|
+
"type"
|
|
1244
|
+
],
|
|
1245
|
+
sort: ["bucket_start_ms"]
|
|
1246
|
+
} },
|
|
1247
|
+
retention
|
|
1248
|
+
});
|
|
1249
|
+
/**
|
|
1250
|
+
* Stored events by outcome and the bundle a bundle filter matches, a row an
|
|
1251
|
+
* hour, which a failed check has none of; and every stored event, a row a UTC
|
|
1252
|
+
* day (`DAILY_EVENTS`). Bundle counts sum the hourly rows, and event lists
|
|
1253
|
+
* read either to skip days without events.
|
|
1254
|
+
*/
|
|
1255
|
+
var insightsOutcomes = (retention) => defineAggregate({
|
|
1256
|
+
platform: {
|
|
1257
|
+
type: "string",
|
|
1258
|
+
maxLength: 16
|
|
1259
|
+
},
|
|
1260
|
+
channel: { type: "string" },
|
|
1261
|
+
type: {
|
|
1262
|
+
type: "string",
|
|
1263
|
+
maxLength: 32
|
|
1264
|
+
},
|
|
1265
|
+
bundle_ref: {
|
|
1266
|
+
type: "string",
|
|
1267
|
+
maxLength: 41
|
|
1268
|
+
},
|
|
1269
|
+
bucket_start_ms: { type: "integer" }
|
|
1270
|
+
}, {
|
|
1271
|
+
key: [
|
|
1272
|
+
"platform",
|
|
1273
|
+
"channel",
|
|
1274
|
+
"type",
|
|
1275
|
+
"bundle_ref",
|
|
1276
|
+
"bucket_start_ms"
|
|
1277
|
+
],
|
|
1278
|
+
counters: ["events"],
|
|
1279
|
+
shards: COUNTER_SHARDS,
|
|
1280
|
+
batched: true,
|
|
1281
|
+
indexes: { byRef: {
|
|
1282
|
+
eq: [
|
|
1283
|
+
"platform",
|
|
1284
|
+
"channel",
|
|
1285
|
+
"type",
|
|
1286
|
+
"bundle_ref"
|
|
1287
|
+
],
|
|
1288
|
+
sort: ["bucket_start_ms"]
|
|
1289
|
+
} },
|
|
1290
|
+
retention
|
|
1291
|
+
});
|
|
1292
|
+
/**
|
|
1293
|
+
* Insights' tables, each expiring after its period. The periods are values
|
|
1294
|
+
* the engine applies at runtime; the tables and indexes are the same for
|
|
1295
|
+
* any periods.
|
|
1296
|
+
*/
|
|
1297
|
+
var createInsightsSchema = ({ rawDays, dailyDays } = DEFAULT_INSIGHTS_RETENTION) => {
|
|
1298
|
+
const hourly = bucketRetention(rawDays);
|
|
1299
|
+
const daily = bucketRetention(dailyDays);
|
|
1300
|
+
return {
|
|
1301
|
+
bundle_events: bundleEvents(rawDays),
|
|
1302
|
+
bundle_event_heads: bundleEventHeads(dailyDays),
|
|
1303
|
+
insights_overview: counters(hourly),
|
|
1304
|
+
insights_sketches: sketches(hourly),
|
|
1305
|
+
insights_overview_daily: counters(daily),
|
|
1306
|
+
insights_sketches_daily: sketches(daily),
|
|
1307
|
+
insights_overview_lifetime: counters(),
|
|
1308
|
+
insights_sketches_lifetime: lifetimeSketches(),
|
|
1309
|
+
insights_distribution: insightsDistribution(daily),
|
|
1310
|
+
insights_latest_by_bundle: insightsLatestByBundle(daily),
|
|
1311
|
+
insights_outcomes: insightsOutcomes(hourly),
|
|
1312
|
+
insights_failures: insightsFailures(hourly)
|
|
1313
|
+
};
|
|
1314
|
+
};
|
|
1315
|
+
createInsightsSchema();
|
|
1316
|
+
30 * DAY_MS;
|
|
1225
1317
|
/** The `error` a failed admin route answered with, or a message naming its status. */
|
|
1226
1318
|
var errorOf = async (response, path) => {
|
|
1227
1319
|
const body = await response.json().catch(() => null);
|
|
@@ -1303,22 +1395,13 @@ var consoleFeatureApis = {
|
|
|
1303
1395
|
apiKeys: featureApi({ local: (api) => api })
|
|
1304
1396
|
};
|
|
1305
1397
|
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
1306
|
-
/**
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
};
|
|
1314
|
-
/** Runs `load` once and keeps its result; a failure is not kept, so the next call retries. */
|
|
1315
|
-
var once = (load) => {
|
|
1316
|
-
let pending;
|
|
1317
|
-
return () => pending ??= load().catch((error) => {
|
|
1318
|
-
pending = void 0;
|
|
1319
|
-
throw error;
|
|
1320
|
-
});
|
|
1321
|
-
};
|
|
1398
|
+
/**
|
|
1399
|
+
* Whether one of Hot Updater's own factories made `plugin`: the brand they
|
|
1400
|
+
* set under a registered symbol, read here with a copy of the key so the
|
|
1401
|
+
* console imports no package's internals. Assembly refuses any other plugin
|
|
1402
|
+
* that takes one of their ids, so features key on it.
|
|
1403
|
+
*/
|
|
1404
|
+
var isOfficialPlugin = (plugin) => isRecord(plugin) && Reflect.get(plugin, Symbol.for("@hot-updater/server/official-plugin")) === true;
|
|
1322
1405
|
var featureIds = Object.keys(consoleFeatureApis);
|
|
1323
1406
|
/**
|
|
1324
1407
|
* What serves each feature, built by its factory: over the plugins' APIs the
|
|
@@ -1330,30 +1413,34 @@ var featureApis = (build) => Object.fromEntries(featureIds.flatMap((feature) =>
|
|
|
1330
1413
|
return api === void 0 ? [] : [[feature, api]];
|
|
1331
1414
|
}));
|
|
1332
1415
|
/**
|
|
1333
|
-
* The console's features and what serves them, for
|
|
1334
|
-
*
|
|
1335
|
-
*
|
|
1336
|
-
*
|
|
1337
|
-
*
|
|
1338
|
-
*
|
|
1416
|
+
* The console's features and what serves them, for the plugins its config
|
|
1417
|
+
* lists, the plugins the server runs:
|
|
1418
|
+
* - a self-hosted server (`standaloneRepository`) runs them itself; the
|
|
1419
|
+
* console serves what that server's admin API serves of their features,
|
|
1420
|
+
* and nothing that needs the database;
|
|
1421
|
+
* - otherwise the console runs them over the database, as the server does,
|
|
1422
|
+
* and serves their features through `api`, their APIs.
|
|
1339
1423
|
*/
|
|
1340
1424
|
var createConsoleRuntime = (config) => {
|
|
1341
1425
|
const { database } = config;
|
|
1426
|
+
const official = new Set(config.plugins.filter(isOfficialPlugin).map((plugin) => plugin.id));
|
|
1342
1427
|
if (isRemoteDatabase(database)) {
|
|
1343
1428
|
const { fetchAdmin } = database;
|
|
1429
|
+
const features = resolveConsoleFeatures([...official], { remote: true });
|
|
1344
1430
|
return {
|
|
1345
1431
|
remote: true,
|
|
1346
|
-
features:
|
|
1432
|
+
features: async () => features,
|
|
1347
1433
|
apis: featureApis((feature) => consoleFeatureApis[feature].remote?.(fetchAdmin))
|
|
1348
1434
|
};
|
|
1349
1435
|
}
|
|
1350
|
-
const api =
|
|
1351
|
-
const features = resolveConsoleFeatures(
|
|
1436
|
+
const api = config.api ?? {};
|
|
1437
|
+
const features = resolveConsoleFeatures([...official], { remote: false });
|
|
1352
1438
|
return {
|
|
1353
1439
|
remote: false,
|
|
1354
1440
|
features: async () => features,
|
|
1355
1441
|
apis: featureApis((feature) => {
|
|
1356
|
-
const
|
|
1442
|
+
const { plugin } = consoleFeatures[feature];
|
|
1443
|
+
const pluginApi = official.has(plugin) ? api[plugin] : void 0;
|
|
1357
1444
|
return pluginApi === void 0 ? void 0 : consoleFeatureApis[feature].local(pluginApi);
|
|
1358
1445
|
})
|
|
1359
1446
|
};
|