@hot-updater/plugin-insights 1.0.0-rc.20
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/LICENSE +33 -0
- package/README.md +24 -0
- package/client/package.json +7 -0
- package/dist/client/eventId.cjs +181 -0
- package/dist/client/eventId.mjs +179 -0
- package/dist/client/index.cjs +234 -0
- package/dist/client/index.d.cts +42 -0
- package/dist/client/index.d.mts +42 -0
- package/dist/client/index.mjs +233 -0
- package/dist/client/sender.cjs +103 -0
- package/dist/client/sender.mjs +103 -0
- package/dist/client/state.cjs +121 -0
- package/dist/client/state.mjs +119 -0
- package/dist/internal.cjs +9 -0
- package/dist/internal.d.cts +5 -0
- package/dist/internal.d.mts +5 -0
- package/dist/internal.mjs +5 -0
- package/dist/server/contract.cjs +193 -0
- package/dist/server/contract.mjs +189 -0
- package/dist/server/domain.d.cts +149 -0
- package/dist/server/domain.d.mts +149 -0
- package/dist/server/errors.cjs +15 -0
- package/dist/server/errors.d.cts +6 -0
- package/dist/server/errors.d.mts +6 -0
- package/dist/server/errors.mjs +14 -0
- package/dist/server/eventInput.cjs +284 -0
- package/dist/server/eventInput.d.cts +6 -0
- package/dist/server/eventInput.d.mts +6 -0
- package/dist/server/eventInput.mjs +282 -0
- package/dist/server/eventRow.cjs +49 -0
- package/dist/server/eventRow.d.cts +64 -0
- package/dist/server/eventRow.d.mts +64 -0
- package/dist/server/eventRow.mjs +47 -0
- package/dist/server/index.cjs +128 -0
- package/dist/server/index.d.cts +394 -0
- package/dist/server/index.d.mts +394 -0
- package/dist/server/index.mjs +121 -0
- package/dist/server/model.cjs +17 -0
- package/dist/server/model.d.cts +10 -0
- package/dist/server/model.d.mts +10 -0
- package/dist/server/model.mjs +17 -0
- package/dist/server/modelTypes.d.cts +176 -0
- package/dist/server/modelTypes.d.mts +176 -0
- package/dist/server/overview.cjs +109 -0
- package/dist/server/overview.d.cts +15 -0
- package/dist/server/overview.d.mts +15 -0
- package/dist/server/overview.mjs +107 -0
- package/dist/server/provider.cjs +375 -0
- package/dist/server/provider.d.cts +6 -0
- package/dist/server/provider.d.mts +6 -0
- package/dist/server/provider.mjs +374 -0
- package/dist/server/queryInput.cjs +93 -0
- package/dist/server/queryInput.mjs +91 -0
- package/dist/server/reads.cjs +681 -0
- package/dist/server/reads.d.cts +60 -0
- package/dist/server/reads.d.mts +60 -0
- package/dist/server/reads.mjs +674 -0
- package/dist/server/recordEvent.cjs +319 -0
- package/dist/server/recordEvent.d.cts +17 -0
- package/dist/server/recordEvent.d.mts +17 -0
- package/dist/server/recordEvent.mjs +316 -0
- package/dist/server/routes.cjs +96 -0
- package/dist/server/routes.d.cts +36 -0
- package/dist/server/routes.d.mts +36 -0
- package/dist/server/routes.mjs +95 -0
- package/dist/server/schema.cjs +489 -0
- package/dist/server/schema.d.cts +706 -0
- package/dist/server/schema.d.mts +706 -0
- package/dist/server/schema.mjs +483 -0
- package/dist/server/testing/eventRow.d.mts +64 -0
- package/dist/server/testing/expectInsightsIndex.mjs +18 -0
- package/dist/server/testing/fixtures.d.mts +8 -0
- package/dist/server/testing/fixtures.mjs +25 -0
- package/dist/server/testing/index.d.mts +24 -0
- package/dist/server/testing/index.mjs +25 -0
- package/dist/server/testing/insightsModelTests.mjs +714 -0
- package/dist/server/testing/modelTypes.d.mts +176 -0
- package/dist/server/testing/setupInsightsHttpTestSuite.d.mts +11 -0
- package/dist/server/testing/setupInsightsHttpTestSuite.mjs +107 -0
- package/dist/server/testing/setupInsightsModelTestSuite.d.mts +11 -0
- package/dist/server/testing/setupInsightsModelTestSuite.mjs +31 -0
- package/dist/server/testing/types.d.mts +26 -0
- package/dist/server/types.d.cts +33 -0
- package/dist/server/types.d.mts +33 -0
- package/package.json +68 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const require_errors = require("./errors.cjs");
|
|
2
|
+
const require_eventInput = require("./eventInput.cjs");
|
|
3
|
+
const require_queryInput = require("./queryInput.cjs");
|
|
4
|
+
let _hot_updater_plugin_core_server_plugin = require("@hot-updater/plugin-core/server-plugin");
|
|
5
|
+
//#region src/server/routes.ts
|
|
6
|
+
/** Seconds a client waits before it resends a request the busy database refused. */
|
|
7
|
+
const RETRY_AFTER_SECONDS = 5;
|
|
8
|
+
const json = (body, status, headers = {}) => Response.json(body, {
|
|
9
|
+
headers: {
|
|
10
|
+
"cache-control": "private, no-store",
|
|
11
|
+
...headers
|
|
12
|
+
},
|
|
13
|
+
status
|
|
14
|
+
});
|
|
15
|
+
const requireParam = (params, key) => {
|
|
16
|
+
const value = params[key];
|
|
17
|
+
if (value === void 0 || value.length === 0) throw new require_errors.InsightsBadRequestError(`Missing route parameter: ${key}`);
|
|
18
|
+
try {
|
|
19
|
+
return decodeURIComponent(value);
|
|
20
|
+
} catch {
|
|
21
|
+
throw new require_errors.InsightsBadRequestError(`Invalid route parameter: ${key}`);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const run = async (operation) => {
|
|
25
|
+
try {
|
|
26
|
+
return await operation();
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error instanceof require_errors.InsightsBadRequestError) return json({ error: error.message }, 400);
|
|
29
|
+
if (error instanceof require_errors.InsightsPayloadTooLargeError) return json({ error: error.message }, 413);
|
|
30
|
+
if ((0, _hot_updater_plugin_core_server_plugin.isDatabaseBusyError)(error)) {
|
|
31
|
+
console.warn("[hot-updater] Insights answered 503: the database is busy.", error);
|
|
32
|
+
return json({ error: "Service unavailable" }, 503, { "retry-after": String(RETRY_AFTER_SECONDS) });
|
|
33
|
+
}
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const query = (operation) => run(async () => json(await operation(), 200));
|
|
38
|
+
/** Every Insights route: where it mounts, and the handler it runs. */
|
|
39
|
+
const INSIGHTS_ROUTES = [
|
|
40
|
+
{
|
|
41
|
+
access: "client",
|
|
42
|
+
method: "POST",
|
|
43
|
+
path: "/events",
|
|
44
|
+
handler: "appendBundleEvent"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
access: "admin",
|
|
48
|
+
method: "GET",
|
|
49
|
+
path: "/events",
|
|
50
|
+
handler: "listEvents"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
access: "admin",
|
|
54
|
+
method: "GET",
|
|
55
|
+
path: "/overview",
|
|
56
|
+
handler: "getReportingOverview"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
access: "admin",
|
|
60
|
+
method: "GET",
|
|
61
|
+
path: "/installations",
|
|
62
|
+
handler: "pageInstallationsByCurrentUserId"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
access: "admin",
|
|
66
|
+
method: "GET",
|
|
67
|
+
path: "/installations/:installId/events",
|
|
68
|
+
handler: "listInstallationEvents"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
access: "admin",
|
|
72
|
+
method: "GET",
|
|
73
|
+
path: "/installations/:installId",
|
|
74
|
+
handler: "getInstallation"
|
|
75
|
+
}
|
|
76
|
+
];
|
|
77
|
+
const createInsightsRouteHandlers = (provider) => ({
|
|
78
|
+
appendBundleEvent: async (_params, request) => run(async () => {
|
|
79
|
+
await provider.appendBundleEvent(await require_eventInput.parseBundleEventRequest(request));
|
|
80
|
+
return new Response(null, { status: 204 });
|
|
81
|
+
}),
|
|
82
|
+
getReportingOverview: (_params, request) => query(() => provider.getReportingOverview(require_queryInput.parseReportingOverviewInput(request))),
|
|
83
|
+
getInstallation: (params) => run(async () => {
|
|
84
|
+
const installation = await provider.getInstallation({ installId: requireParam(params, "installId") });
|
|
85
|
+
return installation === null ? json({ error: "Installation not found" }, 404) : json(installation, 200);
|
|
86
|
+
}),
|
|
87
|
+
listEvents: (_params, request) => query(() => provider.listEvents(require_queryInput.parseEventPageInput(request))),
|
|
88
|
+
listInstallationEvents: (params, request) => query(() => provider.listInstallationEvents({
|
|
89
|
+
...require_queryInput.parseEventPageInput(request),
|
|
90
|
+
installId: requireParam(params, "installId")
|
|
91
|
+
})),
|
|
92
|
+
pageInstallationsByCurrentUserId: (_params, request) => query(() => provider.pageInstallationsByCurrentUserId(require_queryInput.parseUserInstallationPageInput(request)))
|
|
93
|
+
});
|
|
94
|
+
//#endregion
|
|
95
|
+
exports.INSIGHTS_ROUTES = INSIGHTS_ROUTES;
|
|
96
|
+
exports.createInsightsRouteHandlers = createInsightsRouteHandlers;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import "./types.cjs";
|
|
2
|
+
//#region src/server/routes.d.ts
|
|
3
|
+
/** Every Insights route: where it mounts, and the handler it runs. */
|
|
4
|
+
declare const INSIGHTS_ROUTES: readonly [{
|
|
5
|
+
readonly access: "client";
|
|
6
|
+
readonly method: "POST";
|
|
7
|
+
readonly path: "/events";
|
|
8
|
+
readonly handler: "appendBundleEvent";
|
|
9
|
+
}, {
|
|
10
|
+
readonly access: "admin";
|
|
11
|
+
readonly method: "GET";
|
|
12
|
+
readonly path: "/events";
|
|
13
|
+
readonly handler: "listEvents";
|
|
14
|
+
}, {
|
|
15
|
+
readonly access: "admin";
|
|
16
|
+
readonly method: "GET";
|
|
17
|
+
readonly path: "/overview";
|
|
18
|
+
readonly handler: "getReportingOverview";
|
|
19
|
+
}, {
|
|
20
|
+
readonly access: "admin";
|
|
21
|
+
readonly method: "GET";
|
|
22
|
+
readonly path: "/installations";
|
|
23
|
+
readonly handler: "pageInstallationsByCurrentUserId";
|
|
24
|
+
}, {
|
|
25
|
+
readonly access: "admin";
|
|
26
|
+
readonly method: "GET";
|
|
27
|
+
readonly path: "/installations/:installId/events";
|
|
28
|
+
readonly handler: "listInstallationEvents";
|
|
29
|
+
}, {
|
|
30
|
+
readonly access: "admin";
|
|
31
|
+
readonly method: "GET";
|
|
32
|
+
readonly path: "/installations/:installId";
|
|
33
|
+
readonly handler: "getInstallation";
|
|
34
|
+
}];
|
|
35
|
+
//#endregion
|
|
36
|
+
export { INSIGHTS_ROUTES };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import "./types.mjs";
|
|
2
|
+
//#region src/server/routes.d.ts
|
|
3
|
+
/** Every Insights route: where it mounts, and the handler it runs. */
|
|
4
|
+
declare const INSIGHTS_ROUTES: readonly [{
|
|
5
|
+
readonly access: "client";
|
|
6
|
+
readonly method: "POST";
|
|
7
|
+
readonly path: "/events";
|
|
8
|
+
readonly handler: "appendBundleEvent";
|
|
9
|
+
}, {
|
|
10
|
+
readonly access: "admin";
|
|
11
|
+
readonly method: "GET";
|
|
12
|
+
readonly path: "/events";
|
|
13
|
+
readonly handler: "listEvents";
|
|
14
|
+
}, {
|
|
15
|
+
readonly access: "admin";
|
|
16
|
+
readonly method: "GET";
|
|
17
|
+
readonly path: "/overview";
|
|
18
|
+
readonly handler: "getReportingOverview";
|
|
19
|
+
}, {
|
|
20
|
+
readonly access: "admin";
|
|
21
|
+
readonly method: "GET";
|
|
22
|
+
readonly path: "/installations";
|
|
23
|
+
readonly handler: "pageInstallationsByCurrentUserId";
|
|
24
|
+
}, {
|
|
25
|
+
readonly access: "admin";
|
|
26
|
+
readonly method: "GET";
|
|
27
|
+
readonly path: "/installations/:installId/events";
|
|
28
|
+
readonly handler: "listInstallationEvents";
|
|
29
|
+
}, {
|
|
30
|
+
readonly access: "admin";
|
|
31
|
+
readonly method: "GET";
|
|
32
|
+
readonly path: "/installations/:installId";
|
|
33
|
+
readonly handler: "getInstallation";
|
|
34
|
+
}];
|
|
35
|
+
//#endregion
|
|
36
|
+
export { INSIGHTS_ROUTES };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { InsightsBadRequestError, InsightsPayloadTooLargeError } from "./errors.mjs";
|
|
2
|
+
import { parseBundleEventRequest } from "./eventInput.mjs";
|
|
3
|
+
import { parseEventPageInput, parseReportingOverviewInput, parseUserInstallationPageInput } from "./queryInput.mjs";
|
|
4
|
+
import { isDatabaseBusyError } from "@hot-updater/plugin-core/server-plugin";
|
|
5
|
+
//#region src/server/routes.ts
|
|
6
|
+
/** Seconds a client waits before it resends a request the busy database refused. */
|
|
7
|
+
const RETRY_AFTER_SECONDS = 5;
|
|
8
|
+
const json = (body, status, headers = {}) => Response.json(body, {
|
|
9
|
+
headers: {
|
|
10
|
+
"cache-control": "private, no-store",
|
|
11
|
+
...headers
|
|
12
|
+
},
|
|
13
|
+
status
|
|
14
|
+
});
|
|
15
|
+
const requireParam = (params, key) => {
|
|
16
|
+
const value = params[key];
|
|
17
|
+
if (value === void 0 || value.length === 0) throw new InsightsBadRequestError(`Missing route parameter: ${key}`);
|
|
18
|
+
try {
|
|
19
|
+
return decodeURIComponent(value);
|
|
20
|
+
} catch {
|
|
21
|
+
throw new InsightsBadRequestError(`Invalid route parameter: ${key}`);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const run = async (operation) => {
|
|
25
|
+
try {
|
|
26
|
+
return await operation();
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error instanceof InsightsBadRequestError) return json({ error: error.message }, 400);
|
|
29
|
+
if (error instanceof InsightsPayloadTooLargeError) return json({ error: error.message }, 413);
|
|
30
|
+
if (isDatabaseBusyError(error)) {
|
|
31
|
+
console.warn("[hot-updater] Insights answered 503: the database is busy.", error);
|
|
32
|
+
return json({ error: "Service unavailable" }, 503, { "retry-after": String(RETRY_AFTER_SECONDS) });
|
|
33
|
+
}
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const query = (operation) => run(async () => json(await operation(), 200));
|
|
38
|
+
/** Every Insights route: where it mounts, and the handler it runs. */
|
|
39
|
+
const INSIGHTS_ROUTES = [
|
|
40
|
+
{
|
|
41
|
+
access: "client",
|
|
42
|
+
method: "POST",
|
|
43
|
+
path: "/events",
|
|
44
|
+
handler: "appendBundleEvent"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
access: "admin",
|
|
48
|
+
method: "GET",
|
|
49
|
+
path: "/events",
|
|
50
|
+
handler: "listEvents"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
access: "admin",
|
|
54
|
+
method: "GET",
|
|
55
|
+
path: "/overview",
|
|
56
|
+
handler: "getReportingOverview"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
access: "admin",
|
|
60
|
+
method: "GET",
|
|
61
|
+
path: "/installations",
|
|
62
|
+
handler: "pageInstallationsByCurrentUserId"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
access: "admin",
|
|
66
|
+
method: "GET",
|
|
67
|
+
path: "/installations/:installId/events",
|
|
68
|
+
handler: "listInstallationEvents"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
access: "admin",
|
|
72
|
+
method: "GET",
|
|
73
|
+
path: "/installations/:installId",
|
|
74
|
+
handler: "getInstallation"
|
|
75
|
+
}
|
|
76
|
+
];
|
|
77
|
+
const createInsightsRouteHandlers = (provider) => ({
|
|
78
|
+
appendBundleEvent: async (_params, request) => run(async () => {
|
|
79
|
+
await provider.appendBundleEvent(await parseBundleEventRequest(request));
|
|
80
|
+
return new Response(null, { status: 204 });
|
|
81
|
+
}),
|
|
82
|
+
getReportingOverview: (_params, request) => query(() => provider.getReportingOverview(parseReportingOverviewInput(request))),
|
|
83
|
+
getInstallation: (params) => run(async () => {
|
|
84
|
+
const installation = await provider.getInstallation({ installId: requireParam(params, "installId") });
|
|
85
|
+
return installation === null ? json({ error: "Installation not found" }, 404) : json(installation, 200);
|
|
86
|
+
}),
|
|
87
|
+
listEvents: (_params, request) => query(() => provider.listEvents(parseEventPageInput(request))),
|
|
88
|
+
listInstallationEvents: (params, request) => query(() => provider.listInstallationEvents({
|
|
89
|
+
...parseEventPageInput(request),
|
|
90
|
+
installId: requireParam(params, "installId")
|
|
91
|
+
})),
|
|
92
|
+
pageInstallationsByCurrentUserId: (_params, request) => query(() => provider.pageInstallationsByCurrentUserId(parseUserInstallationPageInput(request)))
|
|
93
|
+
});
|
|
94
|
+
//#endregion
|
|
95
|
+
export { INSIGHTS_ROUTES, createInsightsRouteHandlers };
|