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

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 (191) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +1 -1
  2. package/dist/adapters/databaseConstraintErrors.cjs +0 -1
  3. package/dist/adapters/databasePluginUtils.cjs +0 -1
  4. package/dist/adapters/drizzle.cjs +10 -3
  5. package/dist/adapters/drizzle.d.cts +0 -1
  6. package/dist/adapters/drizzle.d.mts +0 -1
  7. package/dist/adapters/drizzle.mjs +11 -3
  8. package/dist/adapters/drizzleCrud.cjs +62 -1
  9. package/dist/adapters/drizzleCrud.mjs +61 -2
  10. package/dist/adapters/drizzleLazyDB.cjs +6 -1
  11. package/dist/adapters/drizzleLazyDB.mjs +6 -1
  12. package/dist/adapters/drizzleQuery.cjs +3 -2
  13. package/dist/adapters/drizzleQuery.mjs +3 -1
  14. package/dist/adapters/insightsTransaction.cjs +22 -0
  15. package/dist/adapters/insightsTransaction.mjs +22 -0
  16. package/dist/adapters/kysely.cjs +2 -1
  17. package/dist/adapters/kysely.d.cts +0 -1
  18. package/dist/adapters/kysely.d.mts +0 -1
  19. package/dist/adapters/kysely.mjs +3 -1
  20. package/dist/adapters/kyselyCrud.cjs +54 -3
  21. package/dist/adapters/kyselyCrud.mjs +54 -3
  22. package/dist/adapters/kyselyQuery.cjs +2 -2
  23. package/dist/adapters/kyselyQuery.mjs +2 -1
  24. package/dist/adapters/mongodb.cjs +27 -1
  25. package/dist/adapters/mongodb.d.cts +1 -1
  26. package/dist/adapters/mongodb.d.mts +1 -1
  27. package/dist/adapters/mongodb.mjs +28 -1
  28. package/dist/adapters/mongodbCollections.cjs +2 -0
  29. package/dist/adapters/mongodbCollections.mjs +2 -0
  30. package/dist/adapters/mongodbQuery.cjs +21 -4
  31. package/dist/adapters/mongodbQuery.mjs +21 -5
  32. package/dist/adapters/mongodbReads.cjs +91 -29
  33. package/dist/adapters/mongodbReads.mjs +92 -29
  34. package/dist/adapters/mongodbWrites.cjs +16 -2
  35. package/dist/adapters/mongodbWrites.mjs +18 -3
  36. package/dist/adapters/prisma.cjs +55 -7
  37. package/dist/adapters/prisma.d.cts +0 -1
  38. package/dist/adapters/prisma.d.mts +0 -1
  39. package/dist/adapters/prisma.mjs +56 -7
  40. package/dist/adapters/prismaQuery.cjs +0 -1
  41. package/dist/adapters/prismaRows.cjs +22 -1
  42. package/dist/adapters/prismaRows.mjs +22 -1
  43. package/dist/apiKeys.cjs +6 -2
  44. package/dist/apiKeys.d.cts +0 -1
  45. package/dist/apiKeys.d.mts +0 -1
  46. package/dist/apiKeys.mjs +3 -3
  47. package/dist/createHotUpdaterCore.cjs +24 -9
  48. package/dist/createHotUpdaterCore.d.cts +1 -1
  49. package/dist/createHotUpdaterCore.d.mts +1 -1
  50. package/dist/createHotUpdaterCore.mjs +23 -9
  51. package/dist/db/createBundleDiff.cjs +2 -2
  52. package/dist/db/createBundleDiff.d.cts +1 -5
  53. package/dist/db/createBundleDiff.d.mts +1 -5
  54. package/dist/db/databasePluginCore.cjs +0 -1
  55. package/dist/db/fixedMigrator.cjs +2 -2
  56. package/dist/db/fixedMigrator.mjs +2 -2
  57. package/dist/db/fixedMigratorMongo.cjs +6 -4
  58. package/dist/db/fixedMigratorMongo.mjs +6 -4
  59. package/dist/db/index.d.cts +0 -1
  60. package/dist/db/index.d.mts +0 -1
  61. package/dist/db/releaseCatalog.cjs +0 -1
  62. package/dist/db/releaseCatalog.d.cts +0 -1
  63. package/dist/db/releaseCatalog.d.mts +0 -1
  64. package/dist/db/schema/insightsCollation.cjs +14 -0
  65. package/dist/db/schema/insightsCollation.mjs +14 -0
  66. package/dist/db/schema/sql.cjs +11 -7
  67. package/dist/db/schema/sql.mjs +8 -8
  68. package/dist/db/schemaGenerators.cjs +28 -9
  69. package/dist/db/schemaGenerators.mjs +28 -9
  70. package/dist/db/schemaReadiness.cjs +2 -0
  71. package/dist/db/schemaReadiness.d.cts +1 -0
  72. package/dist/db/schemaReadiness.d.mts +1 -0
  73. package/dist/db/schemaReadiness.mjs +2 -0
  74. package/dist/db/sqlMigrationExecution.cjs +1 -1
  75. package/dist/db/sqlMigrationExecution.mjs +1 -0
  76. package/dist/db/types.cjs +3 -1
  77. package/dist/db/types.d.cts +3 -6
  78. package/dist/db/types.d.mts +3 -6
  79. package/dist/db/types.mjs +2 -2
  80. package/dist/db/updateArtifacts.cjs +0 -1
  81. package/dist/handler.d.cts +1 -2
  82. package/dist/handler.d.mts +1 -2
  83. package/dist/handlerReleaseCatalogRoutes.cjs +0 -1
  84. package/dist/handlerReleaseManagementRoutes.cjs +0 -1
  85. package/dist/handlerTypes.d.cts +0 -1
  86. package/dist/handlerTypes.d.mts +0 -1
  87. package/dist/handlerVersionRoutes.d.cts +1 -0
  88. package/dist/handlerVersionRoutes.d.mts +1 -0
  89. package/dist/index.cjs +1 -1
  90. package/dist/index.d.cts +4 -4
  91. package/dist/index.d.mts +4 -4
  92. package/dist/index.mjs +1 -1
  93. package/dist/insights/domain.d.cts +37 -69
  94. package/dist/insights/domain.d.mts +37 -69
  95. package/dist/insights/errors.cjs +1 -8
  96. package/dist/insights/errors.mjs +2 -8
  97. package/dist/insights/eventInput.cjs +49 -5
  98. package/dist/insights/eventInput.mjs +48 -6
  99. package/dist/insights/provider.cjs +314 -0
  100. package/dist/insights/provider.d.cts +6 -0
  101. package/dist/insights/provider.d.mts +6 -0
  102. package/dist/insights/provider.mjs +314 -0
  103. package/dist/insights/queryInput.cjs +78 -43
  104. package/dist/insights/queryInput.mjs +76 -40
  105. package/dist/insights/routes.cjs +20 -25
  106. package/dist/insights/routes.mjs +22 -27
  107. package/dist/insights/types.d.cts +27 -12
  108. package/dist/insights/types.d.mts +27 -12
  109. package/dist/node.d.cts +0 -1
  110. package/dist/node.d.mts +0 -1
  111. package/dist/package.cjs +1 -1
  112. package/dist/package.mjs +1 -1
  113. package/dist/schema/index.cjs +17 -3
  114. package/dist/schema/index.mjs +4 -4
  115. package/dist/schema/v1_0_0.cjs +72 -30
  116. package/dist/schema/v1_0_0.mjs +72 -31
  117. package/dist/storageAccess.cjs +0 -1
  118. package/dist/types/index.d.cts +0 -1
  119. package/dist/types/index.d.mts +0 -1
  120. package/package.json +7 -7
  121. package/src/adapters/databasePluginTestDatabase.ts +14 -0
  122. package/src/adapters/drizzle.spec.ts +99 -0
  123. package/src/adapters/drizzle.ts +21 -3
  124. package/src/adapters/drizzleCrud.ts +154 -2
  125. package/src/adapters/drizzleLazyDB.ts +17 -3
  126. package/src/adapters/insightsTransaction.ts +32 -0
  127. package/src/adapters/kysely.spec.ts +29 -0
  128. package/src/adapters/kysely.ts +10 -1
  129. package/src/adapters/kyselyCrud.ts +121 -0
  130. package/src/adapters/mongodb.integration.spec.ts +210 -0
  131. package/src/adapters/mongodb.spec.ts +69 -1
  132. package/src/adapters/mongodb.ts +66 -2
  133. package/src/adapters/mongodbCollections.ts +5 -0
  134. package/src/adapters/mongodbQuery.ts +32 -3
  135. package/src/adapters/mongodbReads.ts +69 -0
  136. package/src/adapters/mongodbTestClient.ts +41 -6
  137. package/src/adapters/mongodbTestFilter.ts +4 -1
  138. package/src/adapters/mongodbWrites.ts +27 -0
  139. package/src/adapters/prisma.spec.ts +97 -0
  140. package/src/adapters/prisma.ts +103 -3
  141. package/src/adapters/prismaRows.ts +37 -1
  142. package/src/adapters/prismaTestClient.ts +22 -5
  143. package/src/apiKeys.integration.spec.ts +1 -1
  144. package/src/createHotUpdaterCore.ts +21 -7
  145. package/src/db/databasePluginUpdateCheck.bench.ts +3 -0
  146. package/src/db/fixedMigrator.spec.ts +65 -0
  147. package/src/db/fixedMigratorMongo.spec.ts +56 -9
  148. package/src/db/fixedMigratorMongo.ts +10 -10
  149. package/src/db/index.spec.ts +8 -1
  150. package/src/db/schema/insightsCollation.ts +21 -0
  151. package/src/db/schema/sql.ts +8 -5
  152. package/src/db/schemaGenerators.ts +41 -13
  153. package/src/db/types.spec.ts +8 -0
  154. package/src/db/types.ts +10 -4
  155. package/src/handler-standalone.integration.spec.ts +5 -7
  156. package/src/index.ts +2 -2
  157. package/src/insights/domain.ts +39 -75
  158. package/src/insights/errors.ts +0 -7
  159. package/src/insights/eventInput.ts +60 -3
  160. package/src/insights/insights.integration.spec.ts +254 -17
  161. package/src/insights/provider.spec.ts +385 -0
  162. package/src/insights/provider.ts +591 -0
  163. package/src/insights/queryInput.ts +112 -73
  164. package/src/insights/routes.ts +37 -56
  165. package/src/insights/types.ts +47 -36
  166. package/src/schema/dsl-all-versions.spec.ts +3 -1
  167. package/src/schema/v1_0_0.spec.ts +77 -3
  168. package/src/schema/v1_0_0.ts +94 -30
  169. package/dist/insights/bounded/activeOverview.cjs +0 -87
  170. package/dist/insights/bounded/activeOverview.mjs +0 -85
  171. package/dist/insights/bounded/installationSearch.cjs +0 -42
  172. package/dist/insights/bounded/installationSearch.mjs +0 -42
  173. package/dist/insights/bounded/persistence.cjs +0 -39
  174. package/dist/insights/bounded/persistence.mjs +0 -38
  175. package/dist/insights/bounded/provider.cjs +0 -170
  176. package/dist/insights/bounded/provider.d.cts +0 -7
  177. package/dist/insights/bounded/provider.d.mts +0 -7
  178. package/dist/insights/bounded/provider.mjs +0 -170
  179. package/dist/insights/bounded/scan.cjs +0 -144
  180. package/dist/insights/bounded/scan.mjs +0 -138
  181. package/dist/insights/persistence.d.cts +0 -6
  182. package/dist/insights/persistence.d.mts +0 -6
  183. package/src/insights/bounded/activeOverview.spec.ts +0 -189
  184. package/src/insights/bounded/activeOverview.ts +0 -148
  185. package/src/insights/bounded/installationSearch.spec.ts +0 -130
  186. package/src/insights/bounded/installationSearch.ts +0 -69
  187. package/src/insights/bounded/persistence.ts +0 -43
  188. package/src/insights/bounded/provider.spec.ts +0 -219
  189. package/src/insights/bounded/provider.ts +0 -229
  190. package/src/insights/bounded/scan.ts +0 -235
  191. package/src/insights/persistence.ts +0 -10
@@ -1,58 +1,93 @@
1
1
  const require_errors = require("./errors.cjs");
2
2
  //#region src/insights/queryInput.ts
3
- const EVENT_LIST_BOUNDS = {
4
- defaultValue: 50,
5
- maximum: 100,
6
- minimum: 1
7
- };
8
- const EVENT_LIST_OFFSET_BOUNDS = {
9
- defaultValue: 0,
10
- minimum: 0
11
- };
12
- const MAX_USER_ID_LENGTH = 1024;
13
- function parseInteger(url, key, bounds) {
14
- const value = url.searchParams.get(key);
15
- if (value === null) return bounds.defaultValue;
16
- const parsed = Number(value);
17
- if (!Number.isSafeInteger(parsed) || parsed < bounds.minimum || bounds.maximum !== void 0 && parsed > bounds.maximum) throw new require_errors.InsightsBadRequestError(`Invalid '${key}' query parameter.`);
18
- return parsed;
19
- }
20
- const parsePagination = (request) => {
3
+ const MAX_PAGE_LIMIT = 100;
4
+ const MAX_IDENTITY_LENGTH = 255;
5
+ const MAX_CURSOR_LENGTH = 8192;
6
+ const readSingle = (url, key) => {
7
+ const values = url.searchParams.getAll(key);
8
+ if (values.length > 1) throw new require_errors.InsightsBadRequestError(`Duplicate '${key}' query parameter.`);
9
+ return values[0];
10
+ };
11
+ const readPageLimit = (url) => {
12
+ const value = readSingle(url, "limit");
13
+ if (value === void 0) return void 0;
14
+ const limit = Number(value);
15
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_PAGE_LIMIT) throw new require_errors.InsightsBadRequestError("Invalid 'limit' query parameter.");
16
+ return limit;
17
+ };
18
+ const readCursor = (url) => {
19
+ const cursor = readSingle(url, "cursor");
20
+ if (cursor !== void 0 && (cursor.length === 0 || cursor.length > MAX_CURSOR_LENGTH)) throw new require_errors.InsightsBadRequestError("Invalid 'cursor' query parameter.");
21
+ return cursor;
22
+ };
23
+ const readId = (url, key, maximumLength = MAX_IDENTITY_LENGTH) => {
24
+ const value = readSingle(url, key);
25
+ if (value === void 0 || value.length === 0 || value.length > maximumLength) throw new require_errors.InsightsBadRequestError(`Invalid '${key}' query parameter.`);
26
+ return value;
27
+ };
28
+ const readTimestamp = (url, key) => {
29
+ const raw = readSingle(url, key);
30
+ if (raw === void 0) return void 0;
31
+ const value = Number(raw);
32
+ if (!raw.length || !Number.isSafeInteger(value) || value < 0) throw new require_errors.InsightsBadRequestError(`Invalid '${key}' query parameter.`);
33
+ return value;
34
+ };
35
+ const readScope = (url) => {
36
+ const platform = readSingle(url, "platform");
37
+ if (platform !== "ios" && platform !== "android") throw new require_errors.InsightsBadRequestError("Invalid 'platform' query parameter.");
38
+ return {
39
+ platform,
40
+ channel: readId(url, "channel", 1024)
41
+ };
42
+ };
43
+ const parseEventPageInput = (request) => {
21
44
  const url = new URL(request.url);
45
+ const bundleFields = [
46
+ "bundleId",
47
+ "outcome",
48
+ "platform",
49
+ "channel"
50
+ ];
51
+ let bundle;
52
+ if (bundleFields.some((key) => url.searchParams.has(key))) {
53
+ const outcome = readSingle(url, "outcome");
54
+ if (outcome !== "applied" && outcome !== "recovered" && outcome !== "adopted") throw new require_errors.InsightsBadRequestError("Invalid 'outcome' query parameter.");
55
+ bundle = {
56
+ ...readScope(url),
57
+ bundleId: readId(url, "bundleId", 1024),
58
+ outcome
59
+ };
60
+ }
22
61
  return {
23
- limit: parseInteger(url, "limit", EVENT_LIST_BOUNDS),
24
- offset: parseInteger(url, "offset", EVENT_LIST_OFFSET_BOUNDS)
62
+ beforeReceivedAtMs: readTimestamp(url, "beforeReceivedAtMs"),
63
+ sinceMs: readTimestamp(url, "sinceMs"),
64
+ cursor: readCursor(url),
65
+ limit: readPageLimit(url),
66
+ ...bundle === void 0 ? {} : { bundle }
25
67
  };
26
68
  };
27
- const parseInsightsQuery = (request) => {
28
- const window = new URL(request.url).searchParams.get("window") ?? "24h";
29
- if (window !== "24h" && window !== "7d" && window !== "30d" && window !== "all") throw new require_errors.InsightsBadRequestError("Invalid 'window' query parameter.");
69
+ const parseUserInstallationPageInput = (request) => {
70
+ const url = new URL(request.url);
71
+ const cursor = readCursor(url);
72
+ const limit = readPageLimit(url);
30
73
  return {
31
- ...parsePagination(request),
32
- window
74
+ userId: readId(url, "userId"),
75
+ ...cursor === void 0 ? {} : { cursor },
76
+ ...limit === void 0 ? {} : { limit }
33
77
  };
34
78
  };
35
- const parseActiveInstallationInput = (request) => {
79
+ const parseReportingOverviewInput = (request) => {
36
80
  const url = new URL(request.url);
37
- const windows = url.searchParams.getAll("window");
38
- if (windows.length > 1) throw new require_errors.InsightsBadRequestError("Duplicate 'window' query parameter.");
39
- const window = windows[0] ?? "30d";
81
+ const window = readSingle(url, "window") ?? "30d";
40
82
  if (window !== "24h" && window !== "7d" && window !== "30d") throw new require_errors.InsightsBadRequestError("Invalid 'window' query parameter.");
41
- const userIds = url.searchParams.getAll("userId");
42
- if (userIds.length > 1) throw new require_errors.InsightsBadRequestError("Duplicate 'userId' query parameter.");
43
- const userId = userIds[0];
44
- if (userId !== void 0 && (userId.length === 0 || userId.length > MAX_USER_ID_LENGTH)) throw new require_errors.InsightsBadRequestError("Invalid 'userId' query parameter.");
45
- return userId === void 0 ? { window } : {
83
+ const bundleId = readSingle(url, "bundleId");
84
+ return {
85
+ ...readScope(url),
46
86
  window,
47
- userId
87
+ ...bundleId === void 0 ? {} : { bundleId: readId(url, "bundleId", 1024) }
48
88
  };
49
89
  };
50
- const parseSearchInput = (request) => ({
51
- ...parsePagination(request),
52
- query: new URL(request.url).searchParams.get("query")?.trim() ?? ""
53
- });
54
90
  //#endregion
55
- exports.parseActiveInstallationInput = parseActiveInstallationInput;
56
- exports.parseInsightsQuery = parseInsightsQuery;
57
- exports.parsePagination = parsePagination;
58
- exports.parseSearchInput = parseSearchInput;
91
+ exports.parseEventPageInput = parseEventPageInput;
92
+ exports.parseReportingOverviewInput = parseReportingOverviewInput;
93
+ exports.parseUserInstallationPageInput = parseUserInstallationPageInput;
@@ -1,55 +1,91 @@
1
1
  import { InsightsBadRequestError } from "./errors.mjs";
2
2
  //#region src/insights/queryInput.ts
3
- const EVENT_LIST_BOUNDS = {
4
- defaultValue: 50,
5
- maximum: 100,
6
- minimum: 1
7
- };
8
- const EVENT_LIST_OFFSET_BOUNDS = {
9
- defaultValue: 0,
10
- minimum: 0
11
- };
12
- const MAX_USER_ID_LENGTH = 1024;
13
- function parseInteger(url, key, bounds) {
14
- const value = url.searchParams.get(key);
15
- if (value === null) return bounds.defaultValue;
16
- const parsed = Number(value);
17
- if (!Number.isSafeInteger(parsed) || parsed < bounds.minimum || bounds.maximum !== void 0 && parsed > bounds.maximum) throw new InsightsBadRequestError(`Invalid '${key}' query parameter.`);
18
- return parsed;
19
- }
20
- const parsePagination = (request) => {
3
+ const MAX_PAGE_LIMIT = 100;
4
+ const MAX_IDENTITY_LENGTH = 255;
5
+ const MAX_CURSOR_LENGTH = 8192;
6
+ const readSingle = (url, key) => {
7
+ const values = url.searchParams.getAll(key);
8
+ if (values.length > 1) throw new InsightsBadRequestError(`Duplicate '${key}' query parameter.`);
9
+ return values[0];
10
+ };
11
+ const readPageLimit = (url) => {
12
+ const value = readSingle(url, "limit");
13
+ if (value === void 0) return void 0;
14
+ const limit = Number(value);
15
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_PAGE_LIMIT) throw new InsightsBadRequestError("Invalid 'limit' query parameter.");
16
+ return limit;
17
+ };
18
+ const readCursor = (url) => {
19
+ const cursor = readSingle(url, "cursor");
20
+ if (cursor !== void 0 && (cursor.length === 0 || cursor.length > MAX_CURSOR_LENGTH)) throw new InsightsBadRequestError("Invalid 'cursor' query parameter.");
21
+ return cursor;
22
+ };
23
+ const readId = (url, key, maximumLength = MAX_IDENTITY_LENGTH) => {
24
+ const value = readSingle(url, key);
25
+ if (value === void 0 || value.length === 0 || value.length > maximumLength) throw new InsightsBadRequestError(`Invalid '${key}' query parameter.`);
26
+ return value;
27
+ };
28
+ const readTimestamp = (url, key) => {
29
+ const raw = readSingle(url, key);
30
+ if (raw === void 0) return void 0;
31
+ const value = Number(raw);
32
+ if (!raw.length || !Number.isSafeInteger(value) || value < 0) throw new InsightsBadRequestError(`Invalid '${key}' query parameter.`);
33
+ return value;
34
+ };
35
+ const readScope = (url) => {
36
+ const platform = readSingle(url, "platform");
37
+ if (platform !== "ios" && platform !== "android") throw new InsightsBadRequestError("Invalid 'platform' query parameter.");
38
+ return {
39
+ platform,
40
+ channel: readId(url, "channel", 1024)
41
+ };
42
+ };
43
+ const parseEventPageInput = (request) => {
21
44
  const url = new URL(request.url);
45
+ const bundleFields = [
46
+ "bundleId",
47
+ "outcome",
48
+ "platform",
49
+ "channel"
50
+ ];
51
+ let bundle;
52
+ if (bundleFields.some((key) => url.searchParams.has(key))) {
53
+ const outcome = readSingle(url, "outcome");
54
+ if (outcome !== "applied" && outcome !== "recovered" && outcome !== "adopted") throw new InsightsBadRequestError("Invalid 'outcome' query parameter.");
55
+ bundle = {
56
+ ...readScope(url),
57
+ bundleId: readId(url, "bundleId", 1024),
58
+ outcome
59
+ };
60
+ }
22
61
  return {
23
- limit: parseInteger(url, "limit", EVENT_LIST_BOUNDS),
24
- offset: parseInteger(url, "offset", EVENT_LIST_OFFSET_BOUNDS)
62
+ beforeReceivedAtMs: readTimestamp(url, "beforeReceivedAtMs"),
63
+ sinceMs: readTimestamp(url, "sinceMs"),
64
+ cursor: readCursor(url),
65
+ limit: readPageLimit(url),
66
+ ...bundle === void 0 ? {} : { bundle }
25
67
  };
26
68
  };
27
- const parseInsightsQuery = (request) => {
28
- const window = new URL(request.url).searchParams.get("window") ?? "24h";
29
- if (window !== "24h" && window !== "7d" && window !== "30d" && window !== "all") throw new InsightsBadRequestError("Invalid 'window' query parameter.");
69
+ const parseUserInstallationPageInput = (request) => {
70
+ const url = new URL(request.url);
71
+ const cursor = readCursor(url);
72
+ const limit = readPageLimit(url);
30
73
  return {
31
- ...parsePagination(request),
32
- window
74
+ userId: readId(url, "userId"),
75
+ ...cursor === void 0 ? {} : { cursor },
76
+ ...limit === void 0 ? {} : { limit }
33
77
  };
34
78
  };
35
- const parseActiveInstallationInput = (request) => {
79
+ const parseReportingOverviewInput = (request) => {
36
80
  const url = new URL(request.url);
37
- const windows = url.searchParams.getAll("window");
38
- if (windows.length > 1) throw new InsightsBadRequestError("Duplicate 'window' query parameter.");
39
- const window = windows[0] ?? "30d";
81
+ const window = readSingle(url, "window") ?? "30d";
40
82
  if (window !== "24h" && window !== "7d" && window !== "30d") throw new InsightsBadRequestError("Invalid 'window' query parameter.");
41
- const userIds = url.searchParams.getAll("userId");
42
- if (userIds.length > 1) throw new InsightsBadRequestError("Duplicate 'userId' query parameter.");
43
- const userId = userIds[0];
44
- if (userId !== void 0 && (userId.length === 0 || userId.length > MAX_USER_ID_LENGTH)) throw new InsightsBadRequestError("Invalid 'userId' query parameter.");
45
- return userId === void 0 ? { window } : {
83
+ const bundleId = readSingle(url, "bundleId");
84
+ return {
85
+ ...readScope(url),
46
86
  window,
47
- userId
87
+ ...bundleId === void 0 ? {} : { bundleId: readId(url, "bundleId", 1024) }
48
88
  };
49
89
  };
50
- const parseSearchInput = (request) => ({
51
- ...parsePagination(request),
52
- query: new URL(request.url).searchParams.get("query")?.trim() ?? ""
53
- });
54
90
  //#endregion
55
- export { parseActiveInstallationInput, parseInsightsQuery, parsePagination, parseSearchInput };
91
+ export { parseEventPageInput, parseReportingOverviewInput, parseUserInstallationPageInput };
@@ -9,7 +9,11 @@ const json = (body, status) => Response.json(body, {
9
9
  const requireParam = (params, key) => {
10
10
  const value = params[key];
11
11
  if (value === void 0 || value.length === 0) throw new require_errors.InsightsBadRequestError(`Missing route parameter: ${key}`);
12
- return value;
12
+ try {
13
+ return decodeURIComponent(value);
14
+ } catch {
15
+ throw new require_errors.InsightsBadRequestError(`Invalid route parameter: ${key}`);
16
+ }
13
17
  };
14
18
  const run = async (operation) => {
15
19
  try {
@@ -17,10 +21,6 @@ const run = async (operation) => {
17
21
  } catch (error) {
18
22
  if (error instanceof require_errors.InsightsBadRequestError) return json({ error: error.message }, 400);
19
23
  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
- limit: error.limit
23
- } }, 503);
24
24
  throw error;
25
25
  }
26
26
  };
@@ -30,32 +30,27 @@ const createInsightsRouteHandlers = (provider) => ({
30
30
  await provider.appendBundleEvent(await require_eventInput.parseBundleEventRequest(request));
31
31
  return new Response(null, { status: 204 });
32
32
  }),
33
- getBundleEventSummary: (params) => query(() => provider.getBundleEventSummary(requireParam(params, "id"))),
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
- }),
38
- getBundleEventOverview: () => query(() => provider.getBundleEventOverview()),
39
- getActiveInstallationOverview: (_params, request) => query(() => provider.getActiveInstallationOverview(require_queryInput.parseActiveInstallationInput(request))),
40
- searchInstallations: (_params, request) => query(() => {
41
- const input = require_queryInput.parseSearchInput(request);
42
- return provider.searchInstallations(input.query, input.limit, input.offset);
33
+ getReportingOverview: (_params, request) => query(() => provider.getReportingOverview(require_queryInput.parseReportingOverviewInput(request))),
34
+ getInstallation: (params) => run(async () => {
35
+ const installation = await provider.getInstallation({ installId: requireParam(params, "installId") });
36
+ return installation === null ? json({ error: "Installation not found" }, 404) : json(installation, 200);
43
37
  }),
44
- getInstallationHistory: (params, request) => query(() => {
45
- const input = require_queryInput.parsePagination(request);
46
- return provider.getInstallationHistory(requireParam(params, "installId"), input.limit, input.offset);
47
- })
38
+ listEvents: (_params, request) => query(() => provider.listEvents(require_queryInput.parseEventPageInput(request))),
39
+ listInstallationEvents: (params, request) => query(() => provider.listInstallationEvents({
40
+ ...require_queryInput.parseEventPageInput(request),
41
+ installId: requireParam(params, "installId")
42
+ })),
43
+ pageInstallationsByCurrentUserId: (_params, request) => query(() => provider.pageInstallationsByCurrentUserId(require_queryInput.parseUserInstallationPageInput(request)))
48
44
  });
49
45
  const registerInsightsClientRoutes = (add) => {
50
46
  add("POST", "/events", "appendBundleEvent");
51
47
  };
52
48
  const registerInsightsAdminRoutes = (add) => {
53
- add("GET", "/bundles/:id/events/summary", "getBundleEventSummary");
54
- add("GET", "/bundles/:id/events/insights", "getBundleEventInsights");
55
- add("GET", "/installations/overview", "getBundleEventOverview");
56
- add("GET", "/installations/active", "getActiveInstallationOverview");
57
- add("GET", "/installations", "searchInstallations");
58
- add("GET", "/installations/:installId/events", "getInstallationHistory");
49
+ add("GET", "/events", "listEvents");
50
+ add("GET", "/overview", "getReportingOverview");
51
+ add("GET", "/installations", "pageInstallationsByCurrentUserId");
52
+ add("GET", "/installations/:installId/events", "listInstallationEvents");
53
+ add("GET", "/installations/:installId", "getInstallation");
59
54
  };
60
55
  //#endregion
61
56
  exports.createInsightsRouteHandlers = createInsightsRouteHandlers;
@@ -1,6 +1,6 @@
1
- import { InsightsBadRequestError, InsightsPayloadTooLargeError, InsightsScanLimitExceededError } from "./errors.mjs";
1
+ import { InsightsBadRequestError, InsightsPayloadTooLargeError } from "./errors.mjs";
2
2
  import { parseBundleEventRequest } from "./eventInput.mjs";
3
- import { parseActiveInstallationInput, parseInsightsQuery, parsePagination, parseSearchInput } from "./queryInput.mjs";
3
+ import { parseEventPageInput, parseReportingOverviewInput, parseUserInstallationPageInput } from "./queryInput.mjs";
4
4
  //#region src/insights/routes.ts
5
5
  const json = (body, status) => Response.json(body, {
6
6
  headers: { "cache-control": "private, no-store" },
@@ -9,7 +9,11 @@ const json = (body, status) => Response.json(body, {
9
9
  const requireParam = (params, key) => {
10
10
  const value = params[key];
11
11
  if (value === void 0 || value.length === 0) throw new InsightsBadRequestError(`Missing route parameter: ${key}`);
12
- return value;
12
+ try {
13
+ return decodeURIComponent(value);
14
+ } catch {
15
+ throw new InsightsBadRequestError(`Invalid route parameter: ${key}`);
16
+ }
13
17
  };
14
18
  const run = async (operation) => {
15
19
  try {
@@ -17,10 +21,6 @@ const run = async (operation) => {
17
21
  } catch (error) {
18
22
  if (error instanceof InsightsBadRequestError) return json({ error: error.message }, 400);
19
23
  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
- limit: error.limit
23
- } }, 503);
24
24
  throw error;
25
25
  }
26
26
  };
@@ -30,32 +30,27 @@ const createInsightsRouteHandlers = (provider) => ({
30
30
  await provider.appendBundleEvent(await parseBundleEventRequest(request));
31
31
  return new Response(null, { status: 204 });
32
32
  }),
33
- getBundleEventSummary: (params) => query(() => provider.getBundleEventSummary(requireParam(params, "id"))),
34
- getBundleEventInsights: (params, request) => query(() => {
35
- const input = parseInsightsQuery(request);
36
- return provider.getBundleEventInsights(requireParam(params, "id"), input.window, input.limit, input.offset);
37
- }),
38
- getBundleEventOverview: () => query(() => provider.getBundleEventOverview()),
39
- getActiveInstallationOverview: (_params, request) => query(() => provider.getActiveInstallationOverview(parseActiveInstallationInput(request))),
40
- searchInstallations: (_params, request) => query(() => {
41
- const input = parseSearchInput(request);
42
- return provider.searchInstallations(input.query, input.limit, input.offset);
33
+ getReportingOverview: (_params, request) => query(() => provider.getReportingOverview(parseReportingOverviewInput(request))),
34
+ getInstallation: (params) => run(async () => {
35
+ const installation = await provider.getInstallation({ installId: requireParam(params, "installId") });
36
+ return installation === null ? json({ error: "Installation not found" }, 404) : json(installation, 200);
43
37
  }),
44
- getInstallationHistory: (params, request) => query(() => {
45
- const input = parsePagination(request);
46
- return provider.getInstallationHistory(requireParam(params, "installId"), input.limit, input.offset);
47
- })
38
+ listEvents: (_params, request) => query(() => provider.listEvents(parseEventPageInput(request))),
39
+ listInstallationEvents: (params, request) => query(() => provider.listInstallationEvents({
40
+ ...parseEventPageInput(request),
41
+ installId: requireParam(params, "installId")
42
+ })),
43
+ pageInstallationsByCurrentUserId: (_params, request) => query(() => provider.pageInstallationsByCurrentUserId(parseUserInstallationPageInput(request)))
48
44
  });
49
45
  const registerInsightsClientRoutes = (add) => {
50
46
  add("POST", "/events", "appendBundleEvent");
51
47
  };
52
48
  const registerInsightsAdminRoutes = (add) => {
53
- add("GET", "/bundles/:id/events/summary", "getBundleEventSummary");
54
- add("GET", "/bundles/:id/events/insights", "getBundleEventInsights");
55
- add("GET", "/installations/overview", "getBundleEventOverview");
56
- add("GET", "/installations/active", "getActiveInstallationOverview");
57
- add("GET", "/installations", "searchInstallations");
58
- add("GET", "/installations/:installId/events", "getInstallationHistory");
49
+ add("GET", "/events", "listEvents");
50
+ add("GET", "/overview", "getReportingOverview");
51
+ add("GET", "/installations", "pageInstallationsByCurrentUserId");
52
+ add("GET", "/installations/:installId/events", "listInstallationEvents");
53
+ add("GET", "/installations/:installId", "getInstallation");
59
54
  };
60
55
  //#endregion
61
56
  export { createInsightsRouteHandlers, registerInsightsAdminRoutes, registerInsightsClientRoutes };
@@ -1,17 +1,32 @@
1
- import { ActiveInstallationInput, ActiveInstallationOverview, BundleEventInsightsResult, BundleEventInsightsWindow, BundleEventOverview, BundleEventSummary, BundleEventSummaryByBundle, CreateBundleEventRequest, InstallationHistoryRow, InstallationSearchRow, OffsetPaginationResult } from "./domain.cjs";
2
-
1
+ import { ActiveInstallationWindow, CreateBundleEventRequest, CursorPage, EventCursorPage, EventHistoryRow, InsightsBundleSelection, InsightsScope, InstallationHistoryRow, InstallationRow, ReportingOverview } from "./domain.cjs";
3
2
  //#region src/insights/types.d.ts
3
+ type InsightsEventPageInput = {
4
+ readonly beforeReceivedAtMs?: number;
5
+ readonly sinceMs?: number;
6
+ readonly bundle?: InsightsBundleSelection;
7
+ readonly cursor?: string;
8
+ readonly limit?: number;
9
+ };
10
+ type InsightsInstallationEventPageInput = Omit<InsightsEventPageInput, "bundle"> & {
11
+ readonly installId: string;
12
+ };
13
+ type InsightsUserInstallationPageInput = {
14
+ readonly userId: string;
15
+ readonly cursor?: string;
16
+ readonly limit?: number;
17
+ };
4
18
  type InsightsProvider = {
5
- readonly mode: "bounded";
6
- readonly maxMatchingRows: number;
7
19
  appendBundleEvent(input: CreateBundleEventRequest): Promise<void>;
8
- getBundleEventSummary(bundleId: string): Promise<BundleEventSummary>;
9
- getBundleEventSummaries(bundleIds: readonly string[], window: BundleEventInsightsWindow): Promise<readonly BundleEventSummaryByBundle[]>;
10
- getBundleEventInsights(bundleId: string, window: BundleEventInsightsWindow, limit: number, offset: number): Promise<BundleEventInsightsResult>;
11
- getBundleEventOverview(): Promise<BundleEventOverview>;
12
- getActiveInstallationOverview(input: ActiveInstallationInput): Promise<ActiveInstallationOverview>;
13
- searchInstallations(query: string, limit: number, offset: number): Promise<OffsetPaginationResult<InstallationSearchRow>>;
14
- getInstallationHistory(installId: string, limit: number, offset: number): Promise<OffsetPaginationResult<InstallationHistoryRow>>;
20
+ listEvents(input: InsightsEventPageInput): Promise<EventCursorPage<EventHistoryRow>>;
21
+ listInstallationEvents(input: InsightsInstallationEventPageInput): Promise<EventCursorPage<InstallationHistoryRow>>;
22
+ getInstallation(input: {
23
+ readonly installId: string;
24
+ }): Promise<InstallationRow | null>;
25
+ pageInstallationsByCurrentUserId(input: InsightsUserInstallationPageInput): Promise<CursorPage<InstallationRow>>;
26
+ getReportingOverview(input: InsightsScope & {
27
+ readonly window: ActiveInstallationWindow;
28
+ readonly bundleId?: string;
29
+ }): Promise<ReportingOverview>;
15
30
  };
16
31
  //#endregion
17
- export { InsightsProvider };
32
+ export { InsightsEventPageInput, InsightsInstallationEventPageInput, InsightsProvider, InsightsUserInstallationPageInput };
@@ -1,17 +1,32 @@
1
- import { ActiveInstallationInput, ActiveInstallationOverview, BundleEventInsightsResult, BundleEventInsightsWindow, BundleEventOverview, BundleEventSummary, BundleEventSummaryByBundle, CreateBundleEventRequest, InstallationHistoryRow, InstallationSearchRow, OffsetPaginationResult } from "./domain.mjs";
2
-
1
+ import { ActiveInstallationWindow, CreateBundleEventRequest, CursorPage, EventCursorPage, EventHistoryRow, InsightsBundleSelection, InsightsScope, InstallationHistoryRow, InstallationRow, ReportingOverview } from "./domain.mjs";
3
2
  //#region src/insights/types.d.ts
3
+ type InsightsEventPageInput = {
4
+ readonly beforeReceivedAtMs?: number;
5
+ readonly sinceMs?: number;
6
+ readonly bundle?: InsightsBundleSelection;
7
+ readonly cursor?: string;
8
+ readonly limit?: number;
9
+ };
10
+ type InsightsInstallationEventPageInput = Omit<InsightsEventPageInput, "bundle"> & {
11
+ readonly installId: string;
12
+ };
13
+ type InsightsUserInstallationPageInput = {
14
+ readonly userId: string;
15
+ readonly cursor?: string;
16
+ readonly limit?: number;
17
+ };
4
18
  type InsightsProvider = {
5
- readonly mode: "bounded";
6
- readonly maxMatchingRows: number;
7
19
  appendBundleEvent(input: CreateBundleEventRequest): Promise<void>;
8
- getBundleEventSummary(bundleId: string): Promise<BundleEventSummary>;
9
- getBundleEventSummaries(bundleIds: readonly string[], window: BundleEventInsightsWindow): Promise<readonly BundleEventSummaryByBundle[]>;
10
- getBundleEventInsights(bundleId: string, window: BundleEventInsightsWindow, limit: number, offset: number): Promise<BundleEventInsightsResult>;
11
- getBundleEventOverview(): Promise<BundleEventOverview>;
12
- getActiveInstallationOverview(input: ActiveInstallationInput): Promise<ActiveInstallationOverview>;
13
- searchInstallations(query: string, limit: number, offset: number): Promise<OffsetPaginationResult<InstallationSearchRow>>;
14
- getInstallationHistory(installId: string, limit: number, offset: number): Promise<OffsetPaginationResult<InstallationHistoryRow>>;
20
+ listEvents(input: InsightsEventPageInput): Promise<EventCursorPage<EventHistoryRow>>;
21
+ listInstallationEvents(input: InsightsInstallationEventPageInput): Promise<EventCursorPage<InstallationHistoryRow>>;
22
+ getInstallation(input: {
23
+ readonly installId: string;
24
+ }): Promise<InstallationRow | null>;
25
+ pageInstallationsByCurrentUserId(input: InsightsUserInstallationPageInput): Promise<CursorPage<InstallationRow>>;
26
+ getReportingOverview(input: InsightsScope & {
27
+ readonly window: ActiveInstallationWindow;
28
+ readonly bundleId?: string;
29
+ }): Promise<ReportingOverview>;
15
30
  };
16
31
  //#endregion
17
- export { InsightsProvider };
32
+ export { InsightsEventPageInput, InsightsInstallationEventPageInput, InsightsProvider, InsightsUserInstallationPageInput };
package/dist/node.d.cts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { HotUpdaterHandler } from "./handlerTypes.cjs";
2
2
  import { HOT_UPDATER_SERVER_VERSION } from "./version.cjs";
3
3
  import { HOT_UPDATER_INFRASTRUCTURE_GENERATION } from "./handlerVersionRoutes.cjs";
4
-
5
4
  //#region src/node.d.ts
6
5
  /**
7
6
  * Converts a Hot Updater handler to a Node.js-compatible middleware
package/dist/node.d.mts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { HotUpdaterHandler } from "./handlerTypes.mjs";
2
2
  import { HOT_UPDATER_SERVER_VERSION } from "./version.mjs";
3
3
  import { HOT_UPDATER_INFRASTRUCTURE_GENERATION } from "./handlerVersionRoutes.mjs";
4
-
5
4
  //#region src/node.d.ts
6
5
  /**
7
6
  * Converts a Hot Updater handler to a Node.js-compatible middleware
package/dist/package.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "1.0.0-rc.1";
2
+ var version = "1.0.0-rc.2";
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.1";
2
+ var version = "1.0.0-rc.2";
3
3
  //#endregion
4
4
  export { version };
@@ -1,6 +1,20 @@
1
- require("./types.cjs");
2
- require("./settings.cjs");
1
+ const require_types = require("./types.cjs");
2
+ const require_settings = require("./settings.cjs");
3
+ const require_v1_0_0 = require("./v1_0_0.cjs");
3
4
  //#region src/schema/index.ts
4
- const hotUpdaterSchemaVersions = [require("./v1_0_0.cjs").v1_0_0];
5
+ const hotUpdaterSchemaVersions = [require_v1_0_0.v1_0_0];
5
6
  //#endregion
7
+ exports.HOT_UPDATER_CORE_SCHEMA_KEY = require_types.HOT_UPDATER_CORE_SCHEMA_KEY;
8
+ exports.HOT_UPDATER_SCHEMA_VERSION = require_types.HOT_UPDATER_SCHEMA_VERSION;
9
+ exports.HOT_UPDATER_SETTINGS_TABLE = require_types.HOT_UPDATER_SETTINGS_TABLE;
10
+ exports.apiKeysV100 = require_v1_0_0.apiKeysV100;
11
+ exports.bundleEventsV100 = require_v1_0_0.bundleEventsV100;
12
+ exports.bundleInstallationsV100 = require_v1_0_0.bundleInstallationsV100;
13
+ exports.bundlePatchesV100 = require_v1_0_0.bundlePatchesV100;
14
+ exports.bundlesV100 = require_v1_0_0.bundlesV100;
15
+ exports.channelsV100 = require_v1_0_0.channelsV100;
16
+ exports.createSettingsTable = require_settings.createSettingsTable;
6
17
  exports.hotUpdaterSchemaVersions = hotUpdaterSchemaVersions;
18
+ exports.releaseCatalogsV100 = require_v1_0_0.releaseCatalogsV100;
19
+ exports.releasesV100 = require_v1_0_0.releasesV100;
20
+ exports.v1_0_0 = require_v1_0_0.v1_0_0;
@@ -1,7 +1,7 @@
1
- import "./types.mjs";
2
- import "./settings.mjs";
3
- import { v1_0_0 } from "./v1_0_0.mjs";
1
+ import { HOT_UPDATER_CORE_SCHEMA_KEY, HOT_UPDATER_SCHEMA_VERSION, HOT_UPDATER_SETTINGS_TABLE } from "./types.mjs";
2
+ import { createSettingsTable } from "./settings.mjs";
3
+ import { apiKeysV100, bundleEventsV100, bundleInstallationsV100, bundlePatchesV100, bundlesV100, channelsV100, releaseCatalogsV100, releasesV100, v1_0_0 } from "./v1_0_0.mjs";
4
4
  //#region src/schema/index.ts
5
5
  const hotUpdaterSchemaVersions = [v1_0_0];
6
6
  //#endregion
7
- export { hotUpdaterSchemaVersions };
7
+ export { HOT_UPDATER_CORE_SCHEMA_KEY, HOT_UPDATER_SCHEMA_VERSION, HOT_UPDATER_SETTINGS_TABLE, apiKeysV100, bundleEventsV100, bundleInstallationsV100, bundlePatchesV100, bundlesV100, channelsV100, createSettingsTable, hotUpdaterSchemaVersions, releaseCatalogsV100, releasesV100, v1_0_0 };