@segmentstream-cloud/app 0.17.0 → 0.17.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.
- package/README.md +28 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/request-timing.d.ts +6 -0
- package/dist/request-timing.js +53 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -698,6 +698,34 @@ Replace segmentstream.semantic with typed warehouse-backed entity aggregates. Se
|
|
|
698
698
|
deployed with `@segmentstream-cloud/ontology` 0.17.0 or later. Requests that use none of
|
|
699
699
|
them work as before.
|
|
700
700
|
|
|
701
|
+
## Request timing
|
|
702
|
+
|
|
703
|
+
Wrap app Route Handlers to add `Server-Timing: app;dur=…`, `x-request-id` and safe
|
|
704
|
+
structured `app.request` runtime logs:
|
|
705
|
+
|
|
706
|
+
```ts
|
|
707
|
+
import { withRequestTiming } from "@segmentstream-cloud/app";
|
|
708
|
+
|
|
709
|
+
export const GET = withRequestTiming(async (request: Request) => {
|
|
710
|
+
return Response.json({ ok: true });
|
|
711
|
+
});
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
The wrapper passes handler arguments through and preserves response headers and
|
|
715
|
+
streaming bodies. It logs duration, method, HTTP status and a generated request ID,
|
|
716
|
+
excluding URLs, cookies, bodies and error details. Errors propagate unchanged.
|
|
717
|
+
Next.js redirects and `notFound()` log their HTTP status and propagate unchanged;
|
|
718
|
+
other Next.js control-flow exceptions log `status: null` because Next owns their eventual response.
|
|
719
|
+
Those exceptions cannot receive timing headers here;
|
|
720
|
+
return an explicit response when the wrapper needs to decorate its headers.
|
|
721
|
+
Read these logs with `segmentstream logs --service app` (deployment permission required).
|
|
722
|
+
Duration ends when the handler returns response headers; it excludes later stream
|
|
723
|
+
work and browser rendering. It is opt-in for Route Handlers, not a page renderer
|
|
724
|
+
or a web vitals collector. A Next.js proxy cannot time the downstream page render.
|
|
725
|
+
|
|
726
|
+
Ontology SDK 0.31.2 adds request timing for ontology calls automatically. Timings
|
|
727
|
+
are available in hosted logs; retained diagnostics are tracked in issue #520.
|
|
728
|
+
|
|
701
729
|
## Upgrading to 0.14
|
|
702
730
|
|
|
703
731
|
- `segmentstream.ontology.aggregate()` accepts the measures of the model by their bare names, typed
|
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export declare function createSegmentStreamApp<O = unknown>(config: SegmentStrea
|
|
|
18
18
|
export { NOT_INVITED, SIGN_IN_LINK_INVALID, SegmentStreamApiError } from "./errors.js";
|
|
19
19
|
export { grants, isAllowed } from "./permissions.js";
|
|
20
20
|
export { sql } from "./parameters.js";
|
|
21
|
+
export { withRequestTiming } from "./request-timing.js";
|
|
21
22
|
export type { AuthenticationMethod, SegmentStreamApp } from "./app.js";
|
|
22
23
|
export { CONSENT_PAGE } from "./oauth.js";
|
|
23
24
|
export { AGENT_CONNECT_HANDLER, AGENT_CONNECT_PAGE } from "./agent.js";
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ export function createSegmentStreamApp(config) {
|
|
|
19
19
|
export { NOT_INVITED, SIGN_IN_LINK_INVALID, SegmentStreamApiError } from "./errors.js";
|
|
20
20
|
export { grants, isAllowed } from "./permissions.js";
|
|
21
21
|
export { sql } from "./parameters.js";
|
|
22
|
+
export { withRequestTiming } from "./request-timing.js";
|
|
22
23
|
export { CONSENT_PAGE } from "./oauth.js";
|
|
23
24
|
export { AGENT_CONNECT_HANDLER, AGENT_CONNECT_PAGE } from "./agent.js";
|
|
24
25
|
export { createBlobStorageClient, } from "./storage.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time an app Route Handler until it returns response headers. Streaming/rendering after that
|
|
3
|
+
* point is excluded. Logs contain no URL, cookies, body or error text. Server Components need
|
|
4
|
+
* framework instrumentation for complete page timing; a Next.js proxy only times authentication.
|
|
5
|
+
*/
|
|
6
|
+
export declare function withRequestTiming<R extends Request, A extends unknown[]>(handler: (request: R, ...args: A) => Promise<Response>): (request: R, ...args: A) => Promise<Response>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { unstable_rethrow } from "next/navigation";
|
|
2
|
+
import { getRedirectStatusCodeFromError } from "next/dist/client/components/redirect.js";
|
|
3
|
+
import { isRedirectError } from "next/dist/client/components/redirect-error.js";
|
|
4
|
+
import { getAccessFallbackHTTPStatus, isHTTPAccessFallbackError, } from "next/dist/client/components/http-access-fallback/http-access-fallback.js";
|
|
5
|
+
/**
|
|
6
|
+
* Time an app Route Handler until it returns response headers. Streaming/rendering after that
|
|
7
|
+
* point is excluded. Logs contain no URL, cookies, body or error text. Server Components need
|
|
8
|
+
* framework instrumentation for complete page timing; a Next.js proxy only times authentication.
|
|
9
|
+
*/
|
|
10
|
+
export function withRequestTiming(handler) {
|
|
11
|
+
return async (request, ...args) => {
|
|
12
|
+
const started = performance.now();
|
|
13
|
+
const requestId = crypto.randomUUID();
|
|
14
|
+
let status = 500;
|
|
15
|
+
let durationMs;
|
|
16
|
+
try {
|
|
17
|
+
const response = await handler(request, ...args);
|
|
18
|
+
const headers = new Headers(response.headers);
|
|
19
|
+
durationMs = Math.round((performance.now() - started) * 100) / 100;
|
|
20
|
+
status = response.status;
|
|
21
|
+
headers.append("server-timing", `app;dur=${durationMs}`);
|
|
22
|
+
headers.set("x-request-id", requestId);
|
|
23
|
+
return new Response(response.body, {
|
|
24
|
+
status,
|
|
25
|
+
statusText: response.statusText,
|
|
26
|
+
headers,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
try {
|
|
31
|
+
unstable_rethrow(error);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Next owns the eventual response for control-flow exceptions, outside this wrapper.
|
|
35
|
+
status = isRedirectError(error)
|
|
36
|
+
? getRedirectStatusCodeFromError(error)
|
|
37
|
+
: isHTTPAccessFallbackError(error)
|
|
38
|
+
? getAccessFallbackHTTPStatus(error)
|
|
39
|
+
: null;
|
|
40
|
+
}
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
console.info(JSON.stringify({
|
|
45
|
+
event: "app.request",
|
|
46
|
+
requestId,
|
|
47
|
+
method: request.method,
|
|
48
|
+
status,
|
|
49
|
+
durationMs: durationMs ?? Math.round((performance.now() - started) * 100) / 100,
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@segmentstream-cloud/app",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"description": "Sign-in, users, permissions, the ontology client and warehouse data access for SegmentStream workspace apps built with Next.js.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "SegmentStream",
|