akanjs 3.0.0-alpha.70 → 3.0.0-alpha.72
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/client/cookie.ts +21 -34
- package/client/csrTypes.ts +0 -12
- package/client/frameConfig.ts +13 -0
- package/client/router.ts +7 -7
- package/common/Logger.ts +287 -67
- package/common/TrustedProxy.ts +134 -0
- package/common/clamp.ts +2 -0
- package/common/deepObjectify.ts +3 -2
- package/common/dynamicRecord.ts +12 -0
- package/common/index.ts +29 -2
- package/common/isValidDate.ts +1 -1
- package/common/logContext.ts +27 -0
- package/common/plainFieldsOf.ts +13 -0
- package/common/requestOrigin.ts +32 -0
- package/common/{httpClient.ts → restClient.ts} +16 -9
- package/constant/crystalize.ts +59 -39
- package/constant/dateSlot.ts +62 -0
- package/constant/getDefault.ts +16 -11
- package/constant/hydrationPlan.ts +72 -0
- package/constant/immerify.ts +4 -6
- package/constant/index.ts +2 -0
- package/constant/types.ts +22 -0
- package/constant/via.ts +14 -14
- package/dictionary/base.dictionary.ts +4 -0
- package/dictionary/trans.ts +24 -5
- package/document/dataLoader.ts +0 -4
- package/document/filterMeta.ts +2 -2
- package/document/loaderInfo.ts +4 -1
- package/fetch/client/fetchClient.ts +67 -72
- package/fetch/client/fetchHandle.ts +57 -0
- package/fetch/client/httpClient.ts +42 -14
- package/fetch/client/sliceInitHandle.ts +101 -0
- package/fetch/fetchType/appliedReturn.type.ts +64 -1
- package/fetch/fetchType/sliceFetch.type.ts +5 -5
- package/fetch/requestStorage.ts +19 -5
- package/index.ts +16 -2
- package/local/apps/serverLifecycle/serverLifecycle-local.db-shm +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-shm +0 -0
- package/package.json +11 -1
- package/server/akanApp.ts +84 -93
- package/server/akanAppHeaders.ts +10 -4
- package/server/akanOption.ts +14 -1
- package/server/akanServer.ts +87 -10
- package/server/cachePolicy.ts +2 -11
- package/server/console.ts +102 -1
- package/server/consoleSession.ts +49 -5
- package/server/decorators.ts +12 -3
- package/server/devtools/constantSerializer.ts +8 -1
- package/server/di/diLifecycle.ts +21 -1
- package/server/di/resolveHierarchy.ts +3 -3
- package/server/logging/childOutputReader.ts +137 -0
- package/server/logging/hubFileSink.ts +39 -0
- package/server/logging/logControlSocket.ts +173 -0
- package/server/logging/logForwarder.ts +131 -0
- package/server/logging/logHub.ts +0 -0
- package/server/logging/logQuery.ts +130 -0
- package/server/logging/logStdoutWriter.ts +39 -0
- package/server/logging/logStreamRoute.ts +87 -0
- package/server/logging/logTailClient.ts +161 -0
- package/server/mcp/McpAuth.ts +4 -15
- package/server/mcp/McpDispatcher.ts +20 -10
- package/server/mcp/McpEventStream.ts +8 -60
- package/server/resolver/database.resolver.ts +13 -8
- package/server/resolver/signal.resolver.ts +83 -56
- package/server/routeTreeBuilder.ts +1 -15
- package/server/routing/apiRouter.ts +6 -5
- package/server/routing/appWsData.ts +9 -6
- package/server/routing/eventStream.ts +93 -0
- package/server/routing/soloAppRoutes.ts +5 -1
- package/server/rscWorker.tsx +27 -9
- package/server/rscWorkerHost.ts +59 -8
- package/server/staticPath.ts +39 -0
- package/server/webRouter.ts +44 -41
- package/service/adapt.ts +1 -5
- package/service/ipcTypes.ts +5 -0
- package/service/predefinedAdaptor/cache.adaptor.ts +8 -4
- package/service/predefinedAdaptor/database.adaptor.ts +37 -1766
- package/service/predefinedAdaptor/logging.adaptor.ts +1 -1
- package/service/predefinedAdaptor/sql/PendingStoreEnsures.ts +22 -0
- package/service/predefinedAdaptor/sql/QueryCompiler.ts +225 -0
- package/service/predefinedAdaptor/sql/SqlDocumentStore.ts +812 -0
- package/service/predefinedAdaptor/sql/UpdateCompiler.ts +67 -0
- package/service/predefinedAdaptor/sql/dialect/postgres.ts +151 -0
- package/service/predefinedAdaptor/sql/dialect/sqlite.ts +134 -0
- package/service/predefinedAdaptor/sql/driver/bunSqlite.ts +29 -0
- package/service/predefinedAdaptor/sql/driver/libsql.ts +37 -0
- package/service/predefinedAdaptor/sql/driver/postgres.ts +42 -0
- package/service/predefinedAdaptor/sql/types.ts +251 -0
- package/service/predefinedAdaptor/sql/values.ts +91 -0
- package/signal/CrossSiteGuard.ts +71 -0
- package/signal/SignalFailure.ts +51 -0
- package/signal/endpoint.ts +2 -2
- package/signal/exception.ts +43 -6
- package/signal/index.ts +2 -0
- package/signal/intercept.ts +5 -2
- package/signal/internal.ts +10 -2
- package/signal/serializer/fetch.serializer.ts +11 -1
- package/signal/signalContext.ts +73 -43
- package/signal/slice.ts +1 -1
- package/signal/trace.ts +268 -9
- package/store/SliceRequest.ts +23 -0
- package/store/action.ts +182 -120
- package/store/agent/AgentBridge.ts +3 -1
- package/store/agentic/StExposeBuilder.ts +7 -4
- package/store/agentic/StStateBuilder.ts +9 -4
- package/store/agentic/StToolBuilder.ts +21 -7
- package/store/store.ts +1 -1
- package/store/storeInstance.ts +3 -3
- package/store/storeRegistry.ts +1 -1
- package/test/testServer.ts +6 -6
- package/types/client/cookie.d.ts +13 -2
- package/types/client/csrTypes.d.ts +0 -1
- package/types/client/frameConfig.d.ts +1 -0
- package/types/common/Logger.d.ts +88 -6
- package/types/common/TrustedProxy.d.ts +24 -0
- package/types/common/clamp.d.ts +2 -0
- package/types/common/dynamicRecord.d.ts +14 -0
- package/types/common/index.d.ts +8 -2
- package/types/common/logContext.d.ts +17 -0
- package/types/common/plainFieldsOf.d.ts +6 -0
- package/types/common/requestOrigin.d.ts +14 -0
- package/types/common/restClient.d.ts +27 -0
- package/types/constant/crystalize.d.ts +3 -0
- package/types/constant/dateSlot.d.ts +14 -0
- package/types/constant/getDefault.d.ts +8 -0
- package/types/constant/hydrationPlan.d.ts +24 -0
- package/types/constant/index.d.ts +2 -0
- package/types/constant/types.d.ts +13 -0
- package/types/dictionary/base.dictionary.d.ts +1 -1
- package/types/dictionary/dictionary.d.ts +8 -8
- package/types/document/dataLoader.d.ts +0 -3
- package/types/document/filterMeta.d.ts +2 -2
- package/types/document/loaderInfo.d.ts +2 -2
- package/types/fetch/client/fetchHandle.d.ts +24 -0
- package/types/fetch/client/httpClient.d.ts +2 -0
- package/types/fetch/client/sliceInitHandle.d.ts +29 -0
- package/types/fetch/fetchType/appliedReturn.type.d.ts +38 -1
- package/types/fetch/fetchType/sliceFetch.type.d.ts +5 -5
- package/types/fetch/requestStorage.d.ts +14 -0
- package/types/index.d.ts +17 -2
- package/types/server/akanOption.d.ts +8 -1
- package/types/server/console.d.ts +10 -0
- package/types/server/consoleSession.d.ts +10 -0
- package/types/server/decorators.d.ts +12 -2
- package/types/server/di/diLifecycle.d.ts +9 -1
- package/types/server/logging/childOutputReader.d.ts +40 -0
- package/types/server/logging/hubFileSink.d.ts +18 -0
- package/types/server/logging/logControlSocket.d.ts +60 -0
- package/types/server/logging/logForwarder.d.ts +37 -0
- package/types/server/logging/logHub.d.ts +70 -0
- package/types/server/logging/logQuery.d.ts +31 -0
- package/types/server/logging/logStdoutWriter.d.ts +35 -0
- package/types/server/logging/logStreamRoute.d.ts +20 -0
- package/types/server/logging/logTailClient.d.ts +42 -0
- package/types/server/mcp/McpAuth.d.ts +2 -6
- package/types/server/mcp/McpEventStream.d.ts +4 -5
- package/types/server/routeTreeBuilder.d.ts +1 -2
- package/types/server/routing/appWsData.d.ts +7 -3
- package/types/server/routing/eventStream.d.ts +23 -0
- package/types/server/routing/soloAppRoutes.d.ts +3 -1
- package/types/server/rscWorkerHost.d.ts +4 -1
- package/types/server/staticPath.d.ts +13 -0
- package/types/server/webRouter.d.ts +3 -1
- package/types/service/adapt.d.ts +1 -1
- package/types/service/ipcTypes.d.ts +11 -0
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +15 -407
- package/types/service/predefinedAdaptor/sql/PendingStoreEnsures.d.ts +6 -0
- package/types/service/predefinedAdaptor/sql/QueryCompiler.d.ts +28 -0
- package/types/service/predefinedAdaptor/sql/SqlDocumentStore.d.ts +127 -0
- package/types/service/predefinedAdaptor/sql/UpdateCompiler.d.ts +17 -0
- package/types/service/predefinedAdaptor/sql/dialect/postgres.d.ts +27 -0
- package/types/service/predefinedAdaptor/sql/dialect/sqlite.d.ts +27 -0
- package/types/service/predefinedAdaptor/sql/driver/bunSqlite.d.ts +16 -0
- package/types/service/predefinedAdaptor/sql/driver/libsql.d.ts +17 -0
- package/types/service/predefinedAdaptor/sql/driver/postgres.d.ts +17 -0
- package/types/service/predefinedAdaptor/sql/types.d.ts +226 -0
- package/types/service/predefinedAdaptor/sql/values.d.ts +13 -0
- package/types/signal/CrossSiteGuard.d.ts +44 -0
- package/types/signal/SignalFailure.d.ts +28 -0
- package/types/signal/exception.d.ts +38 -54774
- package/types/signal/index.d.ts +2 -0
- package/types/signal/intercept.d.ts +3 -3
- package/types/signal/internal.d.ts +5 -1
- package/types/signal/serializer/fetch.serializer.d.ts +7 -1
- package/types/signal/signalContext.d.ts +16 -3
- package/types/signal/trace.d.ts +52 -2
- package/types/store/SliceRequest.d.ts +16 -0
- package/types/store/agentic/StToolBuilder.d.ts +10 -1
- package/types/ui/Agent/Menu.d.ts +1 -1
- package/types/ui/Field/Choice.d.ts +52 -0
- package/types/ui/Field/Collection.d.ts +51 -0
- package/types/ui/Field/DateField.d.ts +32 -0
- package/types/ui/Field/Label.d.ts +9 -0
- package/types/ui/Field/NumberField.d.ts +43 -0
- package/types/ui/Field/Relation.d.ts +80 -0
- package/types/ui/Field/Text.d.ts +101 -0
- package/types/ui/Field/index.d.ts +50 -0
- package/types/ui/Field.d.ts +1 -412
- package/types/ui/Load/Edit_Client.d.ts +3 -1
- package/types/ui/Load/Stream.d.ts +22 -0
- package/types/ui/Load/index.d.ts +2 -0
- package/types/ui/System/Root.d.ts +4 -0
- package/types/ui/index.d.ts +1 -1
- package/types/ui/recipe/badgeRecipe.d.ts +2 -2
- package/types/ui/recipe/buttonRecipe.d.ts +2 -2
- package/types/webkit/useCamera.d.ts +10 -1
- package/types/webkit/usePurchase.d.ts +4 -0
- package/ui/Agent/Menu.tsx +2 -2
- package/ui/Data/TableList.tsx +1 -1
- package/ui/DraggableList.tsx +1 -2
- package/ui/Field/Choice.tsx +172 -0
- package/ui/Field/Collection.tsx +293 -0
- package/ui/Field/DateField.tsx +138 -0
- package/ui/Field/Label.tsx +31 -0
- package/ui/Field/NumberField.tsx +169 -0
- package/ui/Field/Relation.tsx +387 -0
- package/ui/Field/Text.tsx +360 -0
- package/ui/Field/index.tsx +70 -0
- package/ui/Input.tsx +3 -3
- package/ui/Load/Edit.tsx +2 -2
- package/ui/Load/Edit_Client.tsx +11 -3
- package/ui/Load/Pagination.tsx +25 -25
- package/ui/Load/Stream.tsx +38 -0
- package/ui/Load/Units.tsx +50 -37
- package/ui/Load/View.tsx +25 -17
- package/ui/Load/index.ts +2 -0
- package/ui/Model/EditModal.tsx +8 -8
- package/ui/Signal/Message.tsx +7 -7
- package/ui/Signal/PubSub.tsx +4 -1
- package/ui/System/Client.tsx +1 -3
- package/ui/System/Gtag.tsx +1 -1
- package/ui/System/Root.tsx +4 -0
- package/ui/index.ts +1 -1
- package/ui/overlayPosition.ts +1 -2
- package/webkit/useCamera.tsx +22 -6
- package/webkit/useCodepush.tsx +2 -2
- package/webkit/useCsrValues.ts +1 -3
- package/webkit/useFetch.ts +4 -3
- package/webkit/useFrameRuntime.ts +14 -7
- package/webkit/usePurchase.tsx +4 -1
- package/fetch/serializer/fetch.serializer.ts +0 -142
- package/fetch/serializer/index.ts +0 -1
- package/types/common/httpClient.d.ts +0 -20
- package/types/fetch/serializer/fetch.serializer.d.ts +0 -14
- package/types/fetch/serializer/index.d.ts +0 -1
- package/types/fetch/serializer.d.ts +0 -1
- package/ui/Field.tsx +0 -1639
package/client/cookie.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getEnv } from "akanjs/base";
|
|
2
2
|
import { decodeJwtPayload, Logger } from "akanjs/common";
|
|
3
3
|
import type { Account } from "akanjs/fetch";
|
|
4
|
-
import { cookies as serverCookies, headers as serverHeaders } from "akanjs/fetch";
|
|
4
|
+
import { parseCookieHeader, cookies as serverCookies, headers as serverHeaders } from "akanjs/fetch";
|
|
5
5
|
import { loadCapacitorCore } from "./capacitor";
|
|
6
6
|
import { storage } from "./storage";
|
|
7
7
|
import { fetch } from "./useClient";
|
|
@@ -12,23 +12,6 @@ interface CookieOptions {
|
|
|
12
12
|
secure?: boolean;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
function parseCookieHeader(cookieHeader: string): Map<string, { name: string; value: string }> {
|
|
16
|
-
const entries = cookieHeader
|
|
17
|
-
.split(";")
|
|
18
|
-
.map((c) => c.trim())
|
|
19
|
-
.filter(Boolean)
|
|
20
|
-
.map((c) => {
|
|
21
|
-
const eqIdx = c.indexOf("=");
|
|
22
|
-
if (eqIdx === -1) return null;
|
|
23
|
-
const name = c.slice(0, eqIdx).trim();
|
|
24
|
-
const raw = c.slice(eqIdx + 1).trim();
|
|
25
|
-
const value = typeof raw === "string" && raw.startsWith("j:") ? (JSON.parse(raw.slice(2)) as string) : raw;
|
|
26
|
-
return [name, { name, value }] as const;
|
|
27
|
-
})
|
|
28
|
-
.filter((e): e is NonNullable<typeof e> => e !== null);
|
|
29
|
-
return new Map(entries);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
15
|
export const cookies = (): Map<string, { name: string; value: string }> => {
|
|
33
16
|
if (getEnv().side === "server") return serverCookies();
|
|
34
17
|
return parseCookieHeader(document.cookie);
|
|
@@ -53,21 +36,18 @@ export const setCookie = (
|
|
|
53
36
|
.catch(() => undefined);
|
|
54
37
|
};
|
|
55
38
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
?.split("=")[1];
|
|
63
|
-
};
|
|
39
|
+
/**
|
|
40
|
+
* Reads through `cookies()` on both sides, which is the only way the two agree: a hand-rolled
|
|
41
|
+
* `split("=")[1]` truncates a value at its first `=` (base64 padding) and never decodes the `j:` form the
|
|
42
|
+
* server branch does. Capacitor's own docs say to read `document.cookie`, and `cookies()` does.
|
|
43
|
+
*/
|
|
44
|
+
export const getCookie = (key: string): string | undefined => cookies().get(key)?.value;
|
|
64
45
|
|
|
65
46
|
export const removeCookie = (key: string, options: { path: string } = { path: "/" }) => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
47
|
+
|
|
48
|
+
if (getEnv().side === "server") return;
|
|
49
|
+
|
|
50
|
+
document.cookie = `${key}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${options.path};`;
|
|
71
51
|
};
|
|
72
52
|
export const headers = (): Map<string, string> => {
|
|
73
53
|
if (getEnv().side !== "server") return new Map();
|
|
@@ -77,9 +57,15 @@ export const headers = (): Map<string, string> => {
|
|
|
77
57
|
export const getHeader = (key: string): string | undefined => {
|
|
78
58
|
return headers().get(key);
|
|
79
59
|
};
|
|
80
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Decodes the current JWT into account data when it belongs to this app/environment.
|
|
62
|
+
*
|
|
63
|
+
* The two credentials read here are exactly the two the server honours — the `jwt` cookie and
|
|
64
|
+
* `Authorization: Bearer` (`AccountMiddleware`). It used to fall back to a bare `jwt` *header*, which nothing
|
|
65
|
+
* sends and no guard accepts, so a request carrying one read as signed in here and anonymous at the endpoint.
|
|
66
|
+
*/
|
|
81
67
|
export const getAccount = <AddData = unknown>(): Account<AddData> => {
|
|
82
|
-
const jwt = getCookie("jwt") ?? getHeader("
|
|
68
|
+
const jwt = getCookie("jwt") ?? getHeader("authorization")?.replace(/^Bearer\s+/i, "");
|
|
83
69
|
const defaultAccount = { appName: getEnv().appName, environment: getEnv().environment } as Account<AddData>;
|
|
84
70
|
if (!jwt) return defaultAccount;
|
|
85
71
|
const account = decodeJwtPayload<Account<AddData>>(jwt);
|
|
@@ -105,7 +91,8 @@ interface InitAuthOption {
|
|
|
105
91
|
export const initAuth = ({ jwt }: InitAuthOption = {}) => {
|
|
106
92
|
const token = jwt ?? cookies().get("jwt")?.value;
|
|
107
93
|
if (token) setAuth({ jwt: token });
|
|
108
|
-
|
|
94
|
+
|
|
95
|
+
Logger.verbose(`JWT ${token ? "restored from cookie" : "not found in cookie"}`);
|
|
109
96
|
};
|
|
110
97
|
|
|
111
98
|
export const resetAuth = () => {
|
package/client/csrTypes.ts
CHANGED
|
@@ -262,18 +262,6 @@ export type PageState = CsrState & {
|
|
|
262
262
|
topInset: number;
|
|
263
263
|
bottomInset: number;
|
|
264
264
|
};
|
|
265
|
-
export const defaultPageState: PageState = {
|
|
266
|
-
transition: "none",
|
|
267
|
-
topSafeArea: 0,
|
|
268
|
-
bottomSafeArea: 0,
|
|
269
|
-
topInset: 0,
|
|
270
|
-
bottomInset: 0,
|
|
271
|
-
gesture: true,
|
|
272
|
-
cache: false,
|
|
273
|
-
ssr: "stream",
|
|
274
|
-
topSafeAreaColor: "var(--color-background, Canvas)",
|
|
275
|
-
bottomSafeAreaColor: "var(--color-background, Canvas)",
|
|
276
|
-
};
|
|
277
265
|
|
|
278
266
|
export interface Location {
|
|
279
267
|
href: string;
|
package/client/frameConfig.ts
CHANGED
|
@@ -120,6 +120,19 @@ export function getExplicitPageConfigKeys(configChain: PageConfig[] = []): Parti
|
|
|
120
120
|
return explicitKeys;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
export const defaultPageState: PageState = {
|
|
124
|
+
transition: "none",
|
|
125
|
+
topSafeArea: 0,
|
|
126
|
+
bottomSafeArea: 0,
|
|
127
|
+
topInset: 0,
|
|
128
|
+
bottomInset: 0,
|
|
129
|
+
gesture: true,
|
|
130
|
+
cache: false,
|
|
131
|
+
ssr: "stream",
|
|
132
|
+
topSafeAreaColor: "var(--color-background, Canvas)",
|
|
133
|
+
bottomSafeAreaColor: "var(--color-background, Canvas)",
|
|
134
|
+
};
|
|
135
|
+
|
|
123
136
|
export function resolvePageState({
|
|
124
137
|
configChain = [],
|
|
125
138
|
path,
|
package/client/router.ts
CHANGED
|
@@ -167,11 +167,11 @@ class Router {
|
|
|
167
167
|
#instance: InternalRouterInstance = {
|
|
168
168
|
push: (href: string) => {
|
|
169
169
|
const { href: fullHref } = this.#getPathInfo(href);
|
|
170
|
-
Logger.
|
|
170
|
+
Logger.info(`push to:${fullHref}`);
|
|
171
171
|
},
|
|
172
172
|
replace: (href: string) => {
|
|
173
173
|
const { pathname } = this.#getPathInfo(href);
|
|
174
|
-
Logger.
|
|
174
|
+
Logger.info(`replace to:${pathname}`);
|
|
175
175
|
},
|
|
176
176
|
back: () => {
|
|
177
177
|
throw new Error("back is only available in client");
|
|
@@ -191,7 +191,7 @@ class Router {
|
|
|
191
191
|
);
|
|
192
192
|
this.#indexPath = splitHref(options.indexPath ?? "/").path;
|
|
193
193
|
if (this.#routePaths.size > 0 && !this.#routePaths.has(this.#indexPath)) {
|
|
194
|
-
Logger.
|
|
194
|
+
Logger.info(`[router] indexPath '${this.#indexPath}' was not found in route manifest. Falling back to '/'.`);
|
|
195
195
|
this.#indexPath = "/";
|
|
196
196
|
}
|
|
197
197
|
this.#ensureHistoryState();
|
|
@@ -318,7 +318,7 @@ class Router {
|
|
|
318
318
|
return getPathInfo(href, lang, shouldExposeBasePath() ? this.#prefix : "");
|
|
319
319
|
}
|
|
320
320
|
#postPathChange({ path, pathname, hash }: { path: string; pathname: string; hash: string }) {
|
|
321
|
-
Logger.
|
|
321
|
+
Logger.info(`pathChange-start:${path}${hash ? `#${hash}` : ""}`);
|
|
322
322
|
window.parent.postMessage({ type: "pathChange", path, pathname, hash }, "*");
|
|
323
323
|
}
|
|
324
324
|
#postDevSyncNavigation(kind: "push" | "replace", href: string) {
|
|
@@ -338,7 +338,7 @@ class Router {
|
|
|
338
338
|
const normalizedHref = normalizeDeepLinkHref(href);
|
|
339
339
|
const { path, search, hash } = splitHref(normalizedHref);
|
|
340
340
|
if (this.#routePaths.size > 0 && !this.#routePaths.has(path)) {
|
|
341
|
-
Logger.
|
|
341
|
+
Logger.info(`[router] deep link target '${path}' was not found in route manifest.`);
|
|
342
342
|
return [];
|
|
343
343
|
}
|
|
344
344
|
const stack = this.#routePaths.size > 0 ? this.#getExistingSegmentStack(path) : [path];
|
|
@@ -416,7 +416,7 @@ class Router {
|
|
|
416
416
|
const lang = (h.get("x-locale") ?? langFromPath ?? this.#lang) as string;
|
|
417
417
|
const basePath = getServerBasePath(reqPathname, lang, h.get("x-base-path") ?? undefined, this.#prefix);
|
|
418
418
|
const { pathname, href: fullHref } = getPathInfo(href, lang, shouldExposeBasePath() ? basePath : "");
|
|
419
|
-
Logger.
|
|
419
|
+
Logger.info(`redirect to:${pathname}`);
|
|
420
420
|
throw new AkanRedirectError(fullHref, method, status);
|
|
421
421
|
} else {
|
|
422
422
|
this.#instance[method](href);
|
|
@@ -425,7 +425,7 @@ class Router {
|
|
|
425
425
|
}
|
|
426
426
|
notFound(): never {
|
|
427
427
|
if (getEnv().side === "server") {
|
|
428
|
-
Logger.
|
|
428
|
+
Logger.info(`redirect to:/404`);
|
|
429
429
|
throw new AkanNotFoundError();
|
|
430
430
|
}
|
|
431
431
|
this.#checkInitialized();
|
package/common/Logger.ts
CHANGED
|
@@ -1,17 +1,64 @@
|
|
|
1
1
|
import dayjs from "dayjs";
|
|
2
|
+
import { type LogContextSnapshot, readLogContext } from "./logContext";
|
|
2
3
|
|
|
3
|
-
const logLevels = ["trace", "verbose", "debug", "
|
|
4
|
+
export const logLevels = ["trace", "verbose", "debug", "info", "warn", "error"] as const;
|
|
4
5
|
export type LogLevel = (typeof logLevels)[number];
|
|
6
|
+
export type LogLevelInput = LogLevel | "log";
|
|
7
|
+
|
|
8
|
+
export const logSeverity = { trace: 1, verbose: 3, debug: 5, info: 9, warn: 13, error: 17 } as const satisfies {
|
|
9
|
+
[key in LogLevel]: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/** `text` is the human console line; the `ndjson` pair make the container's stdout one JSON record per line. */
|
|
13
|
+
export const logFormats = ["text", "ndjson", "ndjson-only"] as const;
|
|
14
|
+
export type LogFormat = (typeof logFormats)[number];
|
|
15
|
+
|
|
16
|
+
export type LogAttrValue = string | number | boolean | null;
|
|
17
|
+
export interface LogAttrs {
|
|
18
|
+
[key: string]: LogAttrValue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface LogRecord {
|
|
22
|
+
at: number;
|
|
23
|
+
/** Milliseconds since the producing process started — what the `+Nms` in the rendered line shows. */
|
|
24
|
+
elapsedMs: number;
|
|
25
|
+
level: LogLevel | null;
|
|
26
|
+
sev: number;
|
|
27
|
+
name: string;
|
|
28
|
+
context: string;
|
|
29
|
+
message: string;
|
|
30
|
+
stream: "stdout" | "stderr";
|
|
31
|
+
pid: number | null;
|
|
32
|
+
replicaIdx: number | null;
|
|
33
|
+
role: string | null;
|
|
34
|
+
origin: string | null;
|
|
35
|
+
traceId: string | null;
|
|
36
|
+
endpoint: string | null;
|
|
37
|
+
attrs?: LogAttrs;
|
|
38
|
+
}
|
|
5
39
|
|
|
6
40
|
export interface LoggerSinkEntry {
|
|
7
41
|
stream: "stdout" | "stderr";
|
|
8
42
|
level?: LogLevel;
|
|
9
43
|
message: string;
|
|
10
44
|
plainMessage: string;
|
|
45
|
+
record: LogRecord;
|
|
11
46
|
}
|
|
12
47
|
|
|
13
48
|
export type LoggerSink = (entry: LoggerSinkEntry) => void | Promise<void>;
|
|
14
49
|
|
|
50
|
+
export interface LoggerSinkOptions {
|
|
51
|
+
minLevel?: LogLevelInput;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface LoggerEmitInput {
|
|
55
|
+
level: LogLevel;
|
|
56
|
+
name: string;
|
|
57
|
+
message: string;
|
|
58
|
+
context?: string;
|
|
59
|
+
attrs?: LogAttrs;
|
|
60
|
+
}
|
|
61
|
+
|
|
15
62
|
const clc = {
|
|
16
63
|
bold: (text: string) => `\x1B[1m${text}\x1B[0m`,
|
|
17
64
|
green: (text: string) => `\x1B[32m${text}\x1B[39m`,
|
|
@@ -25,7 +72,6 @@ const colorizeMap: { [key in LogLevel]: (text: string) => string } = {
|
|
|
25
72
|
trace: clc.bold,
|
|
26
73
|
verbose: clc.cyanBright,
|
|
27
74
|
debug: clc.magentaBright,
|
|
28
|
-
log: clc.green,
|
|
29
75
|
info: clc.green,
|
|
30
76
|
warn: clc.yellow,
|
|
31
77
|
error: clc.red,
|
|
@@ -33,36 +79,129 @@ const colorizeMap: { [key in LogLevel]: (text: string) => string } = {
|
|
|
33
79
|
|
|
34
80
|
const ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-?]*[ -/]*[@-~]`, "g");
|
|
35
81
|
|
|
82
|
+
const redactedAttrKey = /password|passwd|token|jwt|authorization|cookie|secret|api[-_]?key|private[-_]?key/i;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* `process.env?.X` guards only `env` being absent, never `process` itself — an undeclared `process` is a
|
|
86
|
+
* ReferenceError, and at module scope that takes the whole client bundle with it. This is the most widely
|
|
87
|
+
* imported file in `common/`, and the unguarded form works today only because the browser build ships a shim.
|
|
88
|
+
*/
|
|
89
|
+
const envValue = (key: string): string | undefined => (typeof process === "undefined" ? undefined : process.env?.[key]);
|
|
90
|
+
|
|
36
91
|
/** Log-level aware logger used by Akan runtime, CLI, and application services. */
|
|
37
92
|
export class Logger {
|
|
38
|
-
|
|
39
|
-
static
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
static
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
93
|
+
/** `AKAN_LOG_STDOUT_LEVEL` names what the container's stdout carries; without it the console level is that. */
|
|
94
|
+
static level: LogLevel = Logger.#levelFromEnv(
|
|
95
|
+
envValue("AKAN_LOG_STDOUT_LEVEL"),
|
|
96
|
+
Logger.#levelFromEnv(envValue("AKAN_PUBLIC_LOG_LEVEL"), "info"),
|
|
97
|
+
);
|
|
98
|
+
static fileLevel: LogLevel = Logger.#levelFromEnv(envValue("AKAN_LOG_FILE_LEVEL"), "trace");
|
|
99
|
+
static format: LogFormat = Logger.#formatFromEnv(envValue("AKAN_LOG_FORMAT"));
|
|
100
|
+
/** Which process this is (`gateway`, `all`, `batch`, `rsc-worker`); the owner sets it at boot. */
|
|
101
|
+
static role: string | null = envValue("SERVER_MODE") ?? null;
|
|
102
|
+
/**
|
|
103
|
+
* Whether this process writes its own lines to the terminal. Off in every server process of an ndjson
|
|
104
|
+
* deployment: the hub owner writes the one JSON stream, and a text line from anyone would corrupt it.
|
|
105
|
+
*/
|
|
106
|
+
static consoleOutput = true;
|
|
107
|
+
/** Set by the trace layer when a flight recorder or a per-request debug floor can be active. */
|
|
108
|
+
static contextGate = false;
|
|
109
|
+
static readonly startAt = Date.now();
|
|
110
|
+
static #consoleSev: number = logSeverity[Logger.level];
|
|
111
|
+
static #fileSev: number = logSeverity[Logger.fileLevel];
|
|
112
|
+
static #sinkFloorSev = Number.POSITIVE_INFINITY;
|
|
113
|
+
static #sinks = new Map<LoggerSink, number | null>();
|
|
114
|
+
|
|
115
|
+
static setLevel(level: LogLevelInput) {
|
|
116
|
+
Logger.level = Logger.normalizeLevel(level);
|
|
117
|
+
Logger.#consoleSev = logSeverity[Logger.level];
|
|
118
|
+
}
|
|
119
|
+
static setFileLevel(level: LogLevelInput) {
|
|
120
|
+
Logger.fileLevel = Logger.normalizeLevel(level);
|
|
121
|
+
Logger.#fileSev = logSeverity[Logger.fileLevel];
|
|
122
|
+
Logger.#refreshSinkFloor();
|
|
123
|
+
}
|
|
124
|
+
static addSink(sink: LoggerSink, { minLevel }: LoggerSinkOptions = {}) {
|
|
125
|
+
Logger.#sinks.set(sink, minLevel === undefined ? null : logSeverity[Logger.normalizeLevel(minLevel)]);
|
|
126
|
+
Logger.#refreshSinkFloor();
|
|
51
127
|
return () => Logger.removeSink(sink);
|
|
52
128
|
}
|
|
53
129
|
static removeSink(sink: LoggerSink) {
|
|
54
130
|
Logger.#sinks.delete(sink);
|
|
55
|
-
|
|
56
|
-
static setFileLevel(level: LogLevel) {
|
|
57
|
-
Logger.fileLevel = level;
|
|
58
|
-
Logger.#fileLevelIdx = logLevels.indexOf(level);
|
|
131
|
+
Logger.#refreshSinkFloor();
|
|
59
132
|
}
|
|
60
133
|
static isVerbose() {
|
|
61
|
-
return Logger.#
|
|
134
|
+
return Logger.#consoleSev <= logSeverity.verbose;
|
|
135
|
+
}
|
|
136
|
+
static get isNdjson() {
|
|
137
|
+
return Logger.format !== "text";
|
|
62
138
|
}
|
|
63
139
|
/** For hot-path callers that would otherwise build a message the level is about to discard. */
|
|
64
|
-
static shouldLog(logLevel:
|
|
65
|
-
return Logger.#shouldLog(logLevel);
|
|
140
|
+
static shouldLog(logLevel: LogLevelInput) {
|
|
141
|
+
return Logger.#shouldLog(Logger.normalizeLevel(logLevel));
|
|
142
|
+
}
|
|
143
|
+
static normalizeLevel(level: LogLevelInput): LogLevel {
|
|
144
|
+
return level === "log" ? "info" : level;
|
|
145
|
+
}
|
|
146
|
+
/** The lowest named level whose severity is at least `sev`. */
|
|
147
|
+
static levelAtOrAbove(sev: number): LogLevel {
|
|
148
|
+
return logLevels.find((level) => logSeverity[level] >= sev) ?? "error";
|
|
149
|
+
}
|
|
150
|
+
static render(record: LogRecord): string {
|
|
151
|
+
const level = record.level;
|
|
152
|
+
if (level === null) return record.message;
|
|
153
|
+
const replicaMsg = record.replicaIdx === null ? "" : `#${record.replicaIdx} `;
|
|
154
|
+
const processMsg = Logger.#colorize(`[${record.name}] ${replicaMsg}${record.pid ?? "window"} -`, level);
|
|
155
|
+
const timestampMsg = dayjs(record.at).format("MM/DD/YYYY, HH:mm:ss A");
|
|
156
|
+
const logLevelMsg = Logger.#colorize(level.toUpperCase().padStart(7, " "), level);
|
|
157
|
+
const contextMsg = record.context ? clc.yellow(`[${record.context}] `) : "";
|
|
158
|
+
const contentMsg = Logger.#colorize(record.message, level);
|
|
159
|
+
const attrsMsg = record.attrs ? Logger.formatAttrs(record.attrs) : "";
|
|
160
|
+
const timeDiffMsg = clc.yellow(`+${record.elapsedMs}ms`);
|
|
161
|
+
return `${processMsg} ${timestampMsg} ${logLevelMsg} ${contextMsg} ${contentMsg}${attrsMsg ? ` ${attrsMsg}` : ""} ${timeDiffMsg}\n`;
|
|
162
|
+
}
|
|
163
|
+
/** `key=value` pairs; a string with whitespace is quoted so the line stays splittable. */
|
|
164
|
+
static formatAttrs(attrs: LogAttrs): string {
|
|
165
|
+
return Object.entries(attrs)
|
|
166
|
+
.map(
|
|
167
|
+
([key, value]) =>
|
|
168
|
+
`${key}=${typeof value === "string" && /\s/.test(value) ? JSON.stringify(value) : String(value)}`,
|
|
169
|
+
)
|
|
170
|
+
.join(" ");
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* A record somebody asked for below the level — a failed call's flight recorder, a request carrying
|
|
174
|
+
* `x-akan-debug` — passes every floor between here and the reader; the floors exist to drop what nobody asked for.
|
|
175
|
+
*/
|
|
176
|
+
static isPromoted(record: LogRecord) {
|
|
177
|
+
return record.attrs?.flight === true || record.attrs?.debug === true;
|
|
178
|
+
}
|
|
179
|
+
static redactAttrs(attrs: LogAttrs): LogAttrs {
|
|
180
|
+
let redacted: LogAttrs | null = null;
|
|
181
|
+
for (const key of Object.keys(attrs)) {
|
|
182
|
+
if (!redactedAttrKey.test(key)) continue;
|
|
183
|
+
redacted ??= { ...attrs };
|
|
184
|
+
redacted[key] = "[redacted]";
|
|
185
|
+
}
|
|
186
|
+
return redacted ?? attrs;
|
|
187
|
+
}
|
|
188
|
+
/** A structured record from a caller that has more than a sentence to say — the canonical request line. */
|
|
189
|
+
static emit({ level, name, message, context = "", attrs }: LoggerEmitInput) {
|
|
190
|
+
if (Logger.#shouldLog(level)) Logger.#write(name, message, context, level, attrs);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Writes records that were captured below the gate and are now promoted — a failed request's flight
|
|
194
|
+
* recorder. They bypass the console level, since not showing them is what the recorder was for, and reach
|
|
195
|
+
* sinks under their own floors; each is marked `flight` so a reader knows why a trace line appears at info.
|
|
196
|
+
*/
|
|
197
|
+
static replay(records: LogRecord[], { evicted = 0 }: { evicted?: number } = {}) {
|
|
198
|
+
records.forEach((record, idx) => {
|
|
199
|
+
const attrs: LogAttrs = { ...record.attrs, flight: true };
|
|
200
|
+
if (idx === 0 && evicted > 0) attrs.flightEvicted = evicted;
|
|
201
|
+
const entry = Logger.#entry({ ...record, attrs });
|
|
202
|
+
Logger.#emit(entry);
|
|
203
|
+
Logger.#writeConsole(entry);
|
|
204
|
+
});
|
|
66
205
|
}
|
|
67
206
|
|
|
68
207
|
name?: string;
|
|
@@ -70,25 +209,26 @@ export class Logger {
|
|
|
70
209
|
this.name = name;
|
|
71
210
|
}
|
|
72
211
|
trace(msg: string, context = "", name = this.name ?? "App") {
|
|
73
|
-
if (Logger.#shouldLog("trace")) Logger.#
|
|
212
|
+
if (Logger.#shouldLog("trace")) Logger.#write(name, msg, context, "trace");
|
|
74
213
|
}
|
|
75
214
|
verbose(msg: string, context = "", name = this.name ?? "App") {
|
|
76
|
-
if (Logger.#shouldLog("verbose")) Logger.#
|
|
215
|
+
if (Logger.#shouldLog("verbose")) Logger.#write(name, msg, context, "verbose");
|
|
77
216
|
}
|
|
78
217
|
debug(msg: string, context = "", name = this.name ?? "App") {
|
|
79
|
-
if (Logger.#shouldLog("debug")) Logger.#
|
|
218
|
+
if (Logger.#shouldLog("debug")) Logger.#write(name, msg, context, "debug");
|
|
80
219
|
}
|
|
220
|
+
/** @deprecated Emits at `info`; call `info()`. */
|
|
81
221
|
log(msg: string, context = "", name = this.name ?? "App") {
|
|
82
|
-
if (Logger.#shouldLog("
|
|
222
|
+
if (Logger.#shouldLog("info")) Logger.#write(name, msg, context, "info");
|
|
83
223
|
}
|
|
84
224
|
info(msg: string, context = "", name = this.name ?? "App") {
|
|
85
|
-
if (Logger.#shouldLog("info")) Logger.#
|
|
225
|
+
if (Logger.#shouldLog("info")) Logger.#write(name, msg, context, "info");
|
|
86
226
|
}
|
|
87
227
|
warn(msg: string, context = "", name = this.name ?? "App") {
|
|
88
|
-
if (Logger.#shouldLog("warn")) Logger.#
|
|
228
|
+
if (Logger.#shouldLog("warn")) Logger.#write(name, msg, context, "warn");
|
|
89
229
|
}
|
|
90
230
|
error(msg: string, context = "", name = this.name ?? "App") {
|
|
91
|
-
if (Logger.#shouldLog("error")) Logger.#
|
|
231
|
+
if (Logger.#shouldLog("error")) Logger.#write(name, msg, context, "error");
|
|
92
232
|
}
|
|
93
233
|
raw(msg: string, method?: "console" | "process") {
|
|
94
234
|
Logger.rawLog(msg, method);
|
|
@@ -96,86 +236,166 @@ export class Logger {
|
|
|
96
236
|
rawLog(msg: string, method?: "console" | "process") {
|
|
97
237
|
Logger.rawLog(msg, method);
|
|
98
238
|
}
|
|
99
|
-
|
|
100
239
|
static trace(msg: string, context = "", name = "App") {
|
|
101
|
-
if (Logger.#shouldLog("trace")) Logger.#
|
|
240
|
+
if (Logger.#shouldLog("trace")) Logger.#write(name, msg, context, "trace");
|
|
102
241
|
}
|
|
103
242
|
static verbose(msg: string, context = "", name = "App") {
|
|
104
|
-
if (Logger.#shouldLog("verbose")) Logger.#
|
|
243
|
+
if (Logger.#shouldLog("verbose")) Logger.#write(name, msg, context, "verbose");
|
|
105
244
|
}
|
|
106
245
|
static debug(msg: string, context = "", name = "App") {
|
|
107
|
-
if (Logger.#shouldLog("debug")) Logger.#
|
|
246
|
+
if (Logger.#shouldLog("debug")) Logger.#write(name, msg, context, "debug");
|
|
108
247
|
}
|
|
248
|
+
/** @deprecated Emits at `info`; call `info()`. */
|
|
109
249
|
static log(msg: string, context = "", name = "App") {
|
|
110
|
-
if (Logger.#shouldLog("
|
|
250
|
+
if (Logger.#shouldLog("info")) Logger.#write(name, msg, context, "info");
|
|
111
251
|
}
|
|
112
252
|
static info(msg: string, context = "", name = "App") {
|
|
113
|
-
if (Logger.#shouldLog("info")) Logger.#
|
|
253
|
+
if (Logger.#shouldLog("info")) Logger.#write(name, msg, context, "info");
|
|
114
254
|
}
|
|
115
255
|
static warn(msg: string, context = "", name = "App") {
|
|
116
|
-
if (Logger.#shouldLog("warn")) Logger.#
|
|
256
|
+
if (Logger.#shouldLog("warn")) Logger.#write(name, msg, context, "warn");
|
|
117
257
|
}
|
|
118
258
|
static error(msg: string, context = "", name = "App") {
|
|
119
|
-
if (Logger.#shouldLog("error")) Logger.#
|
|
259
|
+
if (Logger.#shouldLog("error")) Logger.#write(name, msg, context, "error");
|
|
260
|
+
}
|
|
261
|
+
static #levelFromEnv(value: string | undefined, fallback: LogLevel): LogLevel {
|
|
262
|
+
if (value === "log") return "info";
|
|
263
|
+
return (logLevels as readonly string[]).includes(value ?? "") ? (value as LogLevel) : fallback;
|
|
264
|
+
}
|
|
265
|
+
static #formatFromEnv(value: string | undefined): LogFormat {
|
|
266
|
+
return (logFormats as readonly string[]).includes(value ?? "") ? (value as LogFormat) : "text";
|
|
267
|
+
}
|
|
268
|
+
static #refreshSinkFloor() {
|
|
269
|
+
let floor = Number.POSITIVE_INFINITY;
|
|
270
|
+
for (const minSev of Logger.#sinks.values()) floor = Math.min(floor, minSev ?? Logger.#fileSev);
|
|
271
|
+
Logger.#sinkFloorSev = floor;
|
|
120
272
|
}
|
|
121
273
|
static #colorize(msg: string, logLevel: LogLevel) {
|
|
122
274
|
return colorizeMap[logLevel](msg);
|
|
123
275
|
}
|
|
124
|
-
static
|
|
276
|
+
static stripAnsi(msg: string) {
|
|
125
277
|
return msg.replace(ansiPattern, "");
|
|
126
278
|
}
|
|
127
279
|
static #shouldLog(logLevel: LogLevel) {
|
|
128
|
-
|
|
280
|
+
if (Logger.#shouldWriteConsole(logLevel) || Logger.#shouldEmitSink(logLevel)) return true;
|
|
281
|
+
return Logger.contextGate && Logger.#contextFloor(readLogContext()) <= logSeverity[logLevel];
|
|
129
282
|
}
|
|
130
283
|
static #shouldWriteConsole(logLevel: LogLevel) {
|
|
131
|
-
return Logger
|
|
284
|
+
return Logger.consoleOutput && logSeverity[logLevel] >= Logger.#consoleSev;
|
|
132
285
|
}
|
|
133
286
|
static #shouldEmitSink(logLevel: LogLevel) {
|
|
134
|
-
return
|
|
287
|
+
return logSeverity[logLevel] >= Logger.#sinkFloorSev;
|
|
288
|
+
}
|
|
289
|
+
/** The lowest severity the ambient request wants captured or shown, or +∞ outside one. */
|
|
290
|
+
static #contextFloor(ctx: LogContextSnapshot | undefined) {
|
|
291
|
+
if (!ctx) return Number.POSITIVE_INFINITY;
|
|
292
|
+
return Math.min(ctx.flight?.minSev ?? Number.POSITIVE_INFINITY, ctx.debugSev ?? Number.POSITIVE_INFINITY);
|
|
293
|
+
}
|
|
294
|
+
static #buildRecord(
|
|
295
|
+
name: string | undefined,
|
|
296
|
+
message: string,
|
|
297
|
+
context: string,
|
|
298
|
+
level: LogLevel | null,
|
|
299
|
+
stream: "stdout" | "stderr",
|
|
300
|
+
ctx: LogContextSnapshot | undefined,
|
|
301
|
+
attrs?: LogAttrs,
|
|
302
|
+
): LogRecord {
|
|
303
|
+
const proc = process as unknown as NodeJS.Process | undefined;
|
|
304
|
+
const replicaIdx = Number(proc?.env?.AKAN_REPLICA_IDX ?? Number.NaN);
|
|
305
|
+
const at = Date.now();
|
|
306
|
+
return {
|
|
307
|
+
at,
|
|
308
|
+
elapsedMs: at - Logger.startAt,
|
|
309
|
+
level,
|
|
310
|
+
sev: level === null ? 0 : logSeverity[level],
|
|
311
|
+
name: name ?? "App",
|
|
312
|
+
context,
|
|
313
|
+
message,
|
|
314
|
+
stream,
|
|
315
|
+
pid: proc?.pid ?? null,
|
|
316
|
+
replicaIdx: Number.isNaN(replicaIdx) ? null : replicaIdx,
|
|
317
|
+
role: Logger.role,
|
|
318
|
+
origin: ctx?.origin ?? null,
|
|
319
|
+
traceId: ctx?.traceId ?? null,
|
|
320
|
+
endpoint: ctx?.endpoint ?? null,
|
|
321
|
+
...(attrs ? { attrs: Logger.redactAttrs(attrs) } : {}),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
static #entry(record: LogRecord, text?: string): LoggerSinkEntry {
|
|
325
|
+
let message = text;
|
|
326
|
+
let plainMessage: string | undefined;
|
|
327
|
+
return {
|
|
328
|
+
stream: record.stream,
|
|
329
|
+
level: record.level ?? undefined,
|
|
330
|
+
record,
|
|
331
|
+
get message() {
|
|
332
|
+
message ??= Logger.render(record);
|
|
333
|
+
return message;
|
|
334
|
+
},
|
|
335
|
+
get plainMessage() {
|
|
336
|
+
plainMessage ??= Logger.stripAnsi(this.message);
|
|
337
|
+
return plainMessage;
|
|
338
|
+
},
|
|
339
|
+
};
|
|
135
340
|
}
|
|
136
341
|
static #emit(entry: LoggerSinkEntry) {
|
|
137
|
-
for (const sink of Logger.#sinks) {
|
|
342
|
+
for (const [sink, minSev] of Logger.#sinks) {
|
|
343
|
+
|
|
344
|
+
if (
|
|
345
|
+
entry.record.level !== null &&
|
|
346
|
+
entry.record.sev < (minSev ?? Logger.#fileSev) &&
|
|
347
|
+
!Logger.isPromoted(entry.record)
|
|
348
|
+
)
|
|
349
|
+
continue;
|
|
138
350
|
try {
|
|
139
351
|
void Promise.resolve(sink(entry)).catch(() => undefined);
|
|
140
352
|
} catch {
|
|
141
353
|
}
|
|
142
354
|
}
|
|
143
355
|
}
|
|
144
|
-
static #
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
) {
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
|
|
356
|
+
static #writeConsole(entry: LoggerSinkEntry) {
|
|
357
|
+
if (!Logger.consoleOutput) return;
|
|
358
|
+
if (typeof window === "undefined")
|
|
359
|
+
(process[entry.stream] as unknown as NodeJS.WriteStream | undefined)?.write(entry.message);
|
|
360
|
+
else console.log(entry.message);
|
|
361
|
+
}
|
|
362
|
+
static #write(name: string | undefined, message: string, context: string, logLevel: LogLevel, attrs?: LogAttrs) {
|
|
363
|
+
const stream = logLevel === "error" ? "stderr" : "stdout";
|
|
364
|
+
const ctx = readLogContext();
|
|
365
|
+
const sev = logSeverity[logLevel];
|
|
366
|
+
const debug = ctx?.debugSev !== undefined && ctx.debugSev !== null && sev >= ctx.debugSev;
|
|
367
|
+
|
|
368
|
+
const promoted = debug && sev < Logger.#consoleSev;
|
|
369
|
+
const record = Logger.#buildRecord(
|
|
370
|
+
name,
|
|
371
|
+
message,
|
|
372
|
+
context,
|
|
156
373
|
logLevel,
|
|
374
|
+
stream,
|
|
375
|
+
ctx,
|
|
376
|
+
promoted ? { ...attrs, debug: true } : attrs,
|
|
157
377
|
);
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
if (Logger.#
|
|
165
|
-
Logger.#emit({ stream: writeStreamType, level: logLevel, message, plainMessage: Logger.#stripAnsi(message) });
|
|
166
|
-
if (!Logger.#shouldWriteConsole(logLevel)) return;
|
|
167
|
-
if (typeof window === "undefined")
|
|
168
|
-
(process[writeStreamType] as unknown as NodeJS.WriteStream | undefined)?.write(message);
|
|
169
|
-
else console.log(message);
|
|
378
|
+
const toConsole = Logger.#shouldWriteConsole(logLevel) || (debug && Logger.consoleOutput);
|
|
379
|
+
const toSinks = Logger.#shouldEmitSink(logLevel) || debug;
|
|
380
|
+
ctx?.flight?.capture(record, toConsole || toSinks);
|
|
381
|
+
if (!toConsole && !toSinks) return;
|
|
382
|
+
const entry = Logger.#entry(record);
|
|
383
|
+
if (toSinks) Logger.#emit(entry);
|
|
384
|
+
if (toConsole) Logger.#writeConsole(entry);
|
|
170
385
|
}
|
|
171
386
|
static rawLog(msg = "", method?: "console" | "process", outputStream?: "log" | "error") {
|
|
172
387
|
Logger.raw(`${msg}\n`, method, outputStream);
|
|
173
388
|
}
|
|
174
389
|
static raw(msg = "", method?: "console" | "process", outputStream?: "log" | "error") {
|
|
175
390
|
const stream = outputStream === "error" ? "stderr" : "stdout";
|
|
176
|
-
Logger.#emit(
|
|
391
|
+
Logger.#emit(Logger.#entry(Logger.#buildRecord(undefined, msg, "", null, stream, readLogContext()), msg));
|
|
392
|
+
if (!Logger.consoleOutput) return;
|
|
177
393
|
if (typeof window === "undefined" && method !== "console" && (process as unknown as NodeJS.Process | undefined))
|
|
178
394
|
process[stream].write(msg);
|
|
179
395
|
else console[outputStream === "error" ? "error" : "log"](msg.trim());
|
|
180
396
|
}
|
|
397
|
+
static {
|
|
398
|
+
if (envValue("AKAN_PUBLIC_LOG_LEVEL") === "log" && typeof window === "undefined")
|
|
399
|
+
Logger.warn("AKAN_PUBLIC_LOG_LEVEL=log is deprecated and now means info; set AKAN_PUBLIC_LOG_LEVEL=info.");
|
|
400
|
+
}
|
|
181
401
|
}
|