@hot-updater/server 1.0.0-rc.0 → 1.0.0-rc.1

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.
Files changed (75) hide show
  1. package/dist/adapters/drizzle.cjs +3 -3
  2. package/dist/adapters/drizzle.mjs +3 -3
  3. package/dist/createHotUpdaterCore.cjs +6 -6
  4. package/dist/createHotUpdaterCore.d.cts +7 -7
  5. package/dist/createHotUpdaterCore.d.mts +7 -7
  6. package/dist/createHotUpdaterCore.mjs +6 -6
  7. package/dist/db/types.cjs +1 -1
  8. package/dist/db/types.mjs +1 -1
  9. package/dist/handler.cjs +5 -5
  10. package/dist/handler.mjs +5 -5
  11. package/dist/index.cjs +2 -2
  12. package/dist/index.d.cts +4 -4
  13. package/dist/index.d.mts +4 -4
  14. package/dist/index.mjs +2 -2
  15. package/dist/{analytics → insights}/bounded/activeOverview.cjs +1 -1
  16. package/dist/{analytics → insights}/bounded/activeOverview.mjs +1 -1
  17. package/dist/{analytics → insights}/bounded/installationSearch.cjs +1 -1
  18. package/dist/{analytics → insights}/bounded/installationSearch.mjs +1 -1
  19. package/dist/{analytics → insights}/bounded/persistence.cjs +1 -1
  20. package/dist/{analytics → insights}/bounded/persistence.mjs +1 -1
  21. package/dist/{analytics → insights}/bounded/provider.cjs +9 -9
  22. package/dist/insights/bounded/provider.d.cts +7 -0
  23. package/dist/insights/bounded/provider.d.mts +7 -0
  24. package/dist/{analytics → insights}/bounded/provider.mjs +10 -10
  25. package/dist/{analytics → insights}/bounded/scan.cjs +15 -15
  26. package/dist/{analytics → insights}/bounded/scan.mjs +16 -16
  27. package/dist/{analytics → insights}/domain.d.cts +12 -12
  28. package/dist/{analytics → insights}/domain.d.mts +12 -12
  29. package/dist/insights/errors.cjs +22 -0
  30. package/dist/insights/errors.mjs +20 -0
  31. package/dist/{analytics → insights}/eventInput.cjs +10 -10
  32. package/dist/{analytics → insights}/eventInput.mjs +11 -11
  33. package/dist/insights/persistence.d.cts +6 -0
  34. package/dist/insights/persistence.d.mts +6 -0
  35. package/dist/{analytics → insights}/queryInput.cjs +9 -9
  36. package/dist/{analytics → insights}/queryInput.mjs +10 -10
  37. package/dist/{analytics → insights}/routes.cjs +16 -16
  38. package/dist/{analytics → insights}/routes.mjs +16 -16
  39. package/dist/{analytics → insights}/types.d.cts +6 -6
  40. package/dist/{analytics → insights}/types.d.mts +6 -6
  41. package/dist/package.cjs +1 -1
  42. package/dist/package.mjs +1 -1
  43. package/package.json +4 -4
  44. package/src/adapters/drizzle.ts +3 -3
  45. package/src/apiKeys.integration.spec.ts +2 -2
  46. package/src/createHotUpdaterCore.ts +13 -13
  47. package/src/db/types.spec.ts +3 -3
  48. package/src/db/types.ts +7 -7
  49. package/src/handler-standalone.integration.spec.ts +1 -1
  50. package/src/handler.ts +12 -12
  51. package/src/index.ts +3 -3
  52. package/src/{analytics → insights}/bounded/provider.spec.ts +21 -21
  53. package/src/{analytics → insights}/bounded/provider.ts +18 -18
  54. package/src/{analytics → insights}/bounded/scan.ts +26 -26
  55. package/src/{analytics → insights}/domain.ts +10 -10
  56. package/src/insights/errors.ts +18 -0
  57. package/src/{analytics → insights}/eventInput.ts +11 -11
  58. package/src/{analytics/analytics.integration.spec.ts → insights/insights.integration.spec.ts} +6 -6
  59. package/src/insights/persistence.ts +10 -0
  60. package/src/{analytics → insights}/queryInput.ts +11 -11
  61. package/src/{analytics → insights}/routes.ts +18 -18
  62. package/src/{analytics → insights}/types.ts +7 -7
  63. package/dist/analytics/bounded/provider.d.cts +0 -7
  64. package/dist/analytics/bounded/provider.d.mts +0 -7
  65. package/dist/analytics/errors.cjs +0 -22
  66. package/dist/analytics/errors.mjs +0 -20
  67. package/dist/analytics/persistence.d.cts +0 -6
  68. package/dist/analytics/persistence.d.mts +0 -6
  69. package/src/analytics/errors.ts +0 -18
  70. package/src/analytics/persistence.ts +0 -10
  71. /package/src/{analytics → insights}/bounded/activeOverview.spec.ts +0 -0
  72. /package/src/{analytics → insights}/bounded/activeOverview.ts +0 -0
  73. /package/src/{analytics → insights}/bounded/installationSearch.spec.ts +0 -0
  74. /package/src/{analytics → insights}/bounded/installationSearch.ts +0 -0
  75. /package/src/{analytics → insights}/bounded/persistence.ts +0 -0
@@ -1,39 +1,39 @@
1
1
  const require_errors = require("./errors.cjs");
2
2
  const require_eventInput = require("./eventInput.cjs");
3
3
  const require_queryInput = require("./queryInput.cjs");
4
- //#region src/analytics/routes.ts
4
+ //#region src/insights/routes.ts
5
5
  const json = (body, status) => Response.json(body, {
6
6
  headers: { "cache-control": "private, no-store" },
7
7
  status
8
8
  });
9
9
  const requireParam = (params, key) => {
10
10
  const value = params[key];
11
- if (value === void 0 || value.length === 0) throw new require_errors.AnalyticsBadRequestError(`Missing route parameter: ${key}`);
11
+ if (value === void 0 || value.length === 0) throw new require_errors.InsightsBadRequestError(`Missing route parameter: ${key}`);
12
12
  return value;
13
13
  };
14
14
  const run = async (operation) => {
15
15
  try {
16
16
  return await operation();
17
17
  } catch (error) {
18
- if (error instanceof require_errors.AnalyticsBadRequestError) return json({ error: error.message }, 400);
19
- if (error instanceof require_errors.AnalyticsPayloadTooLargeError) return json({ error: error.message }, 413);
20
- if (error instanceof require_errors.AnalyticsScanLimitExceededError) return json({ error: {
21
- code: "ANALYTICS_SCAN_LIMIT_EXCEEDED",
18
+ if (error instanceof require_errors.InsightsBadRequestError) return json({ error: error.message }, 400);
19
+ if (error instanceof require_errors.InsightsPayloadTooLargeError) return json({ error: error.message }, 413);
20
+ if (error instanceof require_errors.InsightsScanLimitExceededError) return json({ error: {
21
+ code: "INSIGHTS_SCAN_LIMIT_EXCEEDED",
22
22
  limit: error.limit
23
23
  } }, 503);
24
24
  throw error;
25
25
  }
26
26
  };
27
27
  const query = (operation) => run(async () => json(await operation(), 200));
28
- const createAnalyticsRouteHandlers = (provider) => ({
28
+ const createInsightsRouteHandlers = (provider) => ({
29
29
  appendBundleEvent: async (_params, request) => run(async () => {
30
30
  await provider.appendBundleEvent(await require_eventInput.parseBundleEventRequest(request));
31
31
  return new Response(null, { status: 204 });
32
32
  }),
33
33
  getBundleEventSummary: (params) => query(() => provider.getBundleEventSummary(requireParam(params, "id"))),
34
- getBundleEventAnalytics: (params, request) => query(() => {
35
- const input = require_queryInput.parseAnalyticsQuery(request);
36
- return provider.getBundleEventAnalytics(requireParam(params, "id"), input.window, input.limit, input.offset);
34
+ getBundleEventInsights: (params, request) => query(() => {
35
+ const input = require_queryInput.parseInsightsQuery(request);
36
+ return provider.getBundleEventInsights(requireParam(params, "id"), input.window, input.limit, input.offset);
37
37
  }),
38
38
  getBundleEventOverview: () => query(() => provider.getBundleEventOverview()),
39
39
  getActiveInstallationOverview: (_params, request) => query(() => provider.getActiveInstallationOverview(require_queryInput.parseActiveInstallationInput(request))),
@@ -46,18 +46,18 @@ const createAnalyticsRouteHandlers = (provider) => ({
46
46
  return provider.getInstallationHistory(requireParam(params, "installId"), input.limit, input.offset);
47
47
  })
48
48
  });
49
- const registerAnalyticsClientRoutes = (add) => {
49
+ const registerInsightsClientRoutes = (add) => {
50
50
  add("POST", "/events", "appendBundleEvent");
51
51
  };
52
- const registerAnalyticsAdminRoutes = (add) => {
52
+ const registerInsightsAdminRoutes = (add) => {
53
53
  add("GET", "/bundles/:id/events/summary", "getBundleEventSummary");
54
- add("GET", "/bundles/:id/events/analytics", "getBundleEventAnalytics");
54
+ add("GET", "/bundles/:id/events/insights", "getBundleEventInsights");
55
55
  add("GET", "/installations/overview", "getBundleEventOverview");
56
56
  add("GET", "/installations/active", "getActiveInstallationOverview");
57
57
  add("GET", "/installations", "searchInstallations");
58
58
  add("GET", "/installations/:installId/events", "getInstallationHistory");
59
59
  };
60
60
  //#endregion
61
- exports.createAnalyticsRouteHandlers = createAnalyticsRouteHandlers;
62
- exports.registerAnalyticsAdminRoutes = registerAnalyticsAdminRoutes;
63
- exports.registerAnalyticsClientRoutes = registerAnalyticsClientRoutes;
61
+ exports.createInsightsRouteHandlers = createInsightsRouteHandlers;
62
+ exports.registerInsightsAdminRoutes = registerInsightsAdminRoutes;
63
+ exports.registerInsightsClientRoutes = registerInsightsClientRoutes;
@@ -1,39 +1,39 @@
1
- import { AnalyticsBadRequestError, AnalyticsPayloadTooLargeError, AnalyticsScanLimitExceededError } from "./errors.mjs";
1
+ import { InsightsBadRequestError, InsightsPayloadTooLargeError, InsightsScanLimitExceededError } from "./errors.mjs";
2
2
  import { parseBundleEventRequest } from "./eventInput.mjs";
3
- import { parseActiveInstallationInput, parseAnalyticsQuery, parsePagination, parseSearchInput } from "./queryInput.mjs";
4
- //#region src/analytics/routes.ts
3
+ import { parseActiveInstallationInput, parseInsightsQuery, parsePagination, parseSearchInput } from "./queryInput.mjs";
4
+ //#region src/insights/routes.ts
5
5
  const json = (body, status) => Response.json(body, {
6
6
  headers: { "cache-control": "private, no-store" },
7
7
  status
8
8
  });
9
9
  const requireParam = (params, key) => {
10
10
  const value = params[key];
11
- if (value === void 0 || value.length === 0) throw new AnalyticsBadRequestError(`Missing route parameter: ${key}`);
11
+ if (value === void 0 || value.length === 0) throw new InsightsBadRequestError(`Missing route parameter: ${key}`);
12
12
  return value;
13
13
  };
14
14
  const run = async (operation) => {
15
15
  try {
16
16
  return await operation();
17
17
  } catch (error) {
18
- if (error instanceof AnalyticsBadRequestError) return json({ error: error.message }, 400);
19
- if (error instanceof AnalyticsPayloadTooLargeError) return json({ error: error.message }, 413);
20
- if (error instanceof AnalyticsScanLimitExceededError) return json({ error: {
21
- code: "ANALYTICS_SCAN_LIMIT_EXCEEDED",
18
+ if (error instanceof InsightsBadRequestError) return json({ error: error.message }, 400);
19
+ if (error instanceof InsightsPayloadTooLargeError) return json({ error: error.message }, 413);
20
+ if (error instanceof InsightsScanLimitExceededError) return json({ error: {
21
+ code: "INSIGHTS_SCAN_LIMIT_EXCEEDED",
22
22
  limit: error.limit
23
23
  } }, 503);
24
24
  throw error;
25
25
  }
26
26
  };
27
27
  const query = (operation) => run(async () => json(await operation(), 200));
28
- const createAnalyticsRouteHandlers = (provider) => ({
28
+ const createInsightsRouteHandlers = (provider) => ({
29
29
  appendBundleEvent: async (_params, request) => run(async () => {
30
30
  await provider.appendBundleEvent(await parseBundleEventRequest(request));
31
31
  return new Response(null, { status: 204 });
32
32
  }),
33
33
  getBundleEventSummary: (params) => query(() => provider.getBundleEventSummary(requireParam(params, "id"))),
34
- getBundleEventAnalytics: (params, request) => query(() => {
35
- const input = parseAnalyticsQuery(request);
36
- return provider.getBundleEventAnalytics(requireParam(params, "id"), input.window, input.limit, input.offset);
34
+ getBundleEventInsights: (params, request) => query(() => {
35
+ const input = parseInsightsQuery(request);
36
+ return provider.getBundleEventInsights(requireParam(params, "id"), input.window, input.limit, input.offset);
37
37
  }),
38
38
  getBundleEventOverview: () => query(() => provider.getBundleEventOverview()),
39
39
  getActiveInstallationOverview: (_params, request) => query(() => provider.getActiveInstallationOverview(parseActiveInstallationInput(request))),
@@ -46,16 +46,16 @@ const createAnalyticsRouteHandlers = (provider) => ({
46
46
  return provider.getInstallationHistory(requireParam(params, "installId"), input.limit, input.offset);
47
47
  })
48
48
  });
49
- const registerAnalyticsClientRoutes = (add) => {
49
+ const registerInsightsClientRoutes = (add) => {
50
50
  add("POST", "/events", "appendBundleEvent");
51
51
  };
52
- const registerAnalyticsAdminRoutes = (add) => {
52
+ const registerInsightsAdminRoutes = (add) => {
53
53
  add("GET", "/bundles/:id/events/summary", "getBundleEventSummary");
54
- add("GET", "/bundles/:id/events/analytics", "getBundleEventAnalytics");
54
+ add("GET", "/bundles/:id/events/insights", "getBundleEventInsights");
55
55
  add("GET", "/installations/overview", "getBundleEventOverview");
56
56
  add("GET", "/installations/active", "getActiveInstallationOverview");
57
57
  add("GET", "/installations", "searchInstallations");
58
58
  add("GET", "/installations/:installId/events", "getInstallationHistory");
59
59
  };
60
60
  //#endregion
61
- export { createAnalyticsRouteHandlers, registerAnalyticsAdminRoutes, registerAnalyticsClientRoutes };
61
+ export { createInsightsRouteHandlers, registerInsightsAdminRoutes, registerInsightsClientRoutes };
@@ -1,17 +1,17 @@
1
- import { ActiveInstallationInput, ActiveInstallationOverview, BundleEventAnalyticsResult, BundleEventAnalyticsWindow, BundleEventOverview, BundleEventSummary, BundleEventSummaryByBundle, CreateBundleEventRequest, InstallationHistoryRow, InstallationSearchRow, OffsetPaginationResult } from "./domain.cjs";
1
+ import { ActiveInstallationInput, ActiveInstallationOverview, BundleEventInsightsResult, BundleEventInsightsWindow, BundleEventOverview, BundleEventSummary, BundleEventSummaryByBundle, CreateBundleEventRequest, InstallationHistoryRow, InstallationSearchRow, OffsetPaginationResult } from "./domain.cjs";
2
2
 
3
- //#region src/analytics/types.d.ts
4
- type AnalyticsProvider = {
3
+ //#region src/insights/types.d.ts
4
+ type InsightsProvider = {
5
5
  readonly mode: "bounded";
6
6
  readonly maxMatchingRows: number;
7
7
  appendBundleEvent(input: CreateBundleEventRequest): Promise<void>;
8
8
  getBundleEventSummary(bundleId: string): Promise<BundleEventSummary>;
9
- getBundleEventSummaries(bundleIds: readonly string[], window: BundleEventAnalyticsWindow): Promise<readonly BundleEventSummaryByBundle[]>;
10
- getBundleEventAnalytics(bundleId: string, window: BundleEventAnalyticsWindow, limit: number, offset: number): Promise<BundleEventAnalyticsResult>;
9
+ getBundleEventSummaries(bundleIds: readonly string[], window: BundleEventInsightsWindow): Promise<readonly BundleEventSummaryByBundle[]>;
10
+ getBundleEventInsights(bundleId: string, window: BundleEventInsightsWindow, limit: number, offset: number): Promise<BundleEventInsightsResult>;
11
11
  getBundleEventOverview(): Promise<BundleEventOverview>;
12
12
  getActiveInstallationOverview(input: ActiveInstallationInput): Promise<ActiveInstallationOverview>;
13
13
  searchInstallations(query: string, limit: number, offset: number): Promise<OffsetPaginationResult<InstallationSearchRow>>;
14
14
  getInstallationHistory(installId: string, limit: number, offset: number): Promise<OffsetPaginationResult<InstallationHistoryRow>>;
15
15
  };
16
16
  //#endregion
17
- export { AnalyticsProvider };
17
+ export { InsightsProvider };
@@ -1,17 +1,17 @@
1
- import { ActiveInstallationInput, ActiveInstallationOverview, BundleEventAnalyticsResult, BundleEventAnalyticsWindow, BundleEventOverview, BundleEventSummary, BundleEventSummaryByBundle, CreateBundleEventRequest, InstallationHistoryRow, InstallationSearchRow, OffsetPaginationResult } from "./domain.mjs";
1
+ import { ActiveInstallationInput, ActiveInstallationOverview, BundleEventInsightsResult, BundleEventInsightsWindow, BundleEventOverview, BundleEventSummary, BundleEventSummaryByBundle, CreateBundleEventRequest, InstallationHistoryRow, InstallationSearchRow, OffsetPaginationResult } from "./domain.mjs";
2
2
 
3
- //#region src/analytics/types.d.ts
4
- type AnalyticsProvider = {
3
+ //#region src/insights/types.d.ts
4
+ type InsightsProvider = {
5
5
  readonly mode: "bounded";
6
6
  readonly maxMatchingRows: number;
7
7
  appendBundleEvent(input: CreateBundleEventRequest): Promise<void>;
8
8
  getBundleEventSummary(bundleId: string): Promise<BundleEventSummary>;
9
- getBundleEventSummaries(bundleIds: readonly string[], window: BundleEventAnalyticsWindow): Promise<readonly BundleEventSummaryByBundle[]>;
10
- getBundleEventAnalytics(bundleId: string, window: BundleEventAnalyticsWindow, limit: number, offset: number): Promise<BundleEventAnalyticsResult>;
9
+ getBundleEventSummaries(bundleIds: readonly string[], window: BundleEventInsightsWindow): Promise<readonly BundleEventSummaryByBundle[]>;
10
+ getBundleEventInsights(bundleId: string, window: BundleEventInsightsWindow, limit: number, offset: number): Promise<BundleEventInsightsResult>;
11
11
  getBundleEventOverview(): Promise<BundleEventOverview>;
12
12
  getActiveInstallationOverview(input: ActiveInstallationInput): Promise<ActiveInstallationOverview>;
13
13
  searchInstallations(query: string, limit: number, offset: number): Promise<OffsetPaginationResult<InstallationSearchRow>>;
14
14
  getInstallationHistory(installId: string, limit: number, offset: number): Promise<OffsetPaginationResult<InstallationHistoryRow>>;
15
15
  };
16
16
  //#endregion
17
- export { AnalyticsProvider };
17
+ export { InsightsProvider };
package/dist/package.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "1.0.0-rc.0";
2
+ var version = "1.0.0-rc.1";
3
3
  //#endregion
4
4
  Object.defineProperty(exports, "version", {
5
5
  enumerable: true,
package/dist/package.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "1.0.0-rc.0";
2
+ var version = "1.0.0-rc.1";
3
3
  //#endregion
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hot-updater/server",
3
- "version": "1.0.0-rc.0",
3
+ "version": "1.0.0-rc.1",
4
4
  "type": "module",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "sideEffects": false,
@@ -55,7 +55,7 @@
55
55
  "dependencies": {
56
56
  "rou3": "0.7.9",
57
57
  "verkit": "0.3.2",
58
- "@hot-updater/plugin-core": "1.0.0-rc.0",
58
+ "@hot-updater/plugin-core": "1.0.0-rc.1",
59
59
  "@hot-updater/bsdiff": "1.0.0-rc.0",
60
60
  "@hot-updater/core": "1.0.0-rc.0"
61
61
  },
@@ -91,8 +91,8 @@
91
91
  "mongodb": "6.20.0",
92
92
  "msw": "^2.7.0",
93
93
  "uuidv7": "^1.0.2",
94
- "@hot-updater/standalone": "1.0.0-rc.0",
95
- "@hot-updater/test-utils": "1.0.0-rc.0"
94
+ "@hot-updater/standalone": "1.0.0-rc.1",
95
+ "@hot-updater/test-utils": "1.0.0-rc.1"
96
96
  },
97
97
  "scripts": {
98
98
  "build": "tsdown",
@@ -115,9 +115,9 @@ export const drizzleAdapter = (
115
115
  list: (input) => getAdapter().models.channels.list(input),
116
116
  delete: (input) => getAdapter().models.channels.delete(input),
117
117
  },
118
- analytics: {
119
- append: (row) => getAdapter().models.analytics.append(row),
120
- scan: (input) => getAdapter().models.analytics.scan(input),
118
+ insights: {
119
+ append: (row) => getAdapter().models.insights.append(row),
120
+ scan: (input) => getAdapter().models.insights.scan(input),
121
121
  },
122
122
  apiKeys: {
123
123
  create: (row) => getAdapter().models.apiKeys.create(row),
@@ -31,7 +31,7 @@ describe("createHotUpdater API keys", () => {
31
31
  ).toThrow("clientAccess.headerName must be a valid header name.");
32
32
  });
33
33
 
34
- it("protects only client OTA and Analytics write routes", async () => {
34
+ it("protects only client OTA and Insights write routes", async () => {
35
35
  const database = createInMemoryDatabasePlugin();
36
36
  await registerApiKey({
37
37
  apiKeys: database.models.apiKeys,
@@ -86,7 +86,7 @@ describe("createHotUpdater API keys", () => {
86
86
  expect(revoked).not.toHaveProperty("hash");
87
87
  });
88
88
 
89
- it("authenticates before parsing Analytics event bodies", async () => {
89
+ it("authenticates before parsing Insights event bodies", async () => {
90
90
  const database = createInMemoryDatabasePlugin();
91
91
  await registerApiKey({
92
92
  apiKeys: database.models.apiKeys,
@@ -3,8 +3,6 @@ import {
3
3
  type StoragePlugin,
4
4
  } from "@hot-updater/plugin-core";
5
5
 
6
- import { createAnalyticsProvider } from "./analytics/bounded/provider";
7
- import type { AnalyticsProvider } from "./analytics/types";
8
6
  import {
9
7
  authenticateApiKey,
10
8
  createApiKeyManagement,
@@ -20,17 +18,19 @@ import {
20
18
  isDatabasePlugin,
21
19
  } from "./db/types";
22
20
  import { createHotUpdaterHandlers, type HotUpdaterHandlers } from "./handler";
21
+ import { createInsightsProvider } from "./insights/bounded/provider";
22
+ import type { InsightsProvider } from "./insights/types";
23
23
  import { createStorageAccess } from "./storageAccess";
24
24
 
25
25
  export type RuntimeHotUpdaterAPI = DatabaseAPI & {
26
26
  readonly handlers: HotUpdaterHandlers;
27
27
  readonly adapterName: string;
28
28
  /**
29
- * Built-in Analytics provider. Client ingestion and admin query routes are
30
- * always mounted; React Native clients independently opt into lifecycle
31
- * reporting with `HotUpdater.init({ analytics: true })`.
29
+ * Built-in Insights provider. Client ingestion and admin query routes are
30
+ * always mounted; React Native clients report lifecycle events by default
31
+ * and can opt out with `HotUpdater.init({ insights: false })`.
32
32
  */
33
- readonly analytics: AnalyticsProvider;
33
+ readonly insights: InsightsProvider;
34
34
  /**
35
35
  * In-process API key lifecycle operations for trusted server tooling.
36
36
  * Creation returns the plaintext once; list and revoke expose only metadata.
@@ -44,7 +44,7 @@ export type HotUpdaterAPI = RuntimeHotUpdaterAPI;
44
44
  export type ClientAccessPolicy =
45
45
  | {
46
46
  /**
47
- * Leaves Release Catalog, artifact, and Analytics ingestion routes
47
+ * Leaves Release Catalog, artifact, and Insights ingestion routes
48
48
  * publicly accessible without a client credential.
49
49
  */
50
50
  readonly type: "public";
@@ -52,7 +52,7 @@ export type ClientAccessPolicy =
52
52
  | {
53
53
  /**
54
54
  * Requires a key registered in `database.models.apiKeys` for
55
- * Release Catalog, artifact, and Analytics ingestion requests.
55
+ * Release Catalog, artifact, and Insights ingestion requests.
56
56
  */
57
57
  readonly type: "api-key";
58
58
  /**
@@ -168,14 +168,14 @@ export function createHotUpdaterCore(
168
168
  readStorageText,
169
169
  });
170
170
  const clientAccess = normalizeClientAccess(options.clientAccess);
171
- const analytics = createAnalyticsProvider({
171
+ const insights = createInsightsProvider({
172
172
  async append(row) {
173
173
  await assertSchemaReady();
174
- return plugin.models.analytics.append(row);
174
+ return plugin.models.insights.append(row);
175
175
  },
176
176
  async scan(input) {
177
177
  await assertSchemaReady();
178
- return plugin.models.analytics.scan(input);
178
+ return plugin.models.insights.scan(input);
179
179
  },
180
180
  });
181
181
  const apiKeys = createApiKeyManagement({
@@ -185,7 +185,7 @@ export function createHotUpdaterCore(
185
185
 
186
186
  const handlers = createHotUpdaterHandlers(
187
187
  core.api,
188
- analytics,
188
+ insights,
189
189
  clientAccess.type === "api-key"
190
190
  ? {
191
191
  authenticate: (request) =>
@@ -204,7 +204,7 @@ export function createHotUpdaterCore(
204
204
  const api: RuntimeHotUpdaterAPI = Object.assign(
205
205
  {
206
206
  adapterName: adapterCapabilities.adapterName ?? core.adapterName,
207
- analytics,
207
+ insights,
208
208
  apiKeys,
209
209
  handlers,
210
210
  },
@@ -22,9 +22,9 @@ describe("isDatabasePlugin", () => {
22
22
  bundles: { ...plugin.models.bundles, findMany: null },
23
23
  },
24
24
  };
25
- const missingAnalytics = {
25
+ const missingInsights = {
26
26
  ...plugin,
27
- models: { ...plugin.models, analytics: undefined },
27
+ models: { ...plugin.models, insights: undefined },
28
28
  };
29
29
  const malformedChannels = {
30
30
  ...plugin,
@@ -47,7 +47,7 @@ describe("isDatabasePlugin", () => {
47
47
 
48
48
  expect(isDatabasePlugin(factory)).toBe(false);
49
49
  expect(isDatabasePlugin(malformedBundles)).toBe(false);
50
- expect(isDatabasePlugin(missingAnalytics)).toBe(false);
50
+ expect(isDatabasePlugin(missingInsights)).toBe(false);
51
51
  expect(isDatabasePlugin(malformedChannels)).toBe(false);
52
52
  expect(isDatabasePlugin(malformedApiKeys)).toBe(false);
53
53
  expect(isDatabasePlugin(malformedDispose)).toBe(false);
package/src/db/types.ts CHANGED
@@ -123,13 +123,13 @@ export function isDatabasePlugin(plugin: unknown): plugin is DatabasePlugin {
123
123
  typeof plugin.models.channels.list === "function" &&
124
124
  "delete" in plugin.models.channels &&
125
125
  typeof plugin.models.channels.delete === "function" &&
126
- "analytics" in plugin.models &&
127
- typeof plugin.models.analytics === "object" &&
128
- plugin.models.analytics !== null &&
129
- "append" in plugin.models.analytics &&
130
- typeof plugin.models.analytics.append === "function" &&
131
- "scan" in plugin.models.analytics &&
132
- typeof plugin.models.analytics.scan === "function" &&
126
+ "insights" in plugin.models &&
127
+ typeof plugin.models.insights === "object" &&
128
+ plugin.models.insights !== null &&
129
+ "append" in plugin.models.insights &&
130
+ typeof plugin.models.insights.append === "function" &&
131
+ "scan" in plugin.models.insights &&
132
+ typeof plugin.models.insights.scan === "function" &&
133
133
  "apiKeys" in plugin.models &&
134
134
  typeof plugin.models.apiKeys === "object" &&
135
135
  plugin.models.apiKeys !== null &&
@@ -156,7 +156,7 @@ const commitTestRelease = async (
156
156
  };
157
157
 
158
158
  describe("Handler <-> Standalone Repository Integration", () => {
159
- it("uses Standalone for bundle management and the server database for Analytics", async () => {
159
+ it("uses Standalone for bundle management and the server database for Insights", async () => {
160
160
  const bundleId = uuidv7();
161
161
  const installId = "standalone-install";
162
162
  await createStandaloneClient().insertBundle(
package/src/handler.ts CHANGED
@@ -1,9 +1,3 @@
1
- import {
2
- createAnalyticsRouteHandlers,
3
- registerAnalyticsAdminRoutes,
4
- registerAnalyticsClientRoutes,
5
- } from "./analytics/routes";
6
- import type { AnalyticsProvider } from "./analytics/types";
7
1
  import { createBundleRouteHandlers } from "./handlerBundleRoutes";
8
2
  import { HandlerBadRequestError } from "./handlerErrors";
9
3
  import { createReleaseCatalogRouteHandlers } from "./handlerReleaseCatalogRoutes";
@@ -15,6 +9,12 @@ import type {
15
9
  RouteHandler,
16
10
  } from "./handlerTypes";
17
11
  import { createVersionRouteHandlers } from "./handlerVersionRoutes";
12
+ import {
13
+ createInsightsRouteHandlers,
14
+ registerInsightsAdminRoutes,
15
+ registerInsightsClientRoutes,
16
+ } from "./insights/routes";
17
+ import type { InsightsProvider } from "./insights/types";
18
18
  import { addRoute, createRouter, findRoute } from "./internalRouter";
19
19
 
20
20
  export type {
@@ -142,7 +142,7 @@ export function createHandlers(api: HandlerAPI): HotUpdaterHandlers {
142
142
 
143
143
  export function createHotUpdaterHandlers(
144
144
  api: HandlerAPI,
145
- analytics?: AnalyticsProvider,
145
+ insights?: InsightsProvider,
146
146
  apiKeyAuth?: {
147
147
  readonly authenticate: (request: Request) => Promise<boolean>;
148
148
  readonly headerName: string;
@@ -157,7 +157,7 @@ export function createHotUpdaterHandlers(
157
157
  ...createReleaseCatalogRouteHandlers(apiKeyAuth?.headerName),
158
158
  ...createReleaseManagementRouteHandlers(),
159
159
  ...createBundleRouteHandlers(),
160
- ...(analytics === undefined ? {} : createAnalyticsRouteHandlers(analytics)),
160
+ ...(insights === undefined ? {} : createInsightsRouteHandlers(insights)),
161
161
  ...(downloadStorageObject === undefined
162
162
  ? {}
163
163
  : {
@@ -196,8 +196,8 @@ export function createHotUpdaterHandlers(
196
196
  "/artifacts/:targetBundleId/from/:currentBundleId",
197
197
  "artifact",
198
198
  );
199
- if (analytics !== undefined) {
200
- registerAnalyticsClientRoutes(addClientRoute);
199
+ if (insights !== undefined) {
200
+ registerInsightsClientRoutes(addClientRoute);
201
201
  }
202
202
 
203
203
  const adminRouter = createRouter<string>();
@@ -224,8 +224,8 @@ export function createHotUpdaterHandlers(
224
224
  addAdminRoute("POST", "/bundles", "createBundles");
225
225
  addAdminRoute("PATCH", "/bundles/:id", "updateBundle");
226
226
  addAdminRoute("DELETE", "/bundles/:id", "deleteBundle");
227
- if (analytics !== undefined) {
228
- registerAnalyticsAdminRoutes(addAdminRoute);
227
+ if (insights !== undefined) {
228
+ registerInsightsAdminRoutes(addAdminRoute);
229
229
  }
230
230
 
231
231
  return Object.freeze({
package/src/index.ts CHANGED
@@ -4,9 +4,9 @@ export type {
4
4
  HotUpdaterHandler,
5
5
  HotUpdaterHandlers,
6
6
  } from "./handler";
7
- export { createAnalyticsProvider } from "./analytics/bounded/provider";
8
- export type * from "./analytics/domain";
9
- export type { AnalyticsProvider } from "./analytics/types";
7
+ export { createInsightsProvider } from "./insights/bounded/provider";
8
+ export type * from "./insights/domain";
9
+ export type { InsightsProvider } from "./insights/types";
10
10
  export {
11
11
  API_KEY_HEADER_NAME,
12
12
  createApiKey,
@@ -1,12 +1,12 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
 
3
- import { AnalyticsScanLimitExceededError } from "../errors";
3
+ import { InsightsScanLimitExceededError } from "../errors";
4
4
  import type {
5
- AnalyticsPersistence,
6
- AnalyticsScanInput,
5
+ InsightsPersistence,
6
+ InsightsScanInput,
7
7
  BundleEventPersistenceRow,
8
8
  } from "../persistence";
9
- import { createAnalyticsProvider } from "./provider";
9
+ import { createInsightsProvider } from "./provider";
10
10
  import { getWindowRange } from "./scan";
11
11
 
12
12
  const eventRow = (
@@ -42,13 +42,13 @@ const compareRows = (
42
42
  const inMemoryPersistence = (
43
43
  initialRows: readonly BundleEventPersistenceRow[],
44
44
  maximumPageSize = Number.MAX_SAFE_INTEGER,
45
- ): AnalyticsPersistence => {
45
+ ): InsightsPersistence => {
46
46
  const rows = [...initialRows];
47
47
  return {
48
48
  async append(row) {
49
49
  rows.push(row);
50
50
  },
51
- async scan(input: AnalyticsScanInput) {
51
+ async scan(input: InsightsScanInput) {
52
52
  return rows
53
53
  .filter((row) => row.received_at_ms < input.beforeReceivedAtMs)
54
54
  .filter(
@@ -64,19 +64,19 @@ const inMemoryPersistence = (
64
64
  };
65
65
  };
66
66
 
67
- describe("createAnalyticsProvider", () => {
68
- it("starts windowed analytics scans at the storage lower boundary", async () => {
67
+ describe("createInsightsProvider", () => {
68
+ it("starts windowed insights scans at the storage lower boundary", async () => {
69
69
  // Given
70
70
  const now = Date.UTC(2026, 1, 2, 12);
71
71
  vi.spyOn(Date, "now").mockReturnValue(now);
72
- const scan = vi.fn<AnalyticsPersistence["scan"]>().mockResolvedValue([]);
73
- const provider = createAnalyticsProvider({
72
+ const scan = vi.fn<InsightsPersistence["scan"]>().mockResolvedValue([]);
73
+ const provider = createInsightsProvider({
74
74
  async append() {},
75
75
  scan,
76
76
  });
77
77
 
78
78
  // When
79
- await provider.getBundleEventAnalytics("new", "24h", 20, 0);
79
+ await provider.getBundleEventInsights("new", "24h", 20, 0);
80
80
 
81
81
  // Then
82
82
  expect(scan).toHaveBeenCalledWith(
@@ -93,7 +93,7 @@ describe("createAnalyticsProvider", () => {
93
93
  const rows = Array.from({ length: 1_001 }, (_, index) =>
94
94
  eventRow(index.toString().padStart(4, "0"), 1_000, `install-${index}`),
95
95
  );
96
- const provider = createAnalyticsProvider(inMemoryPersistence(rows));
96
+ const provider = createInsightsProvider(inMemoryPersistence(rows));
97
97
 
98
98
  await expect(provider.getBundleEventSummary("new")).resolves.toEqual({
99
99
  installed: 1_001,
@@ -105,7 +105,7 @@ describe("createAnalyticsProvider", () => {
105
105
  const rows = Array.from({ length: 101 }, (_, index) =>
106
106
  eventRow(index.toString().padStart(4, "0"), 1_000, `install-${index}`),
107
107
  );
108
- const provider = createAnalyticsProvider(inMemoryPersistence(rows, 17));
108
+ const provider = createInsightsProvider(inMemoryPersistence(rows, 17));
109
109
 
110
110
  await expect(provider.getBundleEventSummary("new")).resolves.toEqual({
111
111
  installed: 101,
@@ -146,7 +146,7 @@ describe("createAnalyticsProvider", () => {
146
146
  other,
147
147
  ]);
148
148
  const scan = vi.spyOn(persistence, "scan");
149
- const provider = createAnalyticsProvider(persistence);
149
+ const provider = createInsightsProvider(persistence);
150
150
 
151
151
  await expect(
152
152
  provider.getBundleEventSummaries(["new", "other", "missing"], "30d"),
@@ -166,7 +166,7 @@ describe("createAnalyticsProvider", () => {
166
166
  from_bundle_id: null,
167
167
  update_strategy: null,
168
168
  };
169
- const provider = createAnalyticsProvider(inMemoryPersistence([unchanged]));
169
+ const provider = createInsightsProvider(inMemoryPersistence([unchanged]));
170
170
 
171
171
  await expect(provider.getBundleEventOverview()).resolves.toEqual({
172
172
  trackedInstallations: 1,
@@ -181,7 +181,7 @@ describe("createAnalyticsProvider", () => {
181
181
  "rejects duplicate ids and rows outside strict cursor order",
182
182
  async (...rows) => {
183
183
  let delivered = false;
184
- const provider = createAnalyticsProvider({
184
+ const provider = createInsightsProvider({
185
185
  async append() {},
186
186
  async scan() {
187
187
  if (delivered) return [];
@@ -192,14 +192,14 @@ describe("createAnalyticsProvider", () => {
192
192
 
193
193
  await expect(provider.getBundleEventSummary("new")).rejects.toMatchObject(
194
194
  {
195
- name: "AnalyticsPersistenceOrderError",
195
+ name: "InsightsPersistenceOrderError",
196
196
  },
197
197
  );
198
198
  },
199
199
  );
200
200
 
201
- it("fails instead of returning partial analytics beyond 50,000 rows", async () => {
202
- const persistence: AnalyticsPersistence = {
201
+ it("fails instead of returning partial insights beyond 50,000 rows", async () => {
202
+ const persistence: InsightsPersistence = {
203
203
  async append() {},
204
204
  async scan(input) {
205
205
  const start =
@@ -210,10 +210,10 @@ describe("createAnalyticsProvider", () => {
210
210
  });
211
211
  },
212
212
  };
213
- const provider = createAnalyticsProvider(persistence);
213
+ const provider = createInsightsProvider(persistence);
214
214
 
215
215
  await expect(provider.getBundleEventSummary("new")).rejects.toBeInstanceOf(
216
- AnalyticsScanLimitExceededError,
216
+ InsightsScanLimitExceededError,
217
217
  );
218
218
  });
219
219
  });