@revealui/core 0.5.5 → 0.6.0

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.
Files changed (142) hide show
  1. package/README.md +30 -24
  2. package/dist/api/compression.d.ts +3 -4
  3. package/dist/api/compression.d.ts.map +1 -1
  4. package/dist/api/compression.js +1 -2
  5. package/dist/api/rate-limit.d.ts +11 -12
  6. package/dist/api/rate-limit.d.ts.map +1 -1
  7. package/dist/api/rate-limit.js +5 -6
  8. package/dist/api/response-cache.d.ts +8 -9
  9. package/dist/api/response-cache.d.ts.map +1 -1
  10. package/dist/api/response-cache.js +5 -4
  11. package/dist/api/rest.d.ts +1 -1
  12. package/dist/api/rest.js +2 -2
  13. package/dist/cache/query-cache.d.ts +1 -1
  14. package/dist/cache/query-cache.js +1 -1
  15. package/dist/caching/index.d.ts +1 -1
  16. package/dist/caching/index.js +1 -1
  17. package/dist/client/admin/components/AdminDashboard.d.ts.map +1 -1
  18. package/dist/client/admin/components/AdminDashboard.js +46 -3
  19. package/dist/client/admin/components/CollectionList.d.ts +3 -1
  20. package/dist/client/admin/components/CollectionList.d.ts.map +1 -1
  21. package/dist/client/admin/components/CollectionList.js +51 -2
  22. package/dist/client/admin/components/DocumentForm.js +2 -2
  23. package/dist/client/admin/layout.d.ts.map +1 -1
  24. package/dist/client/admin/layout.js +1 -3
  25. package/dist/client/admin/page.js +1 -1
  26. package/dist/client/admin/utils/apiClient.d.ts +17 -1
  27. package/dist/client/admin/utils/apiClient.d.ts.map +1 -1
  28. package/dist/client/admin/utils/apiClient.js +25 -1
  29. package/dist/client/hooks.d.ts +1 -1
  30. package/dist/client/hooks.js +1 -1
  31. package/dist/client/richtext/plugins/PastePlugin.d.ts.map +1 -1
  32. package/dist/client/richtext/plugins/PastePlugin.js +30 -0
  33. package/dist/client/ui/index.d.ts +2 -2
  34. package/dist/client/ui/index.js +2 -2
  35. package/dist/collections/operations/fieldHooks.d.ts +2 -2
  36. package/dist/collections/operations/fieldHooks.js +2 -2
  37. package/dist/collections/operations/update.js +1 -1
  38. package/dist/config/index.js +1 -1
  39. package/dist/config/runtime.d.ts +3 -3
  40. package/dist/config/runtime.d.ts.map +1 -1
  41. package/dist/config/runtime.js +2 -2
  42. package/dist/config/utils.d.ts.map +1 -1
  43. package/dist/config/utils.js +5 -0
  44. package/dist/database/safe-parse.d.ts +1 -1
  45. package/dist/database/safe-parse.js +3 -3
  46. package/dist/database/universal-postgres.d.ts +1 -1
  47. package/dist/database/universal-postgres.js +1 -1
  48. package/dist/error-handling/error-reporter.js +4 -4
  49. package/dist/features.d.ts +9 -9
  50. package/dist/features.d.ts.map +1 -1
  51. package/dist/features.js +3 -3
  52. package/dist/generated/index.d.ts +1 -1
  53. package/dist/generated/index.js +1 -1
  54. package/dist/generated/types/{cms.d.ts → admin.d.ts} +3 -3
  55. package/dist/generated/types/admin.d.ts.map +1 -0
  56. package/dist/generated/types/index.d.ts +2 -2
  57. package/dist/generated/types/index.d.ts.map +1 -1
  58. package/dist/generated/types/index.js +2 -2
  59. package/dist/index.d.ts +6 -6
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +14 -10
  62. package/dist/instance/RevealUIInstance.js +2 -2
  63. package/dist/jobs/queue.d.ts +1 -1
  64. package/dist/jobs/queue.d.ts.map +1 -1
  65. package/dist/license-encryption.d.ts +11 -2
  66. package/dist/license-encryption.d.ts.map +1 -1
  67. package/dist/license-encryption.js +79 -24
  68. package/dist/license.d.ts +68 -5
  69. package/dist/license.d.ts.map +1 -1
  70. package/dist/license.js +171 -20
  71. package/dist/monitoring/zombie-detector.js +1 -1
  72. package/dist/nextjs/index.d.ts +0 -1
  73. package/dist/nextjs/index.d.ts.map +1 -1
  74. package/dist/nextjs/index.js +7 -2
  75. package/dist/nextjs/withRevealUI.d.ts +29 -1
  76. package/dist/nextjs/withRevealUI.d.ts.map +1 -1
  77. package/dist/observability/health-check.js +1 -1
  78. package/dist/observability/logger.d.ts +0 -4
  79. package/dist/observability/logger.d.ts.map +1 -1
  80. package/dist/observability/logger.js +2 -29
  81. package/dist/plugins/nested-docs.d.ts +1 -1
  82. package/dist/plugins/nested-docs.d.ts.map +1 -1
  83. package/dist/plugins/nested-docs.js +1 -1
  84. package/dist/relationships/analyzer.d.ts +1 -1
  85. package/dist/relationships/analyzer.js +2 -2
  86. package/dist/relationships/populate-core.d.ts +1 -1
  87. package/dist/relationships/populate-core.d.ts.map +1 -1
  88. package/dist/relationships/populate-core.js +5 -1
  89. package/dist/relationships/population.js +1 -1
  90. package/dist/revealui.d.ts +0 -5
  91. package/dist/revealui.d.ts.map +1 -1
  92. package/dist/revealui.js +0 -10
  93. package/dist/richtext/exports/client/rcc.js +1 -1
  94. package/dist/richtext/exports/server/rsc.d.ts +2 -17
  95. package/dist/richtext/exports/server/rsc.d.ts.map +1 -1
  96. package/dist/richtext/exports/server/rsc.js +9 -54
  97. package/dist/richtext/index.d.ts +1 -1
  98. package/dist/richtext/index.js +1 -1
  99. package/dist/security/index.d.ts +1 -1
  100. package/dist/security/index.js +1 -1
  101. package/dist/server/renderPage.js +1 -1
  102. package/dist/types/admin.d.ts +8 -0
  103. package/dist/types/admin.d.ts.map +1 -0
  104. package/dist/types/admin.js +6 -0
  105. package/dist/types/config.d.ts +2 -2
  106. package/dist/types/config.d.ts.map +1 -1
  107. package/dist/types/config.js +1 -1
  108. package/dist/types/extensions.d.ts +1 -1
  109. package/dist/types/extensions.d.ts.map +1 -1
  110. package/dist/types/generated.d.ts +4 -4
  111. package/dist/types/generated.d.ts.map +1 -1
  112. package/dist/types/generated.js +2 -2
  113. package/dist/types/hooks.d.ts +1 -1
  114. package/dist/types/hooks.d.ts.map +1 -1
  115. package/dist/types/index.d.ts +4 -4
  116. package/dist/types/index.d.ts.map +1 -1
  117. package/dist/types/index.js +6 -6
  118. package/dist/types/jobs.d.ts +1 -1
  119. package/dist/types/jobs.js +1 -1
  120. package/dist/types/legacy.d.ts +1 -1
  121. package/dist/types/legacy.d.ts.map +1 -1
  122. package/dist/types/plugins.d.ts +1 -1
  123. package/dist/types/plugins.d.ts.map +1 -1
  124. package/dist/types/query.d.ts +2 -2
  125. package/dist/types/query.d.ts.map +1 -1
  126. package/dist/types/runtime.d.ts +2 -2
  127. package/dist/types/runtime.d.ts.map +1 -1
  128. package/dist/types/schema.d.ts +1 -1
  129. package/dist/types/schema.d.ts.map +1 -1
  130. package/dist/utils/api-wrapper.d.ts +4 -6
  131. package/dist/utils/api-wrapper.d.ts.map +1 -1
  132. package/dist/utils/api-wrapper.js +6 -9
  133. package/dist/utils/error-responses.js +1 -1
  134. package/dist/utils/field-conversion.js +1 -1
  135. package/dist/utils/type-guards.d.ts +1 -1
  136. package/dist/utils/type-guards.d.ts.map +1 -1
  137. package/package.json +87 -34
  138. package/dist/generated/types/cms.d.ts.map +0 -1
  139. package/dist/types/cms.d.ts +0 -8
  140. package/dist/types/cms.d.ts.map +0 -1
  141. package/dist/types/cms.js +0 -6
  142. /package/dist/generated/types/{cms.js → admin.js} +0 -0
package/dist/license.d.ts CHANGED
@@ -1,18 +1,62 @@
1
1
  /**
2
2
  * License validation for RevealUI Pro/Enterprise tiers.
3
3
  *
4
+ * Edge-compatible: uses the Web Crypto API (`crypto.subtle`) and `jose`
5
+ * exclusively. Safe to import from any runtime (Node, Edge, browser,
6
+ * Workers). No `node:crypto` or filesystem dependencies.
7
+ *
4
8
  * @dependencies
5
- * - jose - JWT token verification (Web Crypto API)
9
+ * - jose - JWT signing/verification (Web Crypto API)
6
10
  * - zod - Schema validation for license payloads
7
11
  */
8
12
  import { z } from 'zod';
9
13
  /** Available license tiers */
10
14
  export type LicenseTier = 'free' | 'pro' | 'max' | 'enterprise';
15
+ /**
16
+ * License operating mode — determines how the system behaves when license
17
+ * checks encounter various failure conditions.
18
+ *
19
+ * - active: License is valid and current
20
+ * - grace: License has an issue but is within a grace period (still allowed)
21
+ * - read-only: Perpetual support lapsed past grace — reads allowed, writes blocked
22
+ * - expired: Grace period exhausted — degraded to free tier
23
+ * - invalid: Signature invalid or tampered — hard fail
24
+ * - missing: No license configured — free tier
25
+ */
26
+ export type LicenseMode = 'active' | 'grace' | 'read-only' | 'expired' | 'invalid' | 'missing';
27
+ /** Detailed result from license status check */
28
+ export interface LicenseCheckResult {
29
+ /** Whether the requested action is allowed */
30
+ allowed: boolean;
31
+ /** Current effective tier */
32
+ tier: LicenseTier;
33
+ /** Operating mode */
34
+ mode: LicenseMode;
35
+ /** Human-readable reason for the current mode */
36
+ reason?: string;
37
+ /** Milliseconds remaining in grace period (undefined if not in grace) */
38
+ graceRemainingMs?: number;
39
+ /** Whether writes should be blocked (read-only mode for lapsed perpetual) */
40
+ readOnly: boolean;
41
+ }
42
+ /** Grace period configuration (in days). Overridable via env for testing. */
43
+ export interface GracePeriodConfig {
44
+ /** Days after subscription expiry before degrading to free (default: 3) */
45
+ subscriptionDays: number;
46
+ /** Days after perpetual support lapse before read-only mode (default: 30) */
47
+ perpetualDays: number;
48
+ /** Days of cached-license grace when infra is unreachable (default: 7) */
49
+ infraDays: number;
50
+ }
51
+ /**
52
+ * Configure grace period durations. Useful for testing.
53
+ */
54
+ export declare function configureGracePeriods(overrides: Partial<GracePeriodConfig>): void;
11
55
  /** Decoded license payload schema */
12
56
  declare const licensePayloadSchema: z.ZodObject<{
13
57
  tier: z.ZodEnum<{
14
- max: "max";
15
58
  pro: "pro";
59
+ max: "max";
16
60
  enterprise: "enterprise";
17
61
  }>;
18
62
  customerId: z.ZodString;
@@ -26,7 +70,7 @@ declare const licensePayloadSchema: z.ZodObject<{
26
70
  export type LicensePayload = z.infer<typeof licensePayloadSchema>;
27
71
  /** License cache TTL configuration */
28
72
  export interface LicenseCacheConfig {
29
- /** Cache TTL in milliseconds (default: 24 hours) */
73
+ /** Cache TTL in milliseconds (default: 15 seconds) */
30
74
  ttlMs: number;
31
75
  }
32
76
  /**
@@ -37,8 +81,10 @@ export declare function configureLicenseCache(overrides: Partial<LicenseCacheCon
37
81
  /**
38
82
  * Computes a deterministic Key ID (kid) from a public key PEM string.
39
83
  * Returns the first 8 characters of the SHA-256 hex digest of the PEM.
84
+ *
85
+ * Async because it uses `crypto.subtle.digest` for full edge compatibility.
40
86
  */
41
- export declare function computeKeyId(publicKeyPem: string): string;
87
+ export declare function computeKeyId(publicKeyPem: string): Promise<string>;
42
88
  /**
43
89
  * Validates a license key JWT and returns the decoded payload.
44
90
  * Returns null if the key is invalid, expired, or missing.
@@ -63,8 +109,24 @@ export declare function getLicensePayload(): LicensePayload | null;
63
109
  /**
64
110
  * Checks whether the current license is at least the given tier.
65
111
  * Also validates that the license has not expired (checks JWT exp claim).
112
+ *
113
+ * Subscription grace: if the JWT has expired but is within the configured
114
+ * grace period (default 3 days), access is still allowed. Use
115
+ * `getLicenseStatus()` to check whether the license is in grace.
66
116
  */
67
117
  export declare function isLicensed(requiredTier: LicenseTier): boolean;
118
+ /**
119
+ * Returns the full license status including mode, grace state, and read-only flag.
120
+ *
121
+ * Use this for UI decisions (banners, warnings) and API response headers.
122
+ * For simple gate checks, `isLicensed()` is sufficient.
123
+ */
124
+ export declare function getLicenseStatus(requiredTier?: LicenseTier): LicenseCheckResult;
125
+ /**
126
+ * Returns the configured grace period durations.
127
+ * Useful for API response headers and customer-facing documentation.
128
+ */
129
+ export declare function getGraceConfig(): Readonly<GracePeriodConfig>;
68
130
  /**
69
131
  * Returns the maximum number of sites allowed by the current license.
70
132
  */
@@ -80,7 +142,8 @@ export declare function getMaxUsers(): number;
80
142
  export declare function getMaxAgentTasks(): number;
81
143
  /**
82
144
  * Generates a signed license key JWT.
83
- * This is a server-only function requires the private key.
145
+ * Server-only in practice (requires the private key) but edge-compatible —
146
+ * `jose.importPKCS8` and `SignJWT` both run on Web Crypto.
84
147
  *
85
148
  * @param payload - License payload (tier, customerId, limits, perpetual flag)
86
149
  * @param privateKey - RS256 private key (PEM format)
@@ -1 +1 @@
1
- {"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,8BAA8B;AAC9B,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,YAAY,CAAC;AAEhE,qCAAqC;AACrC,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;iBAqBxB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,sCAAsC;AACtC,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;CACf;AASD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI,CAElF;AAoCD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA0BhC;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,WAAW,CAAC,CAkC9D;AAaD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAG5C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,IAAI,CAGzD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,YAAY,EAAE,WAAW,GAAG,OAAO,CAqB7D;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAMpC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAMpC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAMzC;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,GAAG,KAAK,CAAC,EAC5C,UAAU,EAAE,MAAM,EAClB,gBAAgB,GAAE,MAAM,GAAG,IAAyB,EACpD,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC"}
1
+ {"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"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"}
package/dist/license.js CHANGED
@@ -1,15 +1,42 @@
1
1
  /**
2
2
  * License validation for RevealUI Pro/Enterprise tiers.
3
3
  *
4
+ * Edge-compatible: uses the Web Crypto API (`crypto.subtle`) and `jose`
5
+ * exclusively. Safe to import from any runtime (Node, Edge, browser,
6
+ * Workers). No `node:crypto` or filesystem dependencies.
7
+ *
4
8
  * @dependencies
5
- * - jose - JWT token verification (Web Crypto API)
9
+ * - jose - JWT signing/verification (Web Crypto API)
6
10
  * - zod - Schema validation for license payloads
7
11
  */
8
- import { createHash } from 'node:crypto';
9
12
  import { decodeProtectedHeader, importPKCS8, importSPKI, jwtVerify, SignJWT } from 'jose';
10
13
  import { z } from 'zod';
11
14
  import { decryptLicenseKey } from './license-encryption.js';
12
15
  import { logger } from './utils/logger.js';
16
+ /** JWT issuer and audience for license tokens — prevents cross-environment replay */
17
+ const LICENSE_ISSUER = process.env.REVEALUI_LICENSE_ISSUER ?? 'https://revealui.com';
18
+ const LICENSE_AUDIENCE = process.env.REVEALUI_LICENSE_AUDIENCE ?? 'revealui-license';
19
+ const DEFAULT_GRACE = {
20
+ subscriptionDays: parseEnvInt('LICENSE_GRACE_SUBSCRIPTION_DAYS', 3),
21
+ perpetualDays: parseEnvInt('LICENSE_GRACE_PERPETUAL_DAYS', 30),
22
+ infraDays: parseEnvInt('LICENSE_GRACE_INFRA_DAYS', 7),
23
+ };
24
+ function parseEnvInt(key, fallback) {
25
+ const val = process.env[key];
26
+ if (val) {
27
+ const parsed = Number.parseInt(val, 10);
28
+ if (Number.isFinite(parsed) && parsed >= 0)
29
+ return parsed;
30
+ }
31
+ return fallback;
32
+ }
33
+ let graceConfig = { ...DEFAULT_GRACE };
34
+ /**
35
+ * Configure grace period durations. Useful for testing.
36
+ */
37
+ export function configureGracePeriods(overrides) {
38
+ graceConfig = { ...DEFAULT_GRACE, ...overrides };
39
+ }
13
40
  /** Decoded license payload schema */
14
41
  const licensePayloadSchema = z.object({
15
42
  /** License tier */
@@ -25,16 +52,25 @@ const licensePayloadSchema = z.object({
25
52
  /**
26
53
  * True for one-time perpetual purchases.
27
54
  * When set, the exp claim is omitted from the JWT and isLicensed() skips
28
- * expiry checksthe license is valid as long as it hasn't been revoked.
55
+ * expiry checks - the license is valid as long as it hasn't been revoked.
29
56
  */
30
57
  perpetual: z.boolean().optional(),
31
58
  /** License issued-at timestamp */
32
59
  iat: z.number().optional(),
33
- /** License expiration timestampabsent for perpetual licenses */
60
+ /** License expiration timestamp - absent for perpetual licenses */
34
61
  exp: z.number().optional(),
35
62
  });
63
+ const DEFAULT_TTL_MS = 15_000; // 15 seconds - revoked licenses lose access quickly
36
64
  const DEFAULT_CACHE_CONFIG = {
37
- ttlMs: 60 * 1000, // 60 seconds — revoked licenses lose access within 1 min
65
+ ttlMs: (() => {
66
+ const envTtl = process.env.LICENSE_CACHE_TTL_MS;
67
+ if (envTtl) {
68
+ const parsed = Number.parseInt(envTtl, 10);
69
+ if (Number.isFinite(parsed) && parsed > 0)
70
+ return parsed;
71
+ }
72
+ return DEFAULT_TTL_MS;
73
+ })(),
38
74
  };
39
75
  let cacheConfig = { ...DEFAULT_CACHE_CONFIG };
40
76
  let cachedAt = 0;
@@ -49,6 +85,7 @@ let cachedState = {
49
85
  tier: 'free',
50
86
  payload: null,
51
87
  validatedAt: null,
88
+ keyPresentButInvalid: false,
52
89
  };
53
90
  /**
54
91
  * The public key used to verify license JWTs.
@@ -57,14 +94,14 @@ let cachedState = {
57
94
  */
58
95
  function getPublicKey() {
59
96
  const raw = process.env.REVEALUI_LICENSE_PUBLIC_KEY ?? null;
60
- // Docker/env files store PEM as single-line with literal \nrestore real newlines
97
+ // Docker/env files store PEM as single-line with literal \n - restore real newlines
61
98
  return raw ? raw.replace(/\\n/g, '\n') : null;
62
99
  }
63
100
  /**
64
101
  * Reads the license key from environment.
65
102
  * Supports encrypted keys (enc:iv:ciphertext:tag format) via REVEALUI_LICENSE_ENCRYPTION_KEY.
66
103
  */
67
- function getLicenseKey() {
104
+ async function getLicenseKey() {
68
105
  const raw = process.env.REVEALUI_LICENSE_KEY ?? null;
69
106
  if (!raw)
70
107
  return null;
@@ -73,9 +110,18 @@ function getLicenseKey() {
73
110
  /**
74
111
  * Computes a deterministic Key ID (kid) from a public key PEM string.
75
112
  * Returns the first 8 characters of the SHA-256 hex digest of the PEM.
113
+ *
114
+ * Async because it uses `crypto.subtle.digest` for full edge compatibility.
76
115
  */
77
- export function computeKeyId(publicKeyPem) {
78
- return createHash('sha256').update(publicKeyPem).digest('hex').slice(0, 8);
116
+ export async function computeKeyId(publicKeyPem) {
117
+ const encoded = new TextEncoder().encode(publicKeyPem);
118
+ const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', encoded));
119
+ let hex = '';
120
+ // Only the first 4 bytes (8 hex chars) — enough to identify rotated keys.
121
+ for (const b of digest.subarray(0, 4)) {
122
+ hex += b.toString(16).padStart(2, '0');
123
+ }
124
+ return hex;
79
125
  }
80
126
  /**
81
127
  * Validates a license key JWT and returns the decoded payload.
@@ -85,14 +131,19 @@ export async function validateLicenseKey(licenseKey, publicKey) {
85
131
  try {
86
132
  // Extract kid from JWT header for forward-compatible key rotation
87
133
  const header = decodeProtectedHeader(licenseKey);
88
- const expectedKid = computeKeyId(publicKey);
134
+ const expectedKid = await computeKeyId(publicKey);
89
135
  if (header.kid && header.kid !== expectedKid) {
90
136
  logger.warn(`JWT kid mismatch: token has "${header.kid}", current key is "${expectedKid}". ` +
91
137
  'Token may have been signed with a rotated key.');
92
138
  }
93
139
  const key = await importSPKI(publicKey, 'RS256');
140
+ // Accept tokens expired within the subscription grace window so the
141
+ // payload is available for grace-period calculations in isLicensed().
94
142
  const { payload } = await jwtVerify(licenseKey, key, {
95
- algorithms: ['RS256', 'ES256'],
143
+ algorithms: ['RS256'],
144
+ clockTolerance: graceConfig.subscriptionDays * 86_400,
145
+ issuer: LICENSE_ISSUER,
146
+ audience: LICENSE_AUDIENCE,
96
147
  });
97
148
  const result = licensePayloadSchema.safeParse(payload);
98
149
  if (!result.success) {
@@ -111,16 +162,27 @@ export async function validateLicenseKey(licenseKey, publicKey) {
111
162
  * @returns The resolved license tier
112
163
  */
113
164
  export async function initializeLicense() {
114
- const licenseKey = getLicenseKey();
165
+ const licenseKey = await getLicenseKey();
115
166
  const publicKey = getPublicKey();
116
167
  if (!(licenseKey && publicKey)) {
117
- cachedState = { tier: 'free', payload: null, validatedAt: Date.now() };
168
+ cachedState = {
169
+ tier: 'free',
170
+ payload: null,
171
+ validatedAt: Date.now(),
172
+ keyPresentButInvalid: false,
173
+ };
118
174
  cachedAt = Date.now();
119
175
  return 'free';
120
176
  }
121
177
  const payload = await validateLicenseKey(licenseKey, publicKey);
122
178
  if (!payload) {
123
- cachedState = { tier: 'free', payload: null, validatedAt: Date.now() };
179
+ // Key was present but failed validation (expired beyond grace, invalid signature, etc.)
180
+ cachedState = {
181
+ tier: 'free',
182
+ payload: null,
183
+ validatedAt: Date.now(),
184
+ keyPresentButInvalid: true,
185
+ };
124
186
  cachedAt = Date.now();
125
187
  return 'free';
126
188
  }
@@ -128,6 +190,7 @@ export async function initializeLicense() {
128
190
  tier: payload.tier,
129
191
  payload,
130
192
  validatedAt: Date.now(),
193
+ keyPresentButInvalid: false,
131
194
  };
132
195
  cachedAt = Date.now();
133
196
  // Clamp cache TTL to license expiry so revoked licenses don't survive the full TTL
@@ -145,7 +208,7 @@ export async function initializeLicense() {
145
208
  */
146
209
  function evictStaleCache() {
147
210
  if (cachedAt > 0 && Date.now() - cachedAt > cacheConfig.ttlMs) {
148
- cachedState = { tier: 'free', payload: null, validatedAt: null };
211
+ cachedState = { tier: 'free', payload: null, validatedAt: null, keyPresentButInvalid: false };
149
212
  cachedAt = 0;
150
213
  }
151
214
  }
@@ -167,6 +230,10 @@ export function getLicensePayload() {
167
230
  /**
168
231
  * Checks whether the current license is at least the given tier.
169
232
  * Also validates that the license has not expired (checks JWT exp claim).
233
+ *
234
+ * Subscription grace: if the JWT has expired but is within the configured
235
+ * grace period (default 3 days), access is still allowed. Use
236
+ * `getLicenseStatus()` to check whether the license is in grace.
170
237
  */
171
238
  export function isLicensed(requiredTier) {
172
239
  evictStaleCache();
@@ -179,15 +246,94 @@ export function isLicensed(requiredTier) {
179
246
  // Free tier is always available
180
247
  if (requiredTier === 'free')
181
248
  return true;
182
- // Perpetual licenses never expireskip the exp check entirely
249
+ // Perpetual licenses never expire - skip the exp check entirely
183
250
  if (!cachedState.payload?.perpetual && cachedState.payload?.exp) {
184
251
  const nowSeconds = Math.floor(Date.now() / 1000);
185
252
  if (cachedState.payload.exp < nowSeconds) {
253
+ // Expired — check subscription grace period
254
+ const graceEndSeconds = cachedState.payload.exp + graceConfig.subscriptionDays * 86_400;
255
+ if (nowSeconds < graceEndSeconds) {
256
+ // Within grace — still allowed, but callers should check getLicenseStatus()
257
+ return tierRank[cachedState.tier] >= tierRank[requiredTier];
258
+ }
186
259
  return false;
187
260
  }
188
261
  }
189
262
  return tierRank[cachedState.tier] >= tierRank[requiredTier];
190
263
  }
264
+ /**
265
+ * Returns the full license status including mode, grace state, and read-only flag.
266
+ *
267
+ * Use this for UI decisions (banners, warnings) and API response headers.
268
+ * For simple gate checks, `isLicensed()` is sufficient.
269
+ */
270
+ export function getLicenseStatus(requiredTier = 'pro') {
271
+ evictStaleCache();
272
+ const tierRank = {
273
+ free: 0,
274
+ pro: 1,
275
+ max: 2,
276
+ enterprise: 3,
277
+ };
278
+ // No license configured — or key was present but failed validation
279
+ if (!cachedState.payload) {
280
+ if (cachedState.keyPresentButInvalid) {
281
+ return {
282
+ allowed: requiredTier === 'free',
283
+ tier: 'free',
284
+ mode: 'expired',
285
+ reason: 'License key failed validation (expired beyond grace or invalid)',
286
+ readOnly: false,
287
+ };
288
+ }
289
+ return {
290
+ allowed: requiredTier === 'free',
291
+ tier: 'free',
292
+ mode: 'missing',
293
+ reason: 'No license configured',
294
+ readOnly: false,
295
+ };
296
+ }
297
+ const nowSeconds = Math.floor(Date.now() / 1000);
298
+ // Check subscription expiry + grace
299
+ if (!cachedState.payload.perpetual && cachedState.payload.exp) {
300
+ if (cachedState.payload.exp < nowSeconds) {
301
+ const graceEndSeconds = cachedState.payload.exp + graceConfig.subscriptionDays * 86_400;
302
+ if (nowSeconds < graceEndSeconds) {
303
+ const graceRemainingMs = (graceEndSeconds - nowSeconds) * 1000;
304
+ return {
305
+ allowed: tierRank[cachedState.tier] >= tierRank[requiredTier],
306
+ tier: cachedState.tier,
307
+ mode: 'grace',
308
+ reason: `Subscription expired, ${Math.ceil(graceRemainingMs / 86_400_000)}-day grace remaining`,
309
+ graceRemainingMs,
310
+ readOnly: false,
311
+ };
312
+ }
313
+ return {
314
+ allowed: requiredTier === 'free',
315
+ tier: 'free',
316
+ mode: 'expired',
317
+ reason: 'Subscription expired and grace period exhausted',
318
+ readOnly: false,
319
+ };
320
+ }
321
+ }
322
+ // Active license
323
+ return {
324
+ allowed: tierRank[cachedState.tier] >= tierRank[requiredTier],
325
+ tier: cachedState.tier,
326
+ mode: 'active',
327
+ readOnly: false,
328
+ };
329
+ }
330
+ /**
331
+ * Returns the configured grace period durations.
332
+ * Useful for API response headers and customer-facing documentation.
333
+ */
334
+ export function getGraceConfig() {
335
+ return graceConfig;
336
+ }
191
337
  /**
192
338
  * Returns the maximum number of sites allowed by the current license.
193
339
  */
@@ -230,7 +376,8 @@ export function getMaxAgentTasks() {
230
376
  }
231
377
  /**
232
378
  * Generates a signed license key JWT.
233
- * This is a server-only function requires the private key.
379
+ * Server-only in practice (requires the private key) but edge-compatible —
380
+ * `jose.importPKCS8` and `SignJWT` both run on Web Crypto.
234
381
  *
235
382
  * @param payload - License payload (tier, customerId, limits, perpetual flag)
236
383
  * @param privateKey - RS256 private key (PEM format)
@@ -242,12 +389,16 @@ export function getMaxAgentTasks() {
242
389
  */
243
390
  export async function generateLicenseKey(payload, privateKey, expiresInSeconds = 365 * 24 * 60 * 60, publicKey) {
244
391
  const key = await importPKCS8(privateKey, 'RS256');
245
- const kid = publicKey ? computeKeyId(publicKey) : undefined;
392
+ const kid = publicKey ? await computeKeyId(publicKey) : undefined;
246
393
  const header = { alg: 'RS256' };
247
394
  if (kid) {
248
395
  header.kid = kid;
249
396
  }
250
- const builder = new SignJWT({ ...payload }).setProtectedHeader(header).setIssuedAt();
397
+ const builder = new SignJWT({ ...payload })
398
+ .setProtectedHeader(header)
399
+ .setIssuedAt()
400
+ .setIssuer(LICENSE_ISSUER)
401
+ .setAudience(LICENSE_AUDIENCE);
251
402
  if (expiresInSeconds !== null) {
252
403
  builder.setExpirationTime(`${expiresInSeconds}s`);
253
404
  }
@@ -257,6 +408,6 @@ export async function generateLicenseKey(payload, privateKey, expiresInSeconds =
257
408
  * Reset license state. Primarily for testing.
258
409
  */
259
410
  export function resetLicenseState() {
260
- cachedState = { tier: 'free', payload: null, validatedAt: null };
411
+ cachedState = { tier: 'free', payload: null, validatedAt: null, keyPresentButInvalid: false };
261
412
  cachedAt = 0;
262
413
  }
@@ -28,7 +28,7 @@ class ZombieDetector {
28
28
  return;
29
29
  // Skip on serverless environments where ps is unavailable or meaningless
30
30
  if (process.env.VERCEL === '1' || process.env.AWS_LAMBDA_FUNCTION_NAME) {
31
- logger.debug('Zombie detection skippedserverless environment detected');
31
+ logger.debug('Zombie detection skipped - serverless environment detected');
32
32
  return;
33
33
  }
34
34
  logger.info('Starting zombie process detector', {
@@ -1,4 +1,3 @@
1
1
  export { getRevealUI } from './utilities.js';
2
2
  export type { WithRevealUIOptions } from './withRevealUI.js';
3
- export { withRevealUI } from './withRevealUI.js';
4
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nextjs/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nextjs/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,3 +1,8 @@
1
- // RevealUI Next.js integration
1
+ // RevealUI Next.js runtime integration.
2
+ //
3
+ // This barrel is intentionally runtime-only. `withRevealUI` is NOT re-exported
4
+ // here because it pulls in `node:fs` + `node:path` at module load, which Next's
5
+ // NFT tracer then attributes to every route that transitively imports this
6
+ // barrel (even via type-only paths). Config-time consumers should import it
7
+ // directly from `@revealui/core/nextjs/withRevealUI`.
2
8
  export { getRevealUI } from './utilities.js';
3
- export { withRevealUI } from './withRevealUI.js';
@@ -1,4 +1,31 @@
1
- import type { NextConfig } from 'next';
1
+ /**
2
+ * Subset of Next.js config shape used by withRevealUI.
3
+ * Defined locally to avoid requiring `next` as a dependency of @revealui/core.
4
+ * Consumers pass their full NextConfig through; we only access these fields.
5
+ */
6
+ interface NextConfig {
7
+ env?: Record<string, string | undefined>;
8
+ webpack?: (config: Record<string, unknown>, context: {
9
+ isServer: boolean;
10
+ dev: boolean;
11
+ dir: string;
12
+ [key: string]: unknown;
13
+ }) => Record<string, unknown>;
14
+ turbopack?: {
15
+ resolveAlias?: Record<string, string>;
16
+ };
17
+ headers?: () => Promise<Array<{
18
+ source: string;
19
+ headers: Array<{
20
+ key: string;
21
+ value: string;
22
+ }>;
23
+ }>>;
24
+ images?: {
25
+ remotePatterns?: Array<Record<string, unknown>>;
26
+ };
27
+ [key: string]: unknown;
28
+ }
2
29
  export interface WithRevealUIOptions {
3
30
  /** Path to the RevealUI config file (relative to Next.js project root) */
4
31
  configPath?: string;
@@ -17,4 +44,5 @@ export interface WithRevealUIOptions {
17
44
  * The alias works with both Webpack (Next.js < 15) and Turbopack (Next.js 16+).
18
45
  */
19
46
  export declare function withRevealUI(nextConfig?: NextConfig, options?: WithRevealUIOptions): NextConfig;
47
+ export {};
20
48
  //# sourceMappingURL=withRevealUI.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"withRevealUI.d.ts","sourceRoot":"","sources":["../../src/nextjs/withRevealUI.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAOvC,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uBAAuB;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,UAAU,GAAE,UAAe,EAC3B,OAAO,GAAE,mBAAwB,GAChC,UAAU,CAgMZ"}
1
+ {"version":3,"file":"withRevealUI.d.ts","sourceRoot":"","sources":["../../src/nextjs/withRevealUI.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,UAAU,UAAU;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,CACR,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,KAC9E,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IACtD,OAAO,CAAC,EAAE,MAAM,OAAO,CACrB,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,KAAK,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAC1E,CAAC;IACF,MAAM,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;KAAE,CAAC;IAC7D,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAOD,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uBAAuB;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,UAAU,GAAE,UAAe,EAC3B,OAAO,GAAE,mBAAwB,GAChC,UAAU,CAgMZ"}
@@ -115,7 +115,7 @@ export function createDatabaseHealthCheck(queryFn) {
115
115
  };
116
116
  }
117
117
  catch (error) {
118
- // Surface the root causeDrizzle wraps Neon errors with "Failed query: ..."
118
+ // Surface the root cause - Drizzle wraps Neon errors with "Failed query: ..."
119
119
  // but the actual HTTP/connection error is in .cause
120
120
  let message = error instanceof Error ? error.message : 'Database connection failed';
121
121
  if (error instanceof Error && error.cause instanceof Error) {
@@ -40,8 +40,4 @@ export declare function logUserAction(action: string, userId?: string, context?:
40
40
  * System event logger
41
41
  */
42
42
  export declare function logSystemEvent(event: string, context?: Record<string, unknown>): void;
43
- /**
44
- * Sanitize sensitive data from logs
45
- */
46
- export declare function sanitizeLogData(data: Record<string, unknown>): Record<string, unknown>;
47
43
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/observability/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,YAAY,EACV,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,QAAQ,GACT,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,YAAY,EACZ,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,QAAQ,GACT,MAAM,wBAAwB,CAAC;AAKhC;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,GAAG,OAAO,EAAE,SAAS,GAAG,OAAO,EACzE,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,IAG/D,SAAS,QAAQ,GAAG;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE;QACR,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;QACrC,OAAO,CAAC,EAAE,MAAM,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;KAC5C,CAAC;CACH,EACD,MAAM,MAAM,OAAO,CAAC,SAAS,CAAC,KAC7B,OAAO,CAAC,SAAS,CAAC,CA6CtB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,IAAI,CASN;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,IAAI,CAgBN;AAED;;GAEG;AACH,wBAAgB,QAAQ,CACtB,SAAS,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,EAC5C,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,IAAI,CAMN;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,IAAI,CAMN;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAKrF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA2BtF"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/observability/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,YAAY,EACV,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,QAAQ,GACT,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,YAAY,EACZ,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,QAAQ,GACT,MAAM,wBAAwB,CAAC;AAKhC;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,GAAG,OAAO,EAAE,SAAS,GAAG,OAAO,EACzE,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,IAG/D,SAAS,QAAQ,GAAG;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE;QACR,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;QACrC,OAAO,CAAC,EAAE,MAAM,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;KAC5C,CAAC;CACH,EACD,MAAM,MAAM,OAAO,CAAC,SAAS,CAAC,KAC7B,OAAO,CAAC,SAAS,CAAC,CA6CtB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,IAAI,CASN;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,IAAI,CAgBN;AAED;;GAEG;AACH,wBAAgB,QAAQ,CACtB,SAAS,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,EAC5C,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,IAAI,CAMN;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,IAAI,CAMN;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAKrF"}
@@ -109,32 +109,5 @@ export function logSystemEvent(event, context) {
109
109
  event,
110
110
  });
111
111
  }
112
- /**
113
- * Sanitize sensitive data from logs
114
- */
115
- export function sanitizeLogData(data) {
116
- const sensitiveKeys = [
117
- 'password',
118
- 'token',
119
- 'secret',
120
- 'apiKey',
121
- 'accessToken',
122
- 'refreshToken',
123
- 'creditCard',
124
- 'ssn',
125
- ];
126
- const sanitized = {};
127
- for (const [key, value] of Object.entries(data)) {
128
- const lowerKey = key.toLowerCase();
129
- if (sensitiveKeys.some((sensitive) => lowerKey.includes(sensitive.toLowerCase()))) {
130
- sanitized[key] = '[REDACTED]';
131
- }
132
- else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
133
- sanitized[key] = sanitizeLogData(value);
134
- }
135
- else {
136
- sanitized[key] = value;
137
- }
138
- }
139
- return sanitized;
140
- }
112
+ // Log redaction lives in @revealui/security — import `redactLogContext`
113
+ // (recursive walker) or `redactLogField` (single key/value) from there.
@@ -3,7 +3,7 @@ export interface NestedDocsPluginConfig {
3
3
  collections?: string[];
4
4
  parentFieldSlug?: string;
5
5
  breadcrumbsFieldSlug?: string;
6
- /** Drizzle DB client getterif not provided, breadcrumbs will be empty */
6
+ /** Drizzle DB client getter - if not provided, breadcrumbs will be empty */
7
7
  getDb?: () => unknown;
8
8
  /** Label field to use for breadcrumb labels (defaults to 'title') */
9
9
  labelField?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"nested-docs.d.ts","sourceRoot":"","sources":["../../src/plugins/nested-docs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,sBAAsB;IACrC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC;IACtB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAgED,wBAAgB,gBAAgB,CAAC,MAAM,GAAE,sBAA2B,GAAG,MAAM,CA2F5E"}
1
+ {"version":3,"file":"nested-docs.d.ts","sourceRoot":"","sources":["../../src/plugins/nested-docs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,sBAAsB;IACrC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC;IACtB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAgED,wBAAgB,gBAAgB,CAAC,MAAM,GAAE,sBAA2B,GAAG,MAAM,CA2F5E"}
@@ -16,7 +16,7 @@ async function buildBreadcrumbs(db, collectionSlug, parentId, parentFieldSlug, l
16
16
  }
17
17
  while (currentId && depth < maxDepth) {
18
18
  try {
19
- // Use parameterized query$1 is the only user-controlled value
19
+ // Use parameterized query - $1 is the only user-controlled value
20
20
  const result = await db.execute({
21
21
  sql: `SELECT id, "${labelField}", "${parentFieldSlug}" FROM "${collectionSlug}" WHERE id = $1 LIMIT 1`,
22
22
  params: [currentId],
@@ -6,7 +6,7 @@ import type { RelationshipMetadata } from '../types/query.js';
6
6
  * Analyzes a collection config and extracts all relationship fields with their metadata.
7
7
  * This is the foundation for depth-based relationship population.
8
8
  *
9
- * Based on RevealUI CMS analysis:
9
+ * Based on RevealUI admin analysis:
10
10
  * - Simple relationships (single, no hasMany): Direct Foreign Keys
11
11
  * - hasMany relationships: Junction Tables
12
12
  * - Polymorphic relationships (relationTo array): Junction Tables with multiple FK columns
@@ -58,7 +58,7 @@ export function getRelationshipFields(config, collectionSlug) {
58
58
  }
59
59
  /**
60
60
  * Creates relationship metadata for a single field.
61
- * Determines storage type based on field properties following RevealUI CMS patterns.
61
+ * Determines storage type based on field properties following RevealUI admin patterns.
62
62
  */
63
63
  function createRelationshipMetadata(field, fieldPath, parentTableName, isLocalized = false) {
64
64
  // Skip if not a relationship field
@@ -72,7 +72,7 @@ function createRelationshipMetadata(field, fieldPath, parentTableName, isLocaliz
72
72
  const relationTo = field.relationTo;
73
73
  const hasMany = field.hasMany ?? false;
74
74
  const isPolymorphic = Array.isArray(relationTo);
75
- // Determine storage type based on RevealUI CMS analysis
75
+ // Determine storage type based on RevealUI admin analysis
76
76
  let storageType;
77
77
  if (isPolymorphic) {
78
78
  storageType = 'polymorphic';