@maykonpaulo/maestro-core 0.3.0-next.0 → 0.3.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +347 -2
- package/dist/index.js +506 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -70,6 +70,52 @@ var ConsoleLogger = class {
|
|
|
70
70
|
}
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
+
// src/logging/ConsoleStructuredLogger.ts
|
|
74
|
+
var ConsoleStructuredLogger = class {
|
|
75
|
+
constructor(minLevel = "info") {
|
|
76
|
+
this.minLevel = minLevel;
|
|
77
|
+
}
|
|
78
|
+
minLevel;
|
|
79
|
+
debug(entry) {
|
|
80
|
+
this.log("debug", entry);
|
|
81
|
+
}
|
|
82
|
+
info(entry) {
|
|
83
|
+
this.log("info", entry);
|
|
84
|
+
}
|
|
85
|
+
warn(entry) {
|
|
86
|
+
this.log("warn", entry);
|
|
87
|
+
}
|
|
88
|
+
error(entry) {
|
|
89
|
+
this.log("error", entry);
|
|
90
|
+
}
|
|
91
|
+
log(level, entry) {
|
|
92
|
+
if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[this.minLevel]) return;
|
|
93
|
+
const output = {
|
|
94
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
95
|
+
level
|
|
96
|
+
};
|
|
97
|
+
if (entry.correlationId !== void 0) output["correlationId"] = entry.correlationId;
|
|
98
|
+
if (entry.operation !== void 0) output["operation"] = entry.operation;
|
|
99
|
+
if (entry.entity !== void 0) output["entity"] = entry.entity;
|
|
100
|
+
if (entry.entityId !== void 0) output["entityId"] = entry.entityId;
|
|
101
|
+
if (entry.actor !== void 0) output["actor"] = entry.actor;
|
|
102
|
+
if (entry.durationMs !== void 0) output["durationMs"] = entry.durationMs;
|
|
103
|
+
if (entry.status !== void 0) output["status"] = entry.status;
|
|
104
|
+
if (entry.severity !== void 0) output["severity"] = entry.severity;
|
|
105
|
+
if (entry.message !== void 0) output["message"] = entry.message;
|
|
106
|
+
if (entry.metadata !== void 0) output["metadata"] = entry.metadata;
|
|
107
|
+
if (entry.error !== void 0) output["error"] = this.serializeError(entry.error);
|
|
108
|
+
const consoleFn = level === "error" ? console.error : level === "warn" ? console.warn : level === "debug" ? console.debug : console.info;
|
|
109
|
+
consoleFn(JSON.stringify(output));
|
|
110
|
+
}
|
|
111
|
+
serializeError(error) {
|
|
112
|
+
if (error instanceof Error) {
|
|
113
|
+
return { name: error.name, message: error.message };
|
|
114
|
+
}
|
|
115
|
+
return error;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
73
119
|
// src/audit/InMemoryAuditRepository.ts
|
|
74
120
|
var InMemoryAuditRepository = class {
|
|
75
121
|
store = /* @__PURE__ */ new Map();
|
|
@@ -85,6 +131,12 @@ var InMemoryAuditRepository = class {
|
|
|
85
131
|
if (filter.action !== void 0) {
|
|
86
132
|
events = events.filter((e) => e.action === filter.action);
|
|
87
133
|
}
|
|
134
|
+
if (filter.entity !== void 0) {
|
|
135
|
+
events = events.filter((e) => e.entity === filter.entity);
|
|
136
|
+
}
|
|
137
|
+
if (filter.entityId !== void 0) {
|
|
138
|
+
events = events.filter((e) => e.entityId === filter.entityId);
|
|
139
|
+
}
|
|
88
140
|
if (filter.resourceType !== void 0) {
|
|
89
141
|
events = events.filter((e) => e.resource?.type === filter.resourceType);
|
|
90
142
|
}
|
|
@@ -107,6 +159,9 @@ var InMemoryAuditRepository = class {
|
|
|
107
159
|
}
|
|
108
160
|
return events;
|
|
109
161
|
}
|
|
162
|
+
async query(filter) {
|
|
163
|
+
return this.list(filter);
|
|
164
|
+
}
|
|
110
165
|
};
|
|
111
166
|
|
|
112
167
|
// src/audit/AuditRecorder.ts
|
|
@@ -124,6 +179,8 @@ var AuditRecorder = class {
|
|
|
124
179
|
actor: input.actor,
|
|
125
180
|
level: input.level ?? "info",
|
|
126
181
|
resource: input.resource,
|
|
182
|
+
entity: input.entity,
|
|
183
|
+
entityId: input.entityId,
|
|
127
184
|
before: input.before,
|
|
128
185
|
after: input.after,
|
|
129
186
|
metadata: input.metadata,
|
|
@@ -2303,25 +2360,471 @@ var IntrospectionRuntime = class {
|
|
|
2303
2360
|
return engine.compare(previous.result, current);
|
|
2304
2361
|
}
|
|
2305
2362
|
};
|
|
2363
|
+
|
|
2364
|
+
// src/correlation/CorrelationId.ts
|
|
2365
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
2366
|
+
function generateCorrelationId() {
|
|
2367
|
+
return randomUUID4();
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
// src/risk/MetadataRiskClassifier.ts
|
|
2371
|
+
var MetadataRiskClassifier = class {
|
|
2372
|
+
constructor(operationRiskMap = {}, defaultRisk = "LOW") {
|
|
2373
|
+
this.operationRiskMap = operationRiskMap;
|
|
2374
|
+
this.defaultRisk = defaultRisk;
|
|
2375
|
+
}
|
|
2376
|
+
operationRiskMap;
|
|
2377
|
+
defaultRisk;
|
|
2378
|
+
classify(input) {
|
|
2379
|
+
const fromMetadata = input.metadata?.["riskLevel"];
|
|
2380
|
+
if (isOperationalRisk(fromMetadata)) return fromMetadata;
|
|
2381
|
+
return this.operationRiskMap[input.operation] ?? this.defaultRisk;
|
|
2382
|
+
}
|
|
2383
|
+
};
|
|
2384
|
+
var RISK_LEVELS = ["LOW", "MEDIUM", "HIGH", "CRITICAL"];
|
|
2385
|
+
function isOperationalRisk(value) {
|
|
2386
|
+
return typeof value === "string" && RISK_LEVELS.includes(value);
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
// src/authorization/ContextualAuthorizationEngine.ts
|
|
2390
|
+
var ContextualAuthorizationEngine = class {
|
|
2391
|
+
constructor(policy, criticalOperations = /* @__PURE__ */ new Set()) {
|
|
2392
|
+
this.criticalOperations = criticalOperations;
|
|
2393
|
+
this.rbac = new RbacEngine(policy);
|
|
2394
|
+
}
|
|
2395
|
+
criticalOperations;
|
|
2396
|
+
rbac;
|
|
2397
|
+
async evaluate(context) {
|
|
2398
|
+
if (!this.rbac.can(context.actor, context.operation)) {
|
|
2399
|
+
return { decision: "DENY", reason: `Actor lacks permission for operation: ${context.operation}` };
|
|
2400
|
+
}
|
|
2401
|
+
const risk = context.riskLevel ?? "LOW";
|
|
2402
|
+
if (risk === "CRITICAL" || this.criticalOperations.has(context.operation)) {
|
|
2403
|
+
return { decision: "REQUIRES_CONFIRMATION", reason: `Operation requires confirmation due to risk level: ${risk}` };
|
|
2404
|
+
}
|
|
2405
|
+
return { decision: "ALLOW" };
|
|
2406
|
+
}
|
|
2407
|
+
};
|
|
2408
|
+
|
|
2409
|
+
// src/policy/PolicyEngine.ts
|
|
2410
|
+
var DECISION_PRIORITY = {
|
|
2411
|
+
ALLOW: 0,
|
|
2412
|
+
REQUIRES_CONFIRMATION: 1,
|
|
2413
|
+
DENY: 2
|
|
2414
|
+
};
|
|
2415
|
+
var PolicyEngine = class {
|
|
2416
|
+
constructor(provider) {
|
|
2417
|
+
this.provider = provider;
|
|
2418
|
+
}
|
|
2419
|
+
provider;
|
|
2420
|
+
evaluate(context) {
|
|
2421
|
+
const violations = [];
|
|
2422
|
+
let decision = "ALLOW";
|
|
2423
|
+
let reason;
|
|
2424
|
+
for (const rule of this.provider.rules()) {
|
|
2425
|
+
const result = rule.evaluate(context);
|
|
2426
|
+
if (result === null) continue;
|
|
2427
|
+
if (DECISION_PRIORITY[result.decision] > DECISION_PRIORITY[decision]) {
|
|
2428
|
+
decision = result.decision;
|
|
2429
|
+
reason = result.reason;
|
|
2430
|
+
}
|
|
2431
|
+
if (result.decision === "DENY" || result.decision === "REQUIRES_CONFIRMATION") {
|
|
2432
|
+
violations.push({
|
|
2433
|
+
ruleId: rule.id,
|
|
2434
|
+
reason: result.reason,
|
|
2435
|
+
context,
|
|
2436
|
+
occurredAt: /* @__PURE__ */ new Date()
|
|
2437
|
+
});
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
return { decision, violations, reason };
|
|
2441
|
+
}
|
|
2442
|
+
};
|
|
2443
|
+
|
|
2444
|
+
// src/policy/InMemoryPolicyProvider.ts
|
|
2445
|
+
var InMemoryPolicyProvider = class {
|
|
2446
|
+
_rules;
|
|
2447
|
+
constructor(rules = []) {
|
|
2448
|
+
this._rules = rules;
|
|
2449
|
+
}
|
|
2450
|
+
rules() {
|
|
2451
|
+
return this._rules;
|
|
2452
|
+
}
|
|
2453
|
+
};
|
|
2454
|
+
|
|
2455
|
+
// src/confirmation/ConfirmationEngine.ts
|
|
2456
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
2457
|
+
var ConfirmationEngine = class {
|
|
2458
|
+
constructor(repository) {
|
|
2459
|
+
this.repository = repository;
|
|
2460
|
+
}
|
|
2461
|
+
repository;
|
|
2462
|
+
async request(input) {
|
|
2463
|
+
const requiredApprovals = input.requiredApprovals ?? 1;
|
|
2464
|
+
const request = {
|
|
2465
|
+
id: randomUUID5(),
|
|
2466
|
+
operation: input.operation,
|
|
2467
|
+
actor: input.actor,
|
|
2468
|
+
resource: input.resource,
|
|
2469
|
+
status: requiredApprovals > 1 ? "AWAITING_CONFIRMATION" : "REQUESTED",
|
|
2470
|
+
requiredApprovals,
|
|
2471
|
+
approvals: [],
|
|
2472
|
+
rejections: [],
|
|
2473
|
+
requestedAt: /* @__PURE__ */ new Date(),
|
|
2474
|
+
expiresAt: input.expiresAt,
|
|
2475
|
+
metadata: input.metadata,
|
|
2476
|
+
correlationId: input.correlationId
|
|
2477
|
+
};
|
|
2478
|
+
await this.repository.save(request);
|
|
2479
|
+
return request;
|
|
2480
|
+
}
|
|
2481
|
+
async approve(requestId, approver, comment) {
|
|
2482
|
+
const request = await this.findOrThrow(requestId);
|
|
2483
|
+
this.assertActive(request);
|
|
2484
|
+
const updated = {
|
|
2485
|
+
...request,
|
|
2486
|
+
approvals: [...request.approvals, { approver, approvedAt: /* @__PURE__ */ new Date(), comment }]
|
|
2487
|
+
};
|
|
2488
|
+
const fullyApproved = updated.approvals.length >= updated.requiredApprovals;
|
|
2489
|
+
updated.status = fullyApproved ? "APPROVED" : "AWAITING_CONFIRMATION";
|
|
2490
|
+
await this.repository.save(updated);
|
|
2491
|
+
return updated;
|
|
2492
|
+
}
|
|
2493
|
+
async reject(requestId, approver, comment) {
|
|
2494
|
+
const request = await this.findOrThrow(requestId);
|
|
2495
|
+
this.assertActive(request);
|
|
2496
|
+
const updated = {
|
|
2497
|
+
...request,
|
|
2498
|
+
status: "REJECTED",
|
|
2499
|
+
rejections: [...request.rejections, { approver, approvedAt: /* @__PURE__ */ new Date(), comment }]
|
|
2500
|
+
};
|
|
2501
|
+
await this.repository.save(updated);
|
|
2502
|
+
return updated;
|
|
2503
|
+
}
|
|
2504
|
+
async execute(requestId) {
|
|
2505
|
+
const request = await this.findOrThrow(requestId);
|
|
2506
|
+
if (request.status !== "APPROVED") {
|
|
2507
|
+
throw new MaestroError(
|
|
2508
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
2509
|
+
`Cannot execute confirmation request in status: ${request.status}`
|
|
2510
|
+
);
|
|
2511
|
+
}
|
|
2512
|
+
const updated = {
|
|
2513
|
+
...request,
|
|
2514
|
+
status: "EXECUTED",
|
|
2515
|
+
executedAt: /* @__PURE__ */ new Date()
|
|
2516
|
+
};
|
|
2517
|
+
await this.repository.save(updated);
|
|
2518
|
+
return updated;
|
|
2519
|
+
}
|
|
2520
|
+
async getPending() {
|
|
2521
|
+
return this.repository.listPending();
|
|
2522
|
+
}
|
|
2523
|
+
async findOrThrow(requestId) {
|
|
2524
|
+
const request = await this.repository.findById(requestId);
|
|
2525
|
+
if (!request) {
|
|
2526
|
+
throw new MaestroError(
|
|
2527
|
+
"NOT_FOUND" /* NOT_FOUND */,
|
|
2528
|
+
`Confirmation request not found: ${requestId}`
|
|
2529
|
+
);
|
|
2530
|
+
}
|
|
2531
|
+
return request;
|
|
2532
|
+
}
|
|
2533
|
+
assertActive(request) {
|
|
2534
|
+
const terminal = ["APPROVED", "REJECTED", "EXECUTED", "EXPIRED"];
|
|
2535
|
+
if (terminal.includes(request.status)) {
|
|
2536
|
+
throw new MaestroError(
|
|
2537
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
2538
|
+
`Confirmation request is already in terminal status: ${request.status}`
|
|
2539
|
+
);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
};
|
|
2543
|
+
|
|
2544
|
+
// src/confirmation/InMemoryConfirmationRepository.ts
|
|
2545
|
+
var InMemoryConfirmationRepository = class {
|
|
2546
|
+
store = /* @__PURE__ */ new Map();
|
|
2547
|
+
async save(request) {
|
|
2548
|
+
this.store.set(request.id, request);
|
|
2549
|
+
}
|
|
2550
|
+
async findById(id) {
|
|
2551
|
+
return this.store.get(id);
|
|
2552
|
+
}
|
|
2553
|
+
async listPending() {
|
|
2554
|
+
return Array.from(this.store.values()).filter(
|
|
2555
|
+
(r) => r.status === "REQUESTED" || r.status === "AWAITING_CONFIRMATION"
|
|
2556
|
+
);
|
|
2557
|
+
}
|
|
2558
|
+
};
|
|
2559
|
+
|
|
2560
|
+
// src/declarative/DeclarativeValidator.ts
|
|
2561
|
+
var SUPPORTED_FIELD_TYPES = [
|
|
2562
|
+
"string",
|
|
2563
|
+
"text",
|
|
2564
|
+
"number",
|
|
2565
|
+
"integer",
|
|
2566
|
+
"decimal",
|
|
2567
|
+
"currency",
|
|
2568
|
+
"boolean",
|
|
2569
|
+
"date",
|
|
2570
|
+
"datetime",
|
|
2571
|
+
"time",
|
|
2572
|
+
"email",
|
|
2573
|
+
"phone",
|
|
2574
|
+
"url",
|
|
2575
|
+
"document",
|
|
2576
|
+
"uuid",
|
|
2577
|
+
"enum",
|
|
2578
|
+
"json",
|
|
2579
|
+
"relation",
|
|
2580
|
+
"array"
|
|
2581
|
+
];
|
|
2582
|
+
var SUPPORTED_OPERATIONAL_RISKS = ["LOW", "MEDIUM", "HIGH", "CRITICAL"];
|
|
2583
|
+
var SUPPORTED_OPERATION_SCOPES = ["global", "entity", "record", "bulk"];
|
|
2584
|
+
function validateEntityDeclaration(input) {
|
|
2585
|
+
const errors = [];
|
|
2586
|
+
if (!input || typeof input !== "object") {
|
|
2587
|
+
return { valid: false, errors: [{ path: "", message: "Entity declaration must be an object." }] };
|
|
2588
|
+
}
|
|
2589
|
+
const decl = input;
|
|
2590
|
+
if (!decl["entity"] || typeof decl["entity"] !== "string" || decl["entity"].trim() === "") {
|
|
2591
|
+
errors.push({ path: "entity", message: "Entity name is required." });
|
|
2592
|
+
}
|
|
2593
|
+
const fields = decl["fields"];
|
|
2594
|
+
if (!fields || typeof fields !== "object" || Array.isArray(fields)) {
|
|
2595
|
+
errors.push({ path: "fields", message: "Entity must declare at least one field." });
|
|
2596
|
+
} else {
|
|
2597
|
+
const fieldMap = fields;
|
|
2598
|
+
if (Object.keys(fieldMap).length === 0) {
|
|
2599
|
+
errors.push({ path: "fields", message: "Entity must declare at least one field." });
|
|
2600
|
+
} else {
|
|
2601
|
+
for (const [name, rawField] of Object.entries(fieldMap)) {
|
|
2602
|
+
validateFieldDeclaration(name, rawField, errors);
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
const operations = decl["operations"];
|
|
2607
|
+
if (operations !== void 0) {
|
|
2608
|
+
if (typeof operations !== "object" || Array.isArray(operations)) {
|
|
2609
|
+
errors.push({ path: "operations", message: "Operations must be an object." });
|
|
2610
|
+
} else {
|
|
2611
|
+
const opMap = operations;
|
|
2612
|
+
for (const [id, rawOp] of Object.entries(opMap)) {
|
|
2613
|
+
validateOperationDeclaration(id, rawOp, errors);
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
return { valid: errors.length === 0, errors };
|
|
2618
|
+
}
|
|
2619
|
+
function validateFieldDeclaration(name, rawField, errors) {
|
|
2620
|
+
const prefix = `fields[${name}]`;
|
|
2621
|
+
if (!rawField || typeof rawField !== "object") {
|
|
2622
|
+
errors.push({ path: prefix, message: `Field '${name}' must be an object.` });
|
|
2623
|
+
return;
|
|
2624
|
+
}
|
|
2625
|
+
const field = rawField;
|
|
2626
|
+
if (!field.type) {
|
|
2627
|
+
errors.push({ path: `${prefix}.type`, message: "Field type is required." });
|
|
2628
|
+
} else if (!SUPPORTED_FIELD_TYPES.includes(field.type)) {
|
|
2629
|
+
errors.push({
|
|
2630
|
+
path: `${prefix}.type`,
|
|
2631
|
+
message: `Field type '${field.type}' is not supported. Supported types: ${SUPPORTED_FIELD_TYPES.join(", ")}.`
|
|
2632
|
+
});
|
|
2633
|
+
}
|
|
2634
|
+
if (field.sensitive === true && field.exportable === true) {
|
|
2635
|
+
errors.push({
|
|
2636
|
+
path: prefix,
|
|
2637
|
+
message: `Field '${name}' cannot be both sensitive and exportable.`
|
|
2638
|
+
});
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
function validateOperationDeclaration(id, rawOp, errors) {
|
|
2642
|
+
const prefix = `operations[${id}]`;
|
|
2643
|
+
if (!rawOp || typeof rawOp !== "object") {
|
|
2644
|
+
errors.push({ path: prefix, message: `Operation '${id}' must be an object.` });
|
|
2645
|
+
return;
|
|
2646
|
+
}
|
|
2647
|
+
const op = rawOp;
|
|
2648
|
+
if (!op.label || typeof op.label !== "string" || op.label.trim() === "") {
|
|
2649
|
+
errors.push({ path: `${prefix}.label`, message: "Operation label is required." });
|
|
2650
|
+
}
|
|
2651
|
+
if (op.risk !== void 0 && !SUPPORTED_OPERATIONAL_RISKS.includes(op.risk)) {
|
|
2652
|
+
errors.push({
|
|
2653
|
+
path: `${prefix}.risk`,
|
|
2654
|
+
message: `Operation risk '${op.risk}' is not valid. Valid values: ${SUPPORTED_OPERATIONAL_RISKS.join(", ")}.`
|
|
2655
|
+
});
|
|
2656
|
+
}
|
|
2657
|
+
if (op.scope !== void 0 && !SUPPORTED_OPERATION_SCOPES.includes(op.scope)) {
|
|
2658
|
+
errors.push({
|
|
2659
|
+
path: `${prefix}.scope`,
|
|
2660
|
+
message: `Operation scope '${op.scope}' is not valid. Valid values: ${SUPPORTED_OPERATION_SCOPES.join(", ")}.`
|
|
2661
|
+
});
|
|
2662
|
+
}
|
|
2663
|
+
if (op.confirmation !== void 0) {
|
|
2664
|
+
const conf = op.confirmation;
|
|
2665
|
+
if (conf.approvers !== void 0 && (typeof conf.approvers !== "number" || conf.approvers < 1)) {
|
|
2666
|
+
errors.push({
|
|
2667
|
+
path: `${prefix}.confirmation.approvers`,
|
|
2668
|
+
message: "Confirmation approvers must be at least 1."
|
|
2669
|
+
});
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
function validateConsumerDeclaration(input, entity) {
|
|
2674
|
+
const errors = [];
|
|
2675
|
+
if (!input || typeof input !== "object") {
|
|
2676
|
+
return { valid: false, errors: [{ path: "", message: "Consumer declaration must be an object." }] };
|
|
2677
|
+
}
|
|
2678
|
+
const decl = input;
|
|
2679
|
+
if (!decl["consumer"] || typeof decl["consumer"] !== "string" || decl["consumer"].trim() === "") {
|
|
2680
|
+
errors.push({ path: "consumer", message: "Consumer name is required." });
|
|
2681
|
+
}
|
|
2682
|
+
if (!decl["entity"] || typeof decl["entity"] !== "string" || decl["entity"].trim() === "") {
|
|
2683
|
+
errors.push({ path: "entity", message: "Entity name is required." });
|
|
2684
|
+
}
|
|
2685
|
+
if (entity) {
|
|
2686
|
+
const knownFields = new Set(Object.keys(entity.fields));
|
|
2687
|
+
const knownOps = new Set(Object.keys(entity.operations ?? {}));
|
|
2688
|
+
const entityName = entity.entity;
|
|
2689
|
+
validateFieldRefs("list.fields", decl["list"], knownFields, entityName, errors);
|
|
2690
|
+
validateFieldRefs("detail.fields", decl["detail"], knownFields, entityName, errors);
|
|
2691
|
+
const forms = decl["forms"];
|
|
2692
|
+
if (forms && typeof forms === "object") {
|
|
2693
|
+
validateFieldRefs("forms.create.fields", forms["create"], knownFields, entityName, errors);
|
|
2694
|
+
validateFieldRefs("forms.update.fields", forms["update"], knownFields, entityName, errors);
|
|
2695
|
+
validateFieldRefs("forms.clone.fields", forms["clone"], knownFields, entityName, errors);
|
|
2696
|
+
}
|
|
2697
|
+
const actions = decl["actions"];
|
|
2698
|
+
if (actions && typeof actions === "object") {
|
|
2699
|
+
validateOperationRefs("actions.row", actions["row"], knownOps, entityName, errors);
|
|
2700
|
+
validateOperationRefs("actions.bulk", actions["bulk"], knownOps, entityName, errors);
|
|
2701
|
+
validateOperationRefs("actions.global", actions["global"], knownOps, entityName, errors);
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
return { valid: errors.length === 0, errors };
|
|
2705
|
+
}
|
|
2706
|
+
function validateFieldRefs(path, container, knownFields, entityName, errors) {
|
|
2707
|
+
if (!container || typeof container !== "object") return;
|
|
2708
|
+
const obj = container;
|
|
2709
|
+
const fields = obj["fields"];
|
|
2710
|
+
if (!Array.isArray(fields)) return;
|
|
2711
|
+
for (let i = 0; i < fields.length; i++) {
|
|
2712
|
+
const name = fields[i];
|
|
2713
|
+
if (!knownFields.has(name)) {
|
|
2714
|
+
errors.push({
|
|
2715
|
+
path: `${path}[${i}]`,
|
|
2716
|
+
message: `Field '${name}' does not exist in entity '${entityName}'.`
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
function validateOperationRefs(path, refs, knownOps, entityName, errors) {
|
|
2722
|
+
if (!Array.isArray(refs)) return;
|
|
2723
|
+
for (let i = 0; i < refs.length; i++) {
|
|
2724
|
+
const id = refs[i];
|
|
2725
|
+
if (!knownOps.has(id)) {
|
|
2726
|
+
errors.push({
|
|
2727
|
+
path: `${path}[${i}]`,
|
|
2728
|
+
message: `Operation '${id}' does not exist in entity '${entityName}'.`
|
|
2729
|
+
});
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
// src/governance/GovernanceEventType.ts
|
|
2735
|
+
var GOVERNANCE_EVENT_TYPES = {
|
|
2736
|
+
OPERATION_EXECUTED: "governance.operation.executed",
|
|
2737
|
+
AUTHORIZATION_DENIED: "governance.authorization.denied",
|
|
2738
|
+
POLICY_TRIGGERED: "governance.policy.triggered",
|
|
2739
|
+
CONFIRMATION_REQUESTED: "governance.confirmation.requested",
|
|
2740
|
+
CONFIRMATION_APPROVED: "governance.confirmation.approved",
|
|
2741
|
+
CONFIRMATION_REJECTED: "governance.confirmation.rejected",
|
|
2742
|
+
AUDIT_RECORDED: "governance.audit.recorded"
|
|
2743
|
+
};
|
|
2744
|
+
|
|
2745
|
+
// src/governance/InMemoryGovernanceEventBus.ts
|
|
2746
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
2747
|
+
var InMemoryGovernanceEventBus = class extends InMemoryEventBus {
|
|
2748
|
+
async publishGovernance(type, payload, correlationId) {
|
|
2749
|
+
const event = {
|
|
2750
|
+
id: randomUUID6(),
|
|
2751
|
+
type,
|
|
2752
|
+
occurredAt: /* @__PURE__ */ new Date(),
|
|
2753
|
+
payload,
|
|
2754
|
+
correlationId
|
|
2755
|
+
};
|
|
2756
|
+
await this.publish(event);
|
|
2757
|
+
return event;
|
|
2758
|
+
}
|
|
2759
|
+
};
|
|
2760
|
+
|
|
2761
|
+
// src/governance/DefaultGovernanceApi.ts
|
|
2762
|
+
var DefaultGovernanceApi = class {
|
|
2763
|
+
constructor(auditRepository, confirmationRepository, violationLog = []) {
|
|
2764
|
+
this.auditRepository = auditRepository;
|
|
2765
|
+
this.confirmationRepository = confirmationRepository;
|
|
2766
|
+
this.violationLog = violationLog;
|
|
2767
|
+
}
|
|
2768
|
+
auditRepository;
|
|
2769
|
+
confirmationRepository;
|
|
2770
|
+
violationLog;
|
|
2771
|
+
async getAuditTimeline(filter) {
|
|
2772
|
+
return this.auditRepository.list(filter);
|
|
2773
|
+
}
|
|
2774
|
+
async getCorrelationTrace(correlationId) {
|
|
2775
|
+
return this.auditRepository.list({ correlationId });
|
|
2776
|
+
}
|
|
2777
|
+
async getPendingConfirmations() {
|
|
2778
|
+
return this.confirmationRepository.listPending();
|
|
2779
|
+
}
|
|
2780
|
+
async getPolicyViolations(filter) {
|
|
2781
|
+
let violations = [...this.violationLog];
|
|
2782
|
+
if (filter?.ruleId !== void 0) {
|
|
2783
|
+
violations = violations.filter((v) => v.ruleId === filter.ruleId);
|
|
2784
|
+
}
|
|
2785
|
+
if (filter?.correlationId !== void 0) {
|
|
2786
|
+
violations = violations.filter((v) => v.context.correlationId === filter.correlationId);
|
|
2787
|
+
}
|
|
2788
|
+
if (filter?.from !== void 0) {
|
|
2789
|
+
const from = filter.from;
|
|
2790
|
+
violations = violations.filter((v) => v.occurredAt >= from);
|
|
2791
|
+
}
|
|
2792
|
+
if (filter?.to !== void 0) {
|
|
2793
|
+
const to = filter.to;
|
|
2794
|
+
violations = violations.filter((v) => v.occurredAt <= to);
|
|
2795
|
+
}
|
|
2796
|
+
return violations;
|
|
2797
|
+
}
|
|
2798
|
+
};
|
|
2306
2799
|
export {
|
|
2307
2800
|
AuditRecorder,
|
|
2801
|
+
ConfirmationEngine,
|
|
2308
2802
|
ConsoleLogger,
|
|
2803
|
+
ConsoleStructuredLogger,
|
|
2804
|
+
ContextualAuthorizationEngine,
|
|
2309
2805
|
CsvExportProvider,
|
|
2310
2806
|
DEFAULT_CAPABILITIES,
|
|
2311
2807
|
DatasourceRegistry,
|
|
2808
|
+
DefaultGovernanceApi,
|
|
2312
2809
|
DiffEngine,
|
|
2313
2810
|
ErrorCode,
|
|
2811
|
+
GOVERNANCE_EVENT_TYPES,
|
|
2314
2812
|
InMemoryAuditRepository,
|
|
2315
2813
|
InMemoryConfigProvider,
|
|
2814
|
+
InMemoryConfirmationRepository,
|
|
2316
2815
|
InMemoryDatasourceProvider,
|
|
2317
2816
|
InMemoryEventBus,
|
|
2318
2817
|
InMemoryFeatureFlagProvider,
|
|
2818
|
+
InMemoryGovernanceEventBus,
|
|
2819
|
+
InMemoryPolicyProvider,
|
|
2319
2820
|
InMemorySnapshotRepository,
|
|
2320
2821
|
IntrospectionRuntime,
|
|
2321
2822
|
MaestroEngine,
|
|
2322
2823
|
MaestroError,
|
|
2323
2824
|
MetadataEngine,
|
|
2825
|
+
MetadataRiskClassifier,
|
|
2324
2826
|
OperationRegistry,
|
|
2827
|
+
PolicyEngine,
|
|
2325
2828
|
RbacEngine,
|
|
2326
2829
|
ReportGenerator,
|
|
2327
2830
|
createMaestro,
|
|
@@ -2329,6 +2832,7 @@ export {
|
|
|
2329
2832
|
createMaestroHttpHandlers,
|
|
2330
2833
|
detectDisplayField,
|
|
2331
2834
|
generateAllConfigs,
|
|
2835
|
+
generateCorrelationId,
|
|
2332
2836
|
generateEntityConfig,
|
|
2333
2837
|
generateRelationConfig,
|
|
2334
2838
|
humanizeFieldName,
|
|
@@ -2341,6 +2845,8 @@ export {
|
|
|
2341
2845
|
parseQueryInput,
|
|
2342
2846
|
tableNameToEntityId,
|
|
2343
2847
|
tableNameToLabel,
|
|
2848
|
+
validateConsumerDeclaration,
|
|
2849
|
+
validateEntityDeclaration,
|
|
2344
2850
|
validateMaestroConfig
|
|
2345
2851
|
};
|
|
2346
2852
|
//# sourceMappingURL=index.js.map
|