@revealui/core 0.12.2 → 0.12.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/admin/components/AdminDashboard.d.ts.map +1 -1
- package/dist/client/admin/components/AdminDashboard.js +103 -6
- package/dist/client/admin/components/DocumentForm.d.ts.map +1 -1
- package/dist/client/admin/components/DocumentForm.js +5 -1
- package/dist/client/admin/page.d.ts.map +1 -1
- package/dist/client/admin/page.js +1 -1
- package/dist/client/admin/utils/apiClient.d.ts.map +1 -1
- package/dist/client/admin/utils/apiClient.js +27 -1
- package/dist/client/richtext/components/ImageUploadButton.d.ts.map +1 -1
- package/dist/client/richtext/components/ImageUploadButton.js +17 -4
- package/dist/client/richtext/components/upload.d.ts +7 -0
- package/dist/client/richtext/components/upload.d.ts.map +1 -1
- package/dist/client/richtext/components/upload.js +10 -0
- package/dist/client/richtext/nodes/DecoratorBlockNode.d.ts +4 -0
- package/dist/client/richtext/nodes/DecoratorBlockNode.d.ts.map +1 -1
- package/dist/client/richtext/nodes/DecoratorBlockNode.js +12 -0
- package/dist/features.d.ts +2 -0
- package/dist/features.d.ts.map +1 -1
- package/dist/features.js +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/license/mint-client.d.ts +6 -0
- package/dist/license/mint-client.d.ts.map +1 -1
- package/dist/license/mint-client.js +34 -16
- package/dist/license.d.ts +6 -0
- package/dist/license.d.ts.map +1 -1
- package/dist/license.js +15 -0
- package/dist/observability/capture.d.ts +63 -0
- package/dist/observability/capture.d.ts.map +1 -0
- package/dist/observability/capture.js +125 -0
- package/dist/observability/cron-failure-alert.d.ts +59 -0
- package/dist/observability/cron-failure-alert.d.ts.map +1 -0
- package/dist/observability/cron-failure-alert.js +109 -0
- package/dist/observability/index.d.ts +2 -0
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +2 -0
- package/dist/revforge-license-boot.d.ts +35 -0
- package/dist/revforge-license-boot.d.ts.map +1 -0
- package/dist/revforge-license-boot.js +115 -0
- package/dist/vite/index.d.ts +2 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +6 -0
- package/dist/vite/withRevealUI.d.ts +62 -0
- package/dist/vite/withRevealUI.d.ts.map +1 -0
- package/dist/vite/withRevealUI.js +94 -0
- package/package.json +30 -3
package/dist/license.d.ts
CHANGED
|
@@ -368,6 +368,12 @@ export declare function coversRenewalBound(storedExp: number | null, newBound: n
|
|
|
368
368
|
* for a perpetual token (no exp claim) or one that cannot be decoded.
|
|
369
369
|
*/
|
|
370
370
|
export declare function readLicenseExp(licenseKey: string): Promise<number | null>;
|
|
371
|
+
/**
|
|
372
|
+
* Reads the `jti` claim from a license JWT WITHOUT verifying its signature.
|
|
373
|
+
* Used when recording per-token denylist rows after a row-level revoke
|
|
374
|
+
* (GAP-260 P4-5). Never use as an authentication decision.
|
|
375
|
+
*/
|
|
376
|
+
export declare function readLicenseJti(licenseKey: string): Promise<string | null>;
|
|
371
377
|
/**
|
|
372
378
|
* Generates a signed license key JWT.
|
|
373
379
|
* Server-only in practice (requires the private key) but edge-compatible —
|
package/dist/license.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"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"}
|
package/dist/license.js
CHANGED
|
@@ -764,6 +764,21 @@ export async function readLicenseExp(licenseKey) {
|
|
|
764
764
|
return null;
|
|
765
765
|
}
|
|
766
766
|
}
|
|
767
|
+
/**
|
|
768
|
+
* Reads the `jti` claim from a license JWT WITHOUT verifying its signature.
|
|
769
|
+
* Used when recording per-token denylist rows after a row-level revoke
|
|
770
|
+
* (GAP-260 P4-5). Never use as an authentication decision.
|
|
771
|
+
*/
|
|
772
|
+
export async function readLicenseJti(licenseKey) {
|
|
773
|
+
try {
|
|
774
|
+
const jose = await getJose();
|
|
775
|
+
const payload = jose.decodeJwt(licenseKey);
|
|
776
|
+
return typeof payload.jti === 'string' && payload.jti.length > 0 ? payload.jti : null;
|
|
777
|
+
}
|
|
778
|
+
catch {
|
|
779
|
+
return null;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
767
782
|
/**
|
|
768
783
|
* Generates a signed license key JWT.
|
|
769
784
|
* Server-only in practice (requires the private key) but edge-compatible —
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-agnostic error/event capture (Phase 2.3.3).
|
|
3
|
+
*
|
|
4
|
+
* Prefer this over `@sentry/nextjs`. Sink is the structured logger
|
|
5
|
+
* (`@revealui/core/observability/logger` → console or remote via LoggerConfig).
|
|
6
|
+
* Browser-safe: no node: builtins. Node handlers are gated on `process`.
|
|
7
|
+
*/
|
|
8
|
+
import type { LogContext } from './logger.js';
|
|
9
|
+
export interface CaptureContext extends LogContext {
|
|
10
|
+
tags?: Record<string, string>;
|
|
11
|
+
}
|
|
12
|
+
export interface InitNodeObservabilityOptions {
|
|
13
|
+
/** Service name for log context (default `app`). */
|
|
14
|
+
service?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Install process `unhandledRejection` / `uncaughtException` handlers.
|
|
17
|
+
* Default true. Idempotent across multiple calls.
|
|
18
|
+
*/
|
|
19
|
+
installProcessHandlers?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface InitBrowserObservabilityOptions {
|
|
22
|
+
/** Service name for log context (default `app`). */
|
|
23
|
+
service?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Install `window` error + unhandledrejection listeners.
|
|
26
|
+
* Default true. Idempotent across multiple calls.
|
|
27
|
+
*/
|
|
28
|
+
installWindowHandlers?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Strip secret-shaped keys from capture context.
|
|
32
|
+
* Never logs form bodies or credentials.
|
|
33
|
+
*/
|
|
34
|
+
export declare function sanitizeCaptureContext(context?: CaptureContext): LogContext | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Capture an exception to the configured sink (console / remote logger).
|
|
37
|
+
*/
|
|
38
|
+
export declare function captureException(error: unknown, context?: CaptureContext): void;
|
|
39
|
+
/**
|
|
40
|
+
* Capture a non-error event/message.
|
|
41
|
+
*/
|
|
42
|
+
export declare function captureMessage(message: string, context?: CaptureContext): void;
|
|
43
|
+
/**
|
|
44
|
+
* Capture a failed server action. Pass action id only — never the body.
|
|
45
|
+
*/
|
|
46
|
+
export declare function captureActionFailure(actionId: string, error: unknown, context?: CaptureContext): void;
|
|
47
|
+
/**
|
|
48
|
+
* Bind a request/correlation id onto the default logger context when present.
|
|
49
|
+
*/
|
|
50
|
+
export declare function bindRequestId(requestId: string | null | undefined): void;
|
|
51
|
+
/**
|
|
52
|
+
* Node/SSR init: service context + optional process-level handlers.
|
|
53
|
+
* Call once from the RSC/Hono entry.
|
|
54
|
+
*/
|
|
55
|
+
export declare function initNodeObservability(options?: InitNodeObservabilityOptions): void;
|
|
56
|
+
/**
|
|
57
|
+
* Browser init: service context + optional window-level handlers.
|
|
58
|
+
* Call once from the client entry (before hydrate).
|
|
59
|
+
*/
|
|
60
|
+
export declare function initBrowserObservability(options?: InitBrowserObservabilityOptions): void;
|
|
61
|
+
/** Test-only: reset install flags (vitest). */
|
|
62
|
+
export declare function resetObservabilityInstallFlagsForTests(): void;
|
|
63
|
+
//# sourceMappingURL=capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../src/observability/capture.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAqB9C,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,4BAA4B;IAC3C,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,+BAA+B;IAC9C,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAKD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,UAAU,GAAG,SAAS,CAQvF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAG/E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAE9E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB,IAAI,CAMN;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAIxE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,4BAAiC,GAAG,IAAI,CAgBtF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,+BAAoC,GAAG,IAAI,CAmB5F;AAED,+CAA+C;AAC/C,wBAAgB,sCAAsC,IAAI,IAAI,CAG7D"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-agnostic error/event capture (Phase 2.3.3).
|
|
3
|
+
*
|
|
4
|
+
* Prefer this over `@sentry/nextjs`. Sink is the structured logger
|
|
5
|
+
* (`@revealui/core/observability/logger` → console or remote via LoggerConfig).
|
|
6
|
+
* Browser-safe: no node: builtins. Node handlers are gated on `process`.
|
|
7
|
+
*/
|
|
8
|
+
import { logError, logger } from './logger.js';
|
|
9
|
+
/** Keys that must never ride along on capture context (secrets / bodies). */
|
|
10
|
+
const FORBIDDEN_CONTEXT_KEYS = new Set([
|
|
11
|
+
'password',
|
|
12
|
+
'secret',
|
|
13
|
+
'token',
|
|
14
|
+
'authorization',
|
|
15
|
+
'cookie',
|
|
16
|
+
'cookies',
|
|
17
|
+
'body',
|
|
18
|
+
'formdata',
|
|
19
|
+
'form',
|
|
20
|
+
'formstate',
|
|
21
|
+
'credentials',
|
|
22
|
+
'apikey',
|
|
23
|
+
'api_key',
|
|
24
|
+
'sessiontoken',
|
|
25
|
+
]);
|
|
26
|
+
let nodeHandlersInstalled = false;
|
|
27
|
+
let browserHandlersInstalled = false;
|
|
28
|
+
/**
|
|
29
|
+
* Strip secret-shaped keys from capture context.
|
|
30
|
+
* Never logs form bodies or credentials.
|
|
31
|
+
*/
|
|
32
|
+
export function sanitizeCaptureContext(context) {
|
|
33
|
+
if (!context)
|
|
34
|
+
return undefined;
|
|
35
|
+
const out = {};
|
|
36
|
+
for (const [key, value] of Object.entries(context)) {
|
|
37
|
+
if (FORBIDDEN_CONTEXT_KEYS.has(key.toLowerCase()))
|
|
38
|
+
continue;
|
|
39
|
+
out[key] = value;
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Capture an exception to the configured sink (console / remote logger).
|
|
45
|
+
*/
|
|
46
|
+
export function captureException(error, context) {
|
|
47
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
48
|
+
logError(err, sanitizeCaptureContext(context));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Capture a non-error event/message.
|
|
52
|
+
*/
|
|
53
|
+
export function captureMessage(message, context) {
|
|
54
|
+
logger.info(message, sanitizeCaptureContext(context));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Capture a failed server action. Pass action id only — never the body.
|
|
58
|
+
*/
|
|
59
|
+
export function captureActionFailure(actionId, error, context) {
|
|
60
|
+
captureException(error, {
|
|
61
|
+
...context,
|
|
62
|
+
actionId,
|
|
63
|
+
kind: 'action_failure',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Bind a request/correlation id onto the default logger context when present.
|
|
68
|
+
*/
|
|
69
|
+
export function bindRequestId(requestId) {
|
|
70
|
+
if (requestId && requestId.length > 0) {
|
|
71
|
+
logger.setContext({ requestId });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Node/SSR init: service context + optional process-level handlers.
|
|
76
|
+
* Call once from the RSC/Hono entry.
|
|
77
|
+
*/
|
|
78
|
+
export function initNodeObservability(options = {}) {
|
|
79
|
+
const service = options.service ?? 'app';
|
|
80
|
+
logger.setContext({ service, runtime: 'node' });
|
|
81
|
+
logger.info('observability: node init', { service, runtime: 'node' });
|
|
82
|
+
if (options.installProcessHandlers === false)
|
|
83
|
+
return;
|
|
84
|
+
if (nodeHandlersInstalled)
|
|
85
|
+
return;
|
|
86
|
+
if (typeof process === 'undefined' || typeof process.on !== 'function')
|
|
87
|
+
return;
|
|
88
|
+
nodeHandlersInstalled = true;
|
|
89
|
+
process.on('unhandledRejection', (reason) => {
|
|
90
|
+
captureException(reason, { kind: 'unhandledRejection' });
|
|
91
|
+
});
|
|
92
|
+
process.on('uncaughtException', (err) => {
|
|
93
|
+
captureException(err, { kind: 'uncaughtException' });
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Browser init: service context + optional window-level handlers.
|
|
98
|
+
* Call once from the client entry (before hydrate).
|
|
99
|
+
*/
|
|
100
|
+
export function initBrowserObservability(options = {}) {
|
|
101
|
+
const service = options.service ?? 'app';
|
|
102
|
+
logger.setContext({ service, runtime: 'browser' });
|
|
103
|
+
logger.info('observability: browser init', { service, runtime: 'browser' });
|
|
104
|
+
if (options.installWindowHandlers === false)
|
|
105
|
+
return;
|
|
106
|
+
if (browserHandlersInstalled)
|
|
107
|
+
return;
|
|
108
|
+
if (typeof window === 'undefined')
|
|
109
|
+
return;
|
|
110
|
+
browserHandlersInstalled = true;
|
|
111
|
+
window.addEventListener('error', (event) => {
|
|
112
|
+
captureException(event.error ?? event.message, {
|
|
113
|
+
kind: 'window.error',
|
|
114
|
+
filename: event.filename,
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
118
|
+
captureException(event.reason, { kind: 'unhandledrejection' });
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/** Test-only: reset install flags (vitest). */
|
|
122
|
+
export function resetObservabilityInstallFlagsForTests() {
|
|
123
|
+
nodeHandlersInstalled = false;
|
|
124
|
+
browserHandlersInstalled = false;
|
|
125
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared cron-failure alert path for apps/admin + apps/server.
|
|
3
|
+
*
|
|
4
|
+
* Apps inject their Sentry SDK (+ optional email) so this module never depends
|
|
5
|
+
* on `@sentry/node` or `@sentry/nextjs`. Logger is always the canonical
|
|
6
|
+
* `@revealui/core/observability/logger` facade.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from the intentional admin/server dual (revealui#835 follow-up)
|
|
9
|
+
* after the 2026-07-31 redundancy audit.
|
|
10
|
+
*/
|
|
11
|
+
export interface CronFailureContext {
|
|
12
|
+
jobName: string;
|
|
13
|
+
error: Error;
|
|
14
|
+
severity?: 'warn' | 'error' | 'fatal';
|
|
15
|
+
metadata?: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
/** Minimal Sentry surface both Node and Next SDKs satisfy. */
|
|
18
|
+
export interface CronFailureSentryScope {
|
|
19
|
+
setTag(key: string, value: string): void;
|
|
20
|
+
setLevel(level: 'fatal' | 'error' | 'warning' | string): void;
|
|
21
|
+
setContext(name: string, context: Record<string, unknown>): void;
|
|
22
|
+
}
|
|
23
|
+
export interface CronFailureSentryLike {
|
|
24
|
+
withScope(callback: (scope: CronFailureSentryScope) => void): void;
|
|
25
|
+
captureException(error: Error): void;
|
|
26
|
+
}
|
|
27
|
+
export interface CronFailureEmailPayload {
|
|
28
|
+
to: string;
|
|
29
|
+
subject: string;
|
|
30
|
+
html: string;
|
|
31
|
+
text: string;
|
|
32
|
+
}
|
|
33
|
+
export interface SendCronFailureAlertOptions {
|
|
34
|
+
/** When omitted, Sentry capture is skipped. */
|
|
35
|
+
sentry?: CronFailureSentryLike;
|
|
36
|
+
/**
|
|
37
|
+
* Destination for the optional email step. When omitted, reads
|
|
38
|
+
* `REVEALUI_ALERT_EMAIL` from `env`. Empty/undefined skips email.
|
|
39
|
+
*/
|
|
40
|
+
alertEmail?: string | null;
|
|
41
|
+
/** Required for email delivery when an alert address is present. */
|
|
42
|
+
sendEmail?: (payload: CronFailureEmailPayload) => Promise<void>;
|
|
43
|
+
env?: NodeJS.ProcessEnv;
|
|
44
|
+
}
|
|
45
|
+
export declare function buildCronFailureEmailBody(context: CronFailureContext): {
|
|
46
|
+
subject: string;
|
|
47
|
+
html: string;
|
|
48
|
+
text: string;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Centralized alert path for cron failures. Always logs at the chosen
|
|
52
|
+
* severity. Optionally captures to Sentry and/or sends email via injected
|
|
53
|
+
* adapters.
|
|
54
|
+
*
|
|
55
|
+
* Never throws — each step is independent. A failure in one step is logged
|
|
56
|
+
* and does not prevent the others.
|
|
57
|
+
*/
|
|
58
|
+
export declare function sendCronFailureAlert(context: CronFailureContext, options?: SendCronFailureAlertOptions): Promise<void>;
|
|
59
|
+
//# sourceMappingURL=cron-failure-alert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron-failure-alert.d.ts","sourceRoot":"","sources":["../../src/observability/cron-failure-alert.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,8DAA8D;AAC9D,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAAC;IAC9D,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAClE;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,GAAG,IAAI,CAAC;IACnE,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,2BAA2B;IAC1C,+CAA+C;IAC/C,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAC/B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,oEAAoE;IACpE,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,kBAAkB,GAAG;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAqCA;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,IAAI,CAAC,CA0Df"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared cron-failure alert path for apps/admin + apps/server.
|
|
3
|
+
*
|
|
4
|
+
* Apps inject their Sentry SDK (+ optional email) so this module never depends
|
|
5
|
+
* on `@sentry/node` or `@sentry/nextjs`. Logger is always the canonical
|
|
6
|
+
* `@revealui/core/observability/logger` facade.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from the intentional admin/server dual (revealui#835 follow-up)
|
|
9
|
+
* after the 2026-07-31 redundancy audit.
|
|
10
|
+
*/
|
|
11
|
+
import { logger } from './logger.js';
|
|
12
|
+
export function buildCronFailureEmailBody(context) {
|
|
13
|
+
const { jobName, error, severity = 'error', metadata } = context;
|
|
14
|
+
const subject = `[CRON FAILURE] ${jobName}: ${error.message}`;
|
|
15
|
+
const metaRows = metadata
|
|
16
|
+
? Object.entries(metadata)
|
|
17
|
+
.map(([k, v]) => `<tr><td style="padding:2px 8px;font-weight:bold">${k}</td><td style="padding:2px 8px">${String(v)}</td></tr>`)
|
|
18
|
+
.join('')
|
|
19
|
+
: '';
|
|
20
|
+
const metaSection = metaRows
|
|
21
|
+
? `<h3>Metadata</h3><table style="border-collapse:collapse">${metaRows}</table>`
|
|
22
|
+
: '';
|
|
23
|
+
const metaText = metadata
|
|
24
|
+
? `\nMetadata:\n${Object.entries(metadata)
|
|
25
|
+
.map(([k, v]) => ` ${k}: ${String(v)}`)
|
|
26
|
+
.join('\n')}`
|
|
27
|
+
: '';
|
|
28
|
+
const html = `<!DOCTYPE html>
|
|
29
|
+
<html>
|
|
30
|
+
<head><meta charset="utf-8"><title>${subject}</title></head>
|
|
31
|
+
<body style="font-family:Arial,sans-serif;line-height:1.6;color:#333;max-width:600px;margin:0 auto;padding:20px">
|
|
32
|
+
<h1 style="color:#dc2626">Cron Job Failure</h1>
|
|
33
|
+
<p><strong>Job:</strong> ${jobName}</p>
|
|
34
|
+
<p><strong>Severity:</strong> ${severity}</p>
|
|
35
|
+
<p><strong>Error:</strong> ${error.message}</p>
|
|
36
|
+
${error.stack ? `<pre style="background:#f5f5f5;padding:12px;overflow:auto;font-size:12px">${error.stack}</pre>` : ''}
|
|
37
|
+
${metaSection}
|
|
38
|
+
<p style="color:#666;font-size:12px">Automated alert from api.revealui.com — ${new Date().toISOString()}</p>
|
|
39
|
+
</body>
|
|
40
|
+
</html>`;
|
|
41
|
+
const text = `Cron Job Failure\n\nJob: ${jobName}\nSeverity: ${severity}\nError: ${error.message}${metaText}\n\nTimestamp: ${new Date().toISOString()}`;
|
|
42
|
+
return { subject, html, text };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Centralized alert path for cron failures. Always logs at the chosen
|
|
46
|
+
* severity. Optionally captures to Sentry and/or sends email via injected
|
|
47
|
+
* adapters.
|
|
48
|
+
*
|
|
49
|
+
* Never throws — each step is independent. A failure in one step is logged
|
|
50
|
+
* and does not prevent the others.
|
|
51
|
+
*/
|
|
52
|
+
export async function sendCronFailureAlert(context, options = {}) {
|
|
53
|
+
const { jobName, error, severity = 'error', metadata } = context;
|
|
54
|
+
const env = options.env ?? process.env;
|
|
55
|
+
// Step 1 — always log
|
|
56
|
+
try {
|
|
57
|
+
const logPayload = { jobName, ...(metadata ?? {}) };
|
|
58
|
+
if (severity === 'fatal' || severity === 'error') {
|
|
59
|
+
logger.error(`[cron-alert] ${jobName} failed: ${error.message}`, error, logPayload);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
logger.warn(`[cron-alert] ${jobName} failed: ${error.message}`, logPayload);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (logErr) {
|
|
66
|
+
// Last-resort: if the logger itself is broken, write directly to stderr
|
|
67
|
+
// so the failure is at least visible in Vercel function logs.
|
|
68
|
+
process.stderr.write(`[cron-alert] logger failed while reporting ${jobName}: ${logErr instanceof Error ? logErr.message : String(logErr)}\n`);
|
|
69
|
+
}
|
|
70
|
+
// Step 2 — Sentry (skipped when no adapter injected)
|
|
71
|
+
const sentry = options.sentry;
|
|
72
|
+
if (sentry) {
|
|
73
|
+
try {
|
|
74
|
+
sentry.withScope((scope) => {
|
|
75
|
+
scope.setTag('cron_job', jobName);
|
|
76
|
+
scope.setTag('alert_severity', severity);
|
|
77
|
+
if (metadata) {
|
|
78
|
+
scope.setContext('metadata', metadata);
|
|
79
|
+
}
|
|
80
|
+
if (severity === 'fatal') {
|
|
81
|
+
scope.setLevel('fatal');
|
|
82
|
+
}
|
|
83
|
+
else if (severity === 'warn') {
|
|
84
|
+
scope.setLevel('warning');
|
|
85
|
+
}
|
|
86
|
+
sentry.captureException(error);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
catch (sentryErr) {
|
|
90
|
+
logger.warn(`[cron-alert] Sentry capture failed for ${jobName}`, {
|
|
91
|
+
detail: sentryErr instanceof Error ? sentryErr.message : String(sentryErr),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Step 3 — optional email
|
|
96
|
+
const alertEmail = options.alertEmail !== undefined ? options.alertEmail : env.REVEALUI_ALERT_EMAIL;
|
|
97
|
+
if (!(alertEmail && options.sendEmail)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const { subject, html, text } = buildCronFailureEmailBody(context);
|
|
102
|
+
await options.sendEmail({ to: alertEmail, subject, html, text });
|
|
103
|
+
}
|
|
104
|
+
catch (emailErr) {
|
|
105
|
+
logger.warn(`[cron-alert] email delivery failed for ${jobName}`, {
|
|
106
|
+
detail: emailErr instanceof Error ? emailErr.message : String(emailErr),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Forge / self-host license boot gate for apps/admin + apps/server.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the intentional admin/server dual after the 2026-08-02 fleet
|
|
5
|
+
* redundancy audit. Server previously owned the full check (customer id, kid
|
|
6
|
+
* mismatch, domain binding); admin had a shortened copy. One implementation
|
|
7
|
+
* so the two cannot drift (instrumentation.ts TODO resolved).
|
|
8
|
+
*
|
|
9
|
+
* Throws on failure. Callers that must not throw (Next.js instrumentation)
|
|
10
|
+
* catch and `process.exit(1)`.
|
|
11
|
+
*/
|
|
12
|
+
export type EnvMap = Record<string, string | undefined>;
|
|
13
|
+
/**
|
|
14
|
+
* GAP-436 (owner-ruled 2026-07-26): plain OSS/self-host opt-in when no key is
|
|
15
|
+
* present. Stamped RevForge kits always bake a key, so they never hit this.
|
|
16
|
+
*/
|
|
17
|
+
export declare const ALLOW_UNLICENSED_SELF_HOST_ENV = "REVEALUI_ALLOW_UNLICENSED_SELF_HOST";
|
|
18
|
+
/**
|
|
19
|
+
* Extracts the `kid` (key id) from a JWT protected header WITHOUT verifying
|
|
20
|
+
* the signature. Used only to sharpen error messages after cryptographic
|
|
21
|
+
* verification has already failed.
|
|
22
|
+
*/
|
|
23
|
+
export declare function decodeJwtKid(jwt: string): string | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Forge-mode license enforcement at process boot.
|
|
26
|
+
*
|
|
27
|
+
* - Hosted mode: no-op.
|
|
28
|
+
* - Forge mode + no key + ALLOW_UNLICENSED_SELF_HOST: Free (OSS) tier log, return.
|
|
29
|
+
* - Forge mode otherwise: require key + public key, verify JWT, optional
|
|
30
|
+
* customerId + domain binding.
|
|
31
|
+
*
|
|
32
|
+
* Honors `SKIP_ENV_VALIDATION=true` for Docker build / tests.
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateForgeLicenseAtStartup(env?: EnvMap): Promise<void>;
|
|
35
|
+
//# sourceMappingURL=revforge-license-boot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revforge-license-boot.d.ts","sourceRoot":"","sources":["../src/revforge-license-boot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,8BAA8B,wCAAwC,CAAC;AAEpF;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAa5D;AAED;;;;;;;;;GASG;AACH,wBAAsB,6BAA6B,CACjD,GAAG,GAAE,MAA8B,GAClC,OAAO,CAAC,IAAI,CAAC,CA6Ef"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Forge / self-host license boot gate for apps/admin + apps/server.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the intentional admin/server dual after the 2026-08-02 fleet
|
|
5
|
+
* redundancy audit. Server previously owned the full check (customer id, kid
|
|
6
|
+
* mismatch, domain binding); admin had a shortened copy. One implementation
|
|
7
|
+
* so the two cannot drift (instrumentation.ts TODO resolved).
|
|
8
|
+
*
|
|
9
|
+
* Throws on failure. Callers that must not throw (Next.js instrumentation)
|
|
10
|
+
* catch and `process.exit(1)`.
|
|
11
|
+
*/
|
|
12
|
+
import { detectDeploymentMode as detectDeploymentModeCore, } from './deployment-mode.js';
|
|
13
|
+
import { computeKeyId, hostMatchesLicensedDomains, validateLicenseKey } from './license.js';
|
|
14
|
+
import { logger } from './observability/logger.js';
|
|
15
|
+
/**
|
|
16
|
+
* GAP-436 (owner-ruled 2026-07-26): plain OSS/self-host opt-in when no key is
|
|
17
|
+
* present. Stamped RevForge kits always bake a key, so they never hit this.
|
|
18
|
+
*/
|
|
19
|
+
export const ALLOW_UNLICENSED_SELF_HOST_ENV = 'REVEALUI_ALLOW_UNLICENSED_SELF_HOST';
|
|
20
|
+
/**
|
|
21
|
+
* Extracts the `kid` (key id) from a JWT protected header WITHOUT verifying
|
|
22
|
+
* the signature. Used only to sharpen error messages after cryptographic
|
|
23
|
+
* verification has already failed.
|
|
24
|
+
*/
|
|
25
|
+
export function decodeJwtKid(jwt) {
|
|
26
|
+
const headerSegment = jwt.split('.')[0];
|
|
27
|
+
if (!headerSegment)
|
|
28
|
+
return undefined;
|
|
29
|
+
try {
|
|
30
|
+
const header = JSON.parse(Buffer.from(headerSegment, 'base64url').toString('utf8'));
|
|
31
|
+
if (header && typeof header === 'object' && 'kid' in header) {
|
|
32
|
+
const { kid } = header;
|
|
33
|
+
return typeof kid === 'string' ? kid : undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Malformed header — caller surfaces generic invalid-license error.
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Forge-mode license enforcement at process boot.
|
|
43
|
+
*
|
|
44
|
+
* - Hosted mode: no-op.
|
|
45
|
+
* - Forge mode + no key + ALLOW_UNLICENSED_SELF_HOST: Free (OSS) tier log, return.
|
|
46
|
+
* - Forge mode otherwise: require key + public key, verify JWT, optional
|
|
47
|
+
* customerId + domain binding.
|
|
48
|
+
*
|
|
49
|
+
* Honors `SKIP_ENV_VALIDATION=true` for Docker build / tests.
|
|
50
|
+
*/
|
|
51
|
+
export async function validateForgeLicenseAtStartup(env = process.env) {
|
|
52
|
+
if (env.SKIP_ENV_VALIDATION === 'true') {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const mode = detectDeploymentModeCore(env);
|
|
56
|
+
if (mode !== 'forge') {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (!env.REVEALUI_LICENSE_KEY) {
|
|
60
|
+
if (env[ALLOW_UNLICENSED_SELF_HOST_ENV] === 'true') {
|
|
61
|
+
logger.info('no license key — running Free (OSS) tier');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
throw new Error('LICENSE VALIDATION FAILED: REVEALUI_LICENSE_KEY is required for RevForge deployments. ' +
|
|
65
|
+
'Run bin/revvault-bootstrap.sh to materialize docker/.env from revvault, ' +
|
|
66
|
+
'or contact the operator who stamped this kit. A plain self-host deployment that ' +
|
|
67
|
+
`intends to run without a license should set ${ALLOW_UNLICENSED_SELF_HOST_ENV}=true to ` +
|
|
68
|
+
'boot at Free (OSS) tier instead.');
|
|
69
|
+
}
|
|
70
|
+
if (!env.REVEALUI_LICENSE_PUBLIC_KEY) {
|
|
71
|
+
throw new Error('LICENSE VALIDATION FAILED: REVEALUI_LICENSE_PUBLIC_KEY is required for RevForge deployments. ' +
|
|
72
|
+
'Stamped kits embed this value in docker/.env.example; check that it survived the bootstrap step.');
|
|
73
|
+
}
|
|
74
|
+
// Restore real newlines if the public key landed as a single-line PEM
|
|
75
|
+
// (stamp.sh .env encoding). Split/join, no authored regex — mirrors
|
|
76
|
+
// @revealui/core/license normalizePem. GAP-259 P0-4.
|
|
77
|
+
const publicKey = env.REVEALUI_LICENSE_PUBLIC_KEY.split('\\n').join('\n');
|
|
78
|
+
const expectedCustomerId = env.REVEALUI_LICENSED_CUSTOMER_ID || undefined;
|
|
79
|
+
const payload = await validateLicenseKey(env.REVEALUI_LICENSE_KEY, publicKey, expectedCustomerId);
|
|
80
|
+
if (!payload) {
|
|
81
|
+
const tokenKid = decodeJwtKid(env.REVEALUI_LICENSE_KEY);
|
|
82
|
+
if (tokenKid !== undefined) {
|
|
83
|
+
const expectedKid = await computeKeyId(publicKey);
|
|
84
|
+
if (tokenKid !== expectedKid) {
|
|
85
|
+
throw new Error('LICENSE VALIDATION FAILED: REVEALUI_LICENSE_PUBLIC_KEY does not match the key that ' +
|
|
86
|
+
`signed REVEALUI_LICENSE_KEY (license key id "${tokenKid}", configured public key id ` +
|
|
87
|
+
`"${expectedKid}"). The stamped kit baked the wrong public key. Re-issue the license ` +
|
|
88
|
+
'with the matching keypair, or bake the public key that pairs with the signing key, ' +
|
|
89
|
+
'then re-run bin/revvault-bootstrap.sh. Contact the operator who stamped this kit.');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
throw new Error('LICENSE VALIDATION FAILED: REVEALUI_LICENSE_KEY is invalid, expired beyond grace, ' +
|
|
93
|
+
'signed with a key that does not match REVEALUI_LICENSE_PUBLIC_KEY, or its ' +
|
|
94
|
+
'customerId does not match REVEALUI_LICENSED_CUSTOMER_ID (if set). ' +
|
|
95
|
+
'Contact the operator who stamped this kit to re-issue the license.');
|
|
96
|
+
}
|
|
97
|
+
if (payload.domains && payload.domains.length > 0) {
|
|
98
|
+
const publicUrl = (env.REVEALUI_PUBLIC_SERVER_URL ?? env.NEXT_PUBLIC_SERVER_URL ?? '').trim();
|
|
99
|
+
if (publicUrl) {
|
|
100
|
+
let host = '';
|
|
101
|
+
try {
|
|
102
|
+
host = new URL(publicUrl).hostname;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
host = '';
|
|
106
|
+
}
|
|
107
|
+
if (!hostMatchesLicensedDomains(host, payload.domains)) {
|
|
108
|
+
throw new Error('LICENSE VALIDATION FAILED: this license is restricted to ' +
|
|
109
|
+
`[${payload.domains.join(', ')}], but REVEALUI_PUBLIC_SERVER_URL host ` +
|
|
110
|
+
`"${host || '(unparseable)'}" is not among them. Set REVEALUI_PUBLIC_SERVER_URL ` +
|
|
111
|
+
'to a licensed domain, or contact the operator who stamped this kit.');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,YAAY,GACb,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// RevealUI Vite integration barrel.
|
|
2
|
+
//
|
|
3
|
+
// `withRevealUI` is re-exported here for convenience. Config-time consumers
|
|
4
|
+
// may also import from `@revealui/core/vite/withRevealUI` directly (mirrors
|
|
5
|
+
// the Next.js subpath that stays out of runtime NFT graphs).
|
|
6
|
+
export { withRevealUI, } from './withRevealUI.js';
|