@open-mercato/shared 0.6.6-develop.6460.1.f94c74174c → 0.6.6-develop.6471.1.9ab5bdd79a
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/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +1 -0
- package/dist/lib/commands/command-bus.js +4 -2
- package/dist/lib/commands/command-bus.js.map +2 -2
- package/dist/lib/commands/command-interceptor-runner.js +4 -2
- package/dist/lib/commands/command-interceptor-runner.js.map +2 -2
- package/dist/lib/commands/flush.js +3 -4
- package/dist/lib/commands/flush.js.map +2 -2
- package/dist/lib/commands/helpers.js +3 -1
- package/dist/lib/commands/helpers.js.map +2 -2
- package/dist/lib/commands/registry.js +5 -3
- package/dist/lib/commands/registry.js.map +2 -2
- package/dist/lib/commands/scope.js +5 -3
- package/dist/lib/commands/scope.js.map +2 -2
- package/dist/lib/crud/advanced-filter-integration.js +3 -1
- package/dist/lib/crud/advanced-filter-integration.js.map +2 -2
- package/dist/lib/crud/cache.js +3 -1
- package/dist/lib/crud/cache.js.map +2 -2
- package/dist/lib/crud/custom-fields.js +4 -2
- package/dist/lib/crud/custom-fields.js.map +2 -2
- package/dist/lib/crud/enricher-runner.js +6 -10
- package/dist/lib/crud/enricher-runner.js.map +2 -2
- package/dist/lib/crud/factory.js +10 -8
- package/dist/lib/crud/factory.js.map +2 -2
- package/dist/lib/crud/interceptor-registry.js +3 -3
- package/dist/lib/crud/interceptor-registry.js.map +2 -2
- package/dist/lib/crud/mutation-guard.js +4 -2
- package/dist/lib/crud/mutation-guard.js.map +2 -2
- package/dist/lib/crud/optimistic-lock.js +5 -6
- package/dist/lib/crud/optimistic-lock.js.map +2 -2
- package/dist/lib/crud/route-mutation-guard.js +3 -1
- package/dist/lib/crud/route-mutation-guard.js.map +2 -2
- package/dist/lib/crud/sync-event-runner.js +3 -1
- package/dist/lib/crud/sync-event-runner.js.map +2 -2
- package/dist/lib/data/engine.js +5 -5
- package/dist/lib/data/engine.js.map +2 -2
- package/dist/lib/db/mikro.js +5 -3
- package/dist/lib/db/mikro.js.map +2 -2
- package/dist/lib/di/container.js +3 -1
- package/dist/lib/di/container.js.map +2 -2
- package/dist/lib/encryption/aes.js +3 -1
- package/dist/lib/encryption/aes.js.map +2 -2
- package/dist/lib/encryption/entityFields.js +3 -1
- package/dist/lib/encryption/entityFields.js.map +2 -2
- package/dist/lib/encryption/entityIds.js +3 -1
- package/dist/lib/encryption/entityIds.js.map +2 -2
- package/dist/lib/encryption/kms.js +25 -32
- package/dist/lib/encryption/kms.js.map +2 -2
- package/dist/lib/encryption/subscriber.js +3 -1
- package/dist/lib/encryption/subscriber.js.map +2 -2
- package/dist/lib/encryption/tenantDataEncryptionService.js +3 -1
- package/dist/lib/encryption/tenantDataEncryptionService.js.map +2 -2
- package/dist/lib/indexers/error-log.js +4 -2
- package/dist/lib/indexers/error-log.js.map +2 -2
- package/dist/lib/indexers/status-log.js +5 -3
- package/dist/lib/indexers/status-log.js.map +2 -2
- package/dist/lib/logger/index.js +28 -0
- package/dist/lib/logger/index.js.map +7 -0
- package/dist/lib/logger/level.js +43 -0
- package/dist/lib/logger/level.js.map +7 -0
- package/dist/lib/logger/transport.console.js +39 -0
- package/dist/lib/logger/transport.console.js.map +7 -0
- package/dist/lib/logger/transport.js +17 -0
- package/dist/lib/logger/transport.js.map +7 -0
- package/dist/lib/logger/transport.pretty.js +85 -0
- package/dist/lib/logger/transport.pretty.js.map +7 -0
- package/dist/lib/logger/transport.server.js +85 -0
- package/dist/lib/logger/transport.server.js.map +7 -0
- package/dist/lib/middleware/page-executor.js +3 -1
- package/dist/lib/middleware/page-executor.js.map +2 -2
- package/dist/lib/modules/registry.js +3 -1
- package/dist/lib/modules/registry.js.map +2 -2
- package/dist/lib/profiler/index.js +3 -2
- package/dist/lib/profiler/index.js.map +2 -2
- package/dist/lib/query/engine.js +5 -6
- package/dist/lib/query/engine.js.map +2 -2
- package/dist/lib/query/query-extension-runner.js +5 -11
- package/dist/lib/query/query-extension-runner.js.map +2 -2
- package/dist/lib/redis/connection.js +3 -1
- package/dist/lib/redis/connection.js.map +2 -2
- package/dist/lib/url.js +7 -5
- package/dist/lib/url.js.map +2 -2
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/dist/modules/analytics.js +3 -1
- package/dist/modules/analytics.js.map +2 -2
- package/dist/modules/dashboard/widgets.js +3 -1
- package/dist/modules/dashboard/widgets.js.map +2 -2
- package/dist/modules/events/factory.js +5 -3
- package/dist/modules/events/factory.js.map +2 -2
- package/dist/modules/overrides.js +16 -34
- package/dist/modules/overrides.js.map +2 -2
- package/dist/modules/registry.js +3 -1
- package/dist/modules/registry.js.map +2 -2
- package/dist/modules/search.js +3 -1
- package/dist/modules/search.js.map +2 -2
- package/dist/modules/widgets/injection-loader.js +6 -6
- package/dist/modules/widgets/injection-loader.js.map +2 -2
- package/dist/modules/widgets/injection-position.js +3 -1
- package/dist/modules/widgets/injection-position.js.map +2 -2
- package/package.json +7 -2
- package/src/lib/commands/__tests__/command-interceptor-runner.test.ts +19 -6
- package/src/lib/commands/__tests__/flush.test.ts +24 -15
- package/src/lib/commands/__tests__/normalizeAuthorUserId.test.ts +14 -2
- package/src/lib/commands/__tests__/registry.test.ts +17 -2
- package/src/lib/commands/__tests__/scope.test.ts +21 -9
- package/src/lib/commands/command-bus.ts +5 -2
- package/src/lib/commands/command-interceptor-runner.ts +5 -2
- package/src/lib/commands/flush.ts +4 -5
- package/src/lib/commands/helpers.ts +9 -2
- package/src/lib/commands/registry.ts +6 -3
- package/src/lib/commands/scope.ts +6 -3
- package/src/lib/crud/__tests__/optimistic-lock.test.ts +21 -6
- package/src/lib/crud/__tests__/route-mutation-guard.test.ts +17 -2
- package/src/lib/crud/__tests__/sync-event-runner.test.ts +17 -3
- package/src/lib/crud/advanced-filter-integration.ts +4 -1
- package/src/lib/crud/cache.ts +4 -1
- package/src/lib/crud/custom-fields.ts +5 -2
- package/src/lib/crud/enricher-runner.ts +7 -10
- package/src/lib/crud/factory.ts +11 -10
- package/src/lib/crud/interceptor-registry.ts +4 -3
- package/src/lib/crud/mutation-guard.ts +5 -2
- package/src/lib/crud/optimistic-lock.ts +6 -10
- package/src/lib/crud/route-mutation-guard.ts +4 -1
- package/src/lib/crud/sync-event-runner.ts +4 -1
- package/src/lib/data/__tests__/engine.event-validation.test.ts +26 -15
- package/src/lib/data/engine.ts +6 -6
- package/src/lib/db/mikro.ts +6 -3
- package/src/lib/di/container.ts +4 -1
- package/src/lib/encryption/aes.ts +4 -2
- package/src/lib/encryption/entityFields.ts +5 -1
- package/src/lib/encryption/entityIds.ts +4 -1
- package/src/lib/encryption/kms.ts +27 -33
- package/src/lib/encryption/subscriber.ts +4 -2
- package/src/lib/encryption/tenantDataEncryptionService.ts +4 -2
- package/src/lib/indexers/__tests__/status-log.test.ts +22 -10
- package/src/lib/indexers/error-log.ts +5 -2
- package/src/lib/indexers/status-log.ts +6 -3
- package/src/lib/logger/__tests__/logger.test.ts +539 -0
- package/src/lib/logger/index.ts +33 -0
- package/src/lib/logger/level.ts +50 -0
- package/src/lib/logger/transport.console.ts +41 -0
- package/src/lib/logger/transport.pretty.ts +94 -0
- package/src/lib/logger/transport.server.ts +125 -0
- package/src/lib/logger/transport.ts +17 -0
- package/src/lib/middleware/page-executor.ts +4 -1
- package/src/lib/modules/__tests__/registry.test.ts +28 -21
- package/src/lib/modules/registry.ts +4 -1
- package/src/lib/profiler/index.ts +4 -2
- package/src/lib/query/__tests__/query-extension-runner.test.ts +21 -7
- package/src/lib/query/engine.ts +7 -10
- package/src/lib/query/query-extension-runner.ts +6 -11
- package/src/lib/redis/connection.ts +5 -1
- package/src/lib/url.ts +10 -5
- package/src/modules/__tests__/overrides.test.ts +17 -3
- package/src/modules/__tests__/route-overrides.test.ts +29 -20
- package/src/modules/analytics.ts +5 -1
- package/src/modules/dashboard/widgets.ts +4 -1
- package/src/modules/events/factory.ts +6 -3
- package/src/modules/overrides.ts +18 -34
- package/src/modules/registry.ts +4 -1
- package/src/modules/search.ts +4 -1
- package/src/modules/widgets/injection-loader.ts +7 -6
- package/src/modules/widgets/injection-position.ts +5 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { sql } from "kysely";
|
|
2
|
+
import { createLogger } from "../logger/index.js";
|
|
3
|
+
const logger = createLogger("shared").child({ component: "indexers" });
|
|
2
4
|
const MAX_MESSAGE_LENGTH = 8192;
|
|
3
5
|
const MAX_STACK_LENGTH = 32768;
|
|
4
6
|
function truncate(input, limit) {
|
|
@@ -48,7 +50,7 @@ function pickDb(deps) {
|
|
|
48
50
|
async function recordIndexerError(deps, input) {
|
|
49
51
|
const db = pickDb(deps);
|
|
50
52
|
if (!db) {
|
|
51
|
-
|
|
53
|
+
logger.error("Unable to record indexer error (missing db connection)", {
|
|
52
54
|
source: input.source,
|
|
53
55
|
handler: input.handler
|
|
54
56
|
});
|
|
@@ -71,7 +73,7 @@ async function recordIndexerError(deps, input) {
|
|
|
71
73
|
occurred_at: now
|
|
72
74
|
}).execute();
|
|
73
75
|
} catch (loggingError) {
|
|
74
|
-
|
|
76
|
+
logger.error("Failed to persist indexer error", { err: loggingError });
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/indexers/error-log.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { type Kysely, sql } from 'kysely'\n\nexport type IndexerErrorSource = 'query_index' | 'vector' | 'fulltext'\n\nexport type RecordIndexerErrorInput = {\n source: IndexerErrorSource\n handler: string\n error: unknown\n entityType?: string | null\n recordId?: string | null\n tenantId?: string | null\n organizationId?: string | null\n payload?: unknown\n}\n\ntype RecordIndexerErrorDeps = {\n em?: EntityManager\n db?: Kysely<any>\n}\n\nconst MAX_MESSAGE_LENGTH = 8_192\nconst MAX_STACK_LENGTH = 32_768\n\nfunction truncate(input: string | null | undefined, limit: number): string | null {\n if (!input) return null\n return input.length > limit ? `${input.slice(0, limit - 3)}...` : input\n}\n\nfunction normalizeError(error: unknown): { message: string; stack: string | null } {\n if (error instanceof Error) {\n return {\n message: error.message || error.name || 'Unknown error',\n stack: typeof error.stack === 'string' ? error.stack : null,\n }\n }\n if (typeof error === 'string') {\n return { message: error, stack: null }\n }\n try {\n const json = JSON.stringify(error)\n return { message: json, stack: null }\n } catch {\n return { message: String(error ?? 'Unknown error'), stack: null }\n }\n}\n\nfunction safeJson(value: unknown): unknown {\n if (value === undefined) return null\n try {\n return JSON.parse(JSON.stringify(value))\n } catch {\n if (value == null) return null\n if (typeof value === 'object') {\n return { note: 'unserializable', asString: String(value) }\n }\n return value\n }\n}\n\nfunction pickDb(deps: RecordIndexerErrorDeps): Kysely<any> | null {\n if (deps.db) return deps.db\n if (deps.em) {\n try {\n return deps.em.getKysely<any>()\n } catch {\n return null\n }\n }\n return null\n}\n\nexport async function recordIndexerError(deps: RecordIndexerErrorDeps, input: RecordIndexerErrorInput): Promise<void> {\n const db = pickDb(deps)\n if (!db) {\n
|
|
5
|
-
"mappings": "AACA,SAAsB,WAAW;
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { type Kysely, sql } from 'kysely'\nimport { createLogger } from '../logger'\n\nconst logger = createLogger('shared').child({ component: 'indexers' })\n\nexport type IndexerErrorSource = 'query_index' | 'vector' | 'fulltext'\n\nexport type RecordIndexerErrorInput = {\n source: IndexerErrorSource\n handler: string\n error: unknown\n entityType?: string | null\n recordId?: string | null\n tenantId?: string | null\n organizationId?: string | null\n payload?: unknown\n}\n\ntype RecordIndexerErrorDeps = {\n em?: EntityManager\n db?: Kysely<any>\n}\n\nconst MAX_MESSAGE_LENGTH = 8_192\nconst MAX_STACK_LENGTH = 32_768\n\nfunction truncate(input: string | null | undefined, limit: number): string | null {\n if (!input) return null\n return input.length > limit ? `${input.slice(0, limit - 3)}...` : input\n}\n\nfunction normalizeError(error: unknown): { message: string; stack: string | null } {\n if (error instanceof Error) {\n return {\n message: error.message || error.name || 'Unknown error',\n stack: typeof error.stack === 'string' ? error.stack : null,\n }\n }\n if (typeof error === 'string') {\n return { message: error, stack: null }\n }\n try {\n const json = JSON.stringify(error)\n return { message: json, stack: null }\n } catch {\n return { message: String(error ?? 'Unknown error'), stack: null }\n }\n}\n\nfunction safeJson(value: unknown): unknown {\n if (value === undefined) return null\n try {\n return JSON.parse(JSON.stringify(value))\n } catch {\n if (value == null) return null\n if (typeof value === 'object') {\n return { note: 'unserializable', asString: String(value) }\n }\n return value\n }\n}\n\nfunction pickDb(deps: RecordIndexerErrorDeps): Kysely<any> | null {\n if (deps.db) return deps.db\n if (deps.em) {\n try {\n return deps.em.getKysely<any>()\n } catch {\n return null\n }\n }\n return null\n}\n\nexport async function recordIndexerError(deps: RecordIndexerErrorDeps, input: RecordIndexerErrorInput): Promise<void> {\n const db = pickDb(deps)\n if (!db) {\n logger.error('Unable to record indexer error (missing db connection)', {\n source: input.source,\n handler: input.handler,\n })\n return\n }\n\n const { message, stack } = normalizeError(input.error)\n const payload = safeJson(input.payload)\n const now = new Date()\n\n try {\n await db\n .insertInto('indexer_error_logs' as any)\n .values({\n source: input.source,\n handler: input.handler,\n entity_type: input.entityType ?? null,\n record_id: input.recordId ?? null,\n tenant_id: input.tenantId ?? null,\n organization_id: input.organizationId ?? null,\n payload: payload === null ? null : sql`${JSON.stringify(payload)}::jsonb`,\n message: truncate(message, MAX_MESSAGE_LENGTH),\n stack: truncate(stack, MAX_STACK_LENGTH),\n occurred_at: now,\n } as any)\n .execute()\n } catch (loggingError) {\n logger.error('Failed to persist indexer error', { err: loggingError })\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAsB,WAAW;AACjC,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,QAAQ,EAAE,MAAM,EAAE,WAAW,WAAW,CAAC;AAoBrE,MAAM,qBAAqB;AAC3B,MAAM,mBAAmB;AAEzB,SAAS,SAAS,OAAkC,OAA8B;AAChF,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,MAAM,SAAS,QAAQ,GAAG,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,QAAQ;AACpE;AAEA,SAAS,eAAe,OAA2D;AACjF,MAAI,iBAAiB,OAAO;AAC1B,WAAO;AAAA,MACL,SAAS,MAAM,WAAW,MAAM,QAAQ;AAAA,MACxC,OAAO,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;AAAA,IACzD;AAAA,EACF;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,EAAE,SAAS,OAAO,OAAO,KAAK;AAAA,EACvC;AACA,MAAI;AACF,UAAM,OAAO,KAAK,UAAU,KAAK;AACjC,WAAO,EAAE,SAAS,MAAM,OAAO,KAAK;AAAA,EACtC,QAAQ;AACN,WAAO,EAAE,SAAS,OAAO,SAAS,eAAe,GAAG,OAAO,KAAK;AAAA,EAClE;AACF;AAEA,SAAS,SAAS,OAAyB;AACzC,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI;AACF,WAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AAAA,EACzC,QAAQ;AACN,QAAI,SAAS,KAAM,QAAO;AAC1B,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO,EAAE,MAAM,kBAAkB,UAAU,OAAO,KAAK,EAAE;AAAA,IAC3D;AACA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,OAAO,MAAkD;AAChE,MAAI,KAAK,GAAI,QAAO,KAAK;AACzB,MAAI,KAAK,IAAI;AACX,QAAI;AACF,aAAO,KAAK,GAAG,UAAe;AAAA,IAChC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,mBAAmB,MAA8B,OAA+C;AACpH,QAAM,KAAK,OAAO,IAAI;AACtB,MAAI,CAAC,IAAI;AACP,WAAO,MAAM,0DAA0D;AAAA,MACrE,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,IACjB,CAAC;AACD;AAAA,EACF;AAEA,QAAM,EAAE,SAAS,MAAM,IAAI,eAAe,MAAM,KAAK;AACrD,QAAM,UAAU,SAAS,MAAM,OAAO;AACtC,QAAM,MAAM,oBAAI,KAAK;AAErB,MAAI;AACF,UAAM,GACH,WAAW,oBAA2B,EACtC,OAAO;AAAA,MACN,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,MACf,aAAa,MAAM,cAAc;AAAA,MACjC,WAAW,MAAM,YAAY;AAAA,MAC7B,WAAW,MAAM,YAAY;AAAA,MAC7B,iBAAiB,MAAM,kBAAkB;AAAA,MACzC,SAAS,YAAY,OAAO,OAAO,MAAM,KAAK,UAAU,OAAO,CAAC;AAAA,MAChE,SAAS,SAAS,SAAS,kBAAkB;AAAA,MAC7C,OAAO,SAAS,OAAO,gBAAgB;AAAA,MACvC,aAAa;AAAA,IACf,CAAQ,EACP,QAAQ;AAAA,EACb,SAAS,cAAc;AACrB,WAAO,MAAM,mCAAmC,EAAE,KAAK,aAAa,CAAC;AAAA,EACvE;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { sql } from "kysely";
|
|
2
|
+
import { createLogger } from "../logger/index.js";
|
|
3
|
+
const logger = createLogger("shared").child({ component: "indexers" });
|
|
2
4
|
const MAX_MESSAGE_LENGTH = 4096;
|
|
3
5
|
const MAX_DELETE_BATCH = 5e3;
|
|
4
6
|
const MAX_LOGS_PER_SOURCE = 1e4;
|
|
@@ -43,7 +45,7 @@ async function pruneExcessLogs(db, source) {
|
|
|
43
45
|
async function recordIndexerLog(deps, input) {
|
|
44
46
|
const db = pickDb(deps);
|
|
45
47
|
if (!db) {
|
|
46
|
-
|
|
48
|
+
logger.warn("Unable to record indexer log (missing db connection)", {
|
|
47
49
|
source: input.source,
|
|
48
50
|
handler: input.handler
|
|
49
51
|
});
|
|
@@ -68,7 +70,7 @@ async function recordIndexerLog(deps, input) {
|
|
|
68
70
|
}).execute();
|
|
69
71
|
} catch (error) {
|
|
70
72
|
if (!isInactiveTransactionError(error)) {
|
|
71
|
-
|
|
73
|
+
logger.error("Failed to persist indexer log", { err: error });
|
|
72
74
|
}
|
|
73
75
|
return;
|
|
74
76
|
}
|
|
@@ -76,7 +78,7 @@ async function recordIndexerLog(deps, input) {
|
|
|
76
78
|
await pruneExcessLogs(db, input.source);
|
|
77
79
|
} catch (pruneError) {
|
|
78
80
|
if (!isInactiveTransactionError(pruneError)) {
|
|
79
|
-
|
|
81
|
+
logger.warn("Failed to prune indexer logs", { err: pruneError });
|
|
80
82
|
}
|
|
81
83
|
}
|
|
82
84
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/indexers/status-log.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { type Kysely, sql } from 'kysely'\nimport type { IndexerErrorSource } from './error-log'\n\nexport type IndexerLogLevel = 'info' | 'warn'\n\nexport type RecordIndexerLogInput = {\n source: IndexerErrorSource\n handler: string\n message: string\n level?: IndexerLogLevel\n entityType?: string | null\n recordId?: string | null\n tenantId?: string | null\n organizationId?: string | null\n details?: unknown\n}\n\ntype RecordIndexerLogDeps = {\n em?: EntityManager\n db?: Kysely<any>\n}\n\nconst MAX_MESSAGE_LENGTH = 4_096\nconst MAX_DELETE_BATCH = 5_000\nconst MAX_LOGS_PER_SOURCE = 10_000\n\nfunction truncate(input: string | null | undefined, limit: number): string | null {\n if (!input) return null\n return input.length > limit ? `${input.slice(0, limit - 3)}...` : input\n}\n\nfunction safeJson(value: unknown): unknown {\n if (value === undefined) return null\n try {\n return JSON.parse(JSON.stringify(value))\n } catch {\n if (value == null) return null\n if (typeof value === 'object') {\n return { note: 'unserializable', asString: String(value) }\n }\n return value\n }\n}\n\nfunction pickDb(deps: RecordIndexerLogDeps): Kysely<any> | null {\n if (deps.db) return deps.db\n if (deps.em) {\n try {\n return deps.em.getKysely<any>()\n } catch {\n return null\n }\n }\n return null\n}\n\n/**\n * Indexer status logging is best-effort observability and must never ride on \u2014\n * or be noisy about \u2014 the caller's transaction lifecycle. When index maintenance\n * runs inline on a request `em` (e.g. an inline force reindex that emits the\n * vector-purge subscriber), the captured Kysely can be a transaction handle that\n * has already committed/rolled back, so a follow-up read/write throws\n * \"Transaction is already committed\". Treat that class of error as a quiet skip\n * rather than an error the operator must triage. A fresh-EM path (the events\n * worker) never hits this; this guard only de-noises the inline path.\n */\nfunction isInactiveTransactionError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error ?? '')\n return /transaction is already (committed|rolled back)/i.test(message)\n}\n\nasync function pruneExcessLogs(db: Kysely<any>, source: IndexerErrorSource): Promise<void> {\n const rows = await db\n .selectFrom('indexer_status_logs' as any)\n .select('id' as any)\n .where('source' as any, '=', source)\n .orderBy('occurred_at' as any, 'desc')\n .orderBy('id' as any, 'desc')\n .offset(MAX_LOGS_PER_SOURCE)\n .limit(MAX_DELETE_BATCH)\n .execute()\n\n if (!rows.length) return\n const ids = rows.map((row: any) => row.id).filter(Boolean)\n if (!ids.length) return\n await db\n .deleteFrom('indexer_status_logs' as any)\n .where('id' as any, 'in', ids)\n .execute()\n}\n\nexport async function recordIndexerLog(\n deps: RecordIndexerLogDeps,\n input: RecordIndexerLogInput,\n): Promise<void> {\n const db = pickDb(deps)\n if (!db) {\n
|
|
5
|
-
"mappings": "AACA,SAAsB,WAAW;
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { type Kysely, sql } from 'kysely'\nimport type { IndexerErrorSource } from './error-log'\nimport { createLogger } from '../logger'\n\nconst logger = createLogger('shared').child({ component: 'indexers' })\n\nexport type IndexerLogLevel = 'info' | 'warn'\n\nexport type RecordIndexerLogInput = {\n source: IndexerErrorSource\n handler: string\n message: string\n level?: IndexerLogLevel\n entityType?: string | null\n recordId?: string | null\n tenantId?: string | null\n organizationId?: string | null\n details?: unknown\n}\n\ntype RecordIndexerLogDeps = {\n em?: EntityManager\n db?: Kysely<any>\n}\n\nconst MAX_MESSAGE_LENGTH = 4_096\nconst MAX_DELETE_BATCH = 5_000\nconst MAX_LOGS_PER_SOURCE = 10_000\n\nfunction truncate(input: string | null | undefined, limit: number): string | null {\n if (!input) return null\n return input.length > limit ? `${input.slice(0, limit - 3)}...` : input\n}\n\nfunction safeJson(value: unknown): unknown {\n if (value === undefined) return null\n try {\n return JSON.parse(JSON.stringify(value))\n } catch {\n if (value == null) return null\n if (typeof value === 'object') {\n return { note: 'unserializable', asString: String(value) }\n }\n return value\n }\n}\n\nfunction pickDb(deps: RecordIndexerLogDeps): Kysely<any> | null {\n if (deps.db) return deps.db\n if (deps.em) {\n try {\n return deps.em.getKysely<any>()\n } catch {\n return null\n }\n }\n return null\n}\n\n/**\n * Indexer status logging is best-effort observability and must never ride on \u2014\n * or be noisy about \u2014 the caller's transaction lifecycle. When index maintenance\n * runs inline on a request `em` (e.g. an inline force reindex that emits the\n * vector-purge subscriber), the captured Kysely can be a transaction handle that\n * has already committed/rolled back, so a follow-up read/write throws\n * \"Transaction is already committed\". Treat that class of error as a quiet skip\n * rather than an error the operator must triage. A fresh-EM path (the events\n * worker) never hits this; this guard only de-noises the inline path.\n */\nfunction isInactiveTransactionError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error ?? '')\n return /transaction is already (committed|rolled back)/i.test(message)\n}\n\nasync function pruneExcessLogs(db: Kysely<any>, source: IndexerErrorSource): Promise<void> {\n const rows = await db\n .selectFrom('indexer_status_logs' as any)\n .select('id' as any)\n .where('source' as any, '=', source)\n .orderBy('occurred_at' as any, 'desc')\n .orderBy('id' as any, 'desc')\n .offset(MAX_LOGS_PER_SOURCE)\n .limit(MAX_DELETE_BATCH)\n .execute()\n\n if (!rows.length) return\n const ids = rows.map((row: any) => row.id).filter(Boolean)\n if (!ids.length) return\n await db\n .deleteFrom('indexer_status_logs' as any)\n .where('id' as any, 'in', ids)\n .execute()\n}\n\nexport async function recordIndexerLog(\n deps: RecordIndexerLogDeps,\n input: RecordIndexerLogInput,\n): Promise<void> {\n const db = pickDb(deps)\n if (!db) {\n logger.warn('Unable to record indexer log (missing db connection)', {\n source: input.source,\n handler: input.handler,\n })\n return\n }\n\n const level: IndexerLogLevel = input.level === 'warn' ? 'warn' : 'info'\n const message = truncate(input.message, MAX_MESSAGE_LENGTH) ?? '\u2014'\n const details = safeJson(input.details)\n const occurredAt = new Date()\n\n try {\n await db\n .insertInto('indexer_status_logs' as any)\n .values({\n source: input.source,\n handler: input.handler,\n level,\n entity_type: input.entityType ?? null,\n record_id: input.recordId ?? null,\n tenant_id: input.tenantId ?? null,\n organization_id: input.organizationId ?? null,\n message,\n details: details === null ? null : sql`${JSON.stringify(details)}::jsonb`,\n occurred_at: occurredAt,\n } as any)\n .execute()\n } catch (error) {\n // A committed/rolled-back caller transaction is an expected, harmless\n // condition for best-effort logging \u2014 skip quietly instead of surfacing it.\n if (!isInactiveTransactionError(error)) {\n logger.error('Failed to persist indexer log', { err: error })\n }\n return\n }\n\n try {\n await pruneExcessLogs(db, input.source)\n } catch (pruneError) {\n if (!isInactiveTransactionError(pruneError)) {\n logger.warn('Failed to prune indexer logs', { err: pruneError })\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { selectTransport } from "./transport.js";
|
|
2
|
+
import { getLogLevel, isLevelEnabled, resetLogLevelCache, OM_LOG_LEVEL_ENV } from "./level.js";
|
|
3
|
+
import { resetServerLoggerCache, OM_LOG_DESTINATION_ENV } from "./transport.server.js";
|
|
4
|
+
import { resetLogPrettyCache, OM_LOG_PRETTY_ENV } from "./transport.pretty.js";
|
|
5
|
+
const loggerRegistry = /* @__PURE__ */ new Map();
|
|
6
|
+
function createLogger(namespace) {
|
|
7
|
+
const existing = loggerRegistry.get(namespace);
|
|
8
|
+
if (existing) return existing;
|
|
9
|
+
const created = selectTransport(namespace);
|
|
10
|
+
loggerRegistry.set(namespace, created);
|
|
11
|
+
return created;
|
|
12
|
+
}
|
|
13
|
+
function resetLoggerRegistry() {
|
|
14
|
+
loggerRegistry.clear();
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
OM_LOG_DESTINATION_ENV,
|
|
18
|
+
OM_LOG_LEVEL_ENV,
|
|
19
|
+
OM_LOG_PRETTY_ENV,
|
|
20
|
+
createLogger,
|
|
21
|
+
getLogLevel,
|
|
22
|
+
isLevelEnabled,
|
|
23
|
+
resetLogLevelCache,
|
|
24
|
+
resetLogPrettyCache,
|
|
25
|
+
resetLoggerRegistry,
|
|
26
|
+
resetServerLoggerCache
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/logger/index.ts"],
|
|
4
|
+
"sourcesContent": ["import { selectTransport } from './transport'\n\nexport type { LogLevel } from './level'\nexport { getLogLevel, isLevelEnabled, resetLogLevelCache, OM_LOG_LEVEL_ENV } from './level'\nexport { resetServerLoggerCache, OM_LOG_DESTINATION_ENV } from './transport.server'\nexport { resetLogPrettyCache, OM_LOG_PRETTY_ENV } from './transport.pretty'\n\nexport type LogBindings = Record<string, unknown>\n\nexport interface Logger {\n debug(msg: string, fields?: LogBindings): void\n info(msg: string, fields?: LogBindings): void\n warn(msg: string, fields?: LogBindings): void\n error(msg: string, fields?: LogBindings): void\n /** Returns a logger with `bindings` merged into every subsequent line. */\n child(bindings: LogBindings): Logger\n}\n\nconst loggerRegistry = new Map<string, Logger>()\n\n/** Create (or reuse) a namespaced logger. `namespace` is attached as `name`. */\nexport function createLogger(namespace: string): Logger {\n const existing = loggerRegistry.get(namespace)\n if (existing) return existing\n const created = selectTransport(namespace)\n loggerRegistry.set(namespace, created)\n return created\n}\n\n/** Internal: clear cached loggers so tests can re-run transport selection. */\nexport function resetLoggerRegistry(): void {\n loggerRegistry.clear()\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,uBAAuB;AAGhC,SAAS,aAAa,gBAAgB,oBAAoB,wBAAwB;AAClF,SAAS,wBAAwB,8BAA8B;AAC/D,SAAS,qBAAqB,yBAAyB;AAavD,MAAM,iBAAiB,oBAAI,IAAoB;AAGxC,SAAS,aAAa,WAA2B;AACtD,QAAM,WAAW,eAAe,IAAI,SAAS;AAC7C,MAAI,SAAU,QAAO;AACrB,QAAM,UAAU,gBAAgB,SAAS;AACzC,iBAAe,IAAI,WAAW,OAAO;AACrC,SAAO;AACT;AAGO,SAAS,sBAA4B;AAC1C,iBAAe,MAAM;AACvB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const OM_LOG_LEVEL_ENV = "OM_LOG_LEVEL";
|
|
2
|
+
const LOG_LEVEL_WEIGHTS = {
|
|
3
|
+
debug: 10,
|
|
4
|
+
info: 20,
|
|
5
|
+
warn: 30,
|
|
6
|
+
error: 40
|
|
7
|
+
};
|
|
8
|
+
const VALID_LEVEL_TOKENS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
|
|
9
|
+
function readProcessEnv() {
|
|
10
|
+
return typeof process === "undefined" ? {} : process.env;
|
|
11
|
+
}
|
|
12
|
+
function defaultLevelFor(env) {
|
|
13
|
+
return env.NODE_ENV === "production" ? "info" : "debug";
|
|
14
|
+
}
|
|
15
|
+
function resolveLevel(env = readProcessEnv()) {
|
|
16
|
+
const raw = env[OM_LOG_LEVEL_ENV];
|
|
17
|
+
if (typeof raw !== "string" || !raw.trim()) return defaultLevelFor(env);
|
|
18
|
+
const normalized = raw.trim().toLowerCase();
|
|
19
|
+
if (VALID_LEVEL_TOKENS.has(normalized)) return normalized;
|
|
20
|
+
const fallback = defaultLevelFor(env);
|
|
21
|
+
console.warn(`[logger] Unrecognized ${OM_LOG_LEVEL_ENV} value "${raw}"; falling back to "${fallback}"`);
|
|
22
|
+
return fallback;
|
|
23
|
+
}
|
|
24
|
+
let cachedLevel = null;
|
|
25
|
+
function getLogLevel() {
|
|
26
|
+
if (cachedLevel === null) cachedLevel = resolveLevel();
|
|
27
|
+
return cachedLevel;
|
|
28
|
+
}
|
|
29
|
+
function isLevelEnabled(level) {
|
|
30
|
+
return LOG_LEVEL_WEIGHTS[level] >= LOG_LEVEL_WEIGHTS[getLogLevel()];
|
|
31
|
+
}
|
|
32
|
+
function resetLogLevelCache() {
|
|
33
|
+
cachedLevel = null;
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
LOG_LEVEL_WEIGHTS,
|
|
37
|
+
OM_LOG_LEVEL_ENV,
|
|
38
|
+
getLogLevel,
|
|
39
|
+
isLevelEnabled,
|
|
40
|
+
resetLogLevelCache,
|
|
41
|
+
resolveLevel
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=level.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/logger/level.ts"],
|
|
4
|
+
"sourcesContent": ["export type LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\nexport const OM_LOG_LEVEL_ENV = 'OM_LOG_LEVEL'\n\nexport const LOG_LEVEL_WEIGHTS: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n}\n\nconst VALID_LEVEL_TOKENS: ReadonlySet<string> = new Set(['debug', 'info', 'warn', 'error'])\n\ntype EnvSource = Record<string, string | undefined>\n\nfunction readProcessEnv(): EnvSource {\n return typeof process === 'undefined' ? {} : process.env\n}\n\nfunction defaultLevelFor(env: EnvSource): LogLevel {\n return env.NODE_ENV === 'production' ? 'info' : 'debug'\n}\n\n/** Resolve the effective log level from `OM_LOG_LEVEL`, falling back on `NODE_ENV`. */\nexport function resolveLevel(env: EnvSource = readProcessEnv()): LogLevel {\n const raw = env[OM_LOG_LEVEL_ENV]\n if (typeof raw !== 'string' || !raw.trim()) return defaultLevelFor(env)\n const normalized = raw.trim().toLowerCase()\n if (VALID_LEVEL_TOKENS.has(normalized)) return normalized as LogLevel\n const fallback = defaultLevelFor(env)\n console.warn(`[logger] Unrecognized ${OM_LOG_LEVEL_ENV} value \"${raw}\"; falling back to \"${fallback}\"`)\n return fallback\n}\n\nlet cachedLevel: LogLevel | null = null\n\n/** Resolve the effective level once, for callers that want to gate expensive work. */\nexport function getLogLevel(): LogLevel {\n if (cachedLevel === null) cachedLevel = resolveLevel()\n return cachedLevel\n}\n\nexport function isLevelEnabled(level: LogLevel): boolean {\n return LOG_LEVEL_WEIGHTS[level] >= LOG_LEVEL_WEIGHTS[getLogLevel()]\n}\n\n/** Internal: clear the memoized level so tests can vary `OM_LOG_LEVEL`. */\nexport function resetLogLevelCache(): void {\n cachedLevel = null\n}\n"],
|
|
5
|
+
"mappings": "AAEO,MAAM,mBAAmB;AAEzB,MAAM,oBAA8C;AAAA,EACzD,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEA,MAAM,qBAA0C,oBAAI,IAAI,CAAC,SAAS,QAAQ,QAAQ,OAAO,CAAC;AAI1F,SAAS,iBAA4B;AACnC,SAAO,OAAO,YAAY,cAAc,CAAC,IAAI,QAAQ;AACvD;AAEA,SAAS,gBAAgB,KAA0B;AACjD,SAAO,IAAI,aAAa,eAAe,SAAS;AAClD;AAGO,SAAS,aAAa,MAAiB,eAAe,GAAa;AACxE,QAAM,MAAM,IAAI,gBAAgB;AAChC,MAAI,OAAO,QAAQ,YAAY,CAAC,IAAI,KAAK,EAAG,QAAO,gBAAgB,GAAG;AACtE,QAAM,aAAa,IAAI,KAAK,EAAE,YAAY;AAC1C,MAAI,mBAAmB,IAAI,UAAU,EAAG,QAAO;AAC/C,QAAM,WAAW,gBAAgB,GAAG;AACpC,UAAQ,KAAK,yBAAyB,gBAAgB,WAAW,GAAG,uBAAuB,QAAQ,GAAG;AACtG,SAAO;AACT;AAEA,IAAI,cAA+B;AAG5B,SAAS,cAAwB;AACtC,MAAI,gBAAgB,KAAM,eAAc,aAAa;AACrD,SAAO;AACT;AAEO,SAAS,eAAe,OAA0B;AACvD,SAAO,kBAAkB,KAAK,KAAK,kBAAkB,YAAY,CAAC;AACpE;AAGO,SAAS,qBAA2B;AACzC,gBAAc;AAChB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isLevelEnabled } from "./level.js";
|
|
2
|
+
function formatBindingValue(value) {
|
|
3
|
+
if (typeof value === "string") return value;
|
|
4
|
+
if (value instanceof Error) return value.message;
|
|
5
|
+
if (typeof value === "object" && value !== null) {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.stringify(value) ?? String(value);
|
|
8
|
+
} catch {
|
|
9
|
+
return String(value);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return String(value);
|
|
13
|
+
}
|
|
14
|
+
function formatBindings(bindings) {
|
|
15
|
+
return Object.entries(bindings).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}=${formatBindingValue(value)}`).join(" ");
|
|
16
|
+
}
|
|
17
|
+
function createConsoleLogger(namespace, bindings = {}) {
|
|
18
|
+
const emit = (level, msg, fields) => {
|
|
19
|
+
if (!isLevelEnabled(level)) return;
|
|
20
|
+
const { err, ...rest } = { ...bindings, ...fields };
|
|
21
|
+
const suffix = formatBindings(rest);
|
|
22
|
+
const line = suffix ? `[${namespace}] ${msg} ${suffix}` : `[${namespace}] ${msg}`;
|
|
23
|
+
if (err instanceof Error) console[level](line, err.stack ?? err.message);
|
|
24
|
+
else if (err !== void 0) console[level](line, err);
|
|
25
|
+
else console[level](line);
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
debug: (msg, fields) => emit("debug", msg, fields),
|
|
29
|
+
info: (msg, fields) => emit("info", msg, fields),
|
|
30
|
+
warn: (msg, fields) => emit("warn", msg, fields),
|
|
31
|
+
error: (msg, fields) => emit("error", msg, fields),
|
|
32
|
+
child: (childBindings) => createConsoleLogger(namespace, { ...bindings, ...childBindings })
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
createConsoleLogger,
|
|
37
|
+
formatBindings
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=transport.console.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/logger/transport.console.ts"],
|
|
4
|
+
"sourcesContent": ["import type { LogBindings, Logger } from './index'\nimport { isLevelEnabled, type LogLevel } from './level'\n\nfunction formatBindingValue(value: unknown): string {\n if (typeof value === 'string') return value\n if (value instanceof Error) return value.message\n if (typeof value === 'object' && value !== null) {\n try {\n return JSON.stringify(value) ?? String(value)\n } catch {\n return String(value)\n }\n }\n return String(value)\n}\n\nexport function formatBindings(bindings: LogBindings): string {\n return Object.entries(bindings)\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => `${key}=${formatBindingValue(value)}`)\n .join(' ')\n}\n\nexport function createConsoleLogger(namespace: string, bindings: LogBindings = {}): Logger {\n const emit = (level: LogLevel, msg: string, fields?: LogBindings): void => {\n if (!isLevelEnabled(level)) return\n const { err, ...rest } = { ...bindings, ...fields }\n const suffix = formatBindings(rest)\n const line = suffix ? `[${namespace}] ${msg} ${suffix}` : `[${namespace}] ${msg}`\n if (err instanceof Error) console[level](line, err.stack ?? err.message)\n else if (err !== undefined) console[level](line, err)\n else console[level](line)\n }\n return {\n debug: (msg, fields) => emit('debug', msg, fields),\n info: (msg, fields) => emit('info', msg, fields),\n warn: (msg, fields) => emit('warn', msg, fields),\n error: (msg, fields) => emit('error', msg, fields),\n child: (childBindings) => createConsoleLogger(namespace, { ...bindings, ...childBindings }),\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,sBAAqC;AAE9C,SAAS,mBAAmB,OAAwB;AAClD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,iBAAiB,MAAO,QAAO,MAAM;AACzC,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,QAAI;AACF,aAAO,KAAK,UAAU,KAAK,KAAK,OAAO,KAAK;AAAA,IAC9C,QAAQ;AACN,aAAO,OAAO,KAAK;AAAA,IACrB;AAAA,EACF;AACA,SAAO,OAAO,KAAK;AACrB;AAEO,SAAS,eAAe,UAA+B;AAC5D,SAAO,OAAO,QAAQ,QAAQ,EAC3B,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,UAAU,MAAS,EACzC,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,IAAI,mBAAmB,KAAK,CAAC,EAAE,EAC3D,KAAK,GAAG;AACb;AAEO,SAAS,oBAAoB,WAAmB,WAAwB,CAAC,GAAW;AACzF,QAAM,OAAO,CAAC,OAAiB,KAAa,WAA+B;AACzE,QAAI,CAAC,eAAe,KAAK,EAAG;AAC5B,UAAM,EAAE,KAAK,GAAG,KAAK,IAAI,EAAE,GAAG,UAAU,GAAG,OAAO;AAClD,UAAM,SAAS,eAAe,IAAI;AAClC,UAAM,OAAO,SAAS,IAAI,SAAS,KAAK,GAAG,IAAI,MAAM,KAAK,IAAI,SAAS,KAAK,GAAG;AAC/E,QAAI,eAAe,MAAO,SAAQ,KAAK,EAAE,MAAM,IAAI,SAAS,IAAI,OAAO;AAAA,aAC9D,QAAQ,OAAW,SAAQ,KAAK,EAAE,MAAM,GAAG;AAAA,QAC/C,SAAQ,KAAK,EAAE,IAAI;AAAA,EAC1B;AACA,SAAO;AAAA,IACL,OAAO,CAAC,KAAK,WAAW,KAAK,SAAS,KAAK,MAAM;AAAA,IACjD,MAAM,CAAC,KAAK,WAAW,KAAK,QAAQ,KAAK,MAAM;AAAA,IAC/C,MAAM,CAAC,KAAK,WAAW,KAAK,QAAQ,KAAK,MAAM;AAAA,IAC/C,OAAO,CAAC,KAAK,WAAW,KAAK,SAAS,KAAK,MAAM;AAAA,IACjD,OAAO,CAAC,kBAAkB,oBAAoB,WAAW,EAAE,GAAG,UAAU,GAAG,cAAc,CAAC;AAAA,EAC5F;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createConsoleLogger } from "./transport.console.js";
|
|
2
|
+
import { createPrettyLogger, isPrettyModeEnabled } from "./transport.pretty.js";
|
|
3
|
+
import { createServerLogger } from "./transport.server.js";
|
|
4
|
+
function isNodeServerRuntime() {
|
|
5
|
+
if (typeof window !== "undefined") return false;
|
|
6
|
+
if (typeof process === "undefined") return false;
|
|
7
|
+
if (process.env.NEXT_RUNTIME === "edge") return false;
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
function selectTransport(namespace) {
|
|
11
|
+
if (!isNodeServerRuntime()) return createConsoleLogger(namespace);
|
|
12
|
+
return isPrettyModeEnabled() ? createPrettyLogger(namespace) : createServerLogger(namespace);
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
selectTransport
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/logger/transport.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Logger } from './index'\nimport { createConsoleLogger } from './transport.console'\nimport { createPrettyLogger, isPrettyModeEnabled } from './transport.pretty'\nimport { createServerLogger } from './transport.server'\n\nfunction isNodeServerRuntime(): boolean {\n if (typeof window !== 'undefined') return false\n if (typeof process === 'undefined') return false\n if (process.env.NEXT_RUNTIME === 'edge') return false\n return true\n}\n\n/** Pick the runtime-appropriate transport: pretty or pino on the Node server, console elsewhere. */\nexport function selectTransport(namespace: string): Logger {\n if (!isNodeServerRuntime()) return createConsoleLogger(namespace)\n return isPrettyModeEnabled() ? createPrettyLogger(namespace) : createServerLogger(namespace)\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,2BAA2B;AACpC,SAAS,oBAAoB,2BAA2B;AACxD,SAAS,0BAA0B;AAEnC,SAAS,sBAA+B;AACtC,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI,OAAO,YAAY,YAAa,QAAO;AAC3C,MAAI,QAAQ,IAAI,iBAAiB,OAAQ,QAAO;AAChD,SAAO;AACT;AAGO,SAAS,gBAAgB,WAA2B;AACzD,MAAI,CAAC,oBAAoB,EAAG,QAAO,oBAAoB,SAAS;AAChE,SAAO,oBAAoB,IAAI,mBAAmB,SAAS,IAAI,mBAAmB,SAAS;AAC7F;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { parseBooleanToken } from "../boolean.js";
|
|
2
|
+
import { isLevelEnabled } from "./level.js";
|
|
3
|
+
import { formatBindings } from "./transport.console.js";
|
|
4
|
+
import { OM_LOG_DESTINATION_ENV, isStderrDestinationToken } from "./transport.server.js";
|
|
5
|
+
const OM_LOG_PRETTY_ENV = "OM_LOG_PRETTY";
|
|
6
|
+
function readProcessEnv() {
|
|
7
|
+
return typeof process === "undefined" ? {} : process.env;
|
|
8
|
+
}
|
|
9
|
+
function resolvePrettyMode(env = readProcessEnv()) {
|
|
10
|
+
const parsed = parseBooleanToken(env[OM_LOG_PRETTY_ENV]);
|
|
11
|
+
if (parsed !== null) return parsed;
|
|
12
|
+
return env.NODE_ENV !== "production";
|
|
13
|
+
}
|
|
14
|
+
let cachedPrettyMode = null;
|
|
15
|
+
function isPrettyModeEnabled() {
|
|
16
|
+
if (cachedPrettyMode === null) cachedPrettyMode = resolvePrettyMode();
|
|
17
|
+
return cachedPrettyMode;
|
|
18
|
+
}
|
|
19
|
+
function resetLogPrettyCache() {
|
|
20
|
+
cachedPrettyMode = null;
|
|
21
|
+
}
|
|
22
|
+
const LEVEL_LABELS = {
|
|
23
|
+
debug: "DEBUG",
|
|
24
|
+
info: "INFO ",
|
|
25
|
+
warn: "WARN ",
|
|
26
|
+
error: "ERROR"
|
|
27
|
+
};
|
|
28
|
+
const ANSI_RESET = "\x1B[0m";
|
|
29
|
+
const ANSI_DIM = "\x1B[2m";
|
|
30
|
+
const LEVEL_COLORS = {
|
|
31
|
+
debug: ANSI_DIM,
|
|
32
|
+
info: "\x1B[36m",
|
|
33
|
+
warn: "\x1B[33m",
|
|
34
|
+
error: "\x1B[31m"
|
|
35
|
+
};
|
|
36
|
+
function resolveStream() {
|
|
37
|
+
const useStderr = isStderrDestinationToken(readProcessEnv()[OM_LOG_DESTINATION_ENV]);
|
|
38
|
+
return useStderr ? process.stderr : process.stdout;
|
|
39
|
+
}
|
|
40
|
+
function formatTimestamp(date) {
|
|
41
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
42
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
43
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
44
|
+
const millis = String(date.getMilliseconds()).padStart(3, "0");
|
|
45
|
+
return `${hours}:${minutes}:${seconds}.${millis}`;
|
|
46
|
+
}
|
|
47
|
+
function createPrettyLogger(namespace, bindings = {}) {
|
|
48
|
+
const emit = (level, msg, fields) => {
|
|
49
|
+
if (!isLevelEnabled(level)) return;
|
|
50
|
+
const stream = resolveStream();
|
|
51
|
+
const { err, ...rest } = { ...bindings, ...fields };
|
|
52
|
+
let scope = namespace;
|
|
53
|
+
if (typeof rest.component === "string" && rest.component) {
|
|
54
|
+
scope = `${namespace}:${rest.component}`;
|
|
55
|
+
delete rest.component;
|
|
56
|
+
}
|
|
57
|
+
const tail = formatBindings(err instanceof Error || err === void 0 ? rest : { ...rest, err });
|
|
58
|
+
const colored = stream.isTTY === true;
|
|
59
|
+
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
60
|
+
const timestampPart = colored ? `${ANSI_DIM}${timestamp}${ANSI_RESET}` : timestamp;
|
|
61
|
+
const label = LEVEL_LABELS[level];
|
|
62
|
+
const levelPart = colored ? `${LEVEL_COLORS[level]}${label}${ANSI_RESET}` : label;
|
|
63
|
+
let line = `${timestampPart} ${levelPart} [${scope}] ${msg}`;
|
|
64
|
+
if (tail) line += ` ${tail}`;
|
|
65
|
+
if (err instanceof Error) line += `
|
|
66
|
+
${err.stack ?? err.message}`;
|
|
67
|
+
stream.write(`${line}
|
|
68
|
+
`);
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
debug: (msg, fields) => emit("debug", msg, fields),
|
|
72
|
+
info: (msg, fields) => emit("info", msg, fields),
|
|
73
|
+
warn: (msg, fields) => emit("warn", msg, fields),
|
|
74
|
+
error: (msg, fields) => emit("error", msg, fields),
|
|
75
|
+
child: (childBindings) => createPrettyLogger(namespace, { ...bindings, ...childBindings })
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
OM_LOG_PRETTY_ENV,
|
|
80
|
+
createPrettyLogger,
|
|
81
|
+
isPrettyModeEnabled,
|
|
82
|
+
resetLogPrettyCache,
|
|
83
|
+
resolvePrettyMode
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=transport.pretty.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/logger/transport.pretty.ts"],
|
|
4
|
+
"sourcesContent": ["import type { LogBindings, Logger } from './index'\nimport { parseBooleanToken } from '../boolean'\nimport { isLevelEnabled, type LogLevel } from './level'\nimport { formatBindings } from './transport.console'\nimport { OM_LOG_DESTINATION_ENV, isStderrDestinationToken } from './transport.server'\n\nexport const OM_LOG_PRETTY_ENV = 'OM_LOG_PRETTY'\n\ntype EnvSource = Record<string, string | undefined>\n\nfunction readProcessEnv(): EnvSource {\n return typeof process === 'undefined' ? {} : process.env\n}\n\n/** Resolve pretty mode from `OM_LOG_PRETTY`, defaulting to on outside production. */\nexport function resolvePrettyMode(env: EnvSource = readProcessEnv()): boolean {\n const parsed = parseBooleanToken(env[OM_LOG_PRETTY_ENV])\n if (parsed !== null) return parsed\n return env.NODE_ENV !== 'production'\n}\n\nlet cachedPrettyMode: boolean | null = null\n\nexport function isPrettyModeEnabled(): boolean {\n if (cachedPrettyMode === null) cachedPrettyMode = resolvePrettyMode()\n return cachedPrettyMode\n}\n\n/** Internal: clear the memoized pretty mode so tests can vary `OM_LOG_PRETTY`. */\nexport function resetLogPrettyCache(): void {\n cachedPrettyMode = null\n}\n\nconst LEVEL_LABELS: Record<LogLevel, string> = {\n debug: 'DEBUG',\n info: 'INFO ',\n warn: 'WARN ',\n error: 'ERROR',\n}\n\nconst ANSI_RESET = '\\x1b[0m'\nconst ANSI_DIM = '\\x1b[2m'\n\nconst LEVEL_COLORS: Record<LogLevel, string> = {\n debug: ANSI_DIM,\n info: '\\x1b[36m',\n warn: '\\x1b[33m',\n error: '\\x1b[31m',\n}\n\ntype WritableLogStream = { write(chunk: string): unknown; isTTY?: boolean }\n\nfunction resolveStream(): WritableLogStream {\n const useStderr = isStderrDestinationToken(readProcessEnv()[OM_LOG_DESTINATION_ENV])\n return useStderr ? process.stderr : process.stdout\n}\n\nfunction formatTimestamp(date: Date): string {\n const hours = String(date.getHours()).padStart(2, '0')\n const minutes = String(date.getMinutes()).padStart(2, '0')\n const seconds = String(date.getSeconds()).padStart(2, '0')\n const millis = String(date.getMilliseconds()).padStart(3, '0')\n return `${hours}:${minutes}:${seconds}.${millis}`\n}\n\nexport function createPrettyLogger(namespace: string, bindings: LogBindings = {}): Logger {\n const emit = (level: LogLevel, msg: string, fields?: LogBindings): void => {\n if (!isLevelEnabled(level)) return\n const stream = resolveStream()\n const { err, ...rest } = { ...bindings, ...fields }\n let scope = namespace\n if (typeof rest.component === 'string' && rest.component) {\n scope = `${namespace}:${rest.component}`\n delete rest.component\n }\n const tail = formatBindings(err instanceof Error || err === undefined ? rest : { ...rest, err })\n const colored = stream.isTTY === true\n const timestamp = formatTimestamp(new Date())\n const timestampPart = colored ? `${ANSI_DIM}${timestamp}${ANSI_RESET}` : timestamp\n const label = LEVEL_LABELS[level]\n const levelPart = colored ? `${LEVEL_COLORS[level]}${label}${ANSI_RESET}` : label\n let line = `${timestampPart} ${levelPart} [${scope}] ${msg}`\n if (tail) line += ` ${tail}`\n if (err instanceof Error) line += `\\n${err.stack ?? err.message}`\n stream.write(`${line}\\n`)\n }\n return {\n debug: (msg, fields) => emit('debug', msg, fields),\n info: (msg, fields) => emit('info', msg, fields),\n warn: (msg, fields) => emit('warn', msg, fields),\n error: (msg, fields) => emit('error', msg, fields),\n child: (childBindings) => createPrettyLogger(namespace, { ...bindings, ...childBindings }),\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { getLogLevel } from "./level.js";
|
|
2
|
+
import { createConsoleLogger } from "./transport.console.js";
|
|
3
|
+
const REDACT_PATHS = [
|
|
4
|
+
"password",
|
|
5
|
+
"*.password",
|
|
6
|
+
"token",
|
|
7
|
+
"*.token",
|
|
8
|
+
"secret",
|
|
9
|
+
"*.secret",
|
|
10
|
+
"authorization",
|
|
11
|
+
"*.authorization",
|
|
12
|
+
"headers.authorization",
|
|
13
|
+
"req.headers.authorization"
|
|
14
|
+
];
|
|
15
|
+
const REDACT_CENSOR = "[Redacted]";
|
|
16
|
+
const OM_LOG_DESTINATION_ENV = "OM_LOG_DESTINATION";
|
|
17
|
+
function isStderrDestinationToken(raw) {
|
|
18
|
+
return typeof raw === "string" && raw.trim().toLowerCase() === "stderr";
|
|
19
|
+
}
|
|
20
|
+
function resolveDestinationStream(nodeProcess) {
|
|
21
|
+
return isStderrDestinationToken(nodeProcess.env?.[OM_LOG_DESTINATION_ENV]) ? nodeProcess.stderr : void 0;
|
|
22
|
+
}
|
|
23
|
+
let cachedRoot;
|
|
24
|
+
let rootGeneration = 0;
|
|
25
|
+
function loadPinoRoot() {
|
|
26
|
+
if (cachedRoot !== void 0) return cachedRoot;
|
|
27
|
+
try {
|
|
28
|
+
const nodeProcess = typeof process === "undefined" ? void 0 : process;
|
|
29
|
+
const moduleBuiltin = nodeProcess?.getBuiltinModule?.("node:module");
|
|
30
|
+
if (!nodeProcess || !moduleBuiltin) {
|
|
31
|
+
cachedRoot = null;
|
|
32
|
+
return cachedRoot;
|
|
33
|
+
}
|
|
34
|
+
const requireFromApp = moduleBuiltin.createRequire(`${nodeProcess.cwd()}/package.json`);
|
|
35
|
+
const pinoExport = requireFromApp("pino");
|
|
36
|
+
const pinoFactory = typeof pinoExport === "function" ? pinoExport : pinoExport.default;
|
|
37
|
+
const destination = resolveDestinationStream(nodeProcess);
|
|
38
|
+
const options = {
|
|
39
|
+
level: getLogLevel(),
|
|
40
|
+
redact: { paths: REDACT_PATHS, censor: REDACT_CENSOR }
|
|
41
|
+
};
|
|
42
|
+
cachedRoot = destination ? pinoFactory(options, destination) : pinoFactory(options);
|
|
43
|
+
} catch {
|
|
44
|
+
cachedRoot = null;
|
|
45
|
+
}
|
|
46
|
+
return cachedRoot;
|
|
47
|
+
}
|
|
48
|
+
function wrapPinoLogger(pinoLogger) {
|
|
49
|
+
return {
|
|
50
|
+
debug: (msg, fields) => pinoLogger.debug(fields ?? {}, msg),
|
|
51
|
+
info: (msg, fields) => pinoLogger.info(fields ?? {}, msg),
|
|
52
|
+
warn: (msg, fields) => pinoLogger.warn(fields ?? {}, msg),
|
|
53
|
+
error: (msg, fields) => pinoLogger.error(fields ?? {}, msg),
|
|
54
|
+
child: (childBindings) => wrapPinoLogger(pinoLogger.child(childBindings))
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function createServerLogger(namespace, bindings = {}) {
|
|
58
|
+
let delegate = null;
|
|
59
|
+
let delegateGeneration = -1;
|
|
60
|
+
const resolveDelegate = () => {
|
|
61
|
+
if (delegate && delegateGeneration === rootGeneration) return delegate;
|
|
62
|
+
delegateGeneration = rootGeneration;
|
|
63
|
+
const root = loadPinoRoot();
|
|
64
|
+
delegate = root ? wrapPinoLogger(root.child({ name: namespace, ...bindings })) : createConsoleLogger(namespace, bindings);
|
|
65
|
+
return delegate;
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
debug: (msg, fields) => resolveDelegate().debug(msg, fields),
|
|
69
|
+
info: (msg, fields) => resolveDelegate().info(msg, fields),
|
|
70
|
+
warn: (msg, fields) => resolveDelegate().warn(msg, fields),
|
|
71
|
+
error: (msg, fields) => resolveDelegate().error(msg, fields),
|
|
72
|
+
child: (childBindings) => createServerLogger(namespace, { ...bindings, ...childBindings })
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function resetServerLoggerCache() {
|
|
76
|
+
cachedRoot = void 0;
|
|
77
|
+
rootGeneration += 1;
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
OM_LOG_DESTINATION_ENV,
|
|
81
|
+
createServerLogger,
|
|
82
|
+
isStderrDestinationToken,
|
|
83
|
+
resetServerLoggerCache
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=transport.server.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/logger/transport.server.ts"],
|
|
4
|
+
"sourcesContent": ["import type { LogBindings, Logger } from './index'\nimport { getLogLevel } from './level'\nimport { createConsoleLogger } from './transport.console'\n\nconst REDACT_PATHS = [\n 'password',\n '*.password',\n 'token',\n '*.token',\n 'secret',\n '*.secret',\n 'authorization',\n '*.authorization',\n 'headers.authorization',\n 'req.headers.authorization',\n]\n\nconst REDACT_CENSOR = '[Redacted]'\n\nexport const OM_LOG_DESTINATION_ENV = 'OM_LOG_DESTINATION'\n\ntype PinoBaseLogger = {\n debug(fields: Record<string, unknown>, msg: string): void\n info(fields: Record<string, unknown>, msg: string): void\n warn(fields: Record<string, unknown>, msg: string): void\n error(fields: Record<string, unknown>, msg: string): void\n child(bindings: Record<string, unknown>): PinoBaseLogger\n}\n\ntype PinoFactory = (options: Record<string, unknown>, destination?: unknown) => PinoBaseLogger\n\ntype NodeModuleBuiltin = {\n createRequire(basePath: string): (id: string) => unknown\n}\n\ntype ProcessWithBuiltins = {\n cwd(): string\n getBuiltinModule?(id: string): unknown\n env?: Record<string, string | undefined>\n stderr?: unknown\n}\n\nexport function isStderrDestinationToken(raw: string | undefined): boolean {\n return typeof raw === 'string' && raw.trim().toLowerCase() === 'stderr'\n}\n\nfunction resolveDestinationStream(nodeProcess: ProcessWithBuiltins): unknown {\n return isStderrDestinationToken(nodeProcess.env?.[OM_LOG_DESTINATION_ENV])\n ? nodeProcess.stderr\n : undefined\n}\n\nlet cachedRoot: PinoBaseLogger | null | undefined\nlet rootGeneration = 0\n\n/**\n * pino is loaded lazily via a runtime `require` obtained from\n * `process.getBuiltinModule('node:module')` \u2014 never through a static import \u2014\n * so browser/edge bundles that reach this file never resolve pino or\n * `node:module`. Any load failure falls back to the console transport.\n */\nfunction loadPinoRoot(): PinoBaseLogger | null {\n if (cachedRoot !== undefined) return cachedRoot\n try {\n const nodeProcess = (typeof process === 'undefined' ? undefined : process) as\n | ProcessWithBuiltins\n | undefined\n const moduleBuiltin = nodeProcess?.getBuiltinModule?.('node:module') as\n | NodeModuleBuiltin\n | undefined\n if (!nodeProcess || !moduleBuiltin) {\n cachedRoot = null\n return cachedRoot\n }\n const requireFromApp = moduleBuiltin.createRequire(`${nodeProcess.cwd()}/package.json`)\n const pinoExport = requireFromApp('pino') as PinoFactory | { default: PinoFactory }\n const pinoFactory = typeof pinoExport === 'function' ? pinoExport : pinoExport.default\n const destination = resolveDestinationStream(nodeProcess)\n const options = {\n level: getLogLevel(),\n redact: { paths: REDACT_PATHS, censor: REDACT_CENSOR },\n }\n cachedRoot = destination ? pinoFactory(options, destination) : pinoFactory(options)\n } catch {\n cachedRoot = null\n }\n return cachedRoot\n}\n\nfunction wrapPinoLogger(pinoLogger: PinoBaseLogger): Logger {\n return {\n debug: (msg, fields) => pinoLogger.debug(fields ?? {}, msg),\n info: (msg, fields) => pinoLogger.info(fields ?? {}, msg),\n warn: (msg, fields) => pinoLogger.warn(fields ?? {}, msg),\n error: (msg, fields) => pinoLogger.error(fields ?? {}, msg),\n child: (childBindings) => wrapPinoLogger(pinoLogger.child(childBindings)),\n }\n}\n\nexport function createServerLogger(namespace: string, bindings: LogBindings = {}): Logger {\n let delegate: Logger | null = null\n let delegateGeneration = -1\n const resolveDelegate = (): Logger => {\n if (delegate && delegateGeneration === rootGeneration) return delegate\n delegateGeneration = rootGeneration\n const root = loadPinoRoot()\n delegate = root\n ? wrapPinoLogger(root.child({ name: namespace, ...bindings }))\n : createConsoleLogger(namespace, bindings)\n return delegate\n }\n return {\n debug: (msg, fields) => resolveDelegate().debug(msg, fields),\n info: (msg, fields) => resolveDelegate().info(msg, fields),\n warn: (msg, fields) => resolveDelegate().warn(msg, fields),\n error: (msg, fields) => resolveDelegate().error(msg, fields),\n child: (childBindings) => createServerLogger(namespace, { ...bindings, ...childBindings }),\n }\n}\n\n/** Internal: clear the cached pino root so tests can re-run transport selection. */\nexport function resetServerLoggerCache(): void {\n cachedRoot = undefined\n rootGeneration += 1\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,mBAAmB;AAC5B,SAAS,2BAA2B;AAEpC,MAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,gBAAgB;AAEf,MAAM,yBAAyB;AAuB/B,SAAS,yBAAyB,KAAkC;AACzE,SAAO,OAAO,QAAQ,YAAY,IAAI,KAAK,EAAE,YAAY,MAAM;AACjE;AAEA,SAAS,yBAAyB,aAA2C;AAC3E,SAAO,yBAAyB,YAAY,MAAM,sBAAsB,CAAC,IACrE,YAAY,SACZ;AACN;AAEA,IAAI;AACJ,IAAI,iBAAiB;AAQrB,SAAS,eAAsC;AAC7C,MAAI,eAAe,OAAW,QAAO;AACrC,MAAI;AACF,UAAM,cAAe,OAAO,YAAY,cAAc,SAAY;AAGlE,UAAM,gBAAgB,aAAa,mBAAmB,aAAa;AAGnE,QAAI,CAAC,eAAe,CAAC,eAAe;AAClC,mBAAa;AACb,aAAO;AAAA,IACT;AACA,UAAM,iBAAiB,cAAc,cAAc,GAAG,YAAY,IAAI,CAAC,eAAe;AACtF,UAAM,aAAa,eAAe,MAAM;AACxC,UAAM,cAAc,OAAO,eAAe,aAAa,aAAa,WAAW;AAC/E,UAAM,cAAc,yBAAyB,WAAW;AACxD,UAAM,UAAU;AAAA,MACd,OAAO,YAAY;AAAA,MACnB,QAAQ,EAAE,OAAO,cAAc,QAAQ,cAAc;AAAA,IACvD;AACA,iBAAa,cAAc,YAAY,SAAS,WAAW,IAAI,YAAY,OAAO;AAAA,EACpF,QAAQ;AACN,iBAAa;AAAA,EACf;AACA,SAAO;AACT;AAEA,SAAS,eAAe,YAAoC;AAC1D,SAAO;AAAA,IACL,OAAO,CAAC,KAAK,WAAW,WAAW,MAAM,UAAU,CAAC,GAAG,GAAG;AAAA,IAC1D,MAAM,CAAC,KAAK,WAAW,WAAW,KAAK,UAAU,CAAC,GAAG,GAAG;AAAA,IACxD,MAAM,CAAC,KAAK,WAAW,WAAW,KAAK,UAAU,CAAC,GAAG,GAAG;AAAA,IACxD,OAAO,CAAC,KAAK,WAAW,WAAW,MAAM,UAAU,CAAC,GAAG,GAAG;AAAA,IAC1D,OAAO,CAAC,kBAAkB,eAAe,WAAW,MAAM,aAAa,CAAC;AAAA,EAC1E;AACF;AAEO,SAAS,mBAAmB,WAAmB,WAAwB,CAAC,GAAW;AACxF,MAAI,WAA0B;AAC9B,MAAI,qBAAqB;AACzB,QAAM,kBAAkB,MAAc;AACpC,QAAI,YAAY,uBAAuB,eAAgB,QAAO;AAC9D,yBAAqB;AACrB,UAAM,OAAO,aAAa;AAC1B,eAAW,OACP,eAAe,KAAK,MAAM,EAAE,MAAM,WAAW,GAAG,SAAS,CAAC,CAAC,IAC3D,oBAAoB,WAAW,QAAQ;AAC3C,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL,OAAO,CAAC,KAAK,WAAW,gBAAgB,EAAE,MAAM,KAAK,MAAM;AAAA,IAC3D,MAAM,CAAC,KAAK,WAAW,gBAAgB,EAAE,KAAK,KAAK,MAAM;AAAA,IACzD,MAAM,CAAC,KAAK,WAAW,gBAAgB,EAAE,KAAK,KAAK,MAAM;AAAA,IACzD,OAAO,CAAC,KAAK,WAAW,gBAAgB,EAAE,MAAM,KAAK,MAAM;AAAA,IAC3D,OAAO,CAAC,kBAAkB,mBAAmB,WAAW,EAAE,GAAG,UAAU,GAAG,cAAc,CAAC;AAAA,EAC3F;AACF;AAGO,SAAS,yBAA+B;AAC7C,eAAa;AACb,oBAAkB;AACpB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { CONTINUE_PAGE_MIDDLEWARE, matchPageMiddlewareTarget } from "@open-mercato/shared/modules/middleware/page";
|
|
2
2
|
import { applyPageGuardOverridesToEntries } from "@open-mercato/shared/modules/overrides";
|
|
3
|
+
import { createLogger } from "../logger/index.js";
|
|
4
|
+
const logger = createLogger("shared").child({ component: "middleware" });
|
|
3
5
|
const DEFAULT_PRIORITY = 100;
|
|
4
6
|
function shouldRunMiddleware(middleware, mode, pathname) {
|
|
5
7
|
if (middleware.mode && middleware.mode !== mode) return false;
|
|
@@ -24,7 +26,7 @@ async function executePageMiddleware(args) {
|
|
|
24
26
|
if (onError) {
|
|
25
27
|
onError(error, { id: middleware.id, priority: middleware.priority });
|
|
26
28
|
} else {
|
|
27
|
-
|
|
29
|
+
logger.error("Page middleware execution failed", { id: middleware.id, err: error });
|
|
28
30
|
}
|
|
29
31
|
throw error;
|
|
30
32
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/middleware/page-executor.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n PageMiddlewareContext,\n PageMiddlewareMode,\n PageMiddlewareRegistryEntry,\n PageMiddlewareResult,\n PageRouteMiddleware,\n} from '@open-mercato/shared/modules/middleware/page'\nimport { CONTINUE_PAGE_MIDDLEWARE, matchPageMiddlewareTarget } from '@open-mercato/shared/modules/middleware/page'\nimport { applyPageGuardOverridesToEntries } from '@open-mercato/shared/modules/overrides'\n\ntype ExecutePageMiddlewareArgs = {\n entries: PageMiddlewareRegistryEntry[]\n context: PageMiddlewareContext\n onError?: (error: unknown, middleware: Pick<PageRouteMiddleware, 'id' | 'priority'>) => void\n}\n\nconst DEFAULT_PRIORITY = 100\n\nfunction shouldRunMiddleware(middleware: PageRouteMiddleware, mode: PageMiddlewareMode, pathname: string): boolean {\n if (middleware.mode && middleware.mode !== mode) return false\n return matchPageMiddlewareTarget(pathname, middleware.target)\n}\n\nfunction compareMiddleware(a: PageRouteMiddleware, b: PageRouteMiddleware): number {\n const priorityDiff = (a.priority ?? DEFAULT_PRIORITY) - (b.priority ?? DEFAULT_PRIORITY)\n if (priorityDiff !== 0) return priorityDiff\n return a.id.localeCompare(b.id)\n}\n\nfunction flattenAndSortMiddleware(\n entries: PageMiddlewareRegistryEntry[],\n mode: PageMiddlewareMode,\n pathname: string\n): PageRouteMiddleware[] {\n return applyPageGuardOverridesToEntries(entries)\n .flatMap((entry) => entry.middleware)\n .filter((middleware) => shouldRunMiddleware(middleware, mode, pathname))\n .sort(compareMiddleware)\n}\n\nexport async function executePageMiddleware(args: ExecutePageMiddlewareArgs): Promise<PageMiddlewareResult> {\n const { entries, context, onError } = args\n const matchedMiddleware = flattenAndSortMiddleware(entries, context.mode, context.pathname)\n for (const middleware of matchedMiddleware) {\n try {\n const result = await middleware.run(context)\n if (result.action === 'redirect') return result\n } catch (error) {\n if (onError) {\n onError(error, { id: middleware.id, priority: middleware.priority })\n } else {\n
|
|
5
|
-
"mappings": "AAOA,SAAS,0BAA0B,iCAAiC;AACpE,SAAS,wCAAwC;
|
|
4
|
+
"sourcesContent": ["import type {\n PageMiddlewareContext,\n PageMiddlewareMode,\n PageMiddlewareRegistryEntry,\n PageMiddlewareResult,\n PageRouteMiddleware,\n} from '@open-mercato/shared/modules/middleware/page'\nimport { CONTINUE_PAGE_MIDDLEWARE, matchPageMiddlewareTarget } from '@open-mercato/shared/modules/middleware/page'\nimport { applyPageGuardOverridesToEntries } from '@open-mercato/shared/modules/overrides'\nimport { createLogger } from '../logger'\n\nconst logger = createLogger('shared').child({ component: 'middleware' })\n\ntype ExecutePageMiddlewareArgs = {\n entries: PageMiddlewareRegistryEntry[]\n context: PageMiddlewareContext\n onError?: (error: unknown, middleware: Pick<PageRouteMiddleware, 'id' | 'priority'>) => void\n}\n\nconst DEFAULT_PRIORITY = 100\n\nfunction shouldRunMiddleware(middleware: PageRouteMiddleware, mode: PageMiddlewareMode, pathname: string): boolean {\n if (middleware.mode && middleware.mode !== mode) return false\n return matchPageMiddlewareTarget(pathname, middleware.target)\n}\n\nfunction compareMiddleware(a: PageRouteMiddleware, b: PageRouteMiddleware): number {\n const priorityDiff = (a.priority ?? DEFAULT_PRIORITY) - (b.priority ?? DEFAULT_PRIORITY)\n if (priorityDiff !== 0) return priorityDiff\n return a.id.localeCompare(b.id)\n}\n\nfunction flattenAndSortMiddleware(\n entries: PageMiddlewareRegistryEntry[],\n mode: PageMiddlewareMode,\n pathname: string\n): PageRouteMiddleware[] {\n return applyPageGuardOverridesToEntries(entries)\n .flatMap((entry) => entry.middleware)\n .filter((middleware) => shouldRunMiddleware(middleware, mode, pathname))\n .sort(compareMiddleware)\n}\n\nexport async function executePageMiddleware(args: ExecutePageMiddlewareArgs): Promise<PageMiddlewareResult> {\n const { entries, context, onError } = args\n const matchedMiddleware = flattenAndSortMiddleware(entries, context.mode, context.pathname)\n for (const middleware of matchedMiddleware) {\n try {\n const result = await middleware.run(context)\n if (result.action === 'redirect') return result\n } catch (error) {\n if (onError) {\n onError(error, { id: middleware.id, priority: middleware.priority })\n } else {\n logger.error('Page middleware execution failed', { id: middleware.id, err: error })\n }\n throw error\n }\n }\n return CONTINUE_PAGE_MIDDLEWARE\n}\n\nexport async function resolvePageMiddlewareRedirect(args: ExecutePageMiddlewareArgs): Promise<string | null> {\n const result = await executePageMiddleware(args)\n if (result.action !== 'redirect') return null\n return result.location\n}\n"],
|
|
5
|
+
"mappings": "AAOA,SAAS,0BAA0B,iCAAiC;AACpE,SAAS,wCAAwC;AACjD,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,QAAQ,EAAE,MAAM,EAAE,WAAW,aAAa,CAAC;AAQvE,MAAM,mBAAmB;AAEzB,SAAS,oBAAoB,YAAiC,MAA0B,UAA2B;AACjH,MAAI,WAAW,QAAQ,WAAW,SAAS,KAAM,QAAO;AACxD,SAAO,0BAA0B,UAAU,WAAW,MAAM;AAC9D;AAEA,SAAS,kBAAkB,GAAwB,GAAgC;AACjF,QAAM,gBAAgB,EAAE,YAAY,qBAAqB,EAAE,YAAY;AACvE,MAAI,iBAAiB,EAAG,QAAO;AAC/B,SAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAChC;AAEA,SAAS,yBACP,SACA,MACA,UACuB;AACvB,SAAO,iCAAiC,OAAO,EAC5C,QAAQ,CAAC,UAAU,MAAM,UAAU,EACnC,OAAO,CAAC,eAAe,oBAAoB,YAAY,MAAM,QAAQ,CAAC,EACtE,KAAK,iBAAiB;AAC3B;AAEA,eAAsB,sBAAsB,MAAgE;AAC1G,QAAM,EAAE,SAAS,SAAS,QAAQ,IAAI;AACtC,QAAM,oBAAoB,yBAAyB,SAAS,QAAQ,MAAM,QAAQ,QAAQ;AAC1F,aAAW,cAAc,mBAAmB;AAC1C,QAAI;AACF,YAAM,SAAS,MAAM,WAAW,IAAI,OAAO;AAC3C,UAAI,OAAO,WAAW,WAAY,QAAO;AAAA,IAC3C,SAAS,OAAO;AACd,UAAI,SAAS;AACX,gBAAQ,OAAO,EAAE,IAAI,WAAW,IAAI,UAAU,WAAW,SAAS,CAAC;AAAA,MACrE,OAAO;AACL,eAAO,MAAM,oCAAoC,EAAE,IAAI,WAAW,IAAI,KAAK,MAAM,CAAC;AAAA,MACpF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,8BAA8B,MAAyD;AAC3G,QAAM,SAAS,MAAM,sBAAsB,IAAI;AAC/C,MAAI,OAAO,WAAW,WAAY,QAAO;AACzC,SAAO,OAAO;AAChB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { applyModuleOverridesToModules } from "@open-mercato/shared/modules/overrides";
|
|
2
2
|
import { invalidateDictionaryCache } from "../i18n/dictionary-cache.js";
|
|
3
|
+
import { createLogger } from "../logger/index.js";
|
|
4
|
+
const logger = createLogger("shared").child({ component: "modules-registry" });
|
|
3
5
|
const GLOBAL_KEY = "__openMercatoModulesRegistry__";
|
|
4
6
|
function getGlobalModules() {
|
|
5
7
|
return globalThis[GLOBAL_KEY] ?? null;
|
|
@@ -33,7 +35,7 @@ function mergeI18nModules(existing, incoming) {
|
|
|
33
35
|
function registerModules(modules) {
|
|
34
36
|
const existing = getGlobalModules();
|
|
35
37
|
if (existing !== null && process.env.NODE_ENV === "development") {
|
|
36
|
-
|
|
38
|
+
logger.debug("Modules re-registered (this may occur during HMR)");
|
|
37
39
|
}
|
|
38
40
|
const nextModules = applyModuleOverridesToModules(modules);
|
|
39
41
|
const shouldMergeI18nOnly = existing !== null && existing.some(hasRuntimeContracts) && isI18nOnlyRegistration(nextModules);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/modules/registry.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Module } from '@open-mercato/shared/modules/registry'\nimport { applyModuleOverridesToModules } from '@open-mercato/shared/modules/overrides'\nimport { invalidateDictionaryCache } from '../i18n/dictionary-cache'\n\n// Registration pattern for publishable packages.\n// Use globalThis to survive tsx/esbuild module duplication where the same\n// registry.ts file can be loaded as multiple module instances when mixing\n// dynamic and static imports \u2014 for example a standalone integration test\n// bootstraps via the source path while a worker handler resolves it through\n// node_modules/@open-mercato/shared/dist/. Mirrors the same workaround used\n// by `getDiRegistrars()` in `../di/container.ts`.\nconst GLOBAL_KEY = '__openMercatoModulesRegistry__'\n\nfunction getGlobalModules(): Module[] | null {\n return (globalThis as any)[GLOBAL_KEY] ?? null\n}\n\nfunction setGlobalModules(modules: Module[]): void {\n ;(globalThis as any)[GLOBAL_KEY] = modules\n}\n\nfunction hasRuntimeContracts(entry: Module): boolean {\n return Object.keys(entry).some((key) => key !== 'id' && key !== 'translations')\n}\n\nfunction isI18nOnlyRegistration(modules: Module[]): boolean {\n return modules.length > 0 && modules.every((entry) => !hasRuntimeContracts(entry))\n}\n\nfunction mergeI18nModules(existing: Module[], incoming: Module[]): Module[] {\n const incomingById = new Map(incoming.map((entry) => [entry.id, entry]))\n const existingIds = new Set(existing.map((entry) => entry.id))\n const merged = existing.map((entry) => {\n const i18nModule = incomingById.get(entry.id)\n if (!i18nModule?.translations) return entry\n return {\n ...entry,\n translations: i18nModule.translations,\n }\n })\n\n for (const entry of incoming) {\n if (!existingIds.has(entry.id)) merged.push(entry)\n }\n\n return merged\n}\n\nexport function registerModules(modules: Module[]) {\n const existing = getGlobalModules()\n if (existing !== null && process.env.NODE_ENV === 'development') {\n
|
|
5
|
-
"mappings": "AACA,SAAS,qCAAqC;AAC9C,SAAS,iCAAiC;
|
|
4
|
+
"sourcesContent": ["import type { Module } from '@open-mercato/shared/modules/registry'\nimport { applyModuleOverridesToModules } from '@open-mercato/shared/modules/overrides'\nimport { invalidateDictionaryCache } from '../i18n/dictionary-cache'\nimport { createLogger } from '../logger'\n\nconst logger = createLogger('shared').child({ component: 'modules-registry' })\n\n// Registration pattern for publishable packages.\n// Use globalThis to survive tsx/esbuild module duplication where the same\n// registry.ts file can be loaded as multiple module instances when mixing\n// dynamic and static imports \u2014 for example a standalone integration test\n// bootstraps via the source path while a worker handler resolves it through\n// node_modules/@open-mercato/shared/dist/. Mirrors the same workaround used\n// by `getDiRegistrars()` in `../di/container.ts`.\nconst GLOBAL_KEY = '__openMercatoModulesRegistry__'\n\nfunction getGlobalModules(): Module[] | null {\n return (globalThis as any)[GLOBAL_KEY] ?? null\n}\n\nfunction setGlobalModules(modules: Module[]): void {\n ;(globalThis as any)[GLOBAL_KEY] = modules\n}\n\nfunction hasRuntimeContracts(entry: Module): boolean {\n return Object.keys(entry).some((key) => key !== 'id' && key !== 'translations')\n}\n\nfunction isI18nOnlyRegistration(modules: Module[]): boolean {\n return modules.length > 0 && modules.every((entry) => !hasRuntimeContracts(entry))\n}\n\nfunction mergeI18nModules(existing: Module[], incoming: Module[]): Module[] {\n const incomingById = new Map(incoming.map((entry) => [entry.id, entry]))\n const existingIds = new Set(existing.map((entry) => entry.id))\n const merged = existing.map((entry) => {\n const i18nModule = incomingById.get(entry.id)\n if (!i18nModule?.translations) return entry\n return {\n ...entry,\n translations: i18nModule.translations,\n }\n })\n\n for (const entry of incoming) {\n if (!existingIds.has(entry.id)) merged.push(entry)\n }\n\n return merged\n}\n\nexport function registerModules(modules: Module[]) {\n const existing = getGlobalModules()\n if (existing !== null && process.env.NODE_ENV === 'development') {\n logger.debug('Modules re-registered (this may occur during HMR)')\n }\n const nextModules = applyModuleOverridesToModules(modules)\n const shouldMergeI18nOnly = existing !== null\n && existing.some(hasRuntimeContracts)\n && isI18nOnlyRegistration(nextModules)\n setGlobalModules(shouldMergeI18nOnly ? mergeI18nModules(existing, nextModules) : nextModules)\n invalidateDictionaryCache()\n}\n\nexport function getModules(): Module[] {\n const modules = getGlobalModules()\n if (!modules) {\n throw new Error('[Bootstrap] Modules not registered. Call registerModules() at bootstrap.')\n }\n return modules\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,qCAAqC;AAC9C,SAAS,iCAAiC;AAC1C,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,QAAQ,EAAE,MAAM,EAAE,WAAW,mBAAmB,CAAC;AAS7E,MAAM,aAAa;AAEnB,SAAS,mBAAoC;AAC3C,SAAQ,WAAmB,UAAU,KAAK;AAC5C;AAEA,SAAS,iBAAiB,SAAyB;AACjD;AAAC,EAAC,WAAmB,UAAU,IAAI;AACrC;AAEA,SAAS,oBAAoB,OAAwB;AACnD,SAAO,OAAO,KAAK,KAAK,EAAE,KAAK,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,cAAc;AAChF;AAEA,SAAS,uBAAuB,SAA4B;AAC1D,SAAO,QAAQ,SAAS,KAAK,QAAQ,MAAM,CAAC,UAAU,CAAC,oBAAoB,KAAK,CAAC;AACnF;AAEA,SAAS,iBAAiB,UAAoB,UAA8B;AAC1E,QAAM,eAAe,IAAI,IAAI,SAAS,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;AACvE,QAAM,cAAc,IAAI,IAAI,SAAS,IAAI,CAAC,UAAU,MAAM,EAAE,CAAC;AAC7D,QAAM,SAAS,SAAS,IAAI,CAAC,UAAU;AACrC,UAAM,aAAa,aAAa,IAAI,MAAM,EAAE;AAC5C,QAAI,CAAC,YAAY,aAAc,QAAO;AACtC,WAAO;AAAA,MACL,GAAG;AAAA,MACH,cAAc,WAAW;AAAA,IAC3B;AAAA,EACF,CAAC;AAED,aAAW,SAAS,UAAU;AAC5B,QAAI,CAAC,YAAY,IAAI,MAAM,EAAE,EAAG,QAAO,KAAK,KAAK;AAAA,EACnD;AAEA,SAAO;AACT;AAEO,SAAS,gBAAgB,SAAmB;AACjD,QAAM,WAAW,iBAAiB;AAClC,MAAI,aAAa,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC/D,WAAO,MAAM,mDAAmD;AAAA,EAClE;AACA,QAAM,cAAc,8BAA8B,OAAO;AACzD,QAAM,sBAAsB,aAAa,QACpC,SAAS,KAAK,mBAAmB,KACjC,uBAAuB,WAAW;AACvC,mBAAiB,sBAAsB,iBAAiB,UAAU,WAAW,IAAI,WAAW;AAC5F,4BAA0B;AAC5B;AAEO,SAAS,aAAuB;AACrC,QAAM,UAAU,iBAAiB;AACjC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,0EAA0E;AAAA,EAC5F;AACA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|