silgi 0.50.3 → 0.51.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/dist/adapters/_fetch-adapter.d.mts +2 -1
- package/dist/adapters/_fetch-adapter.mjs +13 -9
- package/dist/adapters/astro.mjs +3 -3
- package/dist/adapters/express.mjs +1 -1
- package/dist/adapters/nextjs.mjs +3 -2
- package/dist/adapters/remix.mjs +3 -3
- package/dist/adapters/solidstart.mjs +3 -3
- package/dist/adapters/sveltekit.mjs +3 -2
- package/dist/plugins/analytics/alerts.mjs +1 -1
- package/dist/plugins/analytics/routes.mjs +3 -2
- package/dist/plugins/analytics/utils.mjs +19 -3
- package/dist/plugins/analytics.mjs +6 -4
- package/package.json +10 -1
|
@@ -2,7 +2,7 @@ import { WrapHandlerOptions } from "../core/handler.mjs";
|
|
|
2
2
|
|
|
3
3
|
//#region src/adapters/_fetch-adapter.d.ts
|
|
4
4
|
interface FetchAdapterConfig<TCtx extends Record<string, unknown>> extends WrapHandlerOptions {
|
|
5
|
-
/** Route prefix to strip. Default: "/api
|
|
5
|
+
/** Route prefix to strip. Default: "/api" */
|
|
6
6
|
prefix?: string;
|
|
7
7
|
/** Context factory — receives the Request (or framework event via eventMap). */
|
|
8
8
|
context?: (req: Request) => TCtx | Promise<TCtx>;
|
|
@@ -12,6 +12,7 @@ interface FetchAdapterConfig<TCtx extends Record<string, unknown>> extends WrapH
|
|
|
12
12
|
* (SvelteKit RequestEvent, SolidStart event), use this extended config.
|
|
13
13
|
*/
|
|
14
14
|
interface FetchAdapterConfigWithEvent<TCtx extends Record<string, unknown>, TEvent = any> extends WrapHandlerOptions {
|
|
15
|
+
/** Route prefix to strip. Default: "/api" */
|
|
15
16
|
prefix?: string;
|
|
16
17
|
/** Context factory — receives the framework event, not raw Request. */
|
|
17
18
|
context?: (event: TEvent) => TCtx | Promise<TCtx>;
|
|
@@ -25,10 +25,8 @@ function rewriteRequest(request, prefix) {
|
|
|
25
25
|
*/
|
|
26
26
|
function createFetchAdapter(router, options, defaultPrefix) {
|
|
27
27
|
const prefix = options.prefix ?? defaultPrefix;
|
|
28
|
-
const
|
|
29
|
-
return (request) =>
|
|
30
|
-
return handler(rewriteRequest(request, prefix));
|
|
31
|
-
};
|
|
28
|
+
const inner = createFetchHandler(router, options.context ?? (() => ({})));
|
|
29
|
+
return wrapHandler((request) => inner(rewriteRequest(request, prefix)), router, options);
|
|
32
30
|
}
|
|
33
31
|
/**
|
|
34
32
|
* Create a fetch-passthrough adapter for frameworks that pass an event object
|
|
@@ -38,15 +36,21 @@ function createFetchAdapter(router, options, defaultPrefix) {
|
|
|
38
36
|
function createEventFetchAdapter(router, options, defaultPrefix, extractRequest) {
|
|
39
37
|
const prefix = options.prefix ?? defaultPrefix;
|
|
40
38
|
const requestEventMap = /* @__PURE__ */ new WeakMap();
|
|
41
|
-
const
|
|
39
|
+
const inner = createFetchHandler(router, (_req) => {
|
|
42
40
|
const eventRef = requestEventMap.get(_req);
|
|
43
41
|
if (options.context && eventRef) return options.context(eventRef);
|
|
44
42
|
return {};
|
|
45
|
-
})
|
|
43
|
+
});
|
|
44
|
+
const handler = wrapHandler((request) => {
|
|
45
|
+
const rewritten = rewriteRequest(request, prefix);
|
|
46
|
+
const eventRef = requestEventMap.get(request);
|
|
47
|
+
if (eventRef) requestEventMap.set(rewritten, eventRef);
|
|
48
|
+
return inner(rewritten);
|
|
49
|
+
}, router, options);
|
|
46
50
|
return (event) => {
|
|
47
|
-
const
|
|
48
|
-
requestEventMap.set(
|
|
49
|
-
return handler(
|
|
51
|
+
const request = extractRequest(event);
|
|
52
|
+
requestEventMap.set(request, event);
|
|
53
|
+
return handler(request);
|
|
50
54
|
};
|
|
51
55
|
}
|
|
52
56
|
//#endregion
|
package/dist/adapters/astro.mjs
CHANGED
|
@@ -5,13 +5,13 @@ import { createFetchAdapter } from "./_fetch-adapter.mjs";
|
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
7
|
* ```ts
|
|
8
|
-
* // src/pages/api/
|
|
8
|
+
* // src/pages/api/[...path].ts
|
|
9
9
|
* import { createHandler } from "silgi/astro"
|
|
10
10
|
* import { appRouter } from "~/server/rpc"
|
|
11
11
|
*
|
|
12
12
|
* const handler = createHandler(appRouter, {
|
|
13
|
-
* prefix: "/api/rpc",
|
|
14
13
|
* context: (req) => ({ db: getDB() }),
|
|
14
|
+
* analytics: true,
|
|
15
15
|
* })
|
|
16
16
|
*
|
|
17
17
|
* export const GET = handler
|
|
@@ -24,7 +24,7 @@ import { createFetchAdapter } from "./_fetch-adapter.mjs";
|
|
|
24
24
|
* Astro passes { request, params } — we extract request and delegate.
|
|
25
25
|
*/
|
|
26
26
|
function createHandler(router, options = {}) {
|
|
27
|
-
const handler = createFetchAdapter(router, options, "/api
|
|
27
|
+
const handler = createFetchAdapter(router, options, "/api");
|
|
28
28
|
return ({ request }) => handler(request);
|
|
29
29
|
}
|
|
30
30
|
//#endregion
|
|
@@ -11,7 +11,7 @@ import { iteratorToEventStream } from "../core/sse.mjs";
|
|
|
11
11
|
* import { createHandler } from "silgi/express"
|
|
12
12
|
*
|
|
13
13
|
* const app = express()
|
|
14
|
-
* app.use("/
|
|
14
|
+
* app.use("/api", createHandler(appRouter, {
|
|
15
15
|
* context: (req) => ({ db: getDB(), user: req.user }),
|
|
16
16
|
* }))
|
|
17
17
|
* app.listen(3000)
|
package/dist/adapters/nextjs.mjs
CHANGED
|
@@ -5,12 +5,13 @@ import { createFetchAdapter } from "./_fetch-adapter.mjs";
|
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
7
|
* ```ts
|
|
8
|
-
* // app/api/
|
|
8
|
+
* // app/api/[...path]/route.ts
|
|
9
9
|
* import { createHandler } from "silgi/nextjs"
|
|
10
10
|
* import { appRouter } from "~/server/rpc"
|
|
11
11
|
*
|
|
12
12
|
* const handler = createHandler(appRouter, {
|
|
13
13
|
* context: (req) => ({ db: getDB() }),
|
|
14
|
+
* analytics: true,
|
|
14
15
|
* })
|
|
15
16
|
*
|
|
16
17
|
* export { handler as GET, handler as POST }
|
|
@@ -23,7 +24,7 @@ import { createFetchAdapter } from "./_fetch-adapter.mjs";
|
|
|
23
24
|
* including content negotiation (JSON, MessagePack, devalue).
|
|
24
25
|
*/
|
|
25
26
|
function createHandler(router, options = {}) {
|
|
26
|
-
return createFetchAdapter(router, options, "/api
|
|
27
|
+
return createFetchAdapter(router, options, "/api");
|
|
27
28
|
}
|
|
28
29
|
//#endregion
|
|
29
30
|
export { createHandler };
|
package/dist/adapters/remix.mjs
CHANGED
|
@@ -5,13 +5,13 @@ import { createFetchAdapter } from "./_fetch-adapter.mjs";
|
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
7
|
* ```ts
|
|
8
|
-
* // app/routes/
|
|
8
|
+
* // app/routes/api.$.tsx
|
|
9
9
|
* import { createHandler } from "silgi/remix"
|
|
10
10
|
* import { appRouter } from "~/server/rpc"
|
|
11
11
|
*
|
|
12
12
|
* const handler = createHandler(appRouter, {
|
|
13
|
-
* prefix: "/rpc",
|
|
14
13
|
* context: (req) => ({ db: getDB() }),
|
|
14
|
+
* analytics: true,
|
|
15
15
|
* })
|
|
16
16
|
*
|
|
17
17
|
* export const action = handler
|
|
@@ -23,7 +23,7 @@ import { createFetchAdapter } from "./_fetch-adapter.mjs";
|
|
|
23
23
|
* Remix passes { request, params } — we extract request and delegate.
|
|
24
24
|
*/
|
|
25
25
|
function createHandler(router, options = {}) {
|
|
26
|
-
const handler = createFetchAdapter(router, options, "/
|
|
26
|
+
const handler = createFetchAdapter(router, options, "/api");
|
|
27
27
|
return ({ request }) => handler(request);
|
|
28
28
|
}
|
|
29
29
|
//#endregion
|
|
@@ -5,13 +5,13 @@ import { createEventFetchAdapter } from "./_fetch-adapter.mjs";
|
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
7
|
* ```ts
|
|
8
|
-
* // src/routes/api/
|
|
8
|
+
* // src/routes/api/[...path].ts
|
|
9
9
|
* import { createHandler } from "silgi/solidstart"
|
|
10
10
|
* import { appRouter } from "~/server/rpc"
|
|
11
11
|
*
|
|
12
12
|
* const handler = createHandler(appRouter, {
|
|
13
|
-
* prefix: "/api/rpc",
|
|
14
13
|
* context: (event) => ({ db: getDB() }),
|
|
14
|
+
* analytics: true,
|
|
15
15
|
* })
|
|
16
16
|
*
|
|
17
17
|
* export const GET = handler
|
|
@@ -23,7 +23,7 @@ import { createEventFetchAdapter } from "./_fetch-adapter.mjs";
|
|
|
23
23
|
* SolidStart uses Fetch API events — uses Silgi's handler().
|
|
24
24
|
*/
|
|
25
25
|
function createHandler(router, options = {}) {
|
|
26
|
-
return createEventFetchAdapter(router, options, "/api
|
|
26
|
+
return createEventFetchAdapter(router, options, "/api", (event) => event.request ?? event);
|
|
27
27
|
}
|
|
28
28
|
//#endregion
|
|
29
29
|
export { createHandler };
|
|
@@ -5,12 +5,13 @@ import { createEventFetchAdapter } from "./_fetch-adapter.mjs";
|
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
7
|
* ```ts
|
|
8
|
-
* // src/routes/api/
|
|
8
|
+
* // src/routes/api/[...path]/+server.ts
|
|
9
9
|
* import { createHandler } from "silgi/sveltekit"
|
|
10
10
|
* import { appRouter } from "$lib/server/rpc"
|
|
11
11
|
*
|
|
12
12
|
* const handler = createHandler(appRouter, {
|
|
13
13
|
* context: (event) => ({ db: getDB(), user: event.locals.user }),
|
|
14
|
+
* analytics: true,
|
|
14
15
|
* })
|
|
15
16
|
*
|
|
16
17
|
* export const GET = handler
|
|
@@ -24,7 +25,7 @@ import { createEventFetchAdapter } from "./_fetch-adapter.mjs";
|
|
|
24
25
|
* The handler uses Silgi's handler() for full protocol support.
|
|
25
26
|
*/
|
|
26
27
|
function createHandler(router, options = {}) {
|
|
27
|
-
return createEventFetchAdapter(router, options, "/api
|
|
28
|
+
return createEventFetchAdapter(router, options, "/api", (event) => event.request);
|
|
28
29
|
}
|
|
29
30
|
//#endregion
|
|
30
31
|
export { createHandler };
|
|
@@ -65,7 +65,7 @@ var AlertEngine = class {
|
|
|
65
65
|
case "no_requests": return samples.length;
|
|
66
66
|
case "latency_avg": return samples.reduce((sum, s) => sum + s.durationMs, 0) / samples.length;
|
|
67
67
|
case "latency_p95": {
|
|
68
|
-
const sorted = samples.map((s) => s.durationMs).
|
|
68
|
+
const sorted = samples.map((s) => s.durationMs).toSorted((a, b) => a - b);
|
|
69
69
|
const idx = Math.ceil(sorted.length * .95) - 1;
|
|
70
70
|
return sorted[Math.max(0, idx)];
|
|
71
71
|
}
|
|
@@ -69,8 +69,9 @@ document.getElementById('f').onsubmit=function(e){
|
|
|
69
69
|
e.preventDefault();
|
|
70
70
|
var t=document.getElementById('t').value.trim();
|
|
71
71
|
if(!t)return;
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
var base=location.pathname;if(base.slice(-1)==='/')base=base.slice(0,-1);
|
|
73
|
+
document.cookie='silgi-auth='+encodeURIComponent(t)+';path='+base+';samesite=strict';
|
|
74
|
+
fetch(base+'/stats',{headers:{'cookie':'silgi-auth='+encodeURIComponent(t)}}).then(function(){
|
|
74
75
|
location.reload();
|
|
75
76
|
}).catch(function(){location.reload()});
|
|
76
77
|
};
|
|
@@ -45,12 +45,28 @@ function isTrackedRequestPath(pathname) {
|
|
|
45
45
|
const normalized = pathname.startsWith("/") ? pathname.slice(1) : pathname;
|
|
46
46
|
return normalized === "api" || normalized.startsWith("api/") || normalized === "graphql" || normalized.startsWith("graphql/");
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Normalize an incoming path to its analytics sub-path.
|
|
50
|
+
*
|
|
51
|
+
* Returns the sub-path after `analytics/` (e.g. `'stats'`, `'requests/5'`),
|
|
52
|
+
* an empty string for the dashboard root, or `null` if this isn't an
|
|
53
|
+
* analytics path at all.
|
|
54
|
+
*
|
|
55
|
+
* Accepts both `api/analytics*` (mounted under an `/api` RPC prefix) and
|
|
56
|
+
* bare `analytics*` (mounted at a prefix that already ends with `/api`),
|
|
57
|
+
* so the dashboard works regardless of adapter prefix configuration.
|
|
58
|
+
*/
|
|
59
|
+
function normalizeAnalyticsPath(pathname) {
|
|
60
|
+
const p = pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
|
|
61
|
+
if (p === "api/analytics") return "";
|
|
62
|
+
if (p.startsWith("api/analytics/")) return p.slice(14);
|
|
63
|
+
if (p === "analytics") return "";
|
|
64
|
+
if (p.startsWith("analytics/")) return p.slice(10);
|
|
65
|
+
return null;
|
|
50
66
|
}
|
|
51
67
|
/** Helper to safely cast unknown to Record. */
|
|
52
68
|
function asRecord(value) {
|
|
53
69
|
return value && typeof value === "object" ? value : null;
|
|
54
70
|
}
|
|
55
71
|
//#endregion
|
|
56
|
-
export { asRecord,
|
|
72
|
+
export { asRecord, isTrackedRequestPath, matchesPathPrefix, normalizeAnalyticsPath, redactHeaderValue, round, safeStringify, sanitizeHeaders };
|
|
@@ -3,7 +3,7 @@ import { SilgiError, toSilgiError } from "../core/error.mjs";
|
|
|
3
3
|
import { analyticsTraceMap } from "../core/trace-map.mjs";
|
|
4
4
|
import { parseUrlPathname } from "../core/url.mjs";
|
|
5
5
|
import { generateRequestId } from "./analytics/request-id.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { isTrackedRequestPath, normalizeAnalyticsPath, round, sanitizeHeaders } from "./analytics/utils.mjs";
|
|
7
7
|
import { RequestAccumulator } from "./analytics/accumulator.mjs";
|
|
8
8
|
import { AnalyticsCollector } from "./analytics/collector.mjs";
|
|
9
9
|
import { errorToMarkdown, requestToMarkdown } from "./analytics/export.mjs";
|
|
@@ -96,12 +96,14 @@ function wrapWithAnalytics(handler, options = {}) {
|
|
|
96
96
|
});
|
|
97
97
|
return async (request) => {
|
|
98
98
|
const pathname = parseUrlPathname(request.url);
|
|
99
|
-
|
|
99
|
+
const analyticsSub = normalizeAnalyticsPath(pathname);
|
|
100
|
+
if (analyticsSub !== null) {
|
|
101
|
+
const canonical = analyticsSub === "" ? "api/analytics" : `api/analytics/${analyticsSub}`;
|
|
100
102
|
if (auth) {
|
|
101
103
|
const authResult = checkAnalyticsAuth(request, auth);
|
|
102
|
-
if (!(authResult instanceof Promise ? await authResult : authResult)) return analyticsAuthResponse(
|
|
104
|
+
if (!(authResult instanceof Promise ? await authResult : authResult)) return analyticsAuthResponse(canonical);
|
|
103
105
|
}
|
|
104
|
-
return serveAnalyticsRoute(
|
|
106
|
+
return serveAnalyticsRoute(canonical, request, collector, dashboardHtml);
|
|
105
107
|
}
|
|
106
108
|
if (!isTrackedRequestPath(pathname) || collector.isIgnored(pathname)) return handler(request);
|
|
107
109
|
const incomingTraceId = request.headers.get("x-trace-id");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "silgi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.51.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The fastest end-to-end type-safe RPC framework for TypeScript — compiled pipelines, single package, every runtime",
|
|
6
6
|
"keywords": [
|
|
@@ -268,6 +268,7 @@
|
|
|
268
268
|
"ocache": "^0.1.4",
|
|
269
269
|
"ofetch": "2.0.0-alpha.3",
|
|
270
270
|
"ohash": "^2.0.11",
|
|
271
|
+
"rou3": "^0.8.1",
|
|
271
272
|
"srvx": "^0.11.13",
|
|
272
273
|
"unstorage": "^2.0.0-alpha.7"
|
|
273
274
|
},
|
|
@@ -313,6 +314,8 @@
|
|
|
313
314
|
"peerDependencies": {
|
|
314
315
|
"@pinia/colada": ">=0.16.0",
|
|
315
316
|
"@scalar/api-reference": ">=1.0.0",
|
|
317
|
+
"ai": ">=5.0.0",
|
|
318
|
+
"oxc-parser": ">=0.50.0",
|
|
316
319
|
"vue": ">=3.3.0"
|
|
317
320
|
},
|
|
318
321
|
"peerDependenciesMeta": {
|
|
@@ -322,6 +325,12 @@
|
|
|
322
325
|
"@pinia/colada": {
|
|
323
326
|
"optional": true
|
|
324
327
|
},
|
|
328
|
+
"ai": {
|
|
329
|
+
"optional": true
|
|
330
|
+
},
|
|
331
|
+
"oxc-parser": {
|
|
332
|
+
"optional": true
|
|
333
|
+
},
|
|
325
334
|
"vue": {
|
|
326
335
|
"optional": true
|
|
327
336
|
}
|