monora-ai 2.0.0 → 2.1.3

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 (202) hide show
  1. package/README.md +441 -150
  2. package/dist/aims_governance.d.ts +238 -0
  3. package/dist/aims_governance.d.ts.map +1 -0
  4. package/dist/aims_governance.js +922 -0
  5. package/dist/alerts.d.ts +16 -0
  6. package/dist/alerts.d.ts.map +1 -1
  7. package/dist/alerts.js +16 -0
  8. package/dist/api.d.ts +6 -0
  9. package/dist/api.d.ts.map +1 -1
  10. package/dist/api.js +6 -0
  11. package/dist/assessment.d.ts +269 -0
  12. package/dist/assessment.d.ts.map +1 -0
  13. package/dist/assessment.js +1232 -0
  14. package/dist/attestation.js +23 -1
  15. package/dist/attribution.d.ts +349 -0
  16. package/dist/attribution.d.ts.map +1 -0
  17. package/dist/attribution.js +987 -0
  18. package/dist/autodetect.d.ts +69 -1
  19. package/dist/autodetect.d.ts.map +1 -1
  20. package/dist/autodetect.js +644 -1
  21. package/dist/bias.d.ts +130 -0
  22. package/dist/bias.d.ts.map +1 -0
  23. package/dist/bias.js +223 -0
  24. package/dist/circuit_breaker.js +3 -3
  25. package/dist/cli/diagnostics.d.ts +5 -1
  26. package/dist/cli/diagnostics.d.ts.map +1 -1
  27. package/dist/cli/diagnostics.js +31 -8
  28. package/dist/cli/doctor.d.ts +25 -0
  29. package/dist/cli/doctor.d.ts.map +1 -0
  30. package/dist/cli/doctor.js +381 -0
  31. package/dist/cli/fix.d.ts +16 -0
  32. package/dist/cli/fix.d.ts.map +1 -0
  33. package/dist/cli/fix.js +284 -0
  34. package/dist/cli/init.d.ts +57 -0
  35. package/dist/cli/init.d.ts.map +1 -0
  36. package/dist/cli/init.js +205 -0
  37. package/dist/cli.js +1611 -126
  38. package/dist/complianceTargets.d.ts +111 -0
  39. package/dist/complianceTargets.d.ts.map +1 -0
  40. package/dist/complianceTargets.js +521 -0
  41. package/dist/config.d.ts +301 -17
  42. package/dist/config.d.ts.map +1 -1
  43. package/dist/config.js +428 -36
  44. package/dist/config_migrations.d.ts +41 -0
  45. package/dist/config_migrations.d.ts.map +1 -1
  46. package/dist/config_migrations.js +205 -0
  47. package/dist/config_schema.d.ts +2900 -731
  48. package/dist/config_schema.d.ts.map +1 -1
  49. package/dist/config_schema.js +257 -55
  50. package/dist/context.d.ts +34 -0
  51. package/dist/context.d.ts.map +1 -1
  52. package/dist/context.js +118 -7
  53. package/dist/control_backbone.d.ts +122 -0
  54. package/dist/control_backbone.d.ts.map +1 -0
  55. package/dist/control_backbone.js +698 -0
  56. package/dist/data-governance.d.ts +187 -0
  57. package/dist/data-governance.d.ts.map +1 -0
  58. package/dist/data-governance.js +424 -0
  59. package/dist/dataResidency.d.ts +44 -0
  60. package/dist/dataResidency.d.ts.map +1 -0
  61. package/dist/dataResidency.js +203 -0
  62. package/dist/dispatcher.d.ts +32 -0
  63. package/dist/dispatcher.d.ts.map +1 -1
  64. package/dist/dispatcher.js +91 -4
  65. package/dist/events.d.ts.map +1 -1
  66. package/dist/events.js +38 -0
  67. package/dist/evidence_store.d.ts +103 -0
  68. package/dist/evidence_store.d.ts.map +1 -0
  69. package/dist/evidence_store.js +459 -0
  70. package/dist/executiveSummary.d.ts +65 -8
  71. package/dist/executiveSummary.d.ts.map +1 -1
  72. package/dist/executiveSummary.js +289 -26
  73. package/dist/identity.d.ts +143 -0
  74. package/dist/identity.d.ts.map +1 -0
  75. package/dist/identity.js +231 -0
  76. package/dist/impact-assessment.d.ts +350 -0
  77. package/dist/impact-assessment.d.ts.map +1 -0
  78. package/dist/impact-assessment.js +580 -0
  79. package/dist/index.d.ts +25 -5
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +300 -4
  82. package/dist/instrumentation.d.ts +1 -1
  83. package/dist/instrumentation.d.ts.map +1 -1
  84. package/dist/instrumentation.js +243 -27
  85. package/dist/integrations/anthropic.d.ts +3 -0
  86. package/dist/integrations/anthropic.d.ts.map +1 -1
  87. package/dist/integrations/anthropic.js +284 -79
  88. package/dist/integrations/governance.d.ts +33 -0
  89. package/dist/integrations/governance.d.ts.map +1 -0
  90. package/dist/integrations/governance.js +208 -0
  91. package/dist/integrations/langchain.d.ts +7 -0
  92. package/dist/integrations/langchain.d.ts.map +1 -1
  93. package/dist/integrations/langchain.js +387 -143
  94. package/dist/integrations/openai.d.ts +9 -0
  95. package/dist/integrations/openai.d.ts.map +1 -1
  96. package/dist/integrations/openai.js +673 -73
  97. package/dist/iso42001_consolidation.d.ts +16 -0
  98. package/dist/iso42001_consolidation.d.ts.map +1 -0
  99. package/dist/iso42001_consolidation.js +413 -0
  100. package/dist/iso42001_workflows.d.ts +263 -0
  101. package/dist/iso42001_workflows.d.ts.map +1 -0
  102. package/dist/iso42001_workflows.js +781 -0
  103. package/dist/lifecycle.d.ts +299 -0
  104. package/dist/lifecycle.d.ts.map +1 -0
  105. package/dist/lifecycle.js +624 -0
  106. package/dist/lineage.d.ts +2 -2
  107. package/dist/lineage.d.ts.map +1 -1
  108. package/dist/lineage.js +12 -17
  109. package/dist/middleware/express.d.ts.map +1 -1
  110. package/dist/middleware/express.js +33 -3
  111. package/dist/middleware/nextjs.d.ts.map +1 -1
  112. package/dist/middleware/nextjs.js +42 -68
  113. package/dist/model.d.ts +143 -0
  114. package/dist/model.d.ts.map +1 -0
  115. package/dist/model.js +371 -0
  116. package/dist/onboarding.d.ts +42 -0
  117. package/dist/onboarding.d.ts.map +1 -0
  118. package/dist/onboarding.js +1022 -0
  119. package/dist/oversight.d.ts +264 -0
  120. package/dist/oversight.d.ts.map +1 -0
  121. package/dist/oversight.js +497 -0
  122. package/dist/pdf_report.d.ts.map +1 -1
  123. package/dist/pdf_report.js +42 -21
  124. package/dist/presets.d.ts +88 -0
  125. package/dist/presets.d.ts.map +1 -0
  126. package/dist/presets.js +520 -0
  127. package/dist/propagation.d.ts.map +1 -1
  128. package/dist/propagation.js +34 -2
  129. package/dist/quotas.d.ts +171 -0
  130. package/dist/quotas.d.ts.map +1 -0
  131. package/dist/quotas.js +259 -0
  132. package/dist/register.d.ts +13 -0
  133. package/dist/register.d.ts.map +1 -0
  134. package/dist/register.js +99 -0
  135. package/dist/registry.d.ts +1 -0
  136. package/dist/registry.d.ts.map +1 -1
  137. package/dist/registry.js +7 -0
  138. package/dist/registryData.json +43 -6
  139. package/dist/report.d.ts +2 -1
  140. package/dist/report.d.ts.map +1 -1
  141. package/dist/report.js +189 -2
  142. package/dist/reporting.d.ts +125 -0
  143. package/dist/reporting.d.ts.map +1 -1
  144. package/dist/reporting.js +196 -5
  145. package/dist/resources.d.ts +285 -0
  146. package/dist/resources.d.ts.map +1 -0
  147. package/dist/resources.js +643 -0
  148. package/dist/risk.d.ts +120 -0
  149. package/dist/risk.d.ts.map +1 -0
  150. package/dist/risk.js +220 -0
  151. package/dist/runtime.d.ts +74 -1
  152. package/dist/runtime.d.ts.map +1 -1
  153. package/dist/runtime.js +598 -22
  154. package/dist/schemaInference.d.ts +92 -0
  155. package/dist/schemaInference.d.ts.map +1 -0
  156. package/dist/schemaInference.js +466 -0
  157. package/dist/schema_validation.js +2 -2
  158. package/dist/schemas/config.schema.json +169 -6
  159. package/dist/schemas/event.schema.json +4 -0
  160. package/dist/security_report.js +4 -4
  161. package/dist/signing.d.ts +1 -1
  162. package/dist/signing.d.ts.map +1 -1
  163. package/dist/signing.js +4 -0
  164. package/dist/sinks/file.d.ts +19 -1
  165. package/dist/sinks/file.d.ts.map +1 -1
  166. package/dist/sinks/file.js +82 -13
  167. package/dist/sinks/https.d.ts +10 -0
  168. package/dist/sinks/https.d.ts.map +1 -1
  169. package/dist/sinks/https.js +76 -16
  170. package/dist/sinks/stdout.d.ts +1 -0
  171. package/dist/sinks/stdout.d.ts.map +1 -1
  172. package/dist/sinks/stdout.js +12 -1
  173. package/dist/spec.d.ts +159 -0
  174. package/dist/spec.d.ts.map +1 -0
  175. package/dist/spec.js +391 -0
  176. package/dist/stakeholders.d.ts +199 -0
  177. package/dist/stakeholders.d.ts.map +1 -0
  178. package/dist/stakeholders.js +398 -0
  179. package/dist/standards.d.ts.map +1 -1
  180. package/dist/standards.js +160 -2
  181. package/dist/standards_ingest.d.ts +2 -2
  182. package/dist/standards_ingest.d.ts.map +1 -1
  183. package/dist/standards_ingest.js +105 -23
  184. package/dist/streaming.d.ts.map +1 -1
  185. package/dist/streaming.js +7 -2
  186. package/dist/telemetry.d.ts +16 -2
  187. package/dist/telemetry.d.ts.map +1 -1
  188. package/dist/telemetry.js +79 -14
  189. package/dist/templates/controls/iso42001_control_catalog.json +1443 -0
  190. package/dist/traced_emitter.d.ts +3 -0
  191. package/dist/traced_emitter.d.ts.map +1 -1
  192. package/dist/traced_emitter.js +142 -25
  193. package/dist/trust_package.d.ts +21 -1
  194. package/dist/trust_package.d.ts.map +1 -1
  195. package/dist/trust_package.js +101 -4
  196. package/dist/verify.d.ts.map +1 -1
  197. package/dist/verify.js +9 -2
  198. package/dist/wal.d.ts.map +1 -1
  199. package/dist/wal.js +2 -1
  200. package/package.json +14 -1
  201. package/scripts/postinstall.js +119 -97
  202. package/templates/controls/iso42001_control_catalog.json +1443 -0
@@ -0,0 +1,987 @@
1
+ "use strict";
2
+ /**
3
+ * Attribution and project registration for Monora SDK.
4
+ *
5
+ * This module provides opt-in attribution and telemetry features to help
6
+ * improve the SDK and understand usage patterns.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { registerProject, reportUsage, enableTelemetry } from 'monora-ai';
11
+ *
12
+ * // Register your project (optional but encouraged)
13
+ * registerProject({
14
+ * company: 'Acme Corp',
15
+ * role: 'ML Engineer',
16
+ * email: 'dev@acme.com',
17
+ * source: 'npm',
18
+ * useCase: 'Customer Support AI',
19
+ * });
20
+ *
21
+ * // Enable and report usage (opt-in)
22
+ * enableTelemetry({ sendData: true });
23
+ * reportUsage({ framework: 'nextjs', environment: 'production' });
24
+ * ```
25
+ */
26
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ var desc = Object.getOwnPropertyDescriptor(m, k);
29
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
30
+ desc = { enumerable: true, get: function() { return m[k]; } };
31
+ }
32
+ Object.defineProperty(o, k2, desc);
33
+ }) : (function(o, m, k, k2) {
34
+ if (k2 === undefined) k2 = k;
35
+ o[k2] = m[k];
36
+ }));
37
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
38
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
39
+ }) : function(o, v) {
40
+ o["default"] = v;
41
+ });
42
+ var __importStar = (this && this.__importStar) || (function () {
43
+ var ownKeys = function(o) {
44
+ ownKeys = Object.getOwnPropertyNames || function (o) {
45
+ var ar = [];
46
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
47
+ return ar;
48
+ };
49
+ return ownKeys(o);
50
+ };
51
+ return function (mod) {
52
+ if (mod && mod.__esModule) return mod;
53
+ var result = {};
54
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
55
+ __setModuleDefault(result, mod);
56
+ return result;
57
+ };
58
+ })();
59
+ Object.defineProperty(exports, "__esModule", { value: true });
60
+ exports.getInstallId = getInstallId;
61
+ exports.registerProject = registerProject;
62
+ exports.getRegistration = getRegistration;
63
+ exports.enableTelemetry = enableTelemetry;
64
+ exports.disableTelemetry = disableTelemetry;
65
+ exports.isTelemetryEnabled = isTelemetryEnabled;
66
+ exports.recordFeatureUsage = recordFeatureUsage;
67
+ exports.getFeaturesUsed = getFeaturesUsed;
68
+ exports.buildUsageReport = buildUsageReport;
69
+ exports.reportUsage = reportUsage;
70
+ exports.onAttributionEvent = onAttributionEvent;
71
+ exports.setDataResidency = setDataResidency;
72
+ exports.getDataResidency = getDataResidency;
73
+ exports.configureHttp = configureHttp;
74
+ exports.sendTelemetryEvent = sendTelemetryEvent;
75
+ exports.setAuditMetadata = setAuditMetadata;
76
+ exports.getAuditMetadata = getAuditMetadata;
77
+ exports.hasShownFirstRunPrompt = hasShownFirstRunPrompt;
78
+ exports.hasRegistration = hasRegistration;
79
+ exports.promptForRegistration = promptForRegistration;
80
+ exports.showFirstRunPromptIfNeeded = showFirstRunPromptIfNeeded;
81
+ exports.resetFirstRunPrompt = resetFirstRunPrompt;
82
+ const crypto = __importStar(require("crypto"));
83
+ const fs = __importStar(require("fs"));
84
+ const http = __importStar(require("http"));
85
+ const https = __importStar(require("https"));
86
+ const os = __importStar(require("os"));
87
+ const path = __importStar(require("path"));
88
+ const readline = __importStar(require("readline"));
89
+ // HTTP configuration (can be overridden via config or environment)
90
+ // Note: Uses seconds (not milliseconds) for cross-SDK parity with Python
91
+ const HTTP_CONFIG = {
92
+ endpoints: {
93
+ us: process.env.MONORA_ATTRIBUTION_ENDPOINT_US || null,
94
+ eu: process.env.MONORA_ATTRIBUTION_ENDPOINT_EU || null,
95
+ },
96
+ apiKeyEnv: 'MONORA_ATTRIBUTION_KEY',
97
+ timeoutSec: 5,
98
+ retryAttempts: 2,
99
+ backoffBaseSec: 0.5,
100
+ };
101
+ const retryQueue = [];
102
+ const MAX_RETRY_QUEUE_SIZE = 100;
103
+ let retryTimer = null;
104
+ // Get version from package.json
105
+ let SDK_VERSION = '2.1.3';
106
+ try {
107
+ const pkgPath = path.join(__dirname, '..', 'package.json');
108
+ if (fs.existsSync(pkgPath)) {
109
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
110
+ SDK_VERSION = pkg.version || SDK_VERSION;
111
+ }
112
+ }
113
+ catch {
114
+ // Use default version
115
+ }
116
+ // Installation ID storage path
117
+ const INSTALL_ID_PATH = path.join(os.homedir(), '.monora', 'install_id');
118
+ const ATTRIBUTION_STATE_PATH = path.join(os.homedir(), '.monora', 'attribution.json');
119
+ // Registration from postinstall script (collected during npm install)
120
+ const POSTINSTALL_REGISTRATION_PATH = path.join(os.homedir(), '.monora', 'registration.json');
121
+ // First-run prompt state
122
+ const FIRST_RUN_PROMPT_PATH = path.join(os.homedir(), '.monora', 'first_run_prompt.json');
123
+ // Callback registry
124
+ const attributionCallbacks = [];
125
+ // Global state
126
+ let currentRegistration = null;
127
+ let telemetryEnabled = false;
128
+ let sendDataEnabled = false;
129
+ let dataResidency = null;
130
+ let registrationSentAt = null;
131
+ const featuresUsed = new Set();
132
+ let auditMetadata = null;
133
+ let stateLoaded = false;
134
+ function cleanPayload(payload) {
135
+ const cleaned = {};
136
+ for (const [key, value] of Object.entries(payload)) {
137
+ if (value === undefined || value === null) {
138
+ continue;
139
+ }
140
+ if (typeof value === 'string' && value.trim() === '') {
141
+ continue;
142
+ }
143
+ if (Array.isArray(value) && value.length === 0) {
144
+ continue;
145
+ }
146
+ if (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0) {
147
+ continue;
148
+ }
149
+ cleaned[key] = value;
150
+ }
151
+ return cleaned;
152
+ }
153
+ function normalizeRegistrationPayload(payload) {
154
+ return cleanPayload({
155
+ company: payload.company,
156
+ role: payload.role,
157
+ email: payload.email,
158
+ source: payload.source,
159
+ useCase: payload.useCase ?? payload.use_case,
160
+ teamSize: payload.teamSize ?? payload.team_size,
161
+ businessOwner: payload.businessOwner ?? payload.business_owner,
162
+ dataCategories: payload.dataCategories ?? payload.data_categories,
163
+ tags: payload.tags,
164
+ registeredAt: payload.registeredAt ?? payload.registered_at,
165
+ });
166
+ }
167
+ function normalizeAuditPayload(payload) {
168
+ return cleanPayload({
169
+ useCaseName: payload.useCaseName ?? payload.use_case_name,
170
+ businessOwner: payload.businessOwner ?? payload.business_owner,
171
+ dataCategories: payload.dataCategories ?? payload.data_categories,
172
+ riskLevel: payload.riskLevel ?? payload.risk_level,
173
+ complianceFrameworks: payload.complianceFrameworks ?? payload.compliance_frameworks,
174
+ reviewDate: payload.reviewDate ?? payload.review_date,
175
+ reviewer: payload.reviewer,
176
+ notes: payload.notes,
177
+ });
178
+ }
179
+ function ensureStateLoaded() {
180
+ if (stateLoaded) {
181
+ return;
182
+ }
183
+ stateLoaded = true;
184
+ try {
185
+ if (!fs.existsSync(ATTRIBUTION_STATE_PATH)) {
186
+ // Try loading from postinstall registration if attribution state doesn't exist
187
+ loadPostinstallRegistration();
188
+ return;
189
+ }
190
+ const raw = fs.readFileSync(ATTRIBUTION_STATE_PATH, 'utf8');
191
+ if (!raw.trim()) {
192
+ loadPostinstallRegistration();
193
+ return;
194
+ }
195
+ const data = JSON.parse(raw);
196
+ if (data.registration || data.project_registration) {
197
+ currentRegistration = normalizeRegistrationPayload(data.registration || data.project_registration);
198
+ }
199
+ if (data.audit_metadata || data.audit) {
200
+ auditMetadata = normalizeAuditPayload(data.audit_metadata || data.audit);
201
+ }
202
+ if (data.telemetry && typeof data.telemetry === 'object') {
203
+ if (typeof data.telemetry.enabled === 'boolean') {
204
+ telemetryEnabled = data.telemetry.enabled;
205
+ }
206
+ if (typeof data.telemetry.send_data === 'boolean') {
207
+ sendDataEnabled = data.telemetry.send_data;
208
+ }
209
+ if (data.telemetry.data_residency !== undefined) {
210
+ dataResidency = data.telemetry.data_residency;
211
+ }
212
+ }
213
+ if (typeof data.registration_sent_at === 'string') {
214
+ registrationSentAt = data.registration_sent_at;
215
+ }
216
+ // If no registration in attribution state, try postinstall
217
+ if (!currentRegistration) {
218
+ loadPostinstallRegistration();
219
+ }
220
+ }
221
+ catch {
222
+ // Ignore persistence errors
223
+ }
224
+ }
225
+ /**
226
+ * Load registration data from postinstall script (collected during npm install).
227
+ */
228
+ function loadPostinstallRegistration() {
229
+ try {
230
+ if (!fs.existsSync(POSTINSTALL_REGISTRATION_PATH)) {
231
+ return;
232
+ }
233
+ const raw = fs.readFileSync(POSTINSTALL_REGISTRATION_PATH, 'utf8');
234
+ if (!raw.trim()) {
235
+ return;
236
+ }
237
+ const data = JSON.parse(raw);
238
+ if (data && typeof data === 'object') {
239
+ currentRegistration = normalizeRegistrationPayload(data);
240
+ // Save to attribution state for future use
241
+ saveState();
242
+ }
243
+ }
244
+ catch {
245
+ // Ignore errors loading postinstall registration
246
+ }
247
+ }
248
+ function saveState() {
249
+ ensureStateLoaded();
250
+ try {
251
+ const dir = path.dirname(ATTRIBUTION_STATE_PATH);
252
+ if (!fs.existsSync(dir)) {
253
+ fs.mkdirSync(dir, { recursive: true });
254
+ }
255
+ const payload = {
256
+ telemetry: {
257
+ enabled: telemetryEnabled,
258
+ send_data: sendDataEnabled,
259
+ data_residency: dataResidency,
260
+ },
261
+ updated_at: new Date().toISOString(),
262
+ };
263
+ if (currentRegistration) {
264
+ payload.registration = cleanPayload({
265
+ company: currentRegistration.company,
266
+ role: currentRegistration.role,
267
+ email: currentRegistration.email,
268
+ source: currentRegistration.source,
269
+ use_case: currentRegistration.useCase,
270
+ team_size: currentRegistration.teamSize,
271
+ business_owner: currentRegistration.businessOwner,
272
+ data_categories: currentRegistration.dataCategories,
273
+ tags: currentRegistration.tags,
274
+ registered_at: currentRegistration.registeredAt,
275
+ });
276
+ }
277
+ if (auditMetadata) {
278
+ payload.audit_metadata = cleanPayload({
279
+ use_case_name: auditMetadata.useCaseName,
280
+ business_owner: auditMetadata.businessOwner,
281
+ data_categories: auditMetadata.dataCategories,
282
+ risk_level: auditMetadata.riskLevel,
283
+ compliance_frameworks: auditMetadata.complianceFrameworks,
284
+ review_date: auditMetadata.reviewDate,
285
+ reviewer: auditMetadata.reviewer,
286
+ notes: auditMetadata.notes,
287
+ });
288
+ }
289
+ if (registrationSentAt) {
290
+ payload.registration_sent_at = registrationSentAt;
291
+ }
292
+ fs.writeFileSync(ATTRIBUTION_STATE_PATH, JSON.stringify(payload, null, 2));
293
+ }
294
+ catch {
295
+ // Ignore persistence errors
296
+ }
297
+ }
298
+ /**
299
+ * Get or create a persistent anonymous installation ID.
300
+ *
301
+ * The ID is a hash of a random UUID, stored locally.
302
+ * No personally identifiable information is included.
303
+ *
304
+ * @returns Anonymous installation ID string.
305
+ */
306
+ function getInstallId() {
307
+ try {
308
+ const dir = path.dirname(INSTALL_ID_PATH);
309
+ if (!fs.existsSync(dir)) {
310
+ fs.mkdirSync(dir, { recursive: true });
311
+ }
312
+ if (fs.existsSync(INSTALL_ID_PATH)) {
313
+ const installId = fs.readFileSync(INSTALL_ID_PATH, 'utf8').trim();
314
+ if (installId) {
315
+ return installId;
316
+ }
317
+ }
318
+ // Generate new anonymous ID
319
+ const rawId = crypto.randomUUID();
320
+ const installId = crypto.createHash('sha256').update(rawId).digest('hex').slice(0, 32);
321
+ fs.writeFileSync(INSTALL_ID_PATH, installId);
322
+ return installId;
323
+ }
324
+ catch {
325
+ // Fallback to session-only ID if file access fails
326
+ return crypto.createHash('sha256').update(crypto.randomUUID()).digest('hex').slice(0, 32);
327
+ }
328
+ }
329
+ /**
330
+ * Register your project with Monora (optional but encouraged).
331
+ *
332
+ * This helps us understand who is using the SDK and improve the product.
333
+ * All fields are optional. Data is only sent if telemetry is enabled.
334
+ *
335
+ * @param options - Registration options.
336
+ * @returns ProjectRegistration object.
337
+ *
338
+ * @example
339
+ * ```typescript
340
+ * registerProject({
341
+ * company: 'Acme Corp',
342
+ * role: 'ML Engineer',
343
+ * useCase: 'Customer Support AI',
344
+ * dataCategories: ['customer_data'],
345
+ * });
346
+ * ```
347
+ */
348
+ function registerProject(options) {
349
+ ensureStateLoaded();
350
+ const registration = {
351
+ ...options,
352
+ registeredAt: new Date().toISOString(),
353
+ };
354
+ currentRegistration = registration;
355
+ registrationSentAt = null;
356
+ // Notify callbacks
357
+ for (const callback of attributionCallbacks.slice()) {
358
+ try {
359
+ callback({ type: 'project_registered', data: registration });
360
+ }
361
+ catch {
362
+ // Ignore callback errors
363
+ }
364
+ }
365
+ // Send if telemetry enabled
366
+ if (telemetryEnabled && sendDataEnabled) {
367
+ sendRegistration(registration);
368
+ registrationSentAt = new Date().toISOString();
369
+ }
370
+ saveState();
371
+ return registration;
372
+ }
373
+ /**
374
+ * Get the current project registration.
375
+ *
376
+ * @returns Current ProjectRegistration or null if not registered.
377
+ */
378
+ function getRegistration() {
379
+ ensureStateLoaded();
380
+ return currentRegistration;
381
+ }
382
+ /**
383
+ * Enable anonymous usage telemetry.
384
+ *
385
+ * By default, telemetry data stays local. Set sendData=true to
386
+ * share anonymous usage data with Monora to help improve the SDK.
387
+ *
388
+ * @param options - Telemetry options.
389
+ *
390
+ * @example
391
+ * ```typescript
392
+ * enableTelemetry({ sendData: true, dataResidency: 'eu' });
393
+ * ```
394
+ */
395
+ function enableTelemetry(options = {}) {
396
+ ensureStateLoaded();
397
+ telemetryEnabled = true;
398
+ sendDataEnabled = options.sendData ?? false;
399
+ dataResidency = options.dataResidency ?? null;
400
+ if (sendDataEnabled && currentRegistration && !registrationSentAt) {
401
+ sendRegistration(currentRegistration);
402
+ registrationSentAt = new Date().toISOString();
403
+ }
404
+ saveState();
405
+ }
406
+ /**
407
+ * Disable usage telemetry.
408
+ *
409
+ * No data will be collected or sent after this call.
410
+ */
411
+ function disableTelemetry() {
412
+ ensureStateLoaded();
413
+ telemetryEnabled = false;
414
+ sendDataEnabled = false;
415
+ saveState();
416
+ }
417
+ /**
418
+ * Check if telemetry is enabled.
419
+ *
420
+ * @returns True if telemetry is enabled.
421
+ */
422
+ function isTelemetryEnabled() {
423
+ ensureStateLoaded();
424
+ return telemetryEnabled;
425
+ }
426
+ /**
427
+ * Record that a feature was used (internal).
428
+ *
429
+ * This is called internally by SDK components to track which
430
+ * features are being used. No sensitive data is recorded.
431
+ *
432
+ * @param feature - Feature name (e.g., "signing", "wal", "circuit_breaker").
433
+ */
434
+ function recordFeatureUsage(feature) {
435
+ featuresUsed.add(feature);
436
+ }
437
+ /**
438
+ * Get list of features used in this session.
439
+ *
440
+ * @returns Sorted list of feature names.
441
+ */
442
+ function getFeaturesUsed() {
443
+ return Array.from(featuresUsed).sort();
444
+ }
445
+ /**
446
+ * Build an anonymous usage report.
447
+ *
448
+ * @param options - Report options.
449
+ * @returns UsageReport object with anonymous data.
450
+ */
451
+ function buildUsageReport(options = {}) {
452
+ ensureStateLoaded();
453
+ return {
454
+ installId: getInstallId(),
455
+ sdkVersion: SDK_VERSION,
456
+ nodeVersion: process.version,
457
+ osName: os.platform(),
458
+ osVersion: os.release(),
459
+ framework: options.framework,
460
+ featuresUsed: getFeaturesUsed(),
461
+ environment: options.environment ?? 'development',
462
+ reportedAt: new Date().toISOString(),
463
+ };
464
+ }
465
+ /**
466
+ * Report anonymous usage data (if telemetry enabled).
467
+ *
468
+ * This sends anonymous usage data to help improve the SDK.
469
+ * Only called if telemetry is enabled with sendData=true.
470
+ *
471
+ * @param options - Report options.
472
+ * @returns UsageReport object.
473
+ *
474
+ * @example
475
+ * ```typescript
476
+ * enableTelemetry({ sendData: true });
477
+ * reportUsage({ framework: 'nextjs', environment: 'production' });
478
+ * ```
479
+ */
480
+ function reportUsage(options = {}) {
481
+ ensureStateLoaded();
482
+ const report = buildUsageReport(options);
483
+ // Notify callbacks
484
+ for (const callback of attributionCallbacks.slice()) {
485
+ try {
486
+ callback({ type: 'usage_reported', data: report });
487
+ }
488
+ catch {
489
+ // Ignore callback errors
490
+ }
491
+ }
492
+ // Send if enabled
493
+ if (telemetryEnabled && sendDataEnabled) {
494
+ sendUsageReport(report);
495
+ }
496
+ return report;
497
+ }
498
+ /**
499
+ * Register a callback for attribution events.
500
+ *
501
+ * Use this to hook into attribution events for your own logging
502
+ * or monitoring stack.
503
+ *
504
+ * @param callback - Function called with event data.
505
+ * @returns Unsubscribe function.
506
+ *
507
+ * @example
508
+ * ```typescript
509
+ * const unsubscribe = onAttributionEvent((event) => {
510
+ * console.log(`Attribution event: ${event.type}`);
511
+ * });
512
+ * // Later: unsubscribe();
513
+ * ```
514
+ */
515
+ function onAttributionEvent(callback) {
516
+ attributionCallbacks.push(callback);
517
+ return () => {
518
+ const index = attributionCallbacks.indexOf(callback);
519
+ if (index !== -1) {
520
+ attributionCallbacks.splice(index, 1);
521
+ }
522
+ };
523
+ }
524
+ /**
525
+ * Set data residency region.
526
+ *
527
+ * @param region - Region code ("us", "eu", or "none" to disable sending).
528
+ */
529
+ function setDataResidency(region) {
530
+ ensureStateLoaded();
531
+ dataResidency = region;
532
+ saveState();
533
+ }
534
+ /**
535
+ * Get current data residency setting.
536
+ *
537
+ * @returns Region code or null.
538
+ */
539
+ function getDataResidency() {
540
+ ensureStateLoaded();
541
+ return dataResidency;
542
+ }
543
+ /**
544
+ * Configure HTTP settings from Monora config (internal).
545
+ *
546
+ * Called by runtime.init() to pass endpoint configuration.
547
+ */
548
+ function configureHttp(config) {
549
+ if (!config)
550
+ return;
551
+ if (config.endpoints) {
552
+ if (config.endpoints.us)
553
+ HTTP_CONFIG.endpoints.us = config.endpoints.us;
554
+ if (config.endpoints.eu)
555
+ HTTP_CONFIG.endpoints.eu = config.endpoints.eu;
556
+ }
557
+ if (config.apiKeyEnv)
558
+ HTTP_CONFIG.apiKeyEnv = config.apiKeyEnv;
559
+ if (config.timeoutSec)
560
+ HTTP_CONFIG.timeoutSec = config.timeoutSec;
561
+ if (config.retryAttempts)
562
+ HTTP_CONFIG.retryAttempts = config.retryAttempts;
563
+ if (config.backoffBaseSec)
564
+ HTTP_CONFIG.backoffBaseSec = config.backoffBaseSec;
565
+ }
566
+ function getEndpoint(endpointType) {
567
+ ensureStateLoaded();
568
+ const residency = dataResidency || 'us';
569
+ if (residency === 'none')
570
+ return null;
571
+ const baseUrl = HTTP_CONFIG.endpoints[residency] || HTTP_CONFIG.endpoints.us;
572
+ if (!baseUrl)
573
+ return null;
574
+ return `${baseUrl}/${endpointType}`;
575
+ }
576
+ function getApiKey() {
577
+ return process.env[HTTP_CONFIG.apiKeyEnv] || null;
578
+ }
579
+ async function makeRequest(url, data, timeout, retries) {
580
+ const apiKey = getApiKey();
581
+ if (!apiKey)
582
+ return false;
583
+ // Convert seconds to milliseconds for setTimeout
584
+ const effectiveTimeout = (timeout ?? HTTP_CONFIG.timeoutSec) * 1000;
585
+ const effectiveRetries = retries ?? HTTP_CONFIG.retryAttempts;
586
+ const payload = JSON.stringify({
587
+ ...data,
588
+ sdk_type: 'node',
589
+ sdk_version: SDK_VERSION,
590
+ });
591
+ const parsedUrl = new URL(url);
592
+ const isHttps = parsedUrl.protocol === 'https:';
593
+ const requestModule = isHttps ? https : http;
594
+ const options = {
595
+ hostname: parsedUrl.hostname,
596
+ port: parsedUrl.port || (isHttps ? 443 : 80),
597
+ path: parsedUrl.pathname + parsedUrl.search,
598
+ method: 'POST',
599
+ headers: {
600
+ 'Content-Type': 'application/json',
601
+ 'Content-Length': Buffer.byteLength(payload),
602
+ apikey: apiKey,
603
+ Authorization: `Bearer ${apiKey}`,
604
+ 'x-monora-sdk-type': 'node',
605
+ 'x-monora-sdk-version': SDK_VERSION,
606
+ 'x-data-residency': dataResidency || 'us',
607
+ },
608
+ timeout: effectiveTimeout,
609
+ };
610
+ for (let attempt = 0; attempt <= effectiveRetries; attempt++) {
611
+ try {
612
+ const success = await new Promise((resolve) => {
613
+ const req = requestModule.request(options, (res) => {
614
+ let body = '';
615
+ res.on('data', (chunk) => {
616
+ body += chunk.toString();
617
+ });
618
+ res.on('end', () => {
619
+ if (res.statusCode === 200) {
620
+ resolve(true);
621
+ }
622
+ else if (res.statusCode === 429) {
623
+ resolve(false);
624
+ }
625
+ else {
626
+ resolve(false);
627
+ }
628
+ });
629
+ });
630
+ req.on('error', () => resolve(false));
631
+ req.on('timeout', () => {
632
+ req.destroy();
633
+ resolve(false);
634
+ });
635
+ req.write(payload);
636
+ req.end();
637
+ });
638
+ if (success)
639
+ return true;
640
+ if (attempt < effectiveRetries) {
641
+ // Convert seconds to milliseconds for setTimeout
642
+ const delay = HTTP_CONFIG.backoffBaseSec * 1000 * Math.pow(2, attempt) + Math.random() * 100;
643
+ await new Promise((resolve) => setTimeout(resolve, delay));
644
+ }
645
+ }
646
+ catch {
647
+ if (attempt < effectiveRetries) {
648
+ // Convert seconds to milliseconds for setTimeout
649
+ const delay = HTTP_CONFIG.backoffBaseSec * 1000 * Math.pow(2, attempt) + Math.random() * 100;
650
+ await new Promise((resolve) => setTimeout(resolve, delay));
651
+ }
652
+ }
653
+ }
654
+ return false;
655
+ }
656
+ function sendAsync(endpointType, data) {
657
+ const url = getEndpoint(endpointType);
658
+ if (!url)
659
+ return;
660
+ makeRequest(url, data)
661
+ .then((success) => {
662
+ if (!success) {
663
+ queueForRetry(endpointType, data);
664
+ }
665
+ })
666
+ .catch(() => {
667
+ queueForRetry(endpointType, data);
668
+ });
669
+ }
670
+ function queueForRetry(endpointType, data) {
671
+ if (retryQueue.length < MAX_RETRY_QUEUE_SIZE) {
672
+ retryQueue.push({
673
+ endpointType,
674
+ data,
675
+ queuedAt: new Date().toISOString(),
676
+ });
677
+ }
678
+ // Start retry timer if not already running
679
+ if (!retryTimer) {
680
+ retryTimer = setTimeout(processRetryQueue, 30000);
681
+ if (retryTimer.unref) {
682
+ retryTimer.unref(); // Don't block process exit
683
+ }
684
+ }
685
+ }
686
+ async function processRetryQueue() {
687
+ retryTimer = null;
688
+ while (retryQueue.length > 0) {
689
+ const item = retryQueue[0];
690
+ const url = getEndpoint(item.endpointType);
691
+ if (!url) {
692
+ retryQueue.shift();
693
+ continue;
694
+ }
695
+ const success = await makeRequest(url, item.data);
696
+ if (success) {
697
+ retryQueue.shift();
698
+ }
699
+ else {
700
+ // Stop on first failure to preserve order
701
+ break;
702
+ }
703
+ }
704
+ // Restart timer if queue still has items
705
+ if (retryQueue.length > 0) {
706
+ retryTimer = setTimeout(processRetryQueue, 30000);
707
+ if (retryTimer.unref) {
708
+ retryTimer.unref();
709
+ }
710
+ }
711
+ }
712
+ /**
713
+ * Send registration to Monora API (internal).
714
+ * Includes optional email if provided.
715
+ */
716
+ function sendRegistration(registration) {
717
+ if (!sendDataEnabled)
718
+ return;
719
+ const data = {
720
+ install_id: getInstallId(),
721
+ company: registration.company,
722
+ role: registration.role,
723
+ email: registration.email,
724
+ source: registration.source,
725
+ use_case: registration.useCase,
726
+ team_size: registration.teamSize,
727
+ business_owner: registration.businessOwner,
728
+ data_categories: registration.dataCategories,
729
+ tags: registration.tags,
730
+ registered_at: registration.registeredAt || new Date().toISOString(),
731
+ };
732
+ sendAsync('registration', cleanPayload(data));
733
+ }
734
+ /**
735
+ * Send usage report to Monora API (internal).
736
+ */
737
+ function sendUsageReport(report) {
738
+ if (!sendDataEnabled)
739
+ return;
740
+ const data = {
741
+ install_id: report.installId,
742
+ runtime_version: report.nodeVersion,
743
+ os_name: report.osName,
744
+ os_version: report.osVersion,
745
+ framework: report.framework,
746
+ environment: report.environment,
747
+ features_used: report.featuresUsed,
748
+ reported_at: report.reportedAt || new Date().toISOString(),
749
+ };
750
+ sendAsync('usage-report', cleanPayload(data));
751
+ }
752
+ /**
753
+ * Send a telemetry event (e.g., startup, shutdown, error).
754
+ *
755
+ * This is called internally by the SDK to track usage patterns.
756
+ * Only sends if telemetry is enabled with sendData=true.
757
+ *
758
+ * @param options - Event options.
759
+ */
760
+ function sendTelemetryEvent(options) {
761
+ ensureStateLoaded();
762
+ if (!telemetryEnabled || !sendDataEnabled)
763
+ return;
764
+ const { eventType, eventData, framework, environment = 'development' } = options;
765
+ const data = {
766
+ install_id: getInstallId(),
767
+ runtime_version: process.version,
768
+ os_name: os.platform(),
769
+ os_version: os.release(),
770
+ framework,
771
+ environment,
772
+ features_used: getFeaturesUsed(),
773
+ event_type: eventType,
774
+ event_data: eventData || {},
775
+ timestamp: new Date().toISOString(),
776
+ };
777
+ sendAsync('telemetry', cleanPayload(data));
778
+ }
779
+ /**
780
+ * Set audit metadata for compliance purposes.
781
+ *
782
+ * This metadata is included in compliance reports and can be used
783
+ * to satisfy audit requirements for SOC 2, GDPR, ISO 27001, etc.
784
+ *
785
+ * @param metadata - Audit metadata.
786
+ * @returns AuditMetadata object.
787
+ *
788
+ * @example
789
+ * ```typescript
790
+ * setAuditMetadata({
791
+ * useCaseName: 'Customer Support AI',
792
+ * businessOwner: 'Jane Smith',
793
+ * dataCategories: ['customer_data', 'PII'],
794
+ * riskLevel: 'medium',
795
+ * complianceFrameworks: ['SOC2', 'GDPR'],
796
+ * });
797
+ * ```
798
+ */
799
+ function setAuditMetadata(metadata) {
800
+ ensureStateLoaded();
801
+ auditMetadata = { ...metadata };
802
+ saveState();
803
+ return auditMetadata;
804
+ }
805
+ /**
806
+ * Get current audit metadata.
807
+ *
808
+ * @returns AuditMetadata object or null.
809
+ */
810
+ function getAuditMetadata() {
811
+ ensureStateLoaded();
812
+ return auditMetadata;
813
+ }
814
+ /**
815
+ * Load the first-run prompt state from disk.
816
+ */
817
+ function loadFirstRunPromptState() {
818
+ try {
819
+ if (!fs.existsSync(FIRST_RUN_PROMPT_PATH)) {
820
+ return null;
821
+ }
822
+ const raw = fs.readFileSync(FIRST_RUN_PROMPT_PATH, 'utf8');
823
+ return JSON.parse(raw);
824
+ }
825
+ catch {
826
+ return null;
827
+ }
828
+ }
829
+ /**
830
+ * Save the first-run prompt state to disk.
831
+ */
832
+ function saveFirstRunPromptState(state) {
833
+ try {
834
+ const dir = path.dirname(FIRST_RUN_PROMPT_PATH);
835
+ if (!fs.existsSync(dir)) {
836
+ fs.mkdirSync(dir, { recursive: true });
837
+ }
838
+ fs.writeFileSync(FIRST_RUN_PROMPT_PATH, JSON.stringify(state, null, 2));
839
+ }
840
+ catch {
841
+ // Silent fail - don't break user's app
842
+ }
843
+ }
844
+ /**
845
+ * Check if first-run registration prompt has been shown.
846
+ *
847
+ * @returns True if already prompted, false otherwise.
848
+ */
849
+ function hasShownFirstRunPrompt() {
850
+ const state = loadFirstRunPromptState();
851
+ return state !== null && !!state.promptedAt;
852
+ }
853
+ /**
854
+ * Check if user has registered (either via postinstall or programmatically).
855
+ *
856
+ * @returns True if registered, false otherwise.
857
+ */
858
+ function hasRegistration() {
859
+ ensureStateLoaded();
860
+ return currentRegistration !== null;
861
+ }
862
+ /**
863
+ * Prompt the user to register their project (interactive, optional).
864
+ *
865
+ * @param options - Optional context about the environment.
866
+ * @returns True if registration was completed, false otherwise.
867
+ */
868
+ async function promptForRegistration(options) {
869
+ try {
870
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
871
+ return false;
872
+ }
873
+ if (!options?.force) {
874
+ if (options?.environment === 'production') {
875
+ return false;
876
+ }
877
+ if (process.env.CI ||
878
+ process.env.GITHUB_ACTIONS ||
879
+ process.env.GITLAB_CI ||
880
+ process.env.MONORA_SKIP_PROMPT) {
881
+ return false;
882
+ }
883
+ if (hasRegistration()) {
884
+ return false;
885
+ }
886
+ }
887
+ if (options?.silent) {
888
+ return false;
889
+ }
890
+ console.log('');
891
+ console.log('Monora SDK optional project registration');
892
+ console.log('This helps us improve the SDK and share best practices.');
893
+ console.log('Email is stored locally and only sent if telemetry is enabled.');
894
+ console.log('');
895
+ const rl = readline.createInterface({
896
+ input: process.stdin,
897
+ output: process.stdout,
898
+ });
899
+ const ask = (question) => new Promise((resolve) => rl.question(question, (answer) => resolve(answer)));
900
+ try {
901
+ const confirm = (await ask('Register this project now? [y/N]: ')).trim().toLowerCase();
902
+ if (confirm !== 'y' && confirm !== 'yes') {
903
+ return false;
904
+ }
905
+ const company = (await ask('Company (optional): ')).trim() || undefined;
906
+ const role = (await ask('Role (optional): ')).trim() || undefined;
907
+ const email = (await ask('Work email (optional): ')).trim() || undefined;
908
+ const source = (await ask('How did you find Monora? (optional): ')).trim() || undefined;
909
+ const useCase = (await ask('Primary use case (optional): ')).trim() || undefined;
910
+ registerProject({
911
+ company,
912
+ role,
913
+ email,
914
+ source,
915
+ useCase,
916
+ });
917
+ return true;
918
+ }
919
+ finally {
920
+ rl.close();
921
+ }
922
+ }
923
+ catch {
924
+ return false;
925
+ }
926
+ }
927
+ /**
928
+ * Show first-run registration prompt if needed.
929
+ *
930
+ * This logs a one-time message suggesting the user run `npx monora init`
931
+ * to register their project for best practices updates. The message is
932
+ * only shown once per installation.
933
+ *
934
+ * @param options - Optional context about the environment.
935
+ * @returns True if prompt was shown, false if skipped.
936
+ */
937
+ function showFirstRunPromptIfNeeded(options) {
938
+ // Skip in CI environments
939
+ if (process.env.CI ||
940
+ process.env.GITHUB_ACTIONS ||
941
+ process.env.GITLAB_CI ||
942
+ process.env.MONORA_SKIP_PROMPT) {
943
+ return false;
944
+ }
945
+ // Skip if already prompted
946
+ if (hasShownFirstRunPrompt()) {
947
+ return false;
948
+ }
949
+ // Skip if already registered
950
+ if (hasRegistration()) {
951
+ // Mark as prompted so we don't check again
952
+ saveFirstRunPromptState({
953
+ promptedAt: new Date().toISOString(),
954
+ sdkVersion: SDK_VERSION,
955
+ environment: options?.environment,
956
+ });
957
+ return false;
958
+ }
959
+ // Save state first to avoid showing multiple times in parallel inits
960
+ saveFirstRunPromptState({
961
+ promptedAt: new Date().toISOString(),
962
+ sdkVersion: SDK_VERSION,
963
+ environment: options?.environment,
964
+ });
965
+ // Show the prompt (unless silent mode)
966
+ if (!options?.silent) {
967
+ console.log('');
968
+ console.log('\x1b[36m[Monora]\x1b[0m Register your project for best practices updates:');
969
+ console.log(' Run: \x1b[33mnpx monora init\x1b[0m');
970
+ console.log(' Or skip this message by setting MONORA_SKIP_PROMPT=1');
971
+ console.log('');
972
+ }
973
+ return true;
974
+ }
975
+ /**
976
+ * Reset the first-run prompt state (for testing).
977
+ */
978
+ function resetFirstRunPrompt() {
979
+ try {
980
+ if (fs.existsSync(FIRST_RUN_PROMPT_PATH)) {
981
+ fs.unlinkSync(FIRST_RUN_PROMPT_PATH);
982
+ }
983
+ }
984
+ catch {
985
+ // Silent fail
986
+ }
987
+ }