@digilogiclabs/platform-core 1.3.0 → 1.5.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/security-headers.ts"],"sourcesContent":["/**\n * Shared Security Headers Utility\n * Generates consistent security headers for all Next.js apps.\n * Zero heavy dependencies — pure utility function.\n */\n\n// ═══════════════════════════════════════════════════════════════\n// TYPES\n// ═══════════════════════════════════════════════════════════════\n\nexport interface SecurityHeadersConfig {\n /** Enable Content-Security-Policy (default: true) */\n csp?: boolean;\n /** Additional allowed script-src domains */\n cspScriptSrc?: string[];\n /** Additional allowed connect-src domains */\n cspConnectSrc?: string[];\n /** Additional allowed frame-src domains */\n cspFrameSrc?: string[];\n /** Additional allowed style-src domains */\n cspStyleSrc?: string[];\n /** Additional allowed img-src domains */\n cspImgSrc?: string[];\n /** X-Frame-Options value (default: 'DENY') */\n frameOptions?: \"DENY\" | \"SAMEORIGIN\";\n /** Enable HSTS in production (default: true) */\n hsts?: boolean;\n /** HSTS max-age in seconds (default: 31536000 = 1 year) */\n hstsMaxAge?: number;\n /** Whether this is a production build (default: auto-detect from NODE_ENV) */\n isProduction?: boolean;\n}\n\nexport interface NextHeaderEntry {\n source: string;\n headers: Array<{ key: string; value: string }>;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// PRESETS\n// ═══════════════════════════════════════════════════════════════\n\nexport const SecurityHeaderPresets = {\n /** Minimal: basic headers only, no CSP */\n minimal: {\n csp: false,\n hsts: false,\n } satisfies SecurityHeadersConfig,\n\n /** Standard: full CSP + HSTS for most apps */\n standard: {\n csp: true,\n hsts: true,\n frameOptions: \"DENY\",\n } satisfies SecurityHeadersConfig,\n\n /** Strict: deny all permissions, strict CSP, no frame embedding */\n strict: {\n csp: true,\n hsts: true,\n hstsMaxAge: 63072000, // 2 years\n frameOptions: \"DENY\",\n } satisfies SecurityHeadersConfig,\n} as const;\n\n// ═══════════════════════════════════════════════════════════════\n// MAIN FUNCTION\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Generate security headers array compatible with Next.js `headers()` config.\n *\n * Usage in next.config.mjs:\n * ```js\n * const { generateSecurityHeaders } = require('@digilogiclabs/platform-core');\n *\n * module.exports = {\n * async headers() {\n * return generateSecurityHeaders({\n * isProduction: process.env.NODE_ENV === 'production',\n * cspScriptSrc: ['https://js.stripe.com'],\n * cspConnectSrc: ['https://api.stripe.com'],\n * });\n * },\n * };\n * ```\n */\nexport function generateSecurityHeaders(\n config: SecurityHeadersConfig = {},\n): NextHeaderEntry[] {\n const isProduction =\n config.isProduction ?? process.env.NODE_ENV === \"production\";\n const frameOptions = config.frameOptions ?? \"DENY\";\n const enableCsp = config.csp ?? true;\n const enableHsts = config.hsts ?? true;\n const hstsMaxAge = config.hstsMaxAge ?? 31536000;\n\n // Base headers applied to all routes in all environments\n const baseHeaders: Array<{ key: string; value: string }> = [\n { key: \"X-Frame-Options\", value: frameOptions },\n { key: \"X-Content-Type-Options\", value: \"nosniff\" },\n // Modern browsers use CSP, not XSS-Protection. Value '0' disables the\n // legacy filter which can itself introduce vulnerabilities.\n { key: \"X-XSS-Protection\", value: \"0\" },\n {\n key: \"Referrer-Policy\",\n value: \"strict-origin-when-cross-origin\",\n },\n {\n key: \"Permissions-Policy\",\n value: \"camera=(), microphone=(), geolocation=()\",\n },\n ];\n\n const entries: NextHeaderEntry[] = [\n { source: \"/:path*\", headers: baseHeaders },\n ];\n\n // Production-only headers\n if (isProduction) {\n const prodHeaders: Array<{ key: string; value: string }> = [];\n\n // HSTS\n if (enableHsts) {\n prodHeaders.push({\n key: \"Strict-Transport-Security\",\n value: `max-age=${hstsMaxAge}; includeSubDomains`,\n });\n }\n\n // Content-Security-Policy\n if (enableCsp) {\n const csp = buildCsp(config);\n prodHeaders.push({ key: \"Content-Security-Policy\", value: csp });\n }\n\n if (prodHeaders.length > 0) {\n entries.push({ source: \"/:path*\", headers: prodHeaders });\n }\n }\n\n return entries;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// CSP BUILDER\n// ═══════════════════════════════════════════════════════════════\n\nfunction buildCsp(config: SecurityHeadersConfig): string {\n const scriptSrc = [\n \"'self'\",\n \"'unsafe-inline'\",\n \"'unsafe-eval'\",\n ...(config.cspScriptSrc ?? []),\n ];\n\n const styleSrc = [\n \"'self'\",\n \"'unsafe-inline'\",\n \"https://fonts.googleapis.com\",\n ...(config.cspStyleSrc ?? []),\n ];\n\n const imgSrc = [\n \"'self'\",\n \"data:\",\n \"https:\",\n \"blob:\",\n ...(config.cspImgSrc ?? []),\n ];\n\n const fontSrc = [\"'self'\", \"data:\", \"https://fonts.gstatic.com\"];\n\n const connectSrc = [\"'self'\", ...(config.cspConnectSrc ?? [])];\n\n const frameSrc = [...(config.cspFrameSrc ?? [])];\n\n const directives = [\n `default-src 'self'`,\n `script-src ${scriptSrc.join(\" \")}`,\n `style-src ${styleSrc.join(\" \")}`,\n `img-src ${imgSrc.join(\" \")}`,\n `font-src ${fontSrc.join(\" \")}`,\n `connect-src ${connectSrc.join(\" \")}`,\n ];\n\n if (frameSrc.length > 0) {\n directives.push(`frame-src ${frameSrc.join(\" \")}`);\n }\n\n directives.push(\n `object-src 'none'`,\n `base-uri 'self'`,\n `form-action 'self'`,\n `frame-ancestors 'none'`,\n );\n\n return directives.join(\"; \");\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0CO,IAAM,wBAAwB;AAAA;AAAA,EAEnC,SAAS;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA;AAAA,EAGA,UAAU;AAAA,IACR,KAAK;AAAA,IACL,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,IACN,YAAY;AAAA;AAAA,IACZ,cAAc;AAAA,EAChB;AACF;AAwBO,SAAS,wBACd,SAAgC,CAAC,GACd;AACnB,QAAM,eACJ,OAAO,gBAAgB,QAAQ,IAAI,aAAa;AAClD,QAAM,eAAe,OAAO,gBAAgB;AAC5C,QAAM,YAAY,OAAO,OAAO;AAChC,QAAM,aAAa,OAAO,QAAQ;AAClC,QAAM,aAAa,OAAO,cAAc;AAGxC,QAAM,cAAqD;AAAA,IACzD,EAAE,KAAK,mBAAmB,OAAO,aAAa;AAAA,IAC9C,EAAE,KAAK,0BAA0B,OAAO,UAAU;AAAA;AAAA;AAAA,IAGlD,EAAE,KAAK,oBAAoB,OAAO,IAAI;AAAA,IACtC;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,UAA6B;AAAA,IACjC,EAAE,QAAQ,WAAW,SAAS,YAAY;AAAA,EAC5C;AAGA,MAAI,cAAc;AAChB,UAAM,cAAqD,CAAC;AAG5D,QAAI,YAAY;AACd,kBAAY,KAAK;AAAA,QACf,KAAK;AAAA,QACL,OAAO,WAAW,UAAU;AAAA,MAC9B,CAAC;AAAA,IACH;AAGA,QAAI,WAAW;AACb,YAAM,MAAM,SAAS,MAAM;AAC3B,kBAAY,KAAK,EAAE,KAAK,2BAA2B,OAAO,IAAI,CAAC;AAAA,IACjE;AAEA,QAAI,YAAY,SAAS,GAAG;AAC1B,cAAQ,KAAK,EAAE,QAAQ,WAAW,SAAS,YAAY,CAAC;AAAA,IAC1D;AAAA,EACF;AAEA,SAAO;AACT;AAMA,SAAS,SAAS,QAAuC;AACvD,QAAM,YAAY;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,OAAO,gBAAgB,CAAC;AAAA,EAC9B;AAEA,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,OAAO,eAAe,CAAC;AAAA,EAC7B;AAEA,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,OAAO,aAAa,CAAC;AAAA,EAC3B;AAEA,QAAM,UAAU,CAAC,UAAU,SAAS,2BAA2B;AAE/D,QAAM,aAAa,CAAC,UAAU,GAAI,OAAO,iBAAiB,CAAC,CAAE;AAE7D,QAAM,WAAW,CAAC,GAAI,OAAO,eAAe,CAAC,CAAE;AAE/C,QAAM,aAAa;AAAA,IACjB;AAAA,IACA,cAAc,UAAU,KAAK,GAAG,CAAC;AAAA,IACjC,aAAa,SAAS,KAAK,GAAG,CAAC;AAAA,IAC/B,WAAW,OAAO,KAAK,GAAG,CAAC;AAAA,IAC3B,YAAY,QAAQ,KAAK,GAAG,CAAC;AAAA,IAC7B,eAAe,WAAW,KAAK,GAAG,CAAC;AAAA,EACrC;AAEA,MAAI,SAAS,SAAS,GAAG;AACvB,eAAW,KAAK,aAAa,SAAS,KAAK,GAAG,CAAC,EAAE;AAAA,EACnD;AAEA,aAAW;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,WAAW,KAAK,IAAI;AAC7B;","names":[]}
@@ -0,0 +1,111 @@
1
+ // src/security-headers.ts
2
+ var SecurityHeaderPresets = {
3
+ /** Minimal: basic headers only, no CSP */
4
+ minimal: {
5
+ csp: false,
6
+ hsts: false
7
+ },
8
+ /** Standard: full CSP + HSTS for most apps */
9
+ standard: {
10
+ csp: true,
11
+ hsts: true,
12
+ frameOptions: "DENY"
13
+ },
14
+ /** Strict: deny all permissions, strict CSP, no frame embedding */
15
+ strict: {
16
+ csp: true,
17
+ hsts: true,
18
+ hstsMaxAge: 63072e3,
19
+ // 2 years
20
+ frameOptions: "DENY"
21
+ }
22
+ };
23
+ function generateSecurityHeaders(config = {}) {
24
+ const isProduction = config.isProduction ?? process.env.NODE_ENV === "production";
25
+ const frameOptions = config.frameOptions ?? "DENY";
26
+ const enableCsp = config.csp ?? true;
27
+ const enableHsts = config.hsts ?? true;
28
+ const hstsMaxAge = config.hstsMaxAge ?? 31536e3;
29
+ const baseHeaders = [
30
+ { key: "X-Frame-Options", value: frameOptions },
31
+ { key: "X-Content-Type-Options", value: "nosniff" },
32
+ // Modern browsers use CSP, not XSS-Protection. Value '0' disables the
33
+ // legacy filter which can itself introduce vulnerabilities.
34
+ { key: "X-XSS-Protection", value: "0" },
35
+ {
36
+ key: "Referrer-Policy",
37
+ value: "strict-origin-when-cross-origin"
38
+ },
39
+ {
40
+ key: "Permissions-Policy",
41
+ value: "camera=(), microphone=(), geolocation=()"
42
+ }
43
+ ];
44
+ const entries = [
45
+ { source: "/:path*", headers: baseHeaders }
46
+ ];
47
+ if (isProduction) {
48
+ const prodHeaders = [];
49
+ if (enableHsts) {
50
+ prodHeaders.push({
51
+ key: "Strict-Transport-Security",
52
+ value: `max-age=${hstsMaxAge}; includeSubDomains`
53
+ });
54
+ }
55
+ if (enableCsp) {
56
+ const csp = buildCsp(config);
57
+ prodHeaders.push({ key: "Content-Security-Policy", value: csp });
58
+ }
59
+ if (prodHeaders.length > 0) {
60
+ entries.push({ source: "/:path*", headers: prodHeaders });
61
+ }
62
+ }
63
+ return entries;
64
+ }
65
+ function buildCsp(config) {
66
+ const scriptSrc = [
67
+ "'self'",
68
+ "'unsafe-inline'",
69
+ "'unsafe-eval'",
70
+ ...config.cspScriptSrc ?? []
71
+ ];
72
+ const styleSrc = [
73
+ "'self'",
74
+ "'unsafe-inline'",
75
+ "https://fonts.googleapis.com",
76
+ ...config.cspStyleSrc ?? []
77
+ ];
78
+ const imgSrc = [
79
+ "'self'",
80
+ "data:",
81
+ "https:",
82
+ "blob:",
83
+ ...config.cspImgSrc ?? []
84
+ ];
85
+ const fontSrc = ["'self'", "data:", "https://fonts.gstatic.com"];
86
+ const connectSrc = ["'self'", ...config.cspConnectSrc ?? []];
87
+ const frameSrc = [...config.cspFrameSrc ?? []];
88
+ const directives = [
89
+ `default-src 'self'`,
90
+ `script-src ${scriptSrc.join(" ")}`,
91
+ `style-src ${styleSrc.join(" ")}`,
92
+ `img-src ${imgSrc.join(" ")}`,
93
+ `font-src ${fontSrc.join(" ")}`,
94
+ `connect-src ${connectSrc.join(" ")}`
95
+ ];
96
+ if (frameSrc.length > 0) {
97
+ directives.push(`frame-src ${frameSrc.join(" ")}`);
98
+ }
99
+ directives.push(
100
+ `object-src 'none'`,
101
+ `base-uri 'self'`,
102
+ `form-action 'self'`,
103
+ `frame-ancestors 'none'`
104
+ );
105
+ return directives.join("; ");
106
+ }
107
+ export {
108
+ SecurityHeaderPresets,
109
+ generateSecurityHeaders
110
+ };
111
+ //# sourceMappingURL=security-headers.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/security-headers.ts"],"sourcesContent":["/**\n * Shared Security Headers Utility\n * Generates consistent security headers for all Next.js apps.\n * Zero heavy dependencies — pure utility function.\n */\n\n// ═══════════════════════════════════════════════════════════════\n// TYPES\n// ═══════════════════════════════════════════════════════════════\n\nexport interface SecurityHeadersConfig {\n /** Enable Content-Security-Policy (default: true) */\n csp?: boolean;\n /** Additional allowed script-src domains */\n cspScriptSrc?: string[];\n /** Additional allowed connect-src domains */\n cspConnectSrc?: string[];\n /** Additional allowed frame-src domains */\n cspFrameSrc?: string[];\n /** Additional allowed style-src domains */\n cspStyleSrc?: string[];\n /** Additional allowed img-src domains */\n cspImgSrc?: string[];\n /** X-Frame-Options value (default: 'DENY') */\n frameOptions?: \"DENY\" | \"SAMEORIGIN\";\n /** Enable HSTS in production (default: true) */\n hsts?: boolean;\n /** HSTS max-age in seconds (default: 31536000 = 1 year) */\n hstsMaxAge?: number;\n /** Whether this is a production build (default: auto-detect from NODE_ENV) */\n isProduction?: boolean;\n}\n\nexport interface NextHeaderEntry {\n source: string;\n headers: Array<{ key: string; value: string }>;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// PRESETS\n// ═══════════════════════════════════════════════════════════════\n\nexport const SecurityHeaderPresets = {\n /** Minimal: basic headers only, no CSP */\n minimal: {\n csp: false,\n hsts: false,\n } satisfies SecurityHeadersConfig,\n\n /** Standard: full CSP + HSTS for most apps */\n standard: {\n csp: true,\n hsts: true,\n frameOptions: \"DENY\",\n } satisfies SecurityHeadersConfig,\n\n /** Strict: deny all permissions, strict CSP, no frame embedding */\n strict: {\n csp: true,\n hsts: true,\n hstsMaxAge: 63072000, // 2 years\n frameOptions: \"DENY\",\n } satisfies SecurityHeadersConfig,\n} as const;\n\n// ═══════════════════════════════════════════════════════════════\n// MAIN FUNCTION\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Generate security headers array compatible with Next.js `headers()` config.\n *\n * Usage in next.config.mjs:\n * ```js\n * const { generateSecurityHeaders } = require('@digilogiclabs/platform-core');\n *\n * module.exports = {\n * async headers() {\n * return generateSecurityHeaders({\n * isProduction: process.env.NODE_ENV === 'production',\n * cspScriptSrc: ['https://js.stripe.com'],\n * cspConnectSrc: ['https://api.stripe.com'],\n * });\n * },\n * };\n * ```\n */\nexport function generateSecurityHeaders(\n config: SecurityHeadersConfig = {},\n): NextHeaderEntry[] {\n const isProduction =\n config.isProduction ?? process.env.NODE_ENV === \"production\";\n const frameOptions = config.frameOptions ?? \"DENY\";\n const enableCsp = config.csp ?? true;\n const enableHsts = config.hsts ?? true;\n const hstsMaxAge = config.hstsMaxAge ?? 31536000;\n\n // Base headers applied to all routes in all environments\n const baseHeaders: Array<{ key: string; value: string }> = [\n { key: \"X-Frame-Options\", value: frameOptions },\n { key: \"X-Content-Type-Options\", value: \"nosniff\" },\n // Modern browsers use CSP, not XSS-Protection. Value '0' disables the\n // legacy filter which can itself introduce vulnerabilities.\n { key: \"X-XSS-Protection\", value: \"0\" },\n {\n key: \"Referrer-Policy\",\n value: \"strict-origin-when-cross-origin\",\n },\n {\n key: \"Permissions-Policy\",\n value: \"camera=(), microphone=(), geolocation=()\",\n },\n ];\n\n const entries: NextHeaderEntry[] = [\n { source: \"/:path*\", headers: baseHeaders },\n ];\n\n // Production-only headers\n if (isProduction) {\n const prodHeaders: Array<{ key: string; value: string }> = [];\n\n // HSTS\n if (enableHsts) {\n prodHeaders.push({\n key: \"Strict-Transport-Security\",\n value: `max-age=${hstsMaxAge}; includeSubDomains`,\n });\n }\n\n // Content-Security-Policy\n if (enableCsp) {\n const csp = buildCsp(config);\n prodHeaders.push({ key: \"Content-Security-Policy\", value: csp });\n }\n\n if (prodHeaders.length > 0) {\n entries.push({ source: \"/:path*\", headers: prodHeaders });\n }\n }\n\n return entries;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// CSP BUILDER\n// ═══════════════════════════════════════════════════════════════\n\nfunction buildCsp(config: SecurityHeadersConfig): string {\n const scriptSrc = [\n \"'self'\",\n \"'unsafe-inline'\",\n \"'unsafe-eval'\",\n ...(config.cspScriptSrc ?? []),\n ];\n\n const styleSrc = [\n \"'self'\",\n \"'unsafe-inline'\",\n \"https://fonts.googleapis.com\",\n ...(config.cspStyleSrc ?? []),\n ];\n\n const imgSrc = [\n \"'self'\",\n \"data:\",\n \"https:\",\n \"blob:\",\n ...(config.cspImgSrc ?? []),\n ];\n\n const fontSrc = [\"'self'\", \"data:\", \"https://fonts.gstatic.com\"];\n\n const connectSrc = [\"'self'\", ...(config.cspConnectSrc ?? [])];\n\n const frameSrc = [...(config.cspFrameSrc ?? [])];\n\n const directives = [\n `default-src 'self'`,\n `script-src ${scriptSrc.join(\" \")}`,\n `style-src ${styleSrc.join(\" \")}`,\n `img-src ${imgSrc.join(\" \")}`,\n `font-src ${fontSrc.join(\" \")}`,\n `connect-src ${connectSrc.join(\" \")}`,\n ];\n\n if (frameSrc.length > 0) {\n directives.push(`frame-src ${frameSrc.join(\" \")}`);\n }\n\n directives.push(\n `object-src 'none'`,\n `base-uri 'self'`,\n `form-action 'self'`,\n `frame-ancestors 'none'`,\n );\n\n return directives.join(\"; \");\n}\n"],"mappings":";AA0CO,IAAM,wBAAwB;AAAA;AAAA,EAEnC,SAAS;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA;AAAA,EAGA,UAAU;AAAA,IACR,KAAK;AAAA,IACL,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,IACN,YAAY;AAAA;AAAA,IACZ,cAAc;AAAA,EAChB;AACF;AAwBO,SAAS,wBACd,SAAgC,CAAC,GACd;AACnB,QAAM,eACJ,OAAO,gBAAgB,QAAQ,IAAI,aAAa;AAClD,QAAM,eAAe,OAAO,gBAAgB;AAC5C,QAAM,YAAY,OAAO,OAAO;AAChC,QAAM,aAAa,OAAO,QAAQ;AAClC,QAAM,aAAa,OAAO,cAAc;AAGxC,QAAM,cAAqD;AAAA,IACzD,EAAE,KAAK,mBAAmB,OAAO,aAAa;AAAA,IAC9C,EAAE,KAAK,0BAA0B,OAAO,UAAU;AAAA;AAAA;AAAA,IAGlD,EAAE,KAAK,oBAAoB,OAAO,IAAI;AAAA,IACtC;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,UAA6B;AAAA,IACjC,EAAE,QAAQ,WAAW,SAAS,YAAY;AAAA,EAC5C;AAGA,MAAI,cAAc;AAChB,UAAM,cAAqD,CAAC;AAG5D,QAAI,YAAY;AACd,kBAAY,KAAK;AAAA,QACf,KAAK;AAAA,QACL,OAAO,WAAW,UAAU;AAAA,MAC9B,CAAC;AAAA,IACH;AAGA,QAAI,WAAW;AACb,YAAM,MAAM,SAAS,MAAM;AAC3B,kBAAY,KAAK,EAAE,KAAK,2BAA2B,OAAO,IAAI,CAAC;AAAA,IACjE;AAEA,QAAI,YAAY,SAAS,GAAG;AAC1B,cAAQ,KAAK,EAAE,QAAQ,WAAW,SAAS,YAAY,CAAC;AAAA,IAC1D;AAAA,EACF;AAEA,SAAO;AACT;AAMA,SAAS,SAAS,QAAuC;AACvD,QAAM,YAAY;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,OAAO,gBAAgB,CAAC;AAAA,EAC9B;AAEA,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,OAAO,eAAe,CAAC;AAAA,EAC7B;AAEA,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,OAAO,aAAa,CAAC;AAAA,EAC3B;AAEA,QAAM,UAAU,CAAC,UAAU,SAAS,2BAA2B;AAE/D,QAAM,aAAa,CAAC,UAAU,GAAI,OAAO,iBAAiB,CAAC,CAAE;AAE7D,QAAM,WAAW,CAAC,GAAI,OAAO,eAAe,CAAC,CAAE;AAE/C,QAAM,aAAa;AAAA,IACjB;AAAA,IACA,cAAc,UAAU,KAAK,GAAG,CAAC;AAAA,IACjC,aAAa,SAAS,KAAK,GAAG,CAAC;AAAA,IAC/B,WAAW,OAAO,KAAK,GAAG,CAAC;AAAA,IAC3B,YAAY,QAAQ,KAAK,GAAG,CAAC;AAAA,IAC7B,eAAe,WAAW,KAAK,GAAG,CAAC;AAAA,EACrC;AAEA,MAAI,SAAS,SAAS,GAAG;AACvB,eAAW,KAAK,aAAa,SAAS,KAAK,GAAG,CAAC,EAAE;AAAA,EACnD;AAEA,aAAW;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,WAAW,KAAK,IAAI;AAC7B;","names":[]}
@@ -1,5 +1,5 @@
1
- import { I as IPlatform, M as MemoryDatabase, a as MemoryCache, b as MemoryStorage, c as MemoryEmail, d as MemoryQueue, e as IDatabase } from './ConsoleEmail-hUDFsKoA.mjs';
2
- export { C as ConsoleEmail, h as ConsoleLogger, t as EmailMessage, E as EnvSecrets, l as ICache, n as IEmail, p as ILogger, q as IMetrics, k as IQueryBuilder, o as IQueue, r as ISecrets, m as IStorage, J as Job, s as JobOptions, i as MemoryMetrics, f as MemorySecrets, N as NoopLogger, j as NoopMetrics, Q as QueryResult, S as StorageFile, g as createPlatform } from './ConsoleEmail-hUDFsKoA.mjs';
1
+ import { I as IPlatform, M as MemoryDatabase, a as MemoryCache, b as MemoryStorage, c as MemoryEmail, d as MemoryQueue, e as IDatabase } from './ConsoleEmail-ubSVWgTa.mjs';
2
+ export { C as ConsoleEmail, h as ConsoleLogger, t as EmailMessage, E as EnvSecrets, l as ICache, n as IEmail, p as ILogger, q as IMetrics, k as IQueryBuilder, o as IQueue, r as ISecrets, m as IStorage, J as Job, s as JobOptions, i as MemoryMetrics, f as MemorySecrets, N as NoopLogger, j as NoopMetrics, Q as QueryResult, S as StorageFile, g as createPlatform } from './ConsoleEmail-ubSVWgTa.mjs';
3
3
  import 'zod';
4
4
 
5
5
  /**
package/dist/testing.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { I as IPlatform, M as MemoryDatabase, a as MemoryCache, b as MemoryStorage, c as MemoryEmail, d as MemoryQueue, e as IDatabase } from './ConsoleEmail-hUDFsKoA.js';
2
- export { C as ConsoleEmail, h as ConsoleLogger, t as EmailMessage, E as EnvSecrets, l as ICache, n as IEmail, p as ILogger, q as IMetrics, k as IQueryBuilder, o as IQueue, r as ISecrets, m as IStorage, J as Job, s as JobOptions, i as MemoryMetrics, f as MemorySecrets, N as NoopLogger, j as NoopMetrics, Q as QueryResult, S as StorageFile, g as createPlatform } from './ConsoleEmail-hUDFsKoA.js';
1
+ import { I as IPlatform, M as MemoryDatabase, a as MemoryCache, b as MemoryStorage, c as MemoryEmail, d as MemoryQueue, e as IDatabase } from './ConsoleEmail-ubSVWgTa.js';
2
+ export { C as ConsoleEmail, h as ConsoleLogger, t as EmailMessage, E as EnvSecrets, l as ICache, n as IEmail, p as ILogger, q as IMetrics, k as IQueryBuilder, o as IQueue, r as ISecrets, m as IStorage, J as Job, s as JobOptions, i as MemoryMetrics, f as MemorySecrets, N as NoopLogger, j as NoopMetrics, Q as QueryResult, S as StorageFile, g as createPlatform } from './ConsoleEmail-ubSVWgTa.js';
3
3
  import 'zod';
4
4
 
5
5
  /**
package/dist/testing.js CHANGED
@@ -363,10 +363,11 @@ var init_IAI = __esm({
363
363
  });
364
364
 
365
365
  // src/interfaces/IRAG.ts
366
- var ChunkingPresets, MemoryRAG;
366
+ var import_crypto6, ChunkingPresets, MemoryRAG;
367
367
  var init_IRAG = __esm({
368
368
  "src/interfaces/IRAG.ts"() {
369
369
  "use strict";
370
+ import_crypto6 = require("crypto");
370
371
  ChunkingPresets = {
371
372
  default: {
372
373
  strategy: "recursive",
@@ -492,7 +493,7 @@ var init_IRAG = __esm({
492
493
  }
493
494
  async ingestOne(collection, document, options) {
494
495
  const startTime = Date.now();
495
- const docId = `doc_${Date.now()}_${Math.random().toString(36).substring(7)}`;
496
+ const docId = `doc_${Date.now()}_${(0, import_crypto6.randomBytes)(4).toString("hex")}`;
496
497
  const now = /* @__PURE__ */ new Date();
497
498
  try {
498
499
  const col = await this.getCollection(collection);
@@ -1399,6 +1400,7 @@ var MemoryEmail = class {
1399
1400
  };
1400
1401
 
1401
1402
  // src/interfaces/IQueue.ts
1403
+ var import_crypto = require("crypto");
1402
1404
  function calculateBackoff(attempt, options) {
1403
1405
  if (options.type === "fixed") {
1404
1406
  return options.delay;
@@ -1409,19 +1411,20 @@ function calculateBackoff(attempt, options) {
1409
1411
  }
1410
1412
  function generateJobId() {
1411
1413
  const timestamp = Date.now().toString(36);
1412
- const random = Math.random().toString(36).substring(2, 10);
1414
+ const random = (0, import_crypto.randomBytes)(4).toString("hex");
1413
1415
  return `job_${timestamp}_${random}`;
1414
1416
  }
1415
1417
 
1416
1418
  // src/context/CorrelationContext.ts
1417
1419
  var import_async_hooks = require("async_hooks");
1420
+ var import_crypto2 = require("crypto");
1418
1421
  var CorrelationContextManager = class {
1419
1422
  storage = new import_async_hooks.AsyncLocalStorage();
1420
1423
  idGenerator;
1421
1424
  constructor() {
1422
1425
  this.idGenerator = () => {
1423
1426
  const timestamp = Date.now().toString(36);
1424
- const random = Math.random().toString(36).substring(2, 10);
1427
+ const random = (0, import_crypto2.randomBytes)(4).toString("hex");
1425
1428
  return `${timestamp}-${random}`;
1426
1429
  };
1427
1430
  }
@@ -2027,10 +2030,11 @@ var MemoryQueue = class {
2027
2030
  };
2028
2031
 
2029
2032
  // src/adapters/console/ConsoleEmail.ts
2033
+ var import_crypto3 = require("crypto");
2030
2034
  var ConsoleEmail = class {
2031
2035
  sentEmails = [];
2032
2036
  async send(message) {
2033
- const id = `console_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
2037
+ const id = `console_${Date.now()}_${(0, import_crypto3.randomBytes)(4).toString("hex")}`;
2034
2038
  console.log("\n" + "=".repeat(60));
2035
2039
  console.log("\u{1F4E7} EMAIL SENT (Console Adapter)");
2036
2040
  console.log("=".repeat(60));
@@ -2105,6 +2109,7 @@ var ConsoleEmail = class {
2105
2109
  };
2106
2110
 
2107
2111
  // src/interfaces/ISecrets.ts
2112
+ var import_crypto4 = require("crypto");
2108
2113
  var EnvSecrets = class {
2109
2114
  prefix;
2110
2115
  cache = /* @__PURE__ */ new Map();
@@ -2305,12 +2310,7 @@ var MemorySecrets = class {
2305
2310
  return true;
2306
2311
  }
2307
2312
  generateSecureValue(length = 32) {
2308
- const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*";
2309
- let result = "";
2310
- for (let i = 0; i < length; i++) {
2311
- result += chars[Math.floor(Math.random() * chars.length)];
2312
- }
2313
- return result;
2313
+ return (0, import_crypto4.randomBytes)(length).toString("base64url").slice(0, length);
2314
2314
  }
2315
2315
  /**
2316
2316
  * Clear all secrets (for testing)
@@ -2520,6 +2520,27 @@ var RAGConfigSchema = import_zod.z.object({
2520
2520
  message: "Pinecone requires apiKey and indexName; Weaviate requires host"
2521
2521
  }
2522
2522
  );
2523
+ var CryptoConfigSchema = import_zod.z.object({
2524
+ enabled: import_zod.z.boolean().default(false).describe("Enable field-level encryption"),
2525
+ masterKey: import_zod.z.string().optional().describe("256-bit master key as hex (64 chars). Required when enabled."),
2526
+ hmacKey: import_zod.z.string().optional().describe(
2527
+ "HMAC key for deterministic hashing (derived from master key if not provided)"
2528
+ )
2529
+ }).refine(
2530
+ (data) => {
2531
+ if (data.enabled) {
2532
+ return data.masterKey && data.masterKey.length >= 64;
2533
+ }
2534
+ return true;
2535
+ },
2536
+ {
2537
+ message: "Crypto requires a 256-bit master key (64 hex characters) when enabled"
2538
+ }
2539
+ );
2540
+ var SecurityConfigSchema = import_zod.z.object({
2541
+ enforceTls: import_zod.z.boolean().default(true).describe("Enforce TLS for production connections"),
2542
+ tlsWarnOnly: import_zod.z.boolean().default(false).describe("Warn instead of throwing when TLS is missing in production")
2543
+ });
2523
2544
  var RetryConfigSchema = import_zod.z.object({
2524
2545
  enabled: import_zod.z.boolean().default(true).describe("Enable retry for failed operations"),
2525
2546
  maxAttempts: import_zod.z.number().int().min(1).max(10).default(3).describe("Maximum retry attempts"),
@@ -2609,6 +2630,10 @@ var PlatformConfigSchema = import_zod.z.object({
2609
2630
  // AI configurations
2610
2631
  ai: AIConfigSchema.default({ enabled: false }),
2611
2632
  rag: RAGConfigSchema.default({ enabled: false }),
2633
+ // Crypto configuration
2634
+ crypto: CryptoConfigSchema.default({ enabled: false }),
2635
+ // Security configuration
2636
+ security: SecurityConfigSchema.default({}),
2612
2637
  // Resilience configuration
2613
2638
  resilience: ResilienceConfigSchema.default({}),
2614
2639
  // Observability configuration
@@ -2686,6 +2711,15 @@ function loadConfig() {
2686
2711
  embeddingApiKey: process.env.EMBEDDING_API_KEY || process.env.OPENAI_API_KEY,
2687
2712
  embeddingModel: process.env.EMBEDDING_MODEL
2688
2713
  },
2714
+ crypto: {
2715
+ enabled: process.env.CRYPTO_ENABLED === "true",
2716
+ masterKey: process.env.CRYPTO_MASTER_KEY,
2717
+ hmacKey: process.env.CRYPTO_HMAC_KEY
2718
+ },
2719
+ security: {
2720
+ enforceTls: process.env.SECURITY_ENFORCE_TLS !== "false",
2721
+ tlsWarnOnly: process.env.SECURITY_TLS_WARN_ONLY === "true"
2722
+ },
2689
2723
  resilience: {
2690
2724
  retry: {
2691
2725
  enabled: process.env.RESILIENCE_RETRY_ENABLED !== "false",
@@ -2736,6 +2770,7 @@ function loadConfig() {
2736
2770
  }
2737
2771
 
2738
2772
  // src/interfaces/ITracing.ts
2773
+ var import_crypto5 = require("crypto");
2739
2774
  var MemorySpan = class {
2740
2775
  name;
2741
2776
  context;
@@ -2755,7 +2790,7 @@ var MemorySpan = class {
2755
2790
  };
2756
2791
  }
2757
2792
  generateSpanId() {
2758
- return Math.random().toString(16).substring(2, 18).padStart(16, "0");
2793
+ return (0, import_crypto5.randomBytes)(8).toString("hex");
2759
2794
  }
2760
2795
  setAttribute(key, value) {
2761
2796
  this._attributes[key] = value;
@@ -2815,7 +2850,7 @@ var MemoryTracing = class {
2815
2850
  this.traceId = this.generateTraceId();
2816
2851
  }
2817
2852
  generateTraceId() {
2818
- return Math.random().toString(16).substring(2, 34).padStart(32, "0");
2853
+ return (0, import_crypto5.randomBytes)(16).toString("hex");
2819
2854
  }
2820
2855
  startSpan(name, options) {
2821
2856
  const span = new MemorySpan(
@@ -3255,6 +3290,147 @@ var NoopMetrics = class {
3255
3290
  // src/factory.ts
3256
3291
  init_IAI();
3257
3292
  init_IRAG();
3293
+
3294
+ // src/adapters/memory/MemoryCrypto.ts
3295
+ var import_crypto7 = require("crypto");
3296
+ var MemoryCrypto = class {
3297
+ keys = /* @__PURE__ */ new Map();
3298
+ activeKeyId;
3299
+ hmacKey;
3300
+ constructor(options) {
3301
+ const masterKeyBuf = options?.masterKey ? Buffer.from(options.masterKey, "hex") : (0, import_crypto7.randomBytes)(32);
3302
+ this.hmacKey = options?.hmacKey ? Buffer.from(options.hmacKey, "hex") : (0, import_crypto7.randomBytes)(32);
3303
+ const keyId = this.generateKeyId();
3304
+ this.keys.set(keyId, {
3305
+ id: keyId,
3306
+ key: masterKeyBuf,
3307
+ status: "active",
3308
+ createdAt: /* @__PURE__ */ new Date()
3309
+ });
3310
+ this.activeKeyId = keyId;
3311
+ }
3312
+ async encrypt(plaintext, options) {
3313
+ const keyId = options?.keyId || this.activeKeyId;
3314
+ const stored = this.keys.get(keyId);
3315
+ if (!stored) {
3316
+ throw new Error(`Key not found: ${keyId}`);
3317
+ }
3318
+ if (stored.status === "retired") {
3319
+ throw new Error(`Key is retired: ${keyId}`);
3320
+ }
3321
+ if (stored.status === "decrypt-only" && !options?.keyId) {
3322
+ throw new Error(`Key is decrypt-only: ${keyId}`);
3323
+ }
3324
+ const iv = (0, import_crypto7.randomBytes)(12);
3325
+ const cipher = (0, import_crypto7.createCipheriv)("aes-256-gcm", stored.key, iv);
3326
+ if (options?.aad) {
3327
+ cipher.setAAD(Buffer.from(options.aad, "utf8"));
3328
+ }
3329
+ const encrypted = Buffer.concat([
3330
+ cipher.update(plaintext, "utf8"),
3331
+ cipher.final()
3332
+ ]);
3333
+ const tag = cipher.getAuthTag();
3334
+ return {
3335
+ ciphertext: encrypted.toString("base64"),
3336
+ iv: iv.toString("base64"),
3337
+ tag: tag.toString("base64"),
3338
+ keyId,
3339
+ algorithm: "aes-256-gcm",
3340
+ version: 1
3341
+ };
3342
+ }
3343
+ async decrypt(field, options) {
3344
+ const stored = this.keys.get(field.keyId);
3345
+ if (!stored) {
3346
+ throw new Error(`Key not found: ${field.keyId}`);
3347
+ }
3348
+ if (stored.status === "retired") {
3349
+ throw new Error(`Key is retired and cannot decrypt: ${field.keyId}`);
3350
+ }
3351
+ const decipher = (0, import_crypto7.createDecipheriv)(
3352
+ "aes-256-gcm",
3353
+ stored.key,
3354
+ Buffer.from(field.iv, "base64")
3355
+ );
3356
+ decipher.setAuthTag(Buffer.from(field.tag, "base64"));
3357
+ if (options?.aad) {
3358
+ decipher.setAAD(Buffer.from(options.aad, "utf8"));
3359
+ }
3360
+ const decrypted = Buffer.concat([
3361
+ decipher.update(Buffer.from(field.ciphertext, "base64")),
3362
+ decipher.final()
3363
+ ]);
3364
+ return decrypted.toString("utf8");
3365
+ }
3366
+ async encryptDeterministic(plaintext, options) {
3367
+ const hash = await this.computeHash(plaintext);
3368
+ const encrypted = await this.encrypt(plaintext, options);
3369
+ return { hash, encrypted };
3370
+ }
3371
+ async computeHash(plaintext) {
3372
+ return (0, import_crypto7.createHmac)("sha256", this.hmacKey).update(plaintext, "utf8").digest("hex");
3373
+ }
3374
+ async encryptBatch(fields, options) {
3375
+ const result = {};
3376
+ for (const [key, value] of Object.entries(fields)) {
3377
+ result[key] = await this.encrypt(value, options);
3378
+ }
3379
+ return result;
3380
+ }
3381
+ async decryptBatch(fields, options) {
3382
+ const result = {};
3383
+ for (const [key, value] of Object.entries(fields)) {
3384
+ result[key] = await this.decrypt(value, options);
3385
+ }
3386
+ return result;
3387
+ }
3388
+ async rotateKey() {
3389
+ const previousKeyId = this.activeKeyId;
3390
+ const currentKey = this.keys.get(previousKeyId);
3391
+ if (currentKey) {
3392
+ currentKey.status = "decrypt-only";
3393
+ }
3394
+ const newKeyId = this.generateKeyId();
3395
+ this.keys.set(newKeyId, {
3396
+ id: newKeyId,
3397
+ key: (0, import_crypto7.randomBytes)(32),
3398
+ status: "active",
3399
+ createdAt: /* @__PURE__ */ new Date()
3400
+ });
3401
+ this.activeKeyId = newKeyId;
3402
+ return { newKeyId, previousKeyId };
3403
+ }
3404
+ async reEncrypt(field, options) {
3405
+ const plaintext = await this.decrypt(field);
3406
+ return this.encrypt(plaintext, options);
3407
+ }
3408
+ async listKeys() {
3409
+ return Array.from(this.keys.values()).map((k) => ({
3410
+ keyId: k.id,
3411
+ createdAt: k.createdAt,
3412
+ status: k.status
3413
+ }));
3414
+ }
3415
+ async getActiveKeyId() {
3416
+ return this.activeKeyId;
3417
+ }
3418
+ async healthCheck() {
3419
+ try {
3420
+ const testPlain = "health-check-test";
3421
+ const encrypted = await this.encrypt(testPlain);
3422
+ const decrypted = await this.decrypt(encrypted);
3423
+ return decrypted === testPlain;
3424
+ } catch {
3425
+ return false;
3426
+ }
3427
+ }
3428
+ generateKeyId() {
3429
+ return `key_${(0, import_crypto7.randomBytes)(8).toString("hex")}`;
3430
+ }
3431
+ };
3432
+
3433
+ // src/factory.ts
3258
3434
  function createLogger(config) {
3259
3435
  if (!config.observability.logging) {
3260
3436
  return new NoopLogger();
@@ -3289,6 +3465,7 @@ function createPlatform(config) {
3289
3465
  const tracing = finalConfig.observability.tracing.provider === "memory" ? new MemoryTracing() : new NoopTracing();
3290
3466
  const ai = finalConfig.ai.enabled ? new MemoryAI() : null;
3291
3467
  const rag = finalConfig.rag.enabled ? new MemoryRAG() : null;
3468
+ const crypto2 = finalConfig.crypto.enabled ? new MemoryCrypto() : null;
3292
3469
  return createPlatformFromAdapters(
3293
3470
  db,
3294
3471
  cache,
@@ -3299,10 +3476,11 @@ function createPlatform(config) {
3299
3476
  metrics,
3300
3477
  tracing,
3301
3478
  ai,
3302
- rag
3479
+ rag,
3480
+ crypto2
3303
3481
  );
3304
3482
  }
3305
- function createPlatformFromAdapters(db, cache, storage, email, queue, logger, metrics, tracing, ai, rag) {
3483
+ function createPlatformFromAdapters(db, cache, storage, email, queue, logger, metrics, tracing, ai, rag, crypto2) {
3306
3484
  const platform = {
3307
3485
  db,
3308
3486
  cache,
@@ -3356,6 +3534,9 @@ function createPlatformFromAdapters(db, cache, storage, email, queue, logger, me
3356
3534
  if (rag) {
3357
3535
  platform.rag = rag;
3358
3536
  }
3537
+ if (crypto2) {
3538
+ platform.crypto = crypto2;
3539
+ }
3359
3540
  return platform;
3360
3541
  }
3361
3542
  function deepMerge(target, source) {