@stndrds/schema 1.0.0-alpha.78 → 1.0.0-alpha.80
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/{chunk-5SZ5OISG.mjs → chunk-7VNLLASJ.mjs} +0 -2
- package/dist/{chunk-3WTK7ESH.js → chunk-HUT6HYXJ.js} +0 -2
- package/dist/{chunk-JYXUFVUD.mjs → chunk-OR44EVWO.mjs} +1115 -1777
- package/dist/{chunk-KGEV5PIT.js → chunk-WHDJLJRK.js} +1356 -2018
- package/dist/index.d.mts +294 -107
- package/dist/index.d.ts +294 -107
- package/dist/index.js +14 -26
- package/dist/index.mjs +50 -62
- package/dist/{runtime-Df0uvz45.d.ts → runtime-B6Dt5_Gc.d.ts} +594 -1293
- package/dist/{runtime-ntcKC7g8.d.mts → runtime-Ttkdn9W7.d.mts} +594 -1293
- package/dist/runtime.d.mts +2 -2
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.js +3 -19
- package/dist/runtime.mjs +4 -20
- package/dist/validation/validators.d.mts +1 -1
- package/dist/validation/validators.d.ts +1 -1
- package/dist/validation/validators.js +2 -2
- package/dist/validation/validators.mjs +1 -1
- package/dist/{validators-DUB0tEzp.d.mts → validators-BeBrdQCB.d.mts} +3 -14
- package/dist/{validators-BxPuQ2GT.d.ts → validators-UuAMCPc6.d.ts} +3 -14
- package/package.json +2 -2
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
validateDraftOrThrow,
|
|
13
13
|
validateObject,
|
|
14
14
|
validateObjectOrThrow
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-7VNLLASJ.mjs";
|
|
16
16
|
import {
|
|
17
17
|
__require
|
|
18
18
|
} from "./chunk-Y6FXYEAI.mjs";
|
|
@@ -1292,6 +1292,84 @@ function createQueryBuilder(recordService, adapter, objectName, options) {
|
|
|
1292
1292
|
return new QueryBuilder(recordService, adapter, objectName, initialState);
|
|
1293
1293
|
}
|
|
1294
1294
|
|
|
1295
|
+
// src/types/flows.ts
|
|
1296
|
+
function isFlowFieldsRow(row) {
|
|
1297
|
+
return !row.type || row.type === "fields";
|
|
1298
|
+
}
|
|
1299
|
+
function isLayoutRow(row) {
|
|
1300
|
+
return !!row.type && row.type !== "fields";
|
|
1301
|
+
}
|
|
1302
|
+
function isFlowDefinition(obj) {
|
|
1303
|
+
return typeof obj === "object" && obj !== null && "slots" in obj && "pages" in obj && "relations" in obj && "status" in obj;
|
|
1304
|
+
}
|
|
1305
|
+
function isFlowPublished(flow) {
|
|
1306
|
+
return flow.status === "published";
|
|
1307
|
+
}
|
|
1308
|
+
function isSystemFlow(flow) {
|
|
1309
|
+
return flow.system === true;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// src/types/workflows/node-type-registry.ts
|
|
1313
|
+
var NodeTypeRegistryImpl = class {
|
|
1314
|
+
constructor() {
|
|
1315
|
+
this.definitions = /* @__PURE__ */ new Map();
|
|
1316
|
+
}
|
|
1317
|
+
register(definition) {
|
|
1318
|
+
this.definitions.set(definition.type, definition);
|
|
1319
|
+
}
|
|
1320
|
+
get(type) {
|
|
1321
|
+
const def = this.definitions.get(type);
|
|
1322
|
+
if (!def) {
|
|
1323
|
+
throw new Error(`[NodeTypeRegistry] Unknown node type: "${type}"`);
|
|
1324
|
+
}
|
|
1325
|
+
return def;
|
|
1326
|
+
}
|
|
1327
|
+
has(type) {
|
|
1328
|
+
return this.definitions.has(type);
|
|
1329
|
+
}
|
|
1330
|
+
getAll() {
|
|
1331
|
+
return Array.from(this.definitions.values());
|
|
1332
|
+
}
|
|
1333
|
+
getActionTypes() {
|
|
1334
|
+
return this.getAll().filter((d) => !d.structural).map((d) => d.type);
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
var nodeTypeRegistry = new NodeTypeRegistryImpl();
|
|
1338
|
+
function getNodeOutputs(node) {
|
|
1339
|
+
return nodeTypeRegistry.get(node.type).getOutputs(node);
|
|
1340
|
+
}
|
|
1341
|
+
function setNodeNext(node, targetId) {
|
|
1342
|
+
return nodeTypeRegistry.get(node.type).setNext(node, targetId);
|
|
1343
|
+
}
|
|
1344
|
+
function getNodeSlotIds(node) {
|
|
1345
|
+
return nodeTypeRegistry.get(node.type).getSlotIds(node);
|
|
1346
|
+
}
|
|
1347
|
+
function validateNode(node) {
|
|
1348
|
+
return nodeTypeRegistry.get(node.type).validate(node);
|
|
1349
|
+
}
|
|
1350
|
+
function getFormFieldRefs(node) {
|
|
1351
|
+
const refs = [];
|
|
1352
|
+
if (node.fields) {
|
|
1353
|
+
for (const field of node.fields) {
|
|
1354
|
+
if (field.slotId && field.attribute) {
|
|
1355
|
+
refs.push({ slotId: field.slotId, attribute: field.attribute });
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
if (node.rows) {
|
|
1360
|
+
for (const row of node.rows) {
|
|
1361
|
+
if (isFlowFieldsRow(row)) {
|
|
1362
|
+
for (const field of row.fields) {
|
|
1363
|
+
if (field.slotId && field.attribute) {
|
|
1364
|
+
refs.push({ slotId: field.slotId, attribute: field.attribute });
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
return refs;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1295
1373
|
// src/types/workflows/nodes.ts
|
|
1296
1374
|
function isSimpleFormNode(node) {
|
|
1297
1375
|
return node.fields !== void 0 && node.rows === void 0;
|
|
@@ -1308,24 +1386,168 @@ function isFormNode(node) {
|
|
|
1308
1386
|
function isConditionNode(node) {
|
|
1309
1387
|
return node.type === "condition";
|
|
1310
1388
|
}
|
|
1311
|
-
function
|
|
1312
|
-
return node.type === "
|
|
1389
|
+
function isAssignNode(node) {
|
|
1390
|
+
return node.type === "assign";
|
|
1313
1391
|
}
|
|
1314
1392
|
function isEndNode(node) {
|
|
1315
1393
|
return node.type === "end";
|
|
1316
1394
|
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1395
|
+
|
|
1396
|
+
// src/types/workflows/node-types/start.node-type.ts
|
|
1397
|
+
var startNodeType = {
|
|
1398
|
+
type: "start",
|
|
1399
|
+
isLinear: true,
|
|
1400
|
+
structural: true,
|
|
1401
|
+
getOutputs(node) {
|
|
1402
|
+
return node.next ? [node.next] : [];
|
|
1403
|
+
},
|
|
1404
|
+
setNext(node, targetId) {
|
|
1405
|
+
return { ...node, next: targetId };
|
|
1406
|
+
},
|
|
1407
|
+
getSlotIds() {
|
|
1408
|
+
return [];
|
|
1409
|
+
},
|
|
1410
|
+
validate() {
|
|
1411
|
+
return [];
|
|
1327
1412
|
}
|
|
1328
|
-
}
|
|
1413
|
+
};
|
|
1414
|
+
nodeTypeRegistry.register(startNodeType);
|
|
1415
|
+
|
|
1416
|
+
// src/types/workflows/node-types/form.node-type.ts
|
|
1417
|
+
var formNodeType = {
|
|
1418
|
+
type: "form",
|
|
1419
|
+
isLinear: true,
|
|
1420
|
+
structural: false,
|
|
1421
|
+
getOutputs(node) {
|
|
1422
|
+
return node.next ? [node.next] : [];
|
|
1423
|
+
},
|
|
1424
|
+
setNext(node, targetId) {
|
|
1425
|
+
return { ...node, next: targetId };
|
|
1426
|
+
},
|
|
1427
|
+
getSlotIds(node) {
|
|
1428
|
+
const slotIds = /* @__PURE__ */ new Set();
|
|
1429
|
+
if (node.fields) {
|
|
1430
|
+
for (const field of node.fields) {
|
|
1431
|
+
if (field.slotId) {
|
|
1432
|
+
slotIds.add(field.slotId);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
if (node.rows) {
|
|
1437
|
+
for (const row of node.rows) {
|
|
1438
|
+
if (isFlowFieldsRow(row)) {
|
|
1439
|
+
for (const field of row.fields) {
|
|
1440
|
+
if (field.slotId) {
|
|
1441
|
+
slotIds.add(field.slotId);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
return Array.from(slotIds);
|
|
1448
|
+
},
|
|
1449
|
+
validate(node) {
|
|
1450
|
+
const errors = [];
|
|
1451
|
+
const hasFields = node.fields !== void 0 && node.fields.length > 0;
|
|
1452
|
+
const hasRows = node.rows !== void 0 && node.rows.length > 0;
|
|
1453
|
+
if (hasFields && hasRows) {
|
|
1454
|
+
errors.push("FormNode cannot have both 'fields' and 'rows'");
|
|
1455
|
+
}
|
|
1456
|
+
return errors;
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
nodeTypeRegistry.register(formNodeType);
|
|
1460
|
+
|
|
1461
|
+
// src/types/workflows/node-types/condition.node-type.ts
|
|
1462
|
+
var conditionNodeType = {
|
|
1463
|
+
type: "condition",
|
|
1464
|
+
isLinear: false,
|
|
1465
|
+
structural: false,
|
|
1466
|
+
getOutputs(node) {
|
|
1467
|
+
return [node.onTrue, node.onFalse].filter((n) => !!n);
|
|
1468
|
+
},
|
|
1469
|
+
setNext() {
|
|
1470
|
+
throw new Error(
|
|
1471
|
+
"[NodeTypeRegistry] ConditionNode does not support setNext. Use onTrue/onFalse directly."
|
|
1472
|
+
);
|
|
1473
|
+
},
|
|
1474
|
+
getSlotIds() {
|
|
1475
|
+
return [];
|
|
1476
|
+
},
|
|
1477
|
+
validate(node) {
|
|
1478
|
+
const errors = [];
|
|
1479
|
+
if (!node.condition) {
|
|
1480
|
+
errors.push("ConditionNode must have a 'condition'");
|
|
1481
|
+
}
|
|
1482
|
+
return errors;
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
nodeTypeRegistry.register(conditionNodeType);
|
|
1486
|
+
|
|
1487
|
+
// src/types/workflows/node-types/assign.node-type.ts
|
|
1488
|
+
var assignNodeType = {
|
|
1489
|
+
type: "assign",
|
|
1490
|
+
isLinear: true,
|
|
1491
|
+
structural: false,
|
|
1492
|
+
getOutputs(node) {
|
|
1493
|
+
return node.next ? [node.next] : [];
|
|
1494
|
+
},
|
|
1495
|
+
setNext(node, targetId) {
|
|
1496
|
+
return { ...node, next: targetId };
|
|
1497
|
+
},
|
|
1498
|
+
getSlotIds(node) {
|
|
1499
|
+
const slotIds = /* @__PURE__ */ new Set();
|
|
1500
|
+
if (node.targetSlotId) {
|
|
1501
|
+
slotIds.add(node.targetSlotId);
|
|
1502
|
+
}
|
|
1503
|
+
for (const assignment of node.assignments ?? []) {
|
|
1504
|
+
const src = assignment.source;
|
|
1505
|
+
if (src.type === "slot-ref") {
|
|
1506
|
+
slotIds.add(src.slotId);
|
|
1507
|
+
} else if (src.type === "expression") {
|
|
1508
|
+
const matches = src.template.matchAll(/\{\{\s*([\w]+)\.\w+/g);
|
|
1509
|
+
for (const match of matches) {
|
|
1510
|
+
slotIds.add(match[1]);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
return [...slotIds];
|
|
1515
|
+
},
|
|
1516
|
+
validate(node) {
|
|
1517
|
+
const errors = [];
|
|
1518
|
+
if (!node.targetSlotId) {
|
|
1519
|
+
errors.push("AssignNode must have a target slot");
|
|
1520
|
+
}
|
|
1521
|
+
if (!node.assignments?.length) {
|
|
1522
|
+
errors.push("AssignNode must have at least one assignment");
|
|
1523
|
+
}
|
|
1524
|
+
if (!node.next) {
|
|
1525
|
+
errors.push("AssignNode must have a 'next' target");
|
|
1526
|
+
}
|
|
1527
|
+
return errors;
|
|
1528
|
+
}
|
|
1529
|
+
};
|
|
1530
|
+
nodeTypeRegistry.register(assignNodeType);
|
|
1531
|
+
|
|
1532
|
+
// src/types/workflows/node-types/end.node-type.ts
|
|
1533
|
+
var endNodeType = {
|
|
1534
|
+
type: "end",
|
|
1535
|
+
isLinear: false,
|
|
1536
|
+
structural: true,
|
|
1537
|
+
getOutputs() {
|
|
1538
|
+
return [];
|
|
1539
|
+
},
|
|
1540
|
+
setNext() {
|
|
1541
|
+
throw new Error("[NodeTypeRegistry] EndNode does not support setNext.");
|
|
1542
|
+
},
|
|
1543
|
+
getSlotIds() {
|
|
1544
|
+
return [];
|
|
1545
|
+
},
|
|
1546
|
+
validate() {
|
|
1547
|
+
return [];
|
|
1548
|
+
}
|
|
1549
|
+
};
|
|
1550
|
+
nodeTypeRegistry.register(endNodeType);
|
|
1329
1551
|
|
|
1330
1552
|
// src/types/workflows/conditions.ts
|
|
1331
1553
|
function isConditionRule(item) {
|
|
@@ -1459,6 +1681,11 @@ function setContextValue(context, path, value) {
|
|
|
1459
1681
|
current[parts[parts.length - 1]] = value;
|
|
1460
1682
|
}
|
|
1461
1683
|
|
|
1684
|
+
// src/types/workflows/form-context.ts
|
|
1685
|
+
function isFormFieldsRow(row) {
|
|
1686
|
+
return !row.type || row.type === "fields";
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1462
1689
|
// src/types/workflows/theme.ts
|
|
1463
1690
|
var DEFAULT_THEME = {
|
|
1464
1691
|
borderRadius: 8,
|
|
@@ -1510,6 +1737,69 @@ function isInvitationOrGrantEvent(event) {
|
|
|
1510
1737
|
return event.type.startsWith("workflow.invitation.") || event.type.startsWith("workflow.grant.");
|
|
1511
1738
|
}
|
|
1512
1739
|
|
|
1740
|
+
// src/types/workflows/zones.ts
|
|
1741
|
+
var ZONE_ORDER = ["collect", "actions"];
|
|
1742
|
+
var ZONE_CONFIG = {
|
|
1743
|
+
collect: { allowedNodeTypes: ["form", "condition"] },
|
|
1744
|
+
actions: { allowedNodeTypes: ["condition", "assign"] }
|
|
1745
|
+
};
|
|
1746
|
+
var TERMINAL_NODE_TYPES = /* @__PURE__ */ new Set(["start", "end"]);
|
|
1747
|
+
var NODE_TYPE_ZONE = {
|
|
1748
|
+
form: "collect",
|
|
1749
|
+
condition: "collect",
|
|
1750
|
+
// default — overridden by assignNodeZones logic
|
|
1751
|
+
assign: "actions"
|
|
1752
|
+
};
|
|
1753
|
+
function assignNodeZones(orderedNodes) {
|
|
1754
|
+
const result = /* @__PURE__ */ new Map();
|
|
1755
|
+
const hasFormAfter = new Array(orderedNodes.length).fill(false);
|
|
1756
|
+
let foundForm = false;
|
|
1757
|
+
for (let i = orderedNodes.length - 1; i >= 0; i--) {
|
|
1758
|
+
hasFormAfter[i] = foundForm;
|
|
1759
|
+
if (orderedNodes[i].type === "form") {
|
|
1760
|
+
foundForm = true;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
for (let i = 0; i < orderedNodes.length; i++) {
|
|
1764
|
+
const node = orderedNodes[i];
|
|
1765
|
+
if (TERMINAL_NODE_TYPES.has(node.type)) {
|
|
1766
|
+
result.set(node.id, null);
|
|
1767
|
+
} else if (node.type === "condition") {
|
|
1768
|
+
result.set(node.id, hasFormAfter[i] ? "collect" : "actions");
|
|
1769
|
+
} else {
|
|
1770
|
+
result.set(node.id, NODE_TYPE_ZONE[node.type] ?? null);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
return result;
|
|
1774
|
+
}
|
|
1775
|
+
function groupNodesByZone(orderedNodes) {
|
|
1776
|
+
const zoneMap = assignNodeZones(orderedNodes);
|
|
1777
|
+
const result = {
|
|
1778
|
+
start: null,
|
|
1779
|
+
zones: {
|
|
1780
|
+
collect: [],
|
|
1781
|
+
actions: []
|
|
1782
|
+
},
|
|
1783
|
+
end: null
|
|
1784
|
+
};
|
|
1785
|
+
for (const node of orderedNodes) {
|
|
1786
|
+
if (node.type === "start") {
|
|
1787
|
+
result.start = node;
|
|
1788
|
+
} else if (node.type === "end") {
|
|
1789
|
+
result.end = node;
|
|
1790
|
+
} else {
|
|
1791
|
+
const zone = zoneMap.get(node.id);
|
|
1792
|
+
if (zone) {
|
|
1793
|
+
result.zones[zone].push(node);
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
return result;
|
|
1798
|
+
}
|
|
1799
|
+
function getZoneAllowedTypes(zone) {
|
|
1800
|
+
return ZONE_CONFIG[zone].allowedNodeTypes;
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1513
1803
|
// src/types/workflows/validation.ts
|
|
1514
1804
|
import { z } from "zod";
|
|
1515
1805
|
var ConditionOperatorSchema = z.enum([
|
|
@@ -1551,14 +1841,40 @@ var FlowRowFieldSchema = z.object({
|
|
|
1551
1841
|
id: z.string().min(1),
|
|
1552
1842
|
slotId: z.string().min(1),
|
|
1553
1843
|
attribute: z.string().min(1),
|
|
1554
|
-
label: z.string().optional(),
|
|
1844
|
+
label: z.string().max(200).optional(),
|
|
1845
|
+
tooltip: z.string().max(1e3).optional(),
|
|
1555
1846
|
required: z.boolean().optional()
|
|
1556
1847
|
});
|
|
1557
|
-
var
|
|
1848
|
+
var FlowFieldsRowSchema = z.object({
|
|
1558
1849
|
id: z.string().min(1),
|
|
1559
1850
|
order: z.number(),
|
|
1851
|
+
type: z.literal("fields").optional(),
|
|
1560
1852
|
fields: z.array(FlowRowFieldSchema)
|
|
1561
1853
|
});
|
|
1854
|
+
var FlowHeadingRowSchema = z.object({
|
|
1855
|
+
id: z.string().min(1),
|
|
1856
|
+
order: z.number(),
|
|
1857
|
+
type: z.literal("heading"),
|
|
1858
|
+
content: z.string().min(1).max(200),
|
|
1859
|
+
level: z.union([z.literal(1), z.literal(2), z.literal(3)]).optional()
|
|
1860
|
+
});
|
|
1861
|
+
var FlowSeparatorRowSchema = z.object({
|
|
1862
|
+
id: z.string().min(1),
|
|
1863
|
+
order: z.number(),
|
|
1864
|
+
type: z.literal("separator")
|
|
1865
|
+
});
|
|
1866
|
+
var FlowTextRowSchema = z.object({
|
|
1867
|
+
id: z.string().min(1),
|
|
1868
|
+
order: z.number(),
|
|
1869
|
+
type: z.literal("text"),
|
|
1870
|
+
content: z.string().min(1).max(5e3)
|
|
1871
|
+
});
|
|
1872
|
+
var FlowRowSchema = z.union([
|
|
1873
|
+
FlowHeadingRowSchema,
|
|
1874
|
+
FlowSeparatorRowSchema,
|
|
1875
|
+
FlowTextRowSchema,
|
|
1876
|
+
FlowFieldsRowSchema
|
|
1877
|
+
]);
|
|
1562
1878
|
var FormNodeSchema = z.object({
|
|
1563
1879
|
type: z.literal("form"),
|
|
1564
1880
|
id: z.string().min(1),
|
|
@@ -1586,14 +1902,26 @@ var ConditionNodeSchema = z.object({
|
|
|
1586
1902
|
onTrue: z.string().nullish(),
|
|
1587
1903
|
onFalse: z.string().nullish()
|
|
1588
1904
|
});
|
|
1589
|
-
var
|
|
1590
|
-
type: z.literal("
|
|
1905
|
+
var AssignmentSourceSchema = z.discriminatedUnion("type", [
|
|
1906
|
+
z.object({ type: z.literal("expression"), template: z.string().min(1) }),
|
|
1907
|
+
z.object({
|
|
1908
|
+
type: z.literal("slot-ref"),
|
|
1909
|
+
slotId: z.string().min(1),
|
|
1910
|
+
properties: z.record(z.string(), z.unknown()).optional()
|
|
1911
|
+
}),
|
|
1912
|
+
z.object({ type: z.literal("static"), value: z.unknown() })
|
|
1913
|
+
]);
|
|
1914
|
+
var AssignmentMappingSchema = z.object({
|
|
1915
|
+
targetAttribute: z.string().min(1),
|
|
1916
|
+
source: AssignmentSourceSchema
|
|
1917
|
+
});
|
|
1918
|
+
var AssignNodeSchema = z.object({
|
|
1919
|
+
type: z.literal("assign"),
|
|
1591
1920
|
id: z.string().min(1),
|
|
1592
1921
|
label: z.string().min(1),
|
|
1593
1922
|
description: z.string().nullish(),
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
filename: z.string().nullish(),
|
|
1923
|
+
targetSlotId: z.string().min(1, "Target slot is required"),
|
|
1924
|
+
assignments: z.array(AssignmentMappingSchema).min(1),
|
|
1597
1925
|
next: z.string().nullish()
|
|
1598
1926
|
});
|
|
1599
1927
|
var EndNodeSchema = z.object({
|
|
@@ -1606,7 +1934,7 @@ var WorkflowNodeSchema = z.discriminatedUnion("type", [
|
|
|
1606
1934
|
StartNodeSchema,
|
|
1607
1935
|
FormNodeSchema,
|
|
1608
1936
|
ConditionNodeSchema,
|
|
1609
|
-
|
|
1937
|
+
AssignNodeSchema,
|
|
1610
1938
|
EndNodeSchema
|
|
1611
1939
|
]);
|
|
1612
1940
|
var SlotModeSchema = z.enum(["create", "select", "optional"]);
|
|
@@ -1728,15 +2056,8 @@ var WorkflowDefinitionSchema = z.object({
|
|
|
1728
2056
|
).refine(
|
|
1729
2057
|
(def) => {
|
|
1730
2058
|
for (const node of Object.values(def.nodes)) {
|
|
1731
|
-
|
|
1732
|
-
if (
|
|
1733
|
-
return false;
|
|
1734
|
-
}
|
|
1735
|
-
} else if (node.type === "condition") {
|
|
1736
|
-
if (node.onTrue && !(node.onTrue in def.nodes)) {
|
|
1737
|
-
return false;
|
|
1738
|
-
}
|
|
1739
|
-
if (node.onFalse && !(node.onFalse in def.nodes)) {
|
|
2059
|
+
for (const targetId of getNodeOutputs(node)) {
|
|
2060
|
+
if (!(targetId in def.nodes)) {
|
|
1740
2061
|
return false;
|
|
1741
2062
|
}
|
|
1742
2063
|
}
|
|
@@ -2053,85 +2374,6 @@ var ConditionExecutor = class {
|
|
|
2053
2374
|
}
|
|
2054
2375
|
};
|
|
2055
2376
|
|
|
2056
|
-
// src/runtime/executors/document.executor.ts
|
|
2057
|
-
var DocumentExecutor = class {
|
|
2058
|
-
constructor() {
|
|
2059
|
-
this.nodeType = "document";
|
|
2060
|
-
}
|
|
2061
|
-
execute(node, _context) {
|
|
2062
|
-
if (!node.templateId) {
|
|
2063
|
-
return error("MISSING_TEMPLATE", "DocumentNode must have a templateId");
|
|
2064
|
-
}
|
|
2065
|
-
if (!node.next) {
|
|
2066
|
-
return error("MISSING_NEXT", "DocumentNode must have a 'next' target");
|
|
2067
|
-
}
|
|
2068
|
-
const contextUpdates = {
|
|
2069
|
-
documents: {
|
|
2070
|
-
[node.id]: {
|
|
2071
|
-
id: "",
|
|
2072
|
-
// Will be filled by consumer after generation
|
|
2073
|
-
url: "",
|
|
2074
|
-
// Will be filled by consumer after generation
|
|
2075
|
-
filename: node.filename,
|
|
2076
|
-
metadata: {
|
|
2077
|
-
templateId: node.templateId,
|
|
2078
|
-
templateVersion: node.templateVersion ?? 1,
|
|
2079
|
-
targetSlotIds: node.targetSlotIds ?? [],
|
|
2080
|
-
status: "pending"
|
|
2081
|
-
}
|
|
2082
|
-
}
|
|
2083
|
-
}
|
|
2084
|
-
};
|
|
2085
|
-
return success(node.next, contextUpdates);
|
|
2086
|
-
}
|
|
2087
|
-
canExecute(_node, _context) {
|
|
2088
|
-
return true;
|
|
2089
|
-
}
|
|
2090
|
-
validate(node) {
|
|
2091
|
-
const errors = [];
|
|
2092
|
-
if (!node.label) {
|
|
2093
|
-
errors.push("DocumentNode must have a 'label' property");
|
|
2094
|
-
}
|
|
2095
|
-
if (!node.templateId) {
|
|
2096
|
-
errors.push("DocumentNode must have a 'templateId' property");
|
|
2097
|
-
}
|
|
2098
|
-
if (!node.next) {
|
|
2099
|
-
errors.push("DocumentNode must have a 'next' property");
|
|
2100
|
-
}
|
|
2101
|
-
if (node.targetSlotIds && node.targetSlotIds.length > 0) {
|
|
2102
|
-
for (const slotId of node.targetSlotIds) {
|
|
2103
|
-
if (!slotId || typeof slotId !== "string" || slotId.trim() === "") {
|
|
2104
|
-
errors.push("DocumentNode targetSlotIds must contain non-empty string values");
|
|
2105
|
-
break;
|
|
2106
|
-
}
|
|
2107
|
-
}
|
|
2108
|
-
}
|
|
2109
|
-
return errors;
|
|
2110
|
-
}
|
|
2111
|
-
/**
|
|
2112
|
-
* Validate that targetSlotIds reference existing slots in the workflow definition.
|
|
2113
|
-
* This is a context-aware validation that requires the workflow's slot definitions.
|
|
2114
|
-
*
|
|
2115
|
-
* @param node - The document node to validate
|
|
2116
|
-
* @param workflowSlots - All slots defined in the workflow
|
|
2117
|
-
* @returns Array of validation error messages
|
|
2118
|
-
*/
|
|
2119
|
-
validateSlotReferences(node, workflowSlots) {
|
|
2120
|
-
const errors = [];
|
|
2121
|
-
if (node.targetSlotIds && node.targetSlotIds.length > 0) {
|
|
2122
|
-
const slotIdSet = workflowSlots.reduce((set, s) => set.add(s.id), /* @__PURE__ */ new Set());
|
|
2123
|
-
for (const slotId of node.targetSlotIds) {
|
|
2124
|
-
if (!slotIdSet.has(slotId)) {
|
|
2125
|
-
errors.push(
|
|
2126
|
-
`DocumentNode "${node.id}" references unknown slot "${slotId}" in targetSlotIds`
|
|
2127
|
-
);
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
}
|
|
2131
|
-
return errors;
|
|
2132
|
-
}
|
|
2133
|
-
};
|
|
2134
|
-
|
|
2135
2377
|
// src/runtime/executors/end.executor.ts
|
|
2136
2378
|
var EndExecutor = class {
|
|
2137
2379
|
constructor() {
|
|
@@ -2155,6 +2397,13 @@ var FormExecutor = class {
|
|
|
2155
2397
|
}
|
|
2156
2398
|
execute(node, context) {
|
|
2157
2399
|
const { input } = context;
|
|
2400
|
+
const hasContent = (node.fields?.length ?? 0) > 0 || (node.rows?.length ?? 0) > 0;
|
|
2401
|
+
if (!hasContent) {
|
|
2402
|
+
if (!node.next) {
|
|
2403
|
+
return error("MISSING_NEXT", `FormNode "${node.id}" has no 'next' target defined`);
|
|
2404
|
+
}
|
|
2405
|
+
return success(node.next);
|
|
2406
|
+
}
|
|
2158
2407
|
if (isEmpty(input)) {
|
|
2159
2408
|
const requiredParticipationId = node.participantId ?? void 0;
|
|
2160
2409
|
return wait(`Waiting for form submission: ${node.label}`, {
|
|
@@ -2162,10 +2411,7 @@ var FormExecutor = class {
|
|
|
2162
2411
|
});
|
|
2163
2412
|
}
|
|
2164
2413
|
const formInput = input;
|
|
2165
|
-
const slotIds =
|
|
2166
|
-
if (slotIds.size === 0) {
|
|
2167
|
-
return error("MISSING_FIELDS", "FormNode must have fields or rows with slot references");
|
|
2168
|
-
}
|
|
2414
|
+
const slotIds = new Set(getNodeSlotIds(node));
|
|
2169
2415
|
const contextUpdates = {
|
|
2170
2416
|
forms: {
|
|
2171
2417
|
[node.id]: formInput
|
|
@@ -2212,37 +2458,11 @@ var FormExecutor = class {
|
|
|
2212
2458
|
}
|
|
2213
2459
|
const hasFields = node.fields !== void 0 && node.fields.length > 0;
|
|
2214
2460
|
const hasRows = node.rows !== void 0 && node.rows.length > 0;
|
|
2215
|
-
if (!(hasFields || hasRows)) {
|
|
2216
|
-
errors.push("FormNode must have either 'fields' (simple mode) or 'rows' (advanced mode)");
|
|
2217
|
-
}
|
|
2218
2461
|
if (hasFields && hasRows) {
|
|
2219
2462
|
errors.push("FormNode cannot have both 'fields' and 'rows'");
|
|
2220
2463
|
}
|
|
2221
2464
|
return errors;
|
|
2222
2465
|
}
|
|
2223
|
-
/**
|
|
2224
|
-
* Extract all slot IDs referenced in the form
|
|
2225
|
-
*/
|
|
2226
|
-
extractSlotIds(node) {
|
|
2227
|
-
const slotIds = /* @__PURE__ */ new Set();
|
|
2228
|
-
if (node.fields) {
|
|
2229
|
-
for (const field of node.fields) {
|
|
2230
|
-
if (field.slotId) {
|
|
2231
|
-
slotIds.add(field.slotId);
|
|
2232
|
-
}
|
|
2233
|
-
}
|
|
2234
|
-
}
|
|
2235
|
-
if (node.rows) {
|
|
2236
|
-
for (const row of node.rows) {
|
|
2237
|
-
for (const field of row.fields) {
|
|
2238
|
-
if (field.slotId) {
|
|
2239
|
-
slotIds.add(field.slotId);
|
|
2240
|
-
}
|
|
2241
|
-
}
|
|
2242
|
-
}
|
|
2243
|
-
}
|
|
2244
|
-
return slotIds;
|
|
2245
|
-
}
|
|
2246
2466
|
/**
|
|
2247
2467
|
* Validate required fields based on slot mode.
|
|
2248
2468
|
*
|
|
@@ -2258,7 +2478,7 @@ var FormExecutor = class {
|
|
|
2258
2478
|
*/
|
|
2259
2479
|
validateRequiredFields(node, input, slots, objects) {
|
|
2260
2480
|
const errors = [];
|
|
2261
|
-
const fieldRefs =
|
|
2481
|
+
const fieldRefs = getFormFieldRefs(node);
|
|
2262
2482
|
for (const fieldRef of fieldRefs) {
|
|
2263
2483
|
const slot = slots.find((s) => s.id === fieldRef.slotId);
|
|
2264
2484
|
if (!slot) {
|
|
@@ -2279,7 +2499,7 @@ var FormExecutor = class {
|
|
|
2279
2499
|
if (!attribute?.required) continue;
|
|
2280
2500
|
const slotInput = input[fieldRef.slotId];
|
|
2281
2501
|
const value = slotInput?.[fieldRef.attribute];
|
|
2282
|
-
if (value === void 0 || value === null || value === "") {
|
|
2502
|
+
if (value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0) {
|
|
2283
2503
|
errors.push(
|
|
2284
2504
|
`Field "${attribute.label ?? fieldRef.attribute}" is required for ${slot.label}`
|
|
2285
2505
|
);
|
|
@@ -2287,41 +2507,407 @@ var FormExecutor = class {
|
|
|
2287
2507
|
}
|
|
2288
2508
|
return errors;
|
|
2289
2509
|
}
|
|
2290
|
-
/**
|
|
2291
|
-
* Collect all field references from a FormNode
|
|
2292
|
-
*/
|
|
2293
|
-
collectFieldRefs(node) {
|
|
2294
|
-
const refs = [];
|
|
2295
|
-
if (node.fields) {
|
|
2296
|
-
for (const field of node.fields) {
|
|
2297
|
-
if (field.slotId && field.attribute) {
|
|
2298
|
-
refs.push({ slotId: field.slotId, attribute: field.attribute });
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
}
|
|
2302
|
-
if (node.rows) {
|
|
2303
|
-
for (const row of node.rows) {
|
|
2304
|
-
for (const field of row.fields) {
|
|
2305
|
-
if (field.slotId && field.attribute) {
|
|
2306
|
-
refs.push({ slotId: field.slotId, attribute: field.attribute });
|
|
2307
|
-
}
|
|
2308
|
-
}
|
|
2309
|
-
}
|
|
2310
|
-
}
|
|
2311
|
-
return refs;
|
|
2312
|
-
}
|
|
2313
2510
|
};
|
|
2314
2511
|
|
|
2315
|
-
// src/
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2512
|
+
// src/format.ts
|
|
2513
|
+
import { getCountryDisplayNameByIso3 } from "@stndrds/constants";
|
|
2514
|
+
var EMPTY_VALUE_PLACEHOLDER = "\u2014";
|
|
2515
|
+
function formatText(value) {
|
|
2516
|
+
return String(value);
|
|
2517
|
+
}
|
|
2518
|
+
function formatCheckbox(value) {
|
|
2519
|
+
return value ? "Yes" : "No";
|
|
2520
|
+
}
|
|
2521
|
+
function formatNumber(value, attribute) {
|
|
2522
|
+
if (typeof value !== "number") return String(value);
|
|
2523
|
+
const decimals = attribute.decimals;
|
|
2524
|
+
if (attribute.unit === "integer") {
|
|
2525
|
+
return value.toLocaleString(void 0, {
|
|
2526
|
+
minimumFractionDigits: 0,
|
|
2527
|
+
maximumFractionDigits: 0
|
|
2528
|
+
});
|
|
2529
|
+
}
|
|
2530
|
+
if (attribute.unit === "percentage") {
|
|
2531
|
+
return (value / 100).toLocaleString(void 0, {
|
|
2532
|
+
style: "percent",
|
|
2533
|
+
minimumFractionDigits: decimals,
|
|
2534
|
+
maximumFractionDigits: decimals
|
|
2535
|
+
});
|
|
2536
|
+
}
|
|
2537
|
+
return value.toLocaleString(void 0, {
|
|
2538
|
+
minimumFractionDigits: decimals,
|
|
2539
|
+
maximumFractionDigits: decimals
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
2542
|
+
function formatCurrency(value, _attribute) {
|
|
2543
|
+
if (typeof value !== "object" || value === null) return String(value);
|
|
2544
|
+
const currency2 = value;
|
|
2545
|
+
if (!("value" in currency2 && "code" in currency2)) return String(value);
|
|
2546
|
+
const formattedValue = currency2.value.toLocaleString(void 0, {
|
|
2547
|
+
minimumFractionDigits: 2,
|
|
2548
|
+
maximumFractionDigits: 2
|
|
2549
|
+
});
|
|
2550
|
+
return `${formattedValue} ${currency2.code}`;
|
|
2551
|
+
}
|
|
2552
|
+
function formatDate(value) {
|
|
2553
|
+
if (value instanceof Date) {
|
|
2554
|
+
return value.toISOString().split("T")[0];
|
|
2555
|
+
}
|
|
2556
|
+
if (typeof value === "string") {
|
|
2557
|
+
const date2 = new Date(value);
|
|
2558
|
+
if (!Number.isNaN(date2.getTime())) {
|
|
2559
|
+
return date2.toISOString().split("T")[0];
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
return String(value);
|
|
2563
|
+
}
|
|
2564
|
+
function formatPhone(value) {
|
|
2565
|
+
if (typeof value !== "object" || value === null) return String(value);
|
|
2566
|
+
const phone2 = value;
|
|
2567
|
+
if (!("phoneNumber" in phone2)) return String(value);
|
|
2568
|
+
if (!phone2.phoneNumber) return "";
|
|
2569
|
+
if (!phone2.countryCode) return phone2.phoneNumber;
|
|
2570
|
+
return formatPhoneForDisplay(phone2);
|
|
2571
|
+
}
|
|
2572
|
+
function formatLocation(value, attribute) {
|
|
2573
|
+
if (typeof value !== "object" || value === null) return String(value);
|
|
2574
|
+
const loc = value;
|
|
2575
|
+
const granularity = attribute.granularity ?? "full";
|
|
2576
|
+
const parts = [];
|
|
2577
|
+
switch (granularity) {
|
|
2578
|
+
case "country":
|
|
2579
|
+
if (loc.country) parts.push(getCountryDisplayNameByIso3(loc.country));
|
|
2580
|
+
break;
|
|
2581
|
+
case "state":
|
|
2582
|
+
if (loc.state) parts.push(loc.state);
|
|
2583
|
+
if (loc.country) parts.push(getCountryDisplayNameByIso3(loc.country));
|
|
2584
|
+
break;
|
|
2585
|
+
case "city":
|
|
2586
|
+
if (loc.city) parts.push(loc.city);
|
|
2587
|
+
if (loc.state) parts.push(loc.state);
|
|
2588
|
+
if (loc.country) parts.push(getCountryDisplayNameByIso3(loc.country));
|
|
2589
|
+
break;
|
|
2590
|
+
case "coordinates":
|
|
2591
|
+
if (loc.latitude !== void 0 && loc.longitude !== void 0) {
|
|
2592
|
+
parts.push(`${loc.latitude}, ${loc.longitude}`);
|
|
2593
|
+
}
|
|
2594
|
+
break;
|
|
2595
|
+
case "address":
|
|
2596
|
+
if (loc.address) parts.push(loc.address);
|
|
2597
|
+
if (loc.city) parts.push(loc.city);
|
|
2598
|
+
if (loc.state) parts.push(loc.state);
|
|
2599
|
+
if (loc.country) parts.push(getCountryDisplayNameByIso3(loc.country));
|
|
2600
|
+
break;
|
|
2601
|
+
default:
|
|
2602
|
+
if (loc.address) parts.push(loc.address);
|
|
2603
|
+
if (loc.city) parts.push(loc.city);
|
|
2604
|
+
if (loc.state) parts.push(loc.state);
|
|
2605
|
+
if (loc.postalCode) parts.push(loc.postalCode);
|
|
2606
|
+
if (loc.country) parts.push(getCountryDisplayNameByIso3(loc.country));
|
|
2607
|
+
break;
|
|
2608
|
+
}
|
|
2609
|
+
return parts.join(", ") || EMPTY_VALUE_PLACEHOLDER;
|
|
2610
|
+
}
|
|
2611
|
+
function formatSelect(value, attribute) {
|
|
2612
|
+
if (typeof value !== "string") return String(value);
|
|
2613
|
+
const option = attribute.options?.find((o) => o.value === value);
|
|
2614
|
+
return option?.label ?? String(value);
|
|
2615
|
+
}
|
|
2616
|
+
function formatMultiselect(value, attribute) {
|
|
2617
|
+
if (!Array.isArray(value)) return String(value);
|
|
2618
|
+
if (attribute.options) {
|
|
2619
|
+
const labels = value.map((v) => attribute.options.find((o) => o.value === v)?.label).filter(Boolean);
|
|
2620
|
+
return labels.join(", ");
|
|
2621
|
+
}
|
|
2622
|
+
return value.join(", ");
|
|
2623
|
+
}
|
|
2624
|
+
function formatRating(value, attribute) {
|
|
2625
|
+
if (typeof value !== "number") return String(value);
|
|
2626
|
+
const max = attribute.max ?? 5;
|
|
2627
|
+
return `${value}/${max}`;
|
|
2628
|
+
}
|
|
2629
|
+
function formatAttributeValue(value, attribute) {
|
|
2630
|
+
if (value === null || value === void 0 || value === "") {
|
|
2631
|
+
return EMPTY_VALUE_PLACEHOLDER;
|
|
2632
|
+
}
|
|
2633
|
+
switch (attribute.type) {
|
|
2634
|
+
case "text":
|
|
2635
|
+
case "textarea":
|
|
2636
|
+
return formatText(value);
|
|
2637
|
+
case "checkbox":
|
|
2638
|
+
return formatCheckbox(value);
|
|
2639
|
+
case "number":
|
|
2640
|
+
return formatNumber(value, attribute);
|
|
2641
|
+
case "currency":
|
|
2642
|
+
return formatCurrency(value, attribute);
|
|
2643
|
+
case "date":
|
|
2644
|
+
return formatDate(value);
|
|
2645
|
+
case "phone":
|
|
2646
|
+
return formatPhone(value);
|
|
2647
|
+
case "location":
|
|
2648
|
+
return formatLocation(value, attribute);
|
|
2649
|
+
case "select":
|
|
2650
|
+
case "status":
|
|
2651
|
+
return formatSelect(value, attribute);
|
|
2652
|
+
case "multiselect":
|
|
2653
|
+
return formatMultiselect(value, attribute);
|
|
2654
|
+
case "rating":
|
|
2655
|
+
return formatRating(value, attribute);
|
|
2656
|
+
// Unsupported types - return value as-is or placeholder
|
|
2657
|
+
case "file":
|
|
2658
|
+
case "user":
|
|
2659
|
+
case "relation":
|
|
2660
|
+
if (Array.isArray(value)) {
|
|
2661
|
+
return value.join(", ");
|
|
2662
|
+
}
|
|
2663
|
+
return String(value);
|
|
2664
|
+
default: {
|
|
2665
|
+
if (Array.isArray(value)) {
|
|
2666
|
+
return value.join(", ");
|
|
2667
|
+
}
|
|
2668
|
+
return String(value);
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
// src/runtime/template.ts
|
|
2674
|
+
var simplePipes = {
|
|
2675
|
+
/** Convert to uppercase */
|
|
2676
|
+
UPPER: (v) => String(v).toUpperCase(),
|
|
2677
|
+
/** Convert to lowercase */
|
|
2678
|
+
LOWER: (v) => String(v).toLowerCase(),
|
|
2679
|
+
/** Capitalize first letter of each word */
|
|
2680
|
+
capitalize: (v) => String(v).replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
2681
|
+
/** Trim whitespace from both ends */
|
|
2682
|
+
trim: (v) => String(v).trim()
|
|
2683
|
+
};
|
|
2684
|
+
var pipesWithArgs = {
|
|
2685
|
+
/** Add prefix only if value is non-empty */
|
|
2686
|
+
prefix: (v, pre = "") => v ? `${pre}${v}` : "",
|
|
2687
|
+
/** Add suffix only if value is non-empty */
|
|
2688
|
+
suffix: (v, suf = "") => v ? `${v}${suf}` : "",
|
|
2689
|
+
/** Wrap value with prefix and suffix only if non-empty */
|
|
2690
|
+
wrap: (v, pre = "", suf = "") => v ? `${pre}${v}${suf}` : "",
|
|
2691
|
+
/** Show default value if empty */
|
|
2692
|
+
default: (v, def = "") => v || def
|
|
2693
|
+
};
|
|
2694
|
+
function getValue(obj, path) {
|
|
2695
|
+
return path.split(".").reduce((acc, key) => {
|
|
2696
|
+
if (acc == null || typeof acc !== "object") return void 0;
|
|
2697
|
+
return acc[key];
|
|
2698
|
+
}, obj);
|
|
2699
|
+
}
|
|
2700
|
+
var DEFAULT_LABEL_FALLBACK = "(Untitled)";
|
|
2701
|
+
function parsePipeExpression(pipeExpr) {
|
|
2702
|
+
const match = pipeExpr.match(/^(\w+)(?::(.*))?$/);
|
|
2703
|
+
if (!match) return { name: pipeExpr, args: [] };
|
|
2704
|
+
const name = match[1];
|
|
2705
|
+
const argsStr = match[2];
|
|
2706
|
+
if (!argsStr) return { name, args: [] };
|
|
2707
|
+
const args = [];
|
|
2708
|
+
const argRegex = /["']([^"']*?)["']/g;
|
|
2709
|
+
let argMatch;
|
|
2710
|
+
while ((argMatch = argRegex.exec(argsStr)) !== null) {
|
|
2711
|
+
args.push(argMatch[1]);
|
|
2712
|
+
}
|
|
2713
|
+
return { name, args };
|
|
2714
|
+
}
|
|
2715
|
+
function renderLabelExpression(template, values, fallback = DEFAULT_LABEL_FALLBACK) {
|
|
2716
|
+
const result = template.replace(/\{\{\s*([^}]+)\s*\}\}/g, (_, expr) => {
|
|
2717
|
+
const orParts = expr.split("||").map((s) => s.trim());
|
|
2718
|
+
const lastPart = orParts[orParts.length - 1];
|
|
2719
|
+
const pipeSplit = lastPart.split("|").map((s) => s.trim());
|
|
2720
|
+
orParts[orParts.length - 1] = pipeSplit[0];
|
|
2721
|
+
const pipes = pipeSplit.slice(1).filter(Boolean);
|
|
2722
|
+
const alternatives = orParts.filter(Boolean);
|
|
2723
|
+
let value = "";
|
|
2724
|
+
for (const alt of alternatives) {
|
|
2725
|
+
const v = getValue(values, alt);
|
|
2726
|
+
if (v != null && v !== "") {
|
|
2727
|
+
value = v;
|
|
2728
|
+
break;
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
const isEmpty3 = value == null || value === "";
|
|
2732
|
+
if (isEmpty3 && pipes.length === 0) return "";
|
|
2733
|
+
for (const pipeExpr of pipes) {
|
|
2734
|
+
const { name: pipeName, args } = parsePipeExpression(pipeExpr);
|
|
2735
|
+
const simpleFn = simplePipes[pipeName];
|
|
2736
|
+
if (simpleFn) {
|
|
2737
|
+
if (value != null && value !== "") {
|
|
2738
|
+
value = simpleFn(String(value));
|
|
2739
|
+
}
|
|
2740
|
+
} else {
|
|
2741
|
+
const argFn = pipesWithArgs[pipeName];
|
|
2742
|
+
if (argFn) {
|
|
2743
|
+
value = argFn(String(value ?? ""), ...args);
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
return String(value ?? "");
|
|
2748
|
+
}).trim();
|
|
2749
|
+
return result || fallback;
|
|
2750
|
+
}
|
|
2751
|
+
function isLabelExpression(value) {
|
|
2752
|
+
return /\{\{\s*\S+.*\}\}/.test(value);
|
|
2753
|
+
}
|
|
2754
|
+
function extractAttributeNames(template) {
|
|
2755
|
+
const names = [];
|
|
2756
|
+
const regex = /\{\{\s*([^}]+)\s*\}\}/g;
|
|
2757
|
+
let match;
|
|
2758
|
+
while ((match = regex.exec(template)) !== null) {
|
|
2759
|
+
const expr = match[1].trim();
|
|
2760
|
+
const orParts = expr.split("||").map((s) => s.trim());
|
|
2761
|
+
const lastPart = orParts[orParts.length - 1];
|
|
2762
|
+
orParts[orParts.length - 1] = lastPart.split("|")[0].trim();
|
|
2763
|
+
for (const part of orParts) {
|
|
2764
|
+
if (!part) continue;
|
|
2765
|
+
const rootName = part.split(".")[0];
|
|
2766
|
+
if (rootName && !names.includes(rootName)) {
|
|
2767
|
+
names.push(rootName);
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
return names;
|
|
2772
|
+
}
|
|
2773
|
+
function hasOptions(attr) {
|
|
2774
|
+
return "options" in attr && Array.isArray(attr.options) && attr.options.length > 0;
|
|
2775
|
+
}
|
|
2776
|
+
var FORMATTABLE_TYPES = /* @__PURE__ */ new Set([
|
|
2777
|
+
"currency",
|
|
2778
|
+
"location",
|
|
2779
|
+
"phone",
|
|
2780
|
+
"date",
|
|
2781
|
+
"rating",
|
|
2782
|
+
"select",
|
|
2783
|
+
"status",
|
|
2784
|
+
"multiselect",
|
|
2785
|
+
"number"
|
|
2786
|
+
]);
|
|
2787
|
+
function enrichValuesForDisplay(values, attributes) {
|
|
2788
|
+
const enriched = { ...values };
|
|
2789
|
+
for (const attr of attributes) {
|
|
2790
|
+
const value = values[attr.name];
|
|
2791
|
+
if (value == null) continue;
|
|
2792
|
+
if (!FORMATTABLE_TYPES.has(attr.type)) continue;
|
|
2793
|
+
const isSelectLike = attr.type === "select" || attr.type === "status" || attr.type === "multiselect";
|
|
2794
|
+
if (isSelectLike && !hasOptions(attr)) continue;
|
|
2795
|
+
if (attr.type === "multiselect" && Array.isArray(value) && value.length === 0) continue;
|
|
2796
|
+
const formatted = formatAttributeValue(value, attr);
|
|
2797
|
+
if (formatted && formatted !== EMPTY_VALUE_PLACEHOLDER) {
|
|
2798
|
+
enriched[attr.name] = formatted;
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
return enriched;
|
|
2802
|
+
}
|
|
2803
|
+
var enrichValuesWithSelectLabels = enrichValuesForDisplay;
|
|
2804
|
+
function extractRelationIds(val) {
|
|
2805
|
+
if (typeof val === "string") return [val];
|
|
2806
|
+
if (Array.isArray(val) && typeof val[0] === "string") return [val[0]];
|
|
2807
|
+
return [];
|
|
2808
|
+
}
|
|
2809
|
+
async function computeLabelWithRelations(template, values, attributes, resolveRelationIds) {
|
|
2810
|
+
let enrichedValues = enrichValuesForDisplay(values, attributes);
|
|
2811
|
+
const attrNames = extractAttributeNames(template);
|
|
2812
|
+
const relationAttrs = attributes.filter(
|
|
2813
|
+
(attr) => attr.type === "relation" && attrNames.includes(attr.name)
|
|
2814
|
+
);
|
|
2815
|
+
if (relationAttrs.length === 0) {
|
|
2816
|
+
return renderLabelExpression(template, enrichedValues);
|
|
2817
|
+
}
|
|
2818
|
+
const allIds = [];
|
|
2819
|
+
for (const attr of relationAttrs) {
|
|
2820
|
+
const ids = extractRelationIds(values[attr.name]);
|
|
2821
|
+
allIds.push(...ids);
|
|
2822
|
+
}
|
|
2823
|
+
if (allIds.length === 0) {
|
|
2824
|
+
return renderLabelExpression(template, enrichedValues);
|
|
2825
|
+
}
|
|
2826
|
+
const resolvedMap = await resolveRelationIds(allIds);
|
|
2827
|
+
enrichedValues = { ...enrichedValues };
|
|
2828
|
+
for (const attr of relationAttrs) {
|
|
2829
|
+
const ids = extractRelationIds(values[attr.name]);
|
|
2830
|
+
if (ids.length > 0 && resolvedMap.has(ids[0])) {
|
|
2831
|
+
enrichedValues[attr.name] = resolvedMap.get(ids[0]);
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
return renderLabelExpression(template, enrichedValues);
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2837
|
+
// src/runtime/executors/assign.executor.ts
|
|
2838
|
+
function buildSlotRef(src) {
|
|
2839
|
+
const hasProps = src.properties && Object.keys(src.properties).length > 0;
|
|
2840
|
+
if (hasProps) {
|
|
2841
|
+
return { id: `$slot:${src.slotId}`, props: src.properties };
|
|
2842
|
+
}
|
|
2843
|
+
return `$slot:${src.slotId}`;
|
|
2844
|
+
}
|
|
2845
|
+
function buildSlotValuesMap(slots) {
|
|
2846
|
+
const values = {};
|
|
2847
|
+
for (const [slotId, slotData] of Object.entries(slots)) {
|
|
2848
|
+
if (slotData && typeof slotData === "object") {
|
|
2849
|
+
values[slotId] = { ...slotData };
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
return values;
|
|
2853
|
+
}
|
|
2854
|
+
var AssignExecutor = class {
|
|
2855
|
+
constructor() {
|
|
2856
|
+
this.nodeType = "assign";
|
|
2857
|
+
}
|
|
2858
|
+
execute(node, ctx) {
|
|
2859
|
+
if (!node.next) {
|
|
2860
|
+
return error("MISSING_NEXT", "AssignNode must have a 'next' target");
|
|
2861
|
+
}
|
|
2862
|
+
if (!node.targetSlotId) {
|
|
2863
|
+
return error("MISSING_TARGET_SLOT", "AssignNode must have a target slot");
|
|
2864
|
+
}
|
|
2865
|
+
const existing = ctx.executionContext.slots[node.targetSlotId] ?? {};
|
|
2866
|
+
const targetSlot = { ...existing };
|
|
2867
|
+
const slotValues = buildSlotValuesMap(ctx.executionContext.slots);
|
|
2868
|
+
for (const assignment of node.assignments) {
|
|
2869
|
+
targetSlot[assignment.targetAttribute] = resolveSource(assignment.source, slotValues);
|
|
2870
|
+
}
|
|
2871
|
+
const contextUpdates = {
|
|
2872
|
+
slots: { [node.targetSlotId]: targetSlot }
|
|
2873
|
+
};
|
|
2874
|
+
return success(node.next, contextUpdates);
|
|
2875
|
+
}
|
|
2876
|
+
validate(node) {
|
|
2877
|
+
const errors = [];
|
|
2878
|
+
if (!node.targetSlotId) {
|
|
2879
|
+
errors.push("AssignNode must have a target slot");
|
|
2880
|
+
}
|
|
2881
|
+
if (!node.assignments?.length) {
|
|
2882
|
+
errors.push("AssignNode must have at least one assignment");
|
|
2883
|
+
}
|
|
2884
|
+
if (!node.next) {
|
|
2885
|
+
errors.push("AssignNode must have a 'next' target");
|
|
2886
|
+
}
|
|
2887
|
+
return errors;
|
|
2888
|
+
}
|
|
2889
|
+
};
|
|
2890
|
+
function resolveSource(source, slotValues) {
|
|
2891
|
+
switch (source.type) {
|
|
2892
|
+
case "expression":
|
|
2893
|
+
return renderLabelExpression(source.template, slotValues, "");
|
|
2894
|
+
case "slot-ref":
|
|
2895
|
+
return buildSlotRef(source);
|
|
2896
|
+
case "static":
|
|
2897
|
+
return source.value;
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
// src/runtime/executors/start.executor.ts
|
|
2902
|
+
var StartExecutor = class {
|
|
2903
|
+
constructor() {
|
|
2904
|
+
this.nodeType = "start";
|
|
2905
|
+
}
|
|
2906
|
+
execute(node, _context) {
|
|
2907
|
+
if (!node.next) {
|
|
2908
|
+
return error("MISSING_NEXT", `StartNode "${node.id}" has no 'next' target defined`);
|
|
2909
|
+
}
|
|
2910
|
+
return success(node.next);
|
|
2325
2911
|
}
|
|
2326
2912
|
canExecute(_node, _context) {
|
|
2327
2913
|
return true;
|
|
@@ -2341,7 +2927,7 @@ function createDefaultExecutorRegistry() {
|
|
|
2341
2927
|
registry2.register(new StartExecutor());
|
|
2342
2928
|
registry2.register(new FormExecutor());
|
|
2343
2929
|
registry2.register(new ConditionExecutor());
|
|
2344
|
-
registry2.register(new
|
|
2930
|
+
registry2.register(new AssignExecutor());
|
|
2345
2931
|
registry2.register(new EndExecutor());
|
|
2346
2932
|
return registry2;
|
|
2347
2933
|
}
|
|
@@ -3367,512 +3953,188 @@ var SchemaErrorCode = {
|
|
|
3367
3953
|
// Duplicates
|
|
3368
3954
|
DUPLICATE_OBJECT: "SCHEMA_DUPLICATE_OBJECT",
|
|
3369
3955
|
DUPLICATE_ATTRIBUTE: "SCHEMA_DUPLICATE_ATTRIBUTE",
|
|
3370
|
-
// Concurrency
|
|
3371
|
-
CONFLICT: "SCHEMA_CONFLICT"
|
|
3372
|
-
};
|
|
3373
|
-
var SchemaError = class extends Error {
|
|
3374
|
-
constructor(message, code = SchemaErrorCode.UNKNOWN, details) {
|
|
3375
|
-
super(message);
|
|
3376
|
-
this.name = "SchemaError";
|
|
3377
|
-
this.code = code;
|
|
3378
|
-
this.details = details;
|
|
3379
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
3380
|
-
}
|
|
3381
|
-
toJSON() {
|
|
3382
|
-
return {
|
|
3383
|
-
name: this.name,
|
|
3384
|
-
code: this.code,
|
|
3385
|
-
message: this.message,
|
|
3386
|
-
details: this.details
|
|
3387
|
-
};
|
|
3388
|
-
}
|
|
3389
|
-
};
|
|
3390
|
-
var NotFoundError = class extends SchemaError {
|
|
3391
|
-
constructor(resourceType, resourceId, code = SchemaErrorCode.RECORD_NOT_FOUND) {
|
|
3392
|
-
super(`${resourceType} with id "${resourceId}" not found`, code, {
|
|
3393
|
-
resourceType,
|
|
3394
|
-
resourceId
|
|
3395
|
-
});
|
|
3396
|
-
this.name = "NotFoundError";
|
|
3397
|
-
this.resourceType = resourceType;
|
|
3398
|
-
this.resourceId = resourceId;
|
|
3399
|
-
}
|
|
3400
|
-
};
|
|
3401
|
-
var ObjectNotFoundError = class extends NotFoundError {
|
|
3402
|
-
constructor(objectId) {
|
|
3403
|
-
super("Object", objectId, SchemaErrorCode.OBJECT_NOT_FOUND);
|
|
3404
|
-
this.name = "ObjectNotFoundError";
|
|
3405
|
-
}
|
|
3406
|
-
};
|
|
3407
|
-
var AttributeNotFoundError = class extends NotFoundError {
|
|
3408
|
-
constructor(attributeId) {
|
|
3409
|
-
super("Attribute", attributeId, SchemaErrorCode.ATTRIBUTE_NOT_FOUND);
|
|
3410
|
-
this.name = "AttributeNotFoundError";
|
|
3411
|
-
}
|
|
3412
|
-
};
|
|
3413
|
-
var RecordNotFoundError = class extends NotFoundError {
|
|
3414
|
-
constructor(recordId) {
|
|
3415
|
-
super("Record", recordId, SchemaErrorCode.RECORD_NOT_FOUND);
|
|
3416
|
-
this.name = "RecordNotFoundError";
|
|
3417
|
-
}
|
|
3418
|
-
};
|
|
3419
|
-
var UserProfileNotFoundError = class extends NotFoundError {
|
|
3420
|
-
constructor(identifier) {
|
|
3421
|
-
super("UserProfile", identifier, SchemaErrorCode.USER_PROFILE_NOT_FOUND);
|
|
3422
|
-
this.name = "UserProfileNotFoundError";
|
|
3423
|
-
}
|
|
3424
|
-
};
|
|
3425
|
-
var FileNotFoundError = class extends NotFoundError {
|
|
3426
|
-
constructor(fileId) {
|
|
3427
|
-
super("File", fileId, SchemaErrorCode.FILE_NOT_FOUND);
|
|
3428
|
-
this.name = "FileNotFoundError";
|
|
3429
|
-
}
|
|
3430
|
-
};
|
|
3431
|
-
var ValidationError = class _ValidationError extends SchemaError {
|
|
3432
|
-
constructor(message, errors) {
|
|
3433
|
-
super(message, SchemaErrorCode.VALIDATION_FAILED, { errors });
|
|
3434
|
-
this.name = "ValidationError";
|
|
3435
|
-
this.errors = errors;
|
|
3436
|
-
}
|
|
3437
|
-
/**
|
|
3438
|
-
* Create a validation error from Zod-style errors
|
|
3439
|
-
*/
|
|
3440
|
-
static fromZodErrors(errors) {
|
|
3441
|
-
const details = errors.map((err) => ({
|
|
3442
|
-
path: err.path.map(String),
|
|
3443
|
-
message: err.message
|
|
3444
|
-
}));
|
|
3445
|
-
const message = `Validation failed: ${details.map((d) => `${d.path.join(".")}: ${d.message}`).join(", ")}`;
|
|
3446
|
-
return new _ValidationError(message, details);
|
|
3447
|
-
}
|
|
3448
|
-
};
|
|
3449
|
-
var ProtectedResourceError = class extends SchemaError {
|
|
3450
|
-
constructor(resourceType, resourceName, operation) {
|
|
3451
|
-
const code = resourceType === "object" ? SchemaErrorCode.PROTECTED_OBJECT : resourceType === "view" ? SchemaErrorCode.PROTECTED_VIEW : SchemaErrorCode.PROTECTED_ATTRIBUTE;
|
|
3452
|
-
super(`Cannot ${operation} system ${resourceType} "${resourceName}"`, code, {
|
|
3453
|
-
resourceType,
|
|
3454
|
-
resourceName,
|
|
3455
|
-
operation
|
|
3456
|
-
});
|
|
3457
|
-
this.name = "ProtectedResourceError";
|
|
3458
|
-
this.resourceType = resourceType;
|
|
3459
|
-
this.resourceName = resourceName;
|
|
3460
|
-
this.operation = operation;
|
|
3461
|
-
}
|
|
3462
|
-
};
|
|
3463
|
-
var SyncError = class extends SchemaError {
|
|
3464
|
-
constructor(objectName, message, cause) {
|
|
3465
|
-
super(`Failed to sync object "${objectName}": ${message}`, SchemaErrorCode.SYNC_FAILED, {
|
|
3466
|
-
objectName,
|
|
3467
|
-
cause: cause?.message
|
|
3468
|
-
});
|
|
3469
|
-
this.name = "SyncError";
|
|
3470
|
-
this.objectName = objectName;
|
|
3471
|
-
this.cause = cause;
|
|
3472
|
-
}
|
|
3473
|
-
};
|
|
3474
|
-
var NotSystemObjectError = class extends SchemaError {
|
|
3475
|
-
constructor(objectName) {
|
|
3476
|
-
super(
|
|
3477
|
-
`Object "${objectName}" is not marked as system. Native objects must have system=true.`,
|
|
3478
|
-
SchemaErrorCode.NOT_SYSTEM_OBJECT,
|
|
3479
|
-
{ objectName }
|
|
3480
|
-
);
|
|
3481
|
-
this.name = "NotSystemObjectError";
|
|
3482
|
-
this.objectName = objectName;
|
|
3483
|
-
}
|
|
3956
|
+
// Concurrency
|
|
3957
|
+
CONFLICT: "SCHEMA_CONFLICT"
|
|
3484
3958
|
};
|
|
3485
|
-
var
|
|
3486
|
-
constructor(
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3959
|
+
var SchemaError = class extends Error {
|
|
3960
|
+
constructor(message, code = SchemaErrorCode.UNKNOWN, details) {
|
|
3961
|
+
super(message);
|
|
3962
|
+
this.name = "SchemaError";
|
|
3963
|
+
this.code = code;
|
|
3964
|
+
this.details = details;
|
|
3965
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
3966
|
+
}
|
|
3967
|
+
toJSON() {
|
|
3968
|
+
return {
|
|
3969
|
+
name: this.name,
|
|
3970
|
+
code: this.code,
|
|
3971
|
+
message: this.message,
|
|
3972
|
+
details: this.details
|
|
3973
|
+
};
|
|
3496
3974
|
}
|
|
3497
3975
|
};
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
}
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
}
|
|
3504
|
-
function isValidationError(error2) {
|
|
3505
|
-
return error2 instanceof ValidationError;
|
|
3506
|
-
}
|
|
3507
|
-
function isProtectedResourceError(error2) {
|
|
3508
|
-
return error2 instanceof ProtectedResourceError;
|
|
3509
|
-
}
|
|
3510
|
-
var ForbiddenError = class extends SchemaError {
|
|
3511
|
-
constructor(objectName, action, userId) {
|
|
3512
|
-
super(`No ${action} permission on object "${objectName}"`, SchemaErrorCode.FORBIDDEN, {
|
|
3513
|
-
objectName,
|
|
3514
|
-
action,
|
|
3515
|
-
userId
|
|
3976
|
+
var NotFoundError = class extends SchemaError {
|
|
3977
|
+
constructor(resourceType, resourceId, code = SchemaErrorCode.RECORD_NOT_FOUND) {
|
|
3978
|
+
super(`${resourceType} with id "${resourceId}" not found`, code, {
|
|
3979
|
+
resourceType,
|
|
3980
|
+
resourceId
|
|
3516
3981
|
});
|
|
3517
|
-
this.name = "
|
|
3518
|
-
this.
|
|
3519
|
-
this.
|
|
3520
|
-
this.userId = userId;
|
|
3982
|
+
this.name = "NotFoundError";
|
|
3983
|
+
this.resourceType = resourceType;
|
|
3984
|
+
this.resourceId = resourceId;
|
|
3521
3985
|
}
|
|
3522
3986
|
};
|
|
3523
|
-
var
|
|
3524
|
-
constructor(
|
|
3525
|
-
super(
|
|
3526
|
-
|
|
3527
|
-
operation
|
|
3528
|
-
});
|
|
3529
|
-
this.name = "ProtectedRoleError";
|
|
3530
|
-
this.roleName = roleName;
|
|
3531
|
-
this.operation = operation;
|
|
3987
|
+
var ObjectNotFoundError = class extends NotFoundError {
|
|
3988
|
+
constructor(objectId) {
|
|
3989
|
+
super("Object", objectId, SchemaErrorCode.OBJECT_NOT_FOUND);
|
|
3990
|
+
this.name = "ObjectNotFoundError";
|
|
3532
3991
|
}
|
|
3533
3992
|
};
|
|
3534
|
-
var
|
|
3535
|
-
constructor(
|
|
3536
|
-
super("
|
|
3537
|
-
this.name = "
|
|
3993
|
+
var AttributeNotFoundError = class extends NotFoundError {
|
|
3994
|
+
constructor(attributeId) {
|
|
3995
|
+
super("Attribute", attributeId, SchemaErrorCode.ATTRIBUTE_NOT_FOUND);
|
|
3996
|
+
this.name = "AttributeNotFoundError";
|
|
3538
3997
|
}
|
|
3539
3998
|
};
|
|
3540
|
-
|
|
3541
|
-
return error2 instanceof ForbiddenError;
|
|
3542
|
-
}
|
|
3543
|
-
var ConcurrentModificationError = class extends SchemaError {
|
|
3999
|
+
var RecordNotFoundError = class extends NotFoundError {
|
|
3544
4000
|
constructor(recordId) {
|
|
3545
|
-
super(
|
|
3546
|
-
|
|
3547
|
-
SchemaErrorCode.CONFLICT,
|
|
3548
|
-
{ recordId }
|
|
3549
|
-
);
|
|
4001
|
+
super("Record", recordId, SchemaErrorCode.RECORD_NOT_FOUND);
|
|
4002
|
+
this.name = "RecordNotFoundError";
|
|
3550
4003
|
}
|
|
3551
4004
|
};
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
return String(value);
|
|
3557
|
-
}
|
|
3558
|
-
function formatCheckbox(value) {
|
|
3559
|
-
return value ? "Yes" : "No";
|
|
3560
|
-
}
|
|
3561
|
-
function formatNumber(value, attribute) {
|
|
3562
|
-
if (typeof value !== "number") return String(value);
|
|
3563
|
-
const decimals = attribute.decimals;
|
|
3564
|
-
if (attribute.unit === "integer") {
|
|
3565
|
-
return value.toLocaleString(void 0, {
|
|
3566
|
-
minimumFractionDigits: 0,
|
|
3567
|
-
maximumFractionDigits: 0
|
|
3568
|
-
});
|
|
3569
|
-
}
|
|
3570
|
-
if (attribute.unit === "percentage") {
|
|
3571
|
-
return (value / 100).toLocaleString(void 0, {
|
|
3572
|
-
style: "percent",
|
|
3573
|
-
minimumFractionDigits: decimals,
|
|
3574
|
-
maximumFractionDigits: decimals
|
|
3575
|
-
});
|
|
3576
|
-
}
|
|
3577
|
-
return value.toLocaleString(void 0, {
|
|
3578
|
-
minimumFractionDigits: decimals,
|
|
3579
|
-
maximumFractionDigits: decimals
|
|
3580
|
-
});
|
|
3581
|
-
}
|
|
3582
|
-
function formatCurrency(value, _attribute) {
|
|
3583
|
-
if (typeof value !== "object" || value === null) return String(value);
|
|
3584
|
-
const currency2 = value;
|
|
3585
|
-
if (!("value" in currency2 && "code" in currency2)) return String(value);
|
|
3586
|
-
const formattedValue = currency2.value.toLocaleString(void 0, {
|
|
3587
|
-
minimumFractionDigits: 2,
|
|
3588
|
-
maximumFractionDigits: 2
|
|
3589
|
-
});
|
|
3590
|
-
return `${formattedValue} ${currency2.code}`;
|
|
3591
|
-
}
|
|
3592
|
-
function formatDate(value) {
|
|
3593
|
-
if (value instanceof Date) {
|
|
3594
|
-
return value.toISOString().split("T")[0];
|
|
3595
|
-
}
|
|
3596
|
-
if (typeof value === "string") {
|
|
3597
|
-
const date2 = new Date(value);
|
|
3598
|
-
if (!Number.isNaN(date2.getTime())) {
|
|
3599
|
-
return date2.toISOString().split("T")[0];
|
|
3600
|
-
}
|
|
3601
|
-
}
|
|
3602
|
-
return String(value);
|
|
3603
|
-
}
|
|
3604
|
-
function formatPhone(value) {
|
|
3605
|
-
if (typeof value !== "object" || value === null) return String(value);
|
|
3606
|
-
const phone2 = value;
|
|
3607
|
-
if (!("phoneNumber" in phone2)) return String(value);
|
|
3608
|
-
if (!phone2.phoneNumber) return "";
|
|
3609
|
-
if (!phone2.countryCode) return phone2.phoneNumber;
|
|
3610
|
-
return formatPhoneForDisplay(phone2);
|
|
3611
|
-
}
|
|
3612
|
-
function formatLocation(value, attribute) {
|
|
3613
|
-
if (typeof value !== "object" || value === null) return String(value);
|
|
3614
|
-
const loc = value;
|
|
3615
|
-
const granularity = attribute.granularity ?? "full";
|
|
3616
|
-
const parts = [];
|
|
3617
|
-
switch (granularity) {
|
|
3618
|
-
case "country":
|
|
3619
|
-
if (loc.country) parts.push(loc.country);
|
|
3620
|
-
break;
|
|
3621
|
-
case "state":
|
|
3622
|
-
if (loc.state) parts.push(loc.state);
|
|
3623
|
-
if (loc.country) parts.push(loc.country);
|
|
3624
|
-
break;
|
|
3625
|
-
case "city":
|
|
3626
|
-
if (loc.city) parts.push(loc.city);
|
|
3627
|
-
if (loc.state) parts.push(loc.state);
|
|
3628
|
-
if (loc.country) parts.push(loc.country);
|
|
3629
|
-
break;
|
|
3630
|
-
case "coordinates":
|
|
3631
|
-
if (loc.latitude !== void 0 && loc.longitude !== void 0) {
|
|
3632
|
-
parts.push(`${loc.latitude}, ${loc.longitude}`);
|
|
3633
|
-
}
|
|
3634
|
-
break;
|
|
3635
|
-
case "address":
|
|
3636
|
-
if (loc.address) parts.push(loc.address);
|
|
3637
|
-
if (loc.city) parts.push(loc.city);
|
|
3638
|
-
if (loc.state) parts.push(loc.state);
|
|
3639
|
-
if (loc.country) parts.push(loc.country);
|
|
3640
|
-
break;
|
|
3641
|
-
default:
|
|
3642
|
-
if (loc.address) parts.push(loc.address);
|
|
3643
|
-
if (loc.city) parts.push(loc.city);
|
|
3644
|
-
if (loc.state) parts.push(loc.state);
|
|
3645
|
-
if (loc.postalCode) parts.push(loc.postalCode);
|
|
3646
|
-
if (loc.country) parts.push(loc.country);
|
|
3647
|
-
break;
|
|
4005
|
+
var UserProfileNotFoundError = class extends NotFoundError {
|
|
4006
|
+
constructor(identifier) {
|
|
4007
|
+
super("UserProfile", identifier, SchemaErrorCode.USER_PROFILE_NOT_FOUND);
|
|
4008
|
+
this.name = "UserProfileNotFoundError";
|
|
3648
4009
|
}
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
return option?.label ?? String(value);
|
|
3655
|
-
}
|
|
3656
|
-
function formatMultiselect(value, attribute) {
|
|
3657
|
-
if (!Array.isArray(value)) return String(value);
|
|
3658
|
-
if (attribute.options) {
|
|
3659
|
-
const labels = value.map((v) => attribute.options.find((o) => o.value === v)?.label).filter(Boolean);
|
|
3660
|
-
return labels.join(", ");
|
|
4010
|
+
};
|
|
4011
|
+
var FileNotFoundError = class extends NotFoundError {
|
|
4012
|
+
constructor(fileId) {
|
|
4013
|
+
super("File", fileId, SchemaErrorCode.FILE_NOT_FOUND);
|
|
4014
|
+
this.name = "FileNotFoundError";
|
|
3661
4015
|
}
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
}
|
|
3669
|
-
function formatAttributeValue(value, attribute) {
|
|
3670
|
-
if (value === null || value === void 0 || value === "") {
|
|
3671
|
-
return EMPTY_VALUE_PLACEHOLDER;
|
|
4016
|
+
};
|
|
4017
|
+
var ValidationError = class _ValidationError extends SchemaError {
|
|
4018
|
+
constructor(message, errors) {
|
|
4019
|
+
super(message, SchemaErrorCode.VALIDATION_FAILED, { errors });
|
|
4020
|
+
this.name = "ValidationError";
|
|
4021
|
+
this.errors = errors;
|
|
3672
4022
|
}
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
case "date":
|
|
3684
|
-
return formatDate(value);
|
|
3685
|
-
case "phone":
|
|
3686
|
-
return formatPhone(value);
|
|
3687
|
-
case "location":
|
|
3688
|
-
return formatLocation(value, attribute);
|
|
3689
|
-
case "select":
|
|
3690
|
-
case "status":
|
|
3691
|
-
return formatSelect(value, attribute);
|
|
3692
|
-
case "multiselect":
|
|
3693
|
-
return formatMultiselect(value, attribute);
|
|
3694
|
-
case "rating":
|
|
3695
|
-
return formatRating(value, attribute);
|
|
3696
|
-
// Unsupported types - return value as-is or placeholder
|
|
3697
|
-
case "file":
|
|
3698
|
-
case "user":
|
|
3699
|
-
case "relation":
|
|
3700
|
-
if (Array.isArray(value)) {
|
|
3701
|
-
return value.join(", ");
|
|
3702
|
-
}
|
|
3703
|
-
return String(value);
|
|
3704
|
-
default: {
|
|
3705
|
-
if (Array.isArray(value)) {
|
|
3706
|
-
return value.join(", ");
|
|
3707
|
-
}
|
|
3708
|
-
return String(value);
|
|
3709
|
-
}
|
|
4023
|
+
/**
|
|
4024
|
+
* Create a validation error from Zod-style errors
|
|
4025
|
+
*/
|
|
4026
|
+
static fromZodErrors(errors) {
|
|
4027
|
+
const details = errors.map((err) => ({
|
|
4028
|
+
path: err.path.map(String),
|
|
4029
|
+
message: err.message
|
|
4030
|
+
}));
|
|
4031
|
+
const message = `Validation failed: ${details.map((d) => `${d.path.join(".")}: ${d.message}`).join(", ")}`;
|
|
4032
|
+
return new _ValidationError(message, details);
|
|
3710
4033
|
}
|
|
3711
|
-
}
|
|
3712
|
-
|
|
3713
|
-
// src/runtime/template.ts
|
|
3714
|
-
var simplePipes = {
|
|
3715
|
-
/** Convert to uppercase */
|
|
3716
|
-
UPPER: (v) => String(v).toUpperCase(),
|
|
3717
|
-
/** Convert to lowercase */
|
|
3718
|
-
LOWER: (v) => String(v).toLowerCase(),
|
|
3719
|
-
/** Capitalize first letter of each word */
|
|
3720
|
-
capitalize: (v) => String(v).replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
3721
|
-
/** Trim whitespace from both ends */
|
|
3722
|
-
trim: (v) => String(v).trim()
|
|
3723
4034
|
};
|
|
3724
|
-
var
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
4035
|
+
var ProtectedResourceError = class extends SchemaError {
|
|
4036
|
+
constructor(resourceType, resourceName, operation) {
|
|
4037
|
+
const code = resourceType === "object" ? SchemaErrorCode.PROTECTED_OBJECT : resourceType === "view" ? SchemaErrorCode.PROTECTED_VIEW : SchemaErrorCode.PROTECTED_ATTRIBUTE;
|
|
4038
|
+
super(`Cannot ${operation} system ${resourceType} "${resourceName}"`, code, {
|
|
4039
|
+
resourceType,
|
|
4040
|
+
resourceName,
|
|
4041
|
+
operation
|
|
4042
|
+
});
|
|
4043
|
+
this.name = "ProtectedResourceError";
|
|
4044
|
+
this.resourceType = resourceType;
|
|
4045
|
+
this.resourceName = resourceName;
|
|
4046
|
+
this.operation = operation;
|
|
4047
|
+
}
|
|
3733
4048
|
};
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
}
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
if (!match) return { name: pipeExpr, args: [] };
|
|
3744
|
-
const name = match[1];
|
|
3745
|
-
const argsStr = match[2];
|
|
3746
|
-
if (!argsStr) return { name, args: [] };
|
|
3747
|
-
const args = [];
|
|
3748
|
-
const argRegex = /["']([^"']*?)["']/g;
|
|
3749
|
-
let argMatch;
|
|
3750
|
-
while ((argMatch = argRegex.exec(argsStr)) !== null) {
|
|
3751
|
-
args.push(argMatch[1]);
|
|
4049
|
+
var SyncError = class extends SchemaError {
|
|
4050
|
+
constructor(objectName, message, cause) {
|
|
4051
|
+
super(`Failed to sync object "${objectName}": ${message}`, SchemaErrorCode.SYNC_FAILED, {
|
|
4052
|
+
objectName,
|
|
4053
|
+
cause: cause?.message
|
|
4054
|
+
});
|
|
4055
|
+
this.name = "SyncError";
|
|
4056
|
+
this.objectName = objectName;
|
|
4057
|
+
this.cause = cause;
|
|
3752
4058
|
}
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
let value = "";
|
|
3764
|
-
for (const alt of alternatives) {
|
|
3765
|
-
const v = getValue(values, alt);
|
|
3766
|
-
if (v != null && v !== "") {
|
|
3767
|
-
value = v;
|
|
3768
|
-
break;
|
|
3769
|
-
}
|
|
3770
|
-
}
|
|
3771
|
-
const isEmpty3 = value == null || value === "";
|
|
3772
|
-
if (isEmpty3 && pipes.length === 0) return "";
|
|
3773
|
-
for (const pipeExpr of pipes) {
|
|
3774
|
-
const { name: pipeName, args } = parsePipeExpression(pipeExpr);
|
|
3775
|
-
const simpleFn = simplePipes[pipeName];
|
|
3776
|
-
if (simpleFn) {
|
|
3777
|
-
if (value != null && value !== "") {
|
|
3778
|
-
value = simpleFn(String(value));
|
|
3779
|
-
}
|
|
3780
|
-
} else {
|
|
3781
|
-
const argFn = pipesWithArgs[pipeName];
|
|
3782
|
-
if (argFn) {
|
|
3783
|
-
value = argFn(String(value ?? ""), ...args);
|
|
3784
|
-
}
|
|
3785
|
-
}
|
|
3786
|
-
}
|
|
3787
|
-
return String(value ?? "");
|
|
3788
|
-
}).trim();
|
|
3789
|
-
return result || fallback;
|
|
3790
|
-
}
|
|
3791
|
-
function isLabelExpression(value) {
|
|
3792
|
-
return /\{\{\s*\S+.*\}\}/.test(value);
|
|
3793
|
-
}
|
|
3794
|
-
function extractAttributeNames(template) {
|
|
3795
|
-
const names = [];
|
|
3796
|
-
const regex = /\{\{\s*([^}]+)\s*\}\}/g;
|
|
3797
|
-
let match;
|
|
3798
|
-
while ((match = regex.exec(template)) !== null) {
|
|
3799
|
-
const expr = match[1].trim();
|
|
3800
|
-
const orParts = expr.split("||").map((s) => s.trim());
|
|
3801
|
-
const lastPart = orParts[orParts.length - 1];
|
|
3802
|
-
orParts[orParts.length - 1] = lastPart.split("|")[0].trim();
|
|
3803
|
-
for (const part of orParts) {
|
|
3804
|
-
if (!part) continue;
|
|
3805
|
-
const rootName = part.split(".")[0];
|
|
3806
|
-
if (rootName && !names.includes(rootName)) {
|
|
3807
|
-
names.push(rootName);
|
|
3808
|
-
}
|
|
3809
|
-
}
|
|
4059
|
+
};
|
|
4060
|
+
var NotSystemObjectError = class extends SchemaError {
|
|
4061
|
+
constructor(objectName) {
|
|
4062
|
+
super(
|
|
4063
|
+
`Object "${objectName}" is not marked as system. Native objects must have system=true.`,
|
|
4064
|
+
SchemaErrorCode.NOT_SYSTEM_OBJECT,
|
|
4065
|
+
{ objectName }
|
|
4066
|
+
);
|
|
4067
|
+
this.name = "NotSystemObjectError";
|
|
4068
|
+
this.objectName = objectName;
|
|
3810
4069
|
}
|
|
3811
|
-
|
|
4070
|
+
};
|
|
4071
|
+
var DuplicateError = class extends SchemaError {
|
|
4072
|
+
constructor(resourceType, resourceName) {
|
|
4073
|
+
const code = resourceType === "object" ? SchemaErrorCode.DUPLICATE_OBJECT : SchemaErrorCode.DUPLICATE_ATTRIBUTE;
|
|
4074
|
+
super(
|
|
4075
|
+
`${resourceType === "object" ? "Object" : "Attribute"} "${resourceName}" already exists`,
|
|
4076
|
+
code,
|
|
4077
|
+
{ resourceType, resourceName }
|
|
4078
|
+
);
|
|
4079
|
+
this.name = "DuplicateError";
|
|
4080
|
+
this.resourceType = resourceType;
|
|
4081
|
+
this.resourceName = resourceName;
|
|
4082
|
+
}
|
|
4083
|
+
};
|
|
4084
|
+
function isSchemaError(error2) {
|
|
4085
|
+
return error2 instanceof SchemaError;
|
|
3812
4086
|
}
|
|
3813
|
-
function
|
|
3814
|
-
return
|
|
4087
|
+
function isNotFoundError(error2) {
|
|
4088
|
+
return error2 instanceof NotFoundError;
|
|
3815
4089
|
}
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
"location",
|
|
3819
|
-
"phone",
|
|
3820
|
-
"date",
|
|
3821
|
-
"rating",
|
|
3822
|
-
"select",
|
|
3823
|
-
"status",
|
|
3824
|
-
"multiselect",
|
|
3825
|
-
"number"
|
|
3826
|
-
]);
|
|
3827
|
-
function enrichValuesForDisplay(values, attributes) {
|
|
3828
|
-
const enriched = { ...values };
|
|
3829
|
-
for (const attr of attributes) {
|
|
3830
|
-
const value = values[attr.name];
|
|
3831
|
-
if (value == null) continue;
|
|
3832
|
-
if (!FORMATTABLE_TYPES.has(attr.type)) continue;
|
|
3833
|
-
const isSelectLike = attr.type === "select" || attr.type === "status" || attr.type === "multiselect";
|
|
3834
|
-
if (isSelectLike && !hasOptions(attr)) continue;
|
|
3835
|
-
if (attr.type === "multiselect" && Array.isArray(value) && value.length === 0) continue;
|
|
3836
|
-
const formatted = formatAttributeValue(value, attr);
|
|
3837
|
-
if (formatted && formatted !== EMPTY_VALUE_PLACEHOLDER) {
|
|
3838
|
-
enriched[attr.name] = formatted;
|
|
3839
|
-
}
|
|
3840
|
-
}
|
|
3841
|
-
return enriched;
|
|
4090
|
+
function isValidationError(error2) {
|
|
4091
|
+
return error2 instanceof ValidationError;
|
|
3842
4092
|
}
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
if (typeof val === "string") return [val];
|
|
3846
|
-
if (Array.isArray(val) && typeof val[0] === "string") return [val[0]];
|
|
3847
|
-
return [];
|
|
4093
|
+
function isProtectedResourceError(error2) {
|
|
4094
|
+
return error2 instanceof ProtectedResourceError;
|
|
3848
4095
|
}
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
const ids = extractRelationIds(values[attr.name]);
|
|
3861
|
-
allIds.push(...ids);
|
|
4096
|
+
var ForbiddenError = class extends SchemaError {
|
|
4097
|
+
constructor(objectName, action, userId) {
|
|
4098
|
+
super(`No ${action} permission on object "${objectName}"`, SchemaErrorCode.FORBIDDEN, {
|
|
4099
|
+
objectName,
|
|
4100
|
+
action,
|
|
4101
|
+
userId
|
|
4102
|
+
});
|
|
4103
|
+
this.name = "ForbiddenError";
|
|
4104
|
+
this.objectName = objectName;
|
|
4105
|
+
this.action = action;
|
|
4106
|
+
this.userId = userId;
|
|
3862
4107
|
}
|
|
3863
|
-
|
|
3864
|
-
|
|
4108
|
+
};
|
|
4109
|
+
var ProtectedRoleError = class extends SchemaError {
|
|
4110
|
+
constructor(roleName, operation) {
|
|
4111
|
+
super(`Cannot ${operation} system role "${roleName}"`, SchemaErrorCode.PROTECTED_ROLE, {
|
|
4112
|
+
roleName,
|
|
4113
|
+
operation
|
|
4114
|
+
});
|
|
4115
|
+
this.name = "ProtectedRoleError";
|
|
4116
|
+
this.roleName = roleName;
|
|
4117
|
+
this.operation = operation;
|
|
3865
4118
|
}
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
enrichedValues[attr.name] = resolvedMap.get(ids[0]);
|
|
3872
|
-
}
|
|
4119
|
+
};
|
|
4120
|
+
var RoleNotFoundError = class extends NotFoundError {
|
|
4121
|
+
constructor(roleId) {
|
|
4122
|
+
super("Role", roleId, SchemaErrorCode.ROLE_NOT_FOUND);
|
|
4123
|
+
this.name = "RoleNotFoundError";
|
|
3873
4124
|
}
|
|
3874
|
-
|
|
4125
|
+
};
|
|
4126
|
+
function isForbiddenError(error2) {
|
|
4127
|
+
return error2 instanceof ForbiddenError;
|
|
3875
4128
|
}
|
|
4129
|
+
var ConcurrentModificationError = class extends SchemaError {
|
|
4130
|
+
constructor(recordId) {
|
|
4131
|
+
super(
|
|
4132
|
+
`Record ${recordId} was modified by another request. Please refresh and try again.`,
|
|
4133
|
+
SchemaErrorCode.CONFLICT,
|
|
4134
|
+
{ recordId }
|
|
4135
|
+
);
|
|
4136
|
+
}
|
|
4137
|
+
};
|
|
3876
4138
|
|
|
3877
4139
|
// src/runtime/mock/mock-object-records.ts
|
|
3878
4140
|
function createMockObjectRecordsRepository(stores) {
|
|
@@ -4398,9 +4660,12 @@ function createMockUserProfilesRepository(stores) {
|
|
|
4398
4660
|
if (!existing) {
|
|
4399
4661
|
return Promise.reject(new Error(`UserProfile ${id} not found`));
|
|
4400
4662
|
}
|
|
4663
|
+
const sanitized = Object.fromEntries(
|
|
4664
|
+
Object.entries(data).map(([k, v]) => [k, v === null ? void 0 : v])
|
|
4665
|
+
);
|
|
4401
4666
|
const updated = {
|
|
4402
4667
|
...existing,
|
|
4403
|
-
...
|
|
4668
|
+
...sanitized,
|
|
4404
4669
|
updatedAt: /* @__PURE__ */ new Date()
|
|
4405
4670
|
};
|
|
4406
4671
|
stores.userProfiles.set(id, updated);
|
|
@@ -5631,7 +5896,10 @@ var NON_SORTABLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
5631
5896
|
"richtext",
|
|
5632
5897
|
"file",
|
|
5633
5898
|
"document",
|
|
5634
|
-
"location"
|
|
5899
|
+
"location",
|
|
5900
|
+
"user",
|
|
5901
|
+
"multiselect",
|
|
5902
|
+
"relation"
|
|
5635
5903
|
]);
|
|
5636
5904
|
function isAttributeSortable(attr) {
|
|
5637
5905
|
return !NON_SORTABLE_TYPES.has(attr.type);
|
|
@@ -6563,22 +6831,6 @@ var DocumentAttributeBuilder = class extends BaseAttributeBuilder {
|
|
|
6563
6831
|
constructor(name, label) {
|
|
6564
6832
|
super("document", name, label);
|
|
6565
6833
|
}
|
|
6566
|
-
/**
|
|
6567
|
-
* Set a single required template.
|
|
6568
|
-
* Only documents using this template can be attached.
|
|
6569
|
-
*/
|
|
6570
|
-
template(templateId) {
|
|
6571
|
-
this.attr.templateId = templateId;
|
|
6572
|
-
return this;
|
|
6573
|
-
}
|
|
6574
|
-
/**
|
|
6575
|
-
* Set multiple allowed templates.
|
|
6576
|
-
* User can choose which template to use when uploading.
|
|
6577
|
-
*/
|
|
6578
|
-
templates(templateIds) {
|
|
6579
|
-
this.attr.allowedTemplates = templateIds;
|
|
6580
|
-
return this;
|
|
6581
|
-
}
|
|
6582
6834
|
/**
|
|
6583
6835
|
* Allow multiple documents.
|
|
6584
6836
|
* Value becomes string[] instead of string.
|
|
@@ -8092,6 +8344,74 @@ var WorkflowConditionBuilder = class {
|
|
|
8092
8344
|
};
|
|
8093
8345
|
}
|
|
8094
8346
|
};
|
|
8347
|
+
var WorkflowAssignBuilder = class {
|
|
8348
|
+
/** @internal */
|
|
8349
|
+
constructor(workflowBuilder, nodeId, label, targetSlotId) {
|
|
8350
|
+
this.assignments = [];
|
|
8351
|
+
this.workflowBuilder = workflowBuilder;
|
|
8352
|
+
this.nodeId = nodeId;
|
|
8353
|
+
this.label = label;
|
|
8354
|
+
this.targetSlotId = targetSlotId;
|
|
8355
|
+
}
|
|
8356
|
+
/**
|
|
8357
|
+
* Set an attribute using an expression template.
|
|
8358
|
+
* Uses mustache syntax: `{{ slotId.attribute }}`.
|
|
8359
|
+
*/
|
|
8360
|
+
expression(targetAttribute, template) {
|
|
8361
|
+
this.assignments.push({
|
|
8362
|
+
targetAttribute,
|
|
8363
|
+
source: { type: "expression", template }
|
|
8364
|
+
});
|
|
8365
|
+
return this;
|
|
8366
|
+
}
|
|
8367
|
+
/**
|
|
8368
|
+
* Link a relation attribute to a slot's record.
|
|
8369
|
+
* Optionally include qualified relation properties.
|
|
8370
|
+
*/
|
|
8371
|
+
slotRef(targetAttribute, slotId, properties) {
|
|
8372
|
+
this.assignments.push({
|
|
8373
|
+
targetAttribute,
|
|
8374
|
+
source: { type: "slot-ref", slotId, properties }
|
|
8375
|
+
});
|
|
8376
|
+
return this;
|
|
8377
|
+
}
|
|
8378
|
+
/**
|
|
8379
|
+
* Set a static value on an attribute
|
|
8380
|
+
*/
|
|
8381
|
+
setValue(targetAttribute, value) {
|
|
8382
|
+
this.assignments.push({
|
|
8383
|
+
targetAttribute,
|
|
8384
|
+
source: { type: "static", value }
|
|
8385
|
+
});
|
|
8386
|
+
return this;
|
|
8387
|
+
}
|
|
8388
|
+
/**
|
|
8389
|
+
* Set the description for this assign node
|
|
8390
|
+
*/
|
|
8391
|
+
describe(description) {
|
|
8392
|
+
this.nodeDescription = description;
|
|
8393
|
+
return this;
|
|
8394
|
+
}
|
|
8395
|
+
/**
|
|
8396
|
+
* Set the next node and complete the assign definition
|
|
8397
|
+
*/
|
|
8398
|
+
next(nodeId) {
|
|
8399
|
+
if (this.assignments.length === 0) {
|
|
8400
|
+
throw new Error(
|
|
8401
|
+
`[WorkflowBuilder] Assign "${this.nodeId}" must have at least one assignment. Use .expression(), .slotRef() or .setValue() first.`
|
|
8402
|
+
);
|
|
8403
|
+
}
|
|
8404
|
+
return this.workflowBuilder._addNode({
|
|
8405
|
+
type: "assign",
|
|
8406
|
+
id: this.nodeId,
|
|
8407
|
+
label: this.label,
|
|
8408
|
+
description: this.nodeDescription,
|
|
8409
|
+
targetSlotId: this.targetSlotId,
|
|
8410
|
+
assignments: this.assignments,
|
|
8411
|
+
next: nodeId
|
|
8412
|
+
});
|
|
8413
|
+
}
|
|
8414
|
+
};
|
|
8095
8415
|
var WorkflowEndBuilder = class {
|
|
8096
8416
|
/** @internal */
|
|
8097
8417
|
constructor(workflowBuilder, nodeId) {
|
|
@@ -8257,6 +8577,12 @@ var WorkflowBuilder = class {
|
|
|
8257
8577
|
condition(id, label) {
|
|
8258
8578
|
return new WorkflowConditionBuilder(this, id, label);
|
|
8259
8579
|
}
|
|
8580
|
+
/**
|
|
8581
|
+
* Define an assign node (set values on a target slot)
|
|
8582
|
+
*/
|
|
8583
|
+
assign(id, label, targetSlotId) {
|
|
8584
|
+
return new WorkflowAssignBuilder(this, id, label, targetSlotId);
|
|
8585
|
+
}
|
|
8260
8586
|
/**
|
|
8261
8587
|
* Define an end node
|
|
8262
8588
|
*/
|
|
@@ -8334,21 +8660,10 @@ var WorkflowBuilder = class {
|
|
|
8334
8660
|
validateNodeReferences() {
|
|
8335
8661
|
const nodeIds = new Set(Object.keys(this.data.nodes ?? {}));
|
|
8336
8662
|
for (const node of Object.values(this.data.nodes ?? {})) {
|
|
8337
|
-
|
|
8338
|
-
if (
|
|
8339
|
-
throw new Error(
|
|
8340
|
-
`[WorkflowBuilder] Node "${node.id}" references unknown node "${node.next}".`
|
|
8341
|
-
);
|
|
8342
|
-
}
|
|
8343
|
-
} else if (node.type === "condition") {
|
|
8344
|
-
if (node.onTrue && !nodeIds.has(node.onTrue)) {
|
|
8663
|
+
for (const targetId of getNodeOutputs(node)) {
|
|
8664
|
+
if (!nodeIds.has(targetId)) {
|
|
8345
8665
|
throw new Error(
|
|
8346
|
-
`[WorkflowBuilder]
|
|
8347
|
-
);
|
|
8348
|
-
}
|
|
8349
|
-
if (node.onFalse && !nodeIds.has(node.onFalse)) {
|
|
8350
|
-
throw new Error(
|
|
8351
|
-
`[WorkflowBuilder] Condition "${node.id}" references unknown node "${node.onFalse}" for onFalse.`
|
|
8666
|
+
`[WorkflowBuilder] Node "${node.id}" references unknown node "${targetId}".`
|
|
8352
8667
|
);
|
|
8353
8668
|
}
|
|
8354
8669
|
}
|
|
@@ -8357,26 +8672,11 @@ var WorkflowBuilder = class {
|
|
|
8357
8672
|
validateSlotReferences() {
|
|
8358
8673
|
const slotIds = this.data.slots?.reduce((set, s) => set.add(s.id), /* @__PURE__ */ new Set()) ?? /* @__PURE__ */ new Set();
|
|
8359
8674
|
for (const node of Object.values(this.data.nodes ?? {})) {
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
}
|
|
8366
|
-
}
|
|
8367
|
-
if (node.rows) {
|
|
8368
|
-
for (const row of node.rows) {
|
|
8369
|
-
for (const field of row.fields) {
|
|
8370
|
-
referencedSlots.add(field.slotId);
|
|
8371
|
-
}
|
|
8372
|
-
}
|
|
8373
|
-
}
|
|
8374
|
-
for (const slotId of referencedSlots) {
|
|
8375
|
-
if (!slotIds.has(slotId)) {
|
|
8376
|
-
throw new Error(
|
|
8377
|
-
`[WorkflowBuilder] Form "${node.id}" references unknown slot "${slotId}".`
|
|
8378
|
-
);
|
|
8379
|
-
}
|
|
8675
|
+
for (const slotId of getNodeSlotIds(node)) {
|
|
8676
|
+
if (!slotIds.has(slotId)) {
|
|
8677
|
+
throw new Error(
|
|
8678
|
+
`[WorkflowBuilder] Node "${node.id}" references unknown slot "${slotId}".`
|
|
8679
|
+
);
|
|
8380
8680
|
}
|
|
8381
8681
|
}
|
|
8382
8682
|
}
|
|
@@ -8385,24 +8685,12 @@ var WorkflowBuilder = class {
|
|
|
8385
8685
|
const nodes = this.data.nodes ?? {};
|
|
8386
8686
|
const visited = /* @__PURE__ */ new Set();
|
|
8387
8687
|
const recursionStack = /* @__PURE__ */ new Set();
|
|
8388
|
-
const getNextNodes = (nodeId) => {
|
|
8389
|
-
const node = nodes[nodeId];
|
|
8390
|
-
if (!node) return [];
|
|
8391
|
-
switch (node.type) {
|
|
8392
|
-
case "start":
|
|
8393
|
-
case "form":
|
|
8394
|
-
case "document":
|
|
8395
|
-
return node.next ? [node.next] : [];
|
|
8396
|
-
case "condition":
|
|
8397
|
-
return [node.onTrue, node.onFalse].filter((n) => !!n);
|
|
8398
|
-
case "end":
|
|
8399
|
-
return [];
|
|
8400
|
-
}
|
|
8401
|
-
};
|
|
8402
8688
|
const hasCycle = (nodeId) => {
|
|
8403
8689
|
visited.add(nodeId);
|
|
8404
8690
|
recursionStack.add(nodeId);
|
|
8405
|
-
|
|
8691
|
+
const node = nodes[nodeId];
|
|
8692
|
+
if (!node) return false;
|
|
8693
|
+
for (const nextId of getNodeOutputs(node)) {
|
|
8406
8694
|
if (!visited.has(nextId)) {
|
|
8407
8695
|
if (hasCycle(nextId)) return true;
|
|
8408
8696
|
} else if (recursionStack.has(nextId)) {
|
|
@@ -8419,7 +8707,9 @@ var WorkflowBuilder = class {
|
|
|
8419
8707
|
const visit = (nodeId) => {
|
|
8420
8708
|
if (reachable.has(nodeId)) return;
|
|
8421
8709
|
reachable.add(nodeId);
|
|
8422
|
-
|
|
8710
|
+
const node = nodes[nodeId];
|
|
8711
|
+
if (!node) return;
|
|
8712
|
+
for (const nextId of getNodeOutputs(node)) {
|
|
8423
8713
|
visit(nextId);
|
|
8424
8714
|
}
|
|
8425
8715
|
};
|
|
@@ -8528,7 +8818,6 @@ var SYSTEM_ATTRIBUTES = {
|
|
|
8528
8818
|
multiple: true,
|
|
8529
8819
|
icon: "paperclip",
|
|
8530
8820
|
description: "Free-form document attachments"
|
|
8531
|
-
// No templateId = all templates allowed
|
|
8532
8821
|
}
|
|
8533
8822
|
};
|
|
8534
8823
|
function getSystemAttributeList() {
|
|
@@ -10789,14 +11078,20 @@ var RelationPropertiesService = class extends BaseService {
|
|
|
10789
11078
|
if (value === null || value === void 0) {
|
|
10790
11079
|
continue;
|
|
10791
11080
|
}
|
|
10792
|
-
if (attr.cardinality === "many"
|
|
10793
|
-
|
|
10794
|
-
|
|
10795
|
-
|
|
10796
|
-
|
|
10797
|
-
|
|
10798
|
-
|
|
10799
|
-
|
|
11081
|
+
if (attr.cardinality === "many") {
|
|
11082
|
+
if (Array.isArray(value)) {
|
|
11083
|
+
normalized[attr.name] = value.map((item) => {
|
|
11084
|
+
if (typeof item === "string") return item;
|
|
11085
|
+
if (typeof item === "object" && item !== null && "id" in item) {
|
|
11086
|
+
return item.id;
|
|
11087
|
+
}
|
|
11088
|
+
return item;
|
|
11089
|
+
});
|
|
11090
|
+
} else if (typeof value === "object" && value !== null && "id" in value) {
|
|
11091
|
+
normalized[attr.name] = [value.id];
|
|
11092
|
+
} else if (typeof value === "string") {
|
|
11093
|
+
normalized[attr.name] = [value];
|
|
11094
|
+
}
|
|
10800
11095
|
} else if (typeof value === "object" && value !== null && "id" in value) {
|
|
10801
11096
|
normalized[attr.name] = value.id;
|
|
10802
11097
|
}
|
|
@@ -12961,500 +13256,54 @@ var RollupScheduler = class {
|
|
|
12961
13256
|
const timeout = setTimeout(async () => {
|
|
12962
13257
|
await this.executeRecalculation(parentId, parentObjectId);
|
|
12963
13258
|
this.pending.delete(key);
|
|
12964
|
-
}, this.debounceMs);
|
|
12965
|
-
this.pending.set(key, { parentId, parentObjectId, timeout });
|
|
12966
|
-
if (this.pending.size >= this.maxPending) {
|
|
12967
|
-
this.flush();
|
|
12968
|
-
}
|
|
12969
|
-
}
|
|
12970
|
-
/**
|
|
12971
|
-
* Execute all pending recalculations immediately
|
|
12972
|
-
*/
|
|
12973
|
-
async flush() {
|
|
12974
|
-
const entries = Array.from(this.pending.entries());
|
|
12975
|
-
for (const [, entry] of entries) {
|
|
12976
|
-
clearTimeout(entry.timeout);
|
|
12977
|
-
}
|
|
12978
|
-
this.pending.clear();
|
|
12979
|
-
const allIds = entries.map(([, entry]) => entry.parentId);
|
|
12980
|
-
const records = await this.adapter.objectRecords.findByIds(allIds);
|
|
12981
|
-
for (const record of records) {
|
|
12982
|
-
const schema = await this.getSchemaById(record.objectId);
|
|
12983
|
-
if (schema) {
|
|
12984
|
-
await this.rollupService.recalculateAndUpdate(record, schema);
|
|
12985
|
-
}
|
|
12986
|
-
}
|
|
12987
|
-
}
|
|
12988
|
-
/**
|
|
12989
|
-
* Execute a single recalculation
|
|
12990
|
-
*/
|
|
12991
|
-
async executeRecalculation(parentId, parentObjectId) {
|
|
12992
|
-
const record = await this.adapter.objectRecords.findById(parentId);
|
|
12993
|
-
if (!record) return;
|
|
12994
|
-
const schema = await this.getSchemaById(parentObjectId);
|
|
12995
|
-
if (!schema) return;
|
|
12996
|
-
await this.rollupService.recalculateAndUpdate(record, schema);
|
|
12997
|
-
}
|
|
12998
|
-
/**
|
|
12999
|
-
* Get number of pending recalculations
|
|
13000
|
-
*/
|
|
13001
|
-
get pendingCount() {
|
|
13002
|
-
return this.pending.size;
|
|
13003
|
-
}
|
|
13004
|
-
/**
|
|
13005
|
-
* Clear all pending recalculations without executing them
|
|
13006
|
-
*/
|
|
13007
|
-
clear() {
|
|
13008
|
-
for (const [, entry] of this.pending) {
|
|
13009
|
-
clearTimeout(entry.timeout);
|
|
13010
|
-
}
|
|
13011
|
-
this.pending.clear();
|
|
13012
|
-
}
|
|
13013
|
-
};
|
|
13014
|
-
|
|
13015
|
-
// src/runtime/services/document/document-renderer.service.ts
|
|
13016
|
-
import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
|
|
13017
|
-
var DocumentRenderError = class extends Error {
|
|
13018
|
-
constructor(message, templateId, cause) {
|
|
13019
|
-
super(message);
|
|
13020
|
-
this.templateId = templateId;
|
|
13021
|
-
this.cause = cause;
|
|
13022
|
-
this.name = "DocumentRenderError";
|
|
13023
|
-
}
|
|
13024
|
-
};
|
|
13025
|
-
var StorageDownloadNotSupportedError = class extends Error {
|
|
13026
|
-
constructor() {
|
|
13027
|
-
super("Storage adapter does not support download. Required for document rendering.");
|
|
13028
|
-
this.name = "StorageDownloadNotSupportedError";
|
|
13029
|
-
}
|
|
13030
|
-
};
|
|
13031
|
-
var DocumentRendererService = class {
|
|
13032
|
-
constructor(storageAdapter, options) {
|
|
13033
|
-
this.storageAdapter = storageAdapter;
|
|
13034
|
-
this.options = options;
|
|
13035
|
-
this.schemaCache = /* @__PURE__ */ new Map();
|
|
13036
|
-
}
|
|
13037
|
-
/**
|
|
13038
|
-
* Render a document from a template and context.
|
|
13039
|
-
*
|
|
13040
|
-
* @param input - Template, context, and optional filename
|
|
13041
|
-
* @returns Generated PDF buffer with metadata
|
|
13042
|
-
* @throws DocumentRenderError if rendering fails
|
|
13043
|
-
* @throws StorageDownloadNotSupportedError if storage doesn't support download
|
|
13044
|
-
*/
|
|
13045
|
-
async render(input) {
|
|
13046
|
-
const { template, context, workflow: workflow2, filename } = input;
|
|
13047
|
-
if (template.source.type !== "pdf") {
|
|
13048
|
-
throw new DocumentRenderError("Only PDF templates are supported for rendering", template.id);
|
|
13049
|
-
}
|
|
13050
|
-
if (!this.storageAdapter.download) {
|
|
13051
|
-
throw new StorageDownloadNotSupportedError();
|
|
13052
|
-
}
|
|
13053
|
-
try {
|
|
13054
|
-
const templateBytes = await this.downloadTemplate(template.source.fileId);
|
|
13055
|
-
const pdfDoc = await PDFDocument.load(templateBytes);
|
|
13056
|
-
const pages = pdfDoc.getPages();
|
|
13057
|
-
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
|
13058
|
-
const fontBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
|
|
13059
|
-
const resolvedValues = await this.resolveAllFieldValues(
|
|
13060
|
-
template.source.fields,
|
|
13061
|
-
context,
|
|
13062
|
-
workflow2
|
|
13063
|
-
);
|
|
13064
|
-
for (const field of template.source.fields) {
|
|
13065
|
-
this.drawField(pages, field, resolvedValues.get(field.id) ?? "", font, fontBold);
|
|
13066
|
-
}
|
|
13067
|
-
const outputBytes = await pdfDoc.save();
|
|
13068
|
-
const resolvedFilename = this.interpolateFilename(filename, context, template);
|
|
13069
|
-
return {
|
|
13070
|
-
buffer: Buffer.from(outputBytes),
|
|
13071
|
-
filename: resolvedFilename,
|
|
13072
|
-
mimeType: "application/pdf",
|
|
13073
|
-
pageCount: pages.length
|
|
13074
|
-
};
|
|
13075
|
-
} catch (error2) {
|
|
13076
|
-
if (error2 instanceof DocumentRenderError || error2 instanceof StorageDownloadNotSupportedError) {
|
|
13077
|
-
throw error2;
|
|
13078
|
-
}
|
|
13079
|
-
throw new DocumentRenderError(
|
|
13080
|
-
`Failed to render document: ${getErrorMessage(error2)}`,
|
|
13081
|
-
template.id,
|
|
13082
|
-
error2
|
|
13083
|
-
);
|
|
13084
|
-
}
|
|
13085
|
-
}
|
|
13086
|
-
// ============================================================================
|
|
13087
|
-
// PRIVATE METHODS
|
|
13088
|
-
// ============================================================================
|
|
13089
|
-
/**
|
|
13090
|
-
* Download the template PDF from storage
|
|
13091
|
-
*/
|
|
13092
|
-
async downloadTemplate(fileId) {
|
|
13093
|
-
if (!this.storageAdapter.download) {
|
|
13094
|
-
throw new StorageDownloadNotSupportedError();
|
|
13095
|
-
}
|
|
13096
|
-
let storagePath = fileId;
|
|
13097
|
-
if (this.options?.filesRepository) {
|
|
13098
|
-
const file2 = await this.options.filesRepository.findById(fileId);
|
|
13099
|
-
if (!file2) {
|
|
13100
|
-
throw new Error(`Template file not found: ${fileId}`);
|
|
13101
|
-
}
|
|
13102
|
-
storagePath = file2.storagePath;
|
|
13103
|
-
}
|
|
13104
|
-
return await this.storageAdapter.download(storagePath);
|
|
13105
|
-
}
|
|
13106
|
-
/**
|
|
13107
|
-
* Resolve all field values, including relations and formatted attributes
|
|
13108
|
-
*/
|
|
13109
|
-
async resolveAllFieldValues(fields, context, workflow2) {
|
|
13110
|
-
const resolved = /* @__PURE__ */ new Map();
|
|
13111
|
-
const relationBatch = [];
|
|
13112
|
-
for (const field of fields) {
|
|
13113
|
-
const rawValue = getContextValue(context, field.contextPath);
|
|
13114
|
-
const attrInfo = await this.getAttributeInfo(field.contextPath, workflow2);
|
|
13115
|
-
if (attrInfo?.attribute) {
|
|
13116
|
-
if (attrInfo.attribute.type === "relation" && rawValue && this.options?.relationService) {
|
|
13117
|
-
const ids = Array.isArray(rawValue) ? rawValue : [rawValue];
|
|
13118
|
-
const stringIds = ids.filter((id) => typeof id === "string");
|
|
13119
|
-
if (stringIds.length > 0) {
|
|
13120
|
-
relationBatch.push({
|
|
13121
|
-
fieldId: field.id,
|
|
13122
|
-
attributeId: attrInfo.attribute.id ?? field.id,
|
|
13123
|
-
ids: stringIds
|
|
13124
|
-
});
|
|
13125
|
-
continue;
|
|
13126
|
-
}
|
|
13127
|
-
}
|
|
13128
|
-
const formatted = formatAttributeValue(rawValue, attrInfo.attribute);
|
|
13129
|
-
resolved.set(
|
|
13130
|
-
field.id,
|
|
13131
|
-
formatted === EMPTY_VALUE_PLACEHOLDER ? field.fallback ?? "" : formatted
|
|
13132
|
-
);
|
|
13133
|
-
} else {
|
|
13134
|
-
resolved.set(field.id, this.formatValueSimple(rawValue, field.fallback));
|
|
13135
|
-
}
|
|
13136
|
-
}
|
|
13137
|
-
if (relationBatch.length > 0 && this.options?.relationService) {
|
|
13138
|
-
try {
|
|
13139
|
-
const batchResult = await this.options.relationService.resolveIdsBatch(
|
|
13140
|
-
relationBatch.map((r) => ({ attributeId: r.attributeId, ids: r.ids }))
|
|
13141
|
-
);
|
|
13142
|
-
for (const { fieldId, attributeId } of relationBatch) {
|
|
13143
|
-
const options = batchResult[attributeId] ?? [];
|
|
13144
|
-
const labels = options.map((o) => o.label);
|
|
13145
|
-
const field = fields.find((f) => f.id === fieldId);
|
|
13146
|
-
resolved.set(fieldId, labels.join(", ") || field?.fallback || "");
|
|
13147
|
-
}
|
|
13148
|
-
} catch {
|
|
13149
|
-
for (const { fieldId, ids } of relationBatch) {
|
|
13150
|
-
const field = fields.find((f) => f.id === fieldId);
|
|
13151
|
-
resolved.set(fieldId, ids.join(", ") || field?.fallback || "");
|
|
13152
|
-
}
|
|
13153
|
-
}
|
|
13154
|
-
}
|
|
13155
|
-
return resolved;
|
|
13156
|
-
}
|
|
13157
|
-
/**
|
|
13158
|
-
* Get attribute info from contextPath
|
|
13159
|
-
* Parses paths like "slots.client.firstName" to find the attribute definition
|
|
13160
|
-
*/
|
|
13161
|
-
async getAttributeInfo(contextPath, workflow2) {
|
|
13162
|
-
const schemaService = this.options?.schemaService;
|
|
13163
|
-
if (!schemaService) {
|
|
13164
|
-
return null;
|
|
13165
|
-
}
|
|
13166
|
-
if (!workflow2) {
|
|
13167
|
-
return null;
|
|
13168
|
-
}
|
|
13169
|
-
const parts = contextPath.split(".");
|
|
13170
|
-
if (parts.length < 3 || parts[0] !== "slots") {
|
|
13171
|
-
return null;
|
|
13172
|
-
}
|
|
13173
|
-
const slotId = parts[1];
|
|
13174
|
-
const attributeName = parts[2];
|
|
13175
|
-
const slot = workflow2.slots?.find((s) => s.id === slotId);
|
|
13176
|
-
if (!slot) {
|
|
13177
|
-
return null;
|
|
13178
|
-
}
|
|
13179
|
-
let schema = this.schemaCache.get(slot.objectName);
|
|
13180
|
-
if (!schema) {
|
|
13181
|
-
try {
|
|
13182
|
-
schema = await schemaService.getObjectSchemaByName(slot.objectName);
|
|
13183
|
-
this.schemaCache.set(slot.objectName, schema);
|
|
13184
|
-
} catch {
|
|
13185
|
-
return null;
|
|
13186
|
-
}
|
|
13187
|
-
}
|
|
13188
|
-
if (!schema) {
|
|
13189
|
-
return null;
|
|
13190
|
-
}
|
|
13191
|
-
const attribute = schema.attributes.find((a) => a.name === attributeName);
|
|
13192
|
-
if (!attribute) {
|
|
13193
|
-
return null;
|
|
13194
|
-
}
|
|
13195
|
-
return { attribute, objectName: slot.objectName };
|
|
13196
|
-
}
|
|
13197
|
-
/**
|
|
13198
|
-
* Draw a single field on the PDF
|
|
13199
|
-
*/
|
|
13200
|
-
drawField(pages, field, value, font, fontBold) {
|
|
13201
|
-
const page = pages[field.page];
|
|
13202
|
-
if (!page) {
|
|
13203
|
-
return;
|
|
13204
|
-
}
|
|
13205
|
-
if (!value) {
|
|
13206
|
-
return;
|
|
13207
|
-
}
|
|
13208
|
-
const selectedFont = field.fontWeight === "bold" ? fontBold : font;
|
|
13209
|
-
const fontSize = field.fontSize ?? 11;
|
|
13210
|
-
const { height: pageHeight } = page.getSize();
|
|
13211
|
-
let x = field.x;
|
|
13212
|
-
if (field.align === "center" || field.align === "right") {
|
|
13213
|
-
const textWidth = selectedFont.widthOfTextAtSize(value, fontSize);
|
|
13214
|
-
if (field.align === "center") {
|
|
13215
|
-
x = field.x + (field.width - textWidth) / 2;
|
|
13216
|
-
} else {
|
|
13217
|
-
x = field.x + field.width - textWidth;
|
|
13218
|
-
}
|
|
13219
|
-
}
|
|
13220
|
-
const y = pageHeight - field.y - fontSize;
|
|
13221
|
-
page.drawText(value, {
|
|
13222
|
-
x,
|
|
13223
|
-
y,
|
|
13224
|
-
size: fontSize,
|
|
13225
|
-
font: selectedFont,
|
|
13226
|
-
color: rgb(0, 0, 0)
|
|
13227
|
-
});
|
|
13228
|
-
}
|
|
13229
|
-
/**
|
|
13230
|
-
* Simple value formatting (fallback when no attribute definition available)
|
|
13231
|
-
*/
|
|
13232
|
-
formatValueSimple(value, fallback) {
|
|
13233
|
-
if (value === null || value === void 0) {
|
|
13234
|
-
return fallback ?? "";
|
|
13235
|
-
}
|
|
13236
|
-
if (value instanceof Date) {
|
|
13237
|
-
return value.toLocaleDateString();
|
|
13238
|
-
}
|
|
13239
|
-
if (typeof value === "number") {
|
|
13240
|
-
return String(value);
|
|
13241
|
-
}
|
|
13242
|
-
if (typeof value === "boolean") {
|
|
13243
|
-
return value ? "Yes" : "No";
|
|
13244
|
-
}
|
|
13245
|
-
if (Array.isArray(value)) {
|
|
13246
|
-
return value.map((v) => this.formatValueSimple(v)).join(", ");
|
|
13247
|
-
}
|
|
13248
|
-
if (typeof value === "object") {
|
|
13249
|
-
const obj = value;
|
|
13250
|
-
if (typeof obj.label === "string") return obj.label;
|
|
13251
|
-
if (typeof obj.name === "string") return obj.name;
|
|
13252
|
-
return JSON.stringify(value);
|
|
13253
|
-
}
|
|
13254
|
-
return String(value);
|
|
13255
|
-
}
|
|
13256
|
-
/**
|
|
13257
|
-
* Interpolate filename with context values
|
|
13258
|
-
*
|
|
13259
|
-
* Supports {{path}} syntax for variable interpolation.
|
|
13260
|
-
*
|
|
13261
|
-
* @example
|
|
13262
|
-
* ```typescript
|
|
13263
|
-
* interpolateFilename("contract-{{slots.client.name}}.pdf", context, template)
|
|
13264
|
-
* // => "contract-John Doe.pdf"
|
|
13265
|
-
* ```
|
|
13266
|
-
*/
|
|
13267
|
-
interpolateFilename(template, context, docTemplate) {
|
|
13268
|
-
if (!template) {
|
|
13269
|
-
const timestamp = Date.now();
|
|
13270
|
-
const baseName = docTemplate.name || "document";
|
|
13271
|
-
return `${baseName}-${timestamp}.pdf`;
|
|
13272
|
-
}
|
|
13273
|
-
const interpolated = template.replace(/\{\{([^}]+)\}\}/g, (_, path) => {
|
|
13274
|
-
const value = getContextValue(context, path.trim());
|
|
13275
|
-
if (value === null || value === void 0) {
|
|
13276
|
-
return "";
|
|
13277
|
-
}
|
|
13278
|
-
return String(value).replace(/[<>:"/\\|?*]/g, "_").trim();
|
|
13279
|
-
});
|
|
13280
|
-
if (!interpolated.toLowerCase().endsWith(".pdf")) {
|
|
13281
|
-
return `${interpolated}.pdf`;
|
|
13282
|
-
}
|
|
13283
|
-
return interpolated;
|
|
13284
|
-
}
|
|
13285
|
-
};
|
|
13286
|
-
|
|
13287
|
-
// src/runtime/services/workflow/document-processing.hook.ts
|
|
13288
|
-
var DocumentProcessingHook = class extends BaseService {
|
|
13289
|
-
constructor(adapter, storageAdapter, options) {
|
|
13290
|
-
super(adapter);
|
|
13291
|
-
this.adapter = adapter;
|
|
13292
|
-
this.storageAdapter = storageAdapter;
|
|
13293
|
-
this.options = options;
|
|
13294
|
-
this.renderer = new DocumentRendererService(storageAdapter, {
|
|
13295
|
-
schemaService: options.schemaService,
|
|
13296
|
-
relationService: options.relationService,
|
|
13297
|
-
filesRepository: adapter.files
|
|
13298
|
-
});
|
|
13299
|
-
}
|
|
13300
|
-
/**
|
|
13301
|
-
* Process all pending document requests in the context.
|
|
13302
|
-
*
|
|
13303
|
-
* @param context - Current workflow execution context
|
|
13304
|
-
* @param workflow - Workflow definition (for slot/object info)
|
|
13305
|
-
* @param userId - User ID for audit/permissions
|
|
13306
|
-
* @returns Updated context with processed documents
|
|
13307
|
-
*/
|
|
13308
|
-
async process(context, workflow2, userId) {
|
|
13309
|
-
const pendingNodeIds = this.findPendingDocuments(context);
|
|
13310
|
-
if (pendingNodeIds.length === 0) {
|
|
13311
|
-
return context;
|
|
13312
|
-
}
|
|
13313
|
-
const updatedDocuments = { ...context.documents };
|
|
13314
|
-
for (const nodeId of pendingNodeIds) {
|
|
13315
|
-
const doc = context.documents[nodeId];
|
|
13316
|
-
const metadata = doc.metadata;
|
|
13317
|
-
if (!metadata) {
|
|
13318
|
-
continue;
|
|
13319
|
-
}
|
|
13320
|
-
try {
|
|
13321
|
-
updatedDocuments[nodeId] = {
|
|
13322
|
-
...doc,
|
|
13323
|
-
metadata: { ...metadata, status: "processing" }
|
|
13324
|
-
};
|
|
13325
|
-
const template = await this.options.documentGenerationService.getByIdOrThrow(
|
|
13326
|
-
metadata.templateId
|
|
13327
|
-
);
|
|
13328
|
-
const renderResult = await this.renderer.render({
|
|
13329
|
-
template,
|
|
13330
|
-
context,
|
|
13331
|
-
workflow: workflow2,
|
|
13332
|
-
filename: doc.filename
|
|
13333
|
-
});
|
|
13334
|
-
const uploadResult = await this.uploadGeneratedDocument(renderResult, template, userId);
|
|
13335
|
-
const attachedDocumentIds = await this.attachToRecords(
|
|
13336
|
-
renderResult,
|
|
13337
|
-
metadata.targetSlotIds,
|
|
13338
|
-
context,
|
|
13339
|
-
workflow2,
|
|
13340
|
-
userId
|
|
13341
|
-
);
|
|
13342
|
-
updatedDocuments[nodeId] = {
|
|
13343
|
-
id: uploadResult.fileId,
|
|
13344
|
-
url: uploadResult.url,
|
|
13345
|
-
filename: renderResult.filename,
|
|
13346
|
-
mimeType: renderResult.mimeType,
|
|
13347
|
-
size: renderResult.buffer.length,
|
|
13348
|
-
attachedDocumentIds,
|
|
13349
|
-
metadata: { ...metadata, status: "completed" }
|
|
13350
|
-
};
|
|
13351
|
-
} catch (error2) {
|
|
13352
|
-
const errorMessage = getErrorMessage(error2);
|
|
13353
|
-
updatedDocuments[nodeId] = {
|
|
13354
|
-
...doc,
|
|
13355
|
-
metadata: { ...metadata, status: "failed", error: errorMessage }
|
|
13356
|
-
};
|
|
13357
|
-
}
|
|
13259
|
+
}, this.debounceMs);
|
|
13260
|
+
this.pending.set(key, { parentId, parentObjectId, timeout });
|
|
13261
|
+
if (this.pending.size >= this.maxPending) {
|
|
13262
|
+
this.flush();
|
|
13358
13263
|
}
|
|
13359
|
-
return {
|
|
13360
|
-
...context,
|
|
13361
|
-
documents: updatedDocuments
|
|
13362
|
-
};
|
|
13363
13264
|
}
|
|
13364
|
-
// ============================================================================
|
|
13365
|
-
// PRIVATE METHODS
|
|
13366
|
-
// ============================================================================
|
|
13367
13265
|
/**
|
|
13368
|
-
*
|
|
13266
|
+
* Execute all pending recalculations immediately
|
|
13369
13267
|
*/
|
|
13370
|
-
|
|
13371
|
-
const
|
|
13372
|
-
for (const [
|
|
13373
|
-
|
|
13374
|
-
|
|
13375
|
-
|
|
13268
|
+
async flush() {
|
|
13269
|
+
const entries = Array.from(this.pending.entries());
|
|
13270
|
+
for (const [, entry] of entries) {
|
|
13271
|
+
clearTimeout(entry.timeout);
|
|
13272
|
+
}
|
|
13273
|
+
this.pending.clear();
|
|
13274
|
+
const allIds = entries.map(([, entry]) => entry.parentId);
|
|
13275
|
+
const records = await this.adapter.objectRecords.findByIds(allIds);
|
|
13276
|
+
for (const record of records) {
|
|
13277
|
+
const schema = await this.getSchemaById(record.objectId);
|
|
13278
|
+
if (schema) {
|
|
13279
|
+
await this.rollupService.recalculateAndUpdate(record, schema);
|
|
13376
13280
|
}
|
|
13377
13281
|
}
|
|
13378
|
-
return pendingIds;
|
|
13379
13282
|
}
|
|
13380
13283
|
/**
|
|
13381
|
-
*
|
|
13284
|
+
* Execute a single recalculation
|
|
13382
13285
|
*/
|
|
13383
|
-
async
|
|
13384
|
-
const
|
|
13385
|
-
|
|
13386
|
-
|
|
13387
|
-
|
|
13388
|
-
|
|
13389
|
-
tenantId: this.tenantId,
|
|
13390
|
-
folderPath: `generated-documents/${template.name}`
|
|
13391
|
-
});
|
|
13392
|
-
let fileId = `file-${Date.now()}`;
|
|
13393
|
-
if (this.adapter.files) {
|
|
13394
|
-
const fileRecord = await this.adapter.files.create({
|
|
13395
|
-
name: renderResult.filename,
|
|
13396
|
-
originalName: renderResult.filename,
|
|
13397
|
-
mimeType: renderResult.mimeType,
|
|
13398
|
-
size: renderResult.buffer.length,
|
|
13399
|
-
storageProvider: uploadResult.storageProvider,
|
|
13400
|
-
storagePath: uploadResult.storagePath,
|
|
13401
|
-
storageBucket: uploadResult.storageBucket,
|
|
13402
|
-
url: uploadResult.url,
|
|
13403
|
-
uploadedBy: userId,
|
|
13404
|
-
visibility: "private"
|
|
13405
|
-
});
|
|
13406
|
-
fileId = fileRecord.id;
|
|
13407
|
-
}
|
|
13408
|
-
return {
|
|
13409
|
-
fileId,
|
|
13410
|
-
url: uploadResult.url,
|
|
13411
|
-
storagePath: uploadResult.storagePath
|
|
13412
|
-
};
|
|
13286
|
+
async executeRecalculation(parentId, parentObjectId) {
|
|
13287
|
+
const record = await this.adapter.objectRecords.findById(parentId);
|
|
13288
|
+
if (!record) return;
|
|
13289
|
+
const schema = await this.getSchemaById(parentObjectId);
|
|
13290
|
+
if (!schema) return;
|
|
13291
|
+
await this.rollupService.recalculateAndUpdate(record, schema);
|
|
13413
13292
|
}
|
|
13414
13293
|
/**
|
|
13415
|
-
*
|
|
13294
|
+
* Get number of pending recalculations
|
|
13416
13295
|
*/
|
|
13417
|
-
|
|
13418
|
-
|
|
13419
|
-
|
|
13420
|
-
|
|
13421
|
-
|
|
13422
|
-
|
|
13423
|
-
|
|
13424
|
-
|
|
13425
|
-
|
|
13426
|
-
if (!recordId) {
|
|
13427
|
-
continue;
|
|
13428
|
-
}
|
|
13429
|
-
const slotDef = workflow2.slots?.find((s) => s.id === slotId);
|
|
13430
|
-
const objectName = slotDef?.objectName;
|
|
13431
|
-
if (!objectName) {
|
|
13432
|
-
continue;
|
|
13433
|
-
}
|
|
13434
|
-
const result = await documentService.createRecordDocument({
|
|
13435
|
-
objectName,
|
|
13436
|
-
recordId,
|
|
13437
|
-
fileContent: renderResult.buffer,
|
|
13438
|
-
fileName: renderResult.filename,
|
|
13439
|
-
mimeType: renderResult.mimeType,
|
|
13440
|
-
fileSize: renderResult.buffer.length,
|
|
13441
|
-
uploadedBy: userId,
|
|
13442
|
-
title: renderResult.filename
|
|
13443
|
-
});
|
|
13444
|
-
attachedDocumentIds.push(result.document.id);
|
|
13445
|
-
const record = await recordService.getRecord(recordId);
|
|
13446
|
-
if (record) {
|
|
13447
|
-
const attachments = record.values?.attachments ?? [];
|
|
13448
|
-
await recordService.updateRecord(
|
|
13449
|
-
recordId,
|
|
13450
|
-
{ attachments: [...attachments, result.document.id] },
|
|
13451
|
-
{ partial: true }
|
|
13452
|
-
);
|
|
13453
|
-
}
|
|
13454
|
-
} catch {
|
|
13455
|
-
}
|
|
13296
|
+
get pendingCount() {
|
|
13297
|
+
return this.pending.size;
|
|
13298
|
+
}
|
|
13299
|
+
/**
|
|
13300
|
+
* Clear all pending recalculations without executing them
|
|
13301
|
+
*/
|
|
13302
|
+
clear() {
|
|
13303
|
+
for (const [, entry] of this.pending) {
|
|
13304
|
+
clearTimeout(entry.timeout);
|
|
13456
13305
|
}
|
|
13457
|
-
|
|
13306
|
+
this.pending.clear();
|
|
13458
13307
|
}
|
|
13459
13308
|
};
|
|
13460
13309
|
|
|
@@ -13765,7 +13614,6 @@ var WorkflowInstanceService = class extends BaseService {
|
|
|
13765
13614
|
this.executorRegistry = options?.executorRegistry ?? getDefaultExecutorRegistry();
|
|
13766
13615
|
this.schemaService = options?.schemaService;
|
|
13767
13616
|
this.recordService = options?.recordService;
|
|
13768
|
-
this.documentProcessingHook = options?.documentProcessingHook;
|
|
13769
13617
|
}
|
|
13770
13618
|
/**
|
|
13771
13619
|
* Start a new workflow instance
|
|
@@ -14111,14 +13959,7 @@ var WorkflowInstanceService = class extends BaseService {
|
|
|
14111
13959
|
}
|
|
14112
13960
|
case "complete": {
|
|
14113
13961
|
try {
|
|
14114
|
-
|
|
14115
|
-
if (this.documentProcessingHook) {
|
|
14116
|
-
updatedContext = await this.documentProcessingHook.process(
|
|
14117
|
-
updatedContext,
|
|
14118
|
-
current.workflowSnapshot,
|
|
14119
|
-
current.startedBy
|
|
14120
|
-
);
|
|
14121
|
-
}
|
|
13962
|
+
const updatedContext = await this.persistSlots(current);
|
|
14122
13963
|
current = {
|
|
14123
13964
|
...current,
|
|
14124
13965
|
context: updatedContext,
|
|
@@ -15470,471 +15311,10 @@ var UserProfileService = class extends BaseService {
|
|
|
15470
15311
|
}
|
|
15471
15312
|
};
|
|
15472
15313
|
|
|
15473
|
-
// src/runtime/services/document/document-generation.service.ts
|
|
15474
|
-
var DocumentGenerationTemplateNotFoundError = class extends Error {
|
|
15475
|
-
constructor(templateId) {
|
|
15476
|
-
super(`Document generation template not found: ${templateId}`);
|
|
15477
|
-
this.templateId = templateId;
|
|
15478
|
-
this.name = "DocumentGenerationTemplateNotFoundError";
|
|
15479
|
-
}
|
|
15480
|
-
};
|
|
15481
|
-
var DocumentGenerationNotConfiguredError = class extends Error {
|
|
15482
|
-
constructor() {
|
|
15483
|
-
super(
|
|
15484
|
-
"DocumentGenerationTemplatesRepository not available. Enable document generation in adapter."
|
|
15485
|
-
);
|
|
15486
|
-
this.name = "DocumentGenerationNotConfiguredError";
|
|
15487
|
-
}
|
|
15488
|
-
};
|
|
15489
|
-
var DocumentGenerationService = class extends BaseService {
|
|
15490
|
-
/**
|
|
15491
|
-
* Get the document generation templates repository.
|
|
15492
|
-
* @throws DocumentGenerationNotConfiguredError if repository not available
|
|
15493
|
-
*/
|
|
15494
|
-
get repo() {
|
|
15495
|
-
const repo = this.adapter.documentGenerationTemplates;
|
|
15496
|
-
if (!repo) {
|
|
15497
|
-
throw new DocumentGenerationNotConfiguredError();
|
|
15498
|
-
}
|
|
15499
|
-
return repo;
|
|
15500
|
-
}
|
|
15501
|
-
// ============================================================================
|
|
15502
|
-
// READ OPERATIONS
|
|
15503
|
-
// ============================================================================
|
|
15504
|
-
/**
|
|
15505
|
-
* Get a template by ID.
|
|
15506
|
-
*
|
|
15507
|
-
* @param id - Template ID
|
|
15508
|
-
* @returns Template or null if not found
|
|
15509
|
-
*/
|
|
15510
|
-
async getById(id) {
|
|
15511
|
-
return await this.repo.findById(id);
|
|
15512
|
-
}
|
|
15513
|
-
/**
|
|
15514
|
-
* Get a template by ID, throwing if not found.
|
|
15515
|
-
*
|
|
15516
|
-
* @param id - Template ID
|
|
15517
|
-
* @returns Template
|
|
15518
|
-
* @throws DocumentGenerationTemplateNotFoundError if not found
|
|
15519
|
-
*/
|
|
15520
|
-
async getByIdOrThrow(id) {
|
|
15521
|
-
const template = await this.repo.findById(id);
|
|
15522
|
-
if (!template) {
|
|
15523
|
-
throw new DocumentGenerationTemplateNotFoundError(id);
|
|
15524
|
-
}
|
|
15525
|
-
return template;
|
|
15526
|
-
}
|
|
15527
|
-
/**
|
|
15528
|
-
* Get a template by name.
|
|
15529
|
-
*
|
|
15530
|
-
* @param name - Template name (unique within tenant)
|
|
15531
|
-
* @returns Template or null if not found
|
|
15532
|
-
*/
|
|
15533
|
-
async getByName(name) {
|
|
15534
|
-
return await this.repo.findByName(name);
|
|
15535
|
-
}
|
|
15536
|
-
/**
|
|
15537
|
-
* List templates for the current tenant.
|
|
15538
|
-
*
|
|
15539
|
-
* @param options - List options (sourceType filter, pagination)
|
|
15540
|
-
* @returns Array of templates
|
|
15541
|
-
*/
|
|
15542
|
-
async list(options) {
|
|
15543
|
-
return await this.repo.list(options);
|
|
15544
|
-
}
|
|
15545
|
-
// ============================================================================
|
|
15546
|
-
// WRITE OPERATIONS
|
|
15547
|
-
// ============================================================================
|
|
15548
|
-
/**
|
|
15549
|
-
* Create a new document generation template.
|
|
15550
|
-
*
|
|
15551
|
-
* @param input - Template data
|
|
15552
|
-
* @returns Created template
|
|
15553
|
-
*/
|
|
15554
|
-
async create(input) {
|
|
15555
|
-
return await this.repo.create(input);
|
|
15556
|
-
}
|
|
15557
|
-
/**
|
|
15558
|
-
* Update a template.
|
|
15559
|
-
*
|
|
15560
|
-
* @param id - Template ID
|
|
15561
|
-
* @param input - Fields to update
|
|
15562
|
-
* @returns Updated template
|
|
15563
|
-
*/
|
|
15564
|
-
async update(id, input) {
|
|
15565
|
-
return await this.repo.update(id, input);
|
|
15566
|
-
}
|
|
15567
|
-
/**
|
|
15568
|
-
* Delete a template.
|
|
15569
|
-
*
|
|
15570
|
-
* @param id - Template ID
|
|
15571
|
-
*/
|
|
15572
|
-
async delete(id) {
|
|
15573
|
-
await this.repo.delete(id);
|
|
15574
|
-
}
|
|
15575
|
-
};
|
|
15576
|
-
|
|
15577
|
-
// src/templates/index.ts
|
|
15578
|
-
var SYSTEM_TEMPLATE_IDS = {
|
|
15579
|
-
FRENCH_ID_CARD: "00000000-0000-0000-0001-000000000001",
|
|
15580
|
-
PASSPORT: "00000000-0000-0000-0001-000000000002",
|
|
15581
|
-
DRIVING_LICENSE: "00000000-0000-0000-0001-000000000003",
|
|
15582
|
-
PROOF_OF_ADDRESS: "00000000-0000-0000-0001-000000000004",
|
|
15583
|
-
SIGNABLE_CONTRACT: "00000000-0000-0000-0001-000000000005",
|
|
15584
|
-
GENERIC_DOCUMENT: "00000000-0000-0000-0001-000000000006"
|
|
15585
|
-
};
|
|
15586
|
-
var FRENCH_ID_CARD = {
|
|
15587
|
-
id: SYSTEM_TEMPLATE_IDS.FRENCH_ID_CARD,
|
|
15588
|
-
tenantId: null,
|
|
15589
|
-
name: "french_id_card",
|
|
15590
|
-
label: "Carte d'identit\xE9 fran\xE7aise",
|
|
15591
|
-
description: "Carte nationale d'identit\xE9 fran\xE7aise (recto/verso)",
|
|
15592
|
-
icon: "credit-card",
|
|
15593
|
-
system: true,
|
|
15594
|
-
slots: [
|
|
15595
|
-
{
|
|
15596
|
-
name: "front",
|
|
15597
|
-
label: "Recto",
|
|
15598
|
-
description: "Face avant de la carte d'identit\xE9",
|
|
15599
|
-
required: true,
|
|
15600
|
-
allowedMimeTypes: ["image/jpeg", "image/png", "image/webp"],
|
|
15601
|
-
maxSize: 10 * 1024 * 1024,
|
|
15602
|
-
// 10MB
|
|
15603
|
-
order: 1
|
|
15604
|
-
},
|
|
15605
|
-
{
|
|
15606
|
-
name: "back",
|
|
15607
|
-
label: "Verso",
|
|
15608
|
-
description: "Face arri\xE8re de la carte d'identit\xE9",
|
|
15609
|
-
required: true,
|
|
15610
|
-
allowedMimeTypes: ["image/jpeg", "image/png", "image/webp"],
|
|
15611
|
-
maxSize: 10 * 1024 * 1024,
|
|
15612
|
-
order: 2
|
|
15613
|
-
}
|
|
15614
|
-
],
|
|
15615
|
-
allowAdditionalFiles: false,
|
|
15616
|
-
autoProcessing: {
|
|
15617
|
-
ocr: { enabled: true },
|
|
15618
|
-
identityVerification: { enabled: true, documentType: "national_id" }
|
|
15619
|
-
},
|
|
15620
|
-
createdAt: /* @__PURE__ */ new Date("2024-01-01"),
|
|
15621
|
-
updatedAt: /* @__PURE__ */ new Date("2024-01-01")
|
|
15622
|
-
};
|
|
15623
|
-
var PASSPORT = {
|
|
15624
|
-
id: SYSTEM_TEMPLATE_IDS.PASSPORT,
|
|
15625
|
-
tenantId: null,
|
|
15626
|
-
name: "passport",
|
|
15627
|
-
label: "Passeport",
|
|
15628
|
-
description: "Passeport international",
|
|
15629
|
-
icon: "badge",
|
|
15630
|
-
system: true,
|
|
15631
|
-
slots: [
|
|
15632
|
-
{
|
|
15633
|
-
name: "data_page",
|
|
15634
|
-
label: "Page de donn\xE9es",
|
|
15635
|
-
description: "Page avec photo et MRZ",
|
|
15636
|
-
required: true,
|
|
15637
|
-
allowedMimeTypes: ["image/jpeg", "image/png", "image/webp", "application/pdf"],
|
|
15638
|
-
maxSize: 10 * 1024 * 1024,
|
|
15639
|
-
order: 1
|
|
15640
|
-
}
|
|
15641
|
-
],
|
|
15642
|
-
allowAdditionalFiles: true,
|
|
15643
|
-
autoProcessing: {
|
|
15644
|
-
ocr: { enabled: true },
|
|
15645
|
-
identityVerification: { enabled: true, documentType: "passport" }
|
|
15646
|
-
},
|
|
15647
|
-
createdAt: /* @__PURE__ */ new Date("2024-01-01"),
|
|
15648
|
-
updatedAt: /* @__PURE__ */ new Date("2024-01-01")
|
|
15649
|
-
};
|
|
15650
|
-
var DRIVING_LICENSE = {
|
|
15651
|
-
id: SYSTEM_TEMPLATE_IDS.DRIVING_LICENSE,
|
|
15652
|
-
tenantId: null,
|
|
15653
|
-
name: "driving_license",
|
|
15654
|
-
label: "Permis de conduire",
|
|
15655
|
-
description: "Permis de conduire",
|
|
15656
|
-
icon: "credit-card-check",
|
|
15657
|
-
system: true,
|
|
15658
|
-
slots: [
|
|
15659
|
-
{
|
|
15660
|
-
name: "front",
|
|
15661
|
-
label: "Recto",
|
|
15662
|
-
required: true,
|
|
15663
|
-
allowedMimeTypes: ["image/jpeg", "image/png", "image/webp"],
|
|
15664
|
-
maxSize: 10 * 1024 * 1024,
|
|
15665
|
-
order: 1
|
|
15666
|
-
},
|
|
15667
|
-
{
|
|
15668
|
-
name: "back",
|
|
15669
|
-
label: "Verso",
|
|
15670
|
-
required: false,
|
|
15671
|
-
allowedMimeTypes: ["image/jpeg", "image/png", "image/webp"],
|
|
15672
|
-
maxSize: 10 * 1024 * 1024,
|
|
15673
|
-
order: 2
|
|
15674
|
-
}
|
|
15675
|
-
],
|
|
15676
|
-
allowAdditionalFiles: false,
|
|
15677
|
-
autoProcessing: {
|
|
15678
|
-
ocr: { enabled: true },
|
|
15679
|
-
identityVerification: { enabled: true, documentType: "driving_license" }
|
|
15680
|
-
},
|
|
15681
|
-
createdAt: /* @__PURE__ */ new Date("2024-01-01"),
|
|
15682
|
-
updatedAt: /* @__PURE__ */ new Date("2024-01-01")
|
|
15683
|
-
};
|
|
15684
|
-
var PROOF_OF_ADDRESS = {
|
|
15685
|
-
id: SYSTEM_TEMPLATE_IDS.PROOF_OF_ADDRESS,
|
|
15686
|
-
tenantId: null,
|
|
15687
|
-
name: "proof_of_address",
|
|
15688
|
-
label: "Justificatif de domicile",
|
|
15689
|
-
description: "Facture, relev\xE9 bancaire ou attestation de moins de 3 mois",
|
|
15690
|
-
icon: "home",
|
|
15691
|
-
system: true,
|
|
15692
|
-
slots: [
|
|
15693
|
-
{
|
|
15694
|
-
name: "document",
|
|
15695
|
-
label: "Document",
|
|
15696
|
-
required: true,
|
|
15697
|
-
allowedMimeTypes: ["image/jpeg", "image/png", "image/webp", "application/pdf"],
|
|
15698
|
-
maxSize: 10 * 1024 * 1024,
|
|
15699
|
-
order: 1
|
|
15700
|
-
}
|
|
15701
|
-
],
|
|
15702
|
-
allowAdditionalFiles: false,
|
|
15703
|
-
autoProcessing: {
|
|
15704
|
-
ocr: { enabled: true }
|
|
15705
|
-
},
|
|
15706
|
-
createdAt: /* @__PURE__ */ new Date("2024-01-01"),
|
|
15707
|
-
updatedAt: /* @__PURE__ */ new Date("2024-01-01")
|
|
15708
|
-
};
|
|
15709
|
-
var SIGNABLE_CONTRACT = {
|
|
15710
|
-
id: SYSTEM_TEMPLATE_IDS.SIGNABLE_CONTRACT,
|
|
15711
|
-
tenantId: null,
|
|
15712
|
-
name: "signable_contract",
|
|
15713
|
-
label: "Contrat \xE0 signer",
|
|
15714
|
-
description: "Document PDF n\xE9cessitant une signature \xE9lectronique",
|
|
15715
|
-
icon: "file-text",
|
|
15716
|
-
system: true,
|
|
15717
|
-
slots: [
|
|
15718
|
-
{
|
|
15719
|
-
name: "contract",
|
|
15720
|
-
label: "Contrat",
|
|
15721
|
-
required: true,
|
|
15722
|
-
allowedMimeTypes: ["application/pdf"],
|
|
15723
|
-
maxSize: 50 * 1024 * 1024,
|
|
15724
|
-
// 50MB for contracts
|
|
15725
|
-
order: 1
|
|
15726
|
-
}
|
|
15727
|
-
],
|
|
15728
|
-
allowAdditionalFiles: true,
|
|
15729
|
-
autoProcessing: {
|
|
15730
|
-
signature: { enabled: true }
|
|
15731
|
-
},
|
|
15732
|
-
createdAt: /* @__PURE__ */ new Date("2024-01-01"),
|
|
15733
|
-
updatedAt: /* @__PURE__ */ new Date("2024-01-01")
|
|
15734
|
-
};
|
|
15735
|
-
var GENERIC_DOCUMENT = {
|
|
15736
|
-
id: SYSTEM_TEMPLATE_IDS.GENERIC_DOCUMENT,
|
|
15737
|
-
tenantId: null,
|
|
15738
|
-
name: "generic_document",
|
|
15739
|
-
label: "Document",
|
|
15740
|
-
description: "Document g\xE9n\xE9rique",
|
|
15741
|
-
icon: "file",
|
|
15742
|
-
system: true,
|
|
15743
|
-
slots: [
|
|
15744
|
-
{
|
|
15745
|
-
name: "file",
|
|
15746
|
-
label: "Fichier",
|
|
15747
|
-
required: true,
|
|
15748
|
-
order: 1
|
|
15749
|
-
}
|
|
15750
|
-
],
|
|
15751
|
-
allowAdditionalFiles: true,
|
|
15752
|
-
autoProcessing: {},
|
|
15753
|
-
createdAt: /* @__PURE__ */ new Date("2024-01-01"),
|
|
15754
|
-
updatedAt: /* @__PURE__ */ new Date("2024-01-01")
|
|
15755
|
-
};
|
|
15756
|
-
var SYSTEM_TEMPLATES = [
|
|
15757
|
-
FRENCH_ID_CARD,
|
|
15758
|
-
PASSPORT,
|
|
15759
|
-
DRIVING_LICENSE,
|
|
15760
|
-
PROOF_OF_ADDRESS,
|
|
15761
|
-
SIGNABLE_CONTRACT,
|
|
15762
|
-
GENERIC_DOCUMENT
|
|
15763
|
-
];
|
|
15764
|
-
function getSystemTemplate(name) {
|
|
15765
|
-
return SYSTEM_TEMPLATES.find((t) => t.name === name);
|
|
15766
|
-
}
|
|
15767
|
-
function isSystemTemplate(name) {
|
|
15768
|
-
return SYSTEM_TEMPLATES.some((t) => t.name === name);
|
|
15769
|
-
}
|
|
15770
|
-
|
|
15771
|
-
// src/runtime/services/document/document-template.service.ts
|
|
15772
|
-
var DocumentTemplateService = class extends BaseService {
|
|
15773
|
-
constructor(adapter) {
|
|
15774
|
-
super(adapter);
|
|
15775
|
-
}
|
|
15776
|
-
// ============================================================================
|
|
15777
|
-
// READ
|
|
15778
|
-
// ============================================================================
|
|
15779
|
-
/**
|
|
15780
|
-
* Get a template by ID.
|
|
15781
|
-
* Checks custom templates first, then system templates.
|
|
15782
|
-
*/
|
|
15783
|
-
async getTemplate(templateId) {
|
|
15784
|
-
const systemTemplate = SYSTEM_TEMPLATES.find((t) => t.id === templateId);
|
|
15785
|
-
if (systemTemplate) {
|
|
15786
|
-
return systemTemplate;
|
|
15787
|
-
}
|
|
15788
|
-
if (!this.adapter.documentTemplates) {
|
|
15789
|
-
return null;
|
|
15790
|
-
}
|
|
15791
|
-
return await this.adapter.documentTemplates.findById(templateId);
|
|
15792
|
-
}
|
|
15793
|
-
/**
|
|
15794
|
-
* Get a template by name.
|
|
15795
|
-
* Checks custom templates first (tenant-specific), then system templates.
|
|
15796
|
-
*/
|
|
15797
|
-
async getTemplateByName(name) {
|
|
15798
|
-
if (this.adapter.documentTemplates) {
|
|
15799
|
-
const customTemplate = await this.adapter.documentTemplates.findByName(name);
|
|
15800
|
-
if (customTemplate) {
|
|
15801
|
-
return customTemplate;
|
|
15802
|
-
}
|
|
15803
|
-
}
|
|
15804
|
-
return getSystemTemplate(name) ?? null;
|
|
15805
|
-
}
|
|
15806
|
-
/**
|
|
15807
|
-
* Get multiple templates by names.
|
|
15808
|
-
*/
|
|
15809
|
-
async getTemplatesByNames(names) {
|
|
15810
|
-
const results = [];
|
|
15811
|
-
const missingNames = [];
|
|
15812
|
-
for (const name of names) {
|
|
15813
|
-
const systemTemplate = getSystemTemplate(name);
|
|
15814
|
-
if (systemTemplate) {
|
|
15815
|
-
results.push(systemTemplate);
|
|
15816
|
-
} else {
|
|
15817
|
-
missingNames.push(name);
|
|
15818
|
-
}
|
|
15819
|
-
}
|
|
15820
|
-
if (missingNames.length > 0 && this.adapter.documentTemplates) {
|
|
15821
|
-
const customTemplates = await this.adapter.documentTemplates.findByNames(missingNames);
|
|
15822
|
-
results.push(...customTemplates);
|
|
15823
|
-
}
|
|
15824
|
-
return results;
|
|
15825
|
-
}
|
|
15826
|
-
/**
|
|
15827
|
-
* Get a template or throw if not found.
|
|
15828
|
-
*/
|
|
15829
|
-
async getTemplateOrThrow(templateId) {
|
|
15830
|
-
const template = await this.getTemplate(templateId);
|
|
15831
|
-
if (!template) {
|
|
15832
|
-
throw new Error(`Document template with id "${templateId}" not found`);
|
|
15833
|
-
}
|
|
15834
|
-
return template;
|
|
15835
|
-
}
|
|
15836
|
-
/**
|
|
15837
|
-
* Get a template by name or throw if not found.
|
|
15838
|
-
*/
|
|
15839
|
-
async getTemplateByNameOrThrow(name) {
|
|
15840
|
-
const template = await this.getTemplateByName(name);
|
|
15841
|
-
if (!template) {
|
|
15842
|
-
throw new Error(`Document template "${name}" not found`);
|
|
15843
|
-
}
|
|
15844
|
-
return template;
|
|
15845
|
-
}
|
|
15846
|
-
// ============================================================================
|
|
15847
|
-
// LIST
|
|
15848
|
-
// ============================================================================
|
|
15849
|
-
/**
|
|
15850
|
-
* List all available templates.
|
|
15851
|
-
* Includes both system templates and tenant-specific templates.
|
|
15852
|
-
*/
|
|
15853
|
-
async listTemplates(options) {
|
|
15854
|
-
if (options?.systemOnly) {
|
|
15855
|
-
return SYSTEM_TEMPLATES;
|
|
15856
|
-
}
|
|
15857
|
-
const templates = [...SYSTEM_TEMPLATES];
|
|
15858
|
-
if (this.adapter.documentTemplates) {
|
|
15859
|
-
const customTemplates = await this.adapter.documentTemplates.list(options);
|
|
15860
|
-
templates.push(...customTemplates);
|
|
15861
|
-
}
|
|
15862
|
-
return templates;
|
|
15863
|
-
}
|
|
15864
|
-
/**
|
|
15865
|
-
* Get only system templates.
|
|
15866
|
-
*/
|
|
15867
|
-
getSystemTemplates() {
|
|
15868
|
-
return SYSTEM_TEMPLATES;
|
|
15869
|
-
}
|
|
15870
|
-
// ============================================================================
|
|
15871
|
-
// CREATE
|
|
15872
|
-
// ============================================================================
|
|
15873
|
-
/**
|
|
15874
|
-
* Create a custom template.
|
|
15875
|
-
* System templates cannot be created via this method.
|
|
15876
|
-
*/
|
|
15877
|
-
async createTemplate(data) {
|
|
15878
|
-
if (!this.adapter.documentTemplates) {
|
|
15879
|
-
throw new Error("Document templates repository is not configured");
|
|
15880
|
-
}
|
|
15881
|
-
const existingSystem = getSystemTemplate(data.name);
|
|
15882
|
-
if (existingSystem) {
|
|
15883
|
-
throw new Error(`Template name "${data.name}" is reserved for a system template`);
|
|
15884
|
-
}
|
|
15885
|
-
const existing = await this.adapter.documentTemplates.findByName(data.name);
|
|
15886
|
-
if (existing) {
|
|
15887
|
-
throw new Error(`Template with name "${data.name}" already exists`);
|
|
15888
|
-
}
|
|
15889
|
-
return await this.adapter.documentTemplates.create(data);
|
|
15890
|
-
}
|
|
15891
|
-
// ============================================================================
|
|
15892
|
-
// UPDATE
|
|
15893
|
-
// ============================================================================
|
|
15894
|
-
/**
|
|
15895
|
-
* Update a custom template.
|
|
15896
|
-
* System templates cannot be updated.
|
|
15897
|
-
*/
|
|
15898
|
-
async updateTemplate(templateId, data) {
|
|
15899
|
-
if (!this.adapter.documentTemplates) {
|
|
15900
|
-
throw new Error("Document templates repository is not configured");
|
|
15901
|
-
}
|
|
15902
|
-
const existing = await this.getTemplate(templateId);
|
|
15903
|
-
if (!existing) {
|
|
15904
|
-
throw new Error(`Document template with id "${templateId}" not found`);
|
|
15905
|
-
}
|
|
15906
|
-
if (existing.system) {
|
|
15907
|
-
throw new Error("System templates cannot be modified");
|
|
15908
|
-
}
|
|
15909
|
-
return await this.adapter.documentTemplates.update(templateId, data);
|
|
15910
|
-
}
|
|
15911
|
-
// ============================================================================
|
|
15912
|
-
// DELETE
|
|
15913
|
-
// ============================================================================
|
|
15914
|
-
/**
|
|
15915
|
-
* Delete a custom template.
|
|
15916
|
-
* System templates cannot be deleted.
|
|
15917
|
-
*/
|
|
15918
|
-
async deleteTemplate(templateId) {
|
|
15919
|
-
if (!this.adapter.documentTemplates) {
|
|
15920
|
-
throw new Error("Document templates repository is not configured");
|
|
15921
|
-
}
|
|
15922
|
-
const existing = await this.getTemplate(templateId);
|
|
15923
|
-
if (!existing) {
|
|
15924
|
-
throw new Error(`Document template with id "${templateId}" not found`);
|
|
15925
|
-
}
|
|
15926
|
-
if (existing.system) {
|
|
15927
|
-
throw new Error("System templates cannot be deleted");
|
|
15928
|
-
}
|
|
15929
|
-
await this.adapter.documentTemplates.delete(templateId);
|
|
15930
|
-
}
|
|
15931
|
-
};
|
|
15932
|
-
|
|
15933
15314
|
// src/runtime/services/document/document.service.ts
|
|
15934
15315
|
var DocumentService = class extends BaseService {
|
|
15935
15316
|
constructor(adapter, options) {
|
|
15936
15317
|
super(adapter);
|
|
15937
|
-
this.templateService = options?.templateService ?? new DocumentTemplateService(adapter);
|
|
15938
15318
|
this.fileService = options?.fileService ?? null;
|
|
15939
15319
|
}
|
|
15940
15320
|
// ============================================================================
|
|
@@ -15950,22 +15330,8 @@ var DocumentService = class extends BaseService {
|
|
|
15950
15330
|
if (!this.adapter.documents) {
|
|
15951
15331
|
throw new Error("Documents repository is not configured");
|
|
15952
15332
|
}
|
|
15953
|
-
const template = await this.templateService.getTemplate(data.templateId);
|
|
15954
|
-
if (!template) {
|
|
15955
|
-
throw new Error(`Template with id "${data.templateId}" not found`);
|
|
15956
|
-
}
|
|
15957
15333
|
return await this.adapter.documents.create(data);
|
|
15958
15334
|
}
|
|
15959
|
-
/**
|
|
15960
|
-
* Create a document with a template name instead of ID.
|
|
15961
|
-
*/
|
|
15962
|
-
async createDocumentByTemplateName(templateName, data) {
|
|
15963
|
-
const template = await this.templateService.getTemplateByNameOrThrow(templateName);
|
|
15964
|
-
return await this.createDocument({
|
|
15965
|
-
...data,
|
|
15966
|
-
templateId: template.id
|
|
15967
|
-
});
|
|
15968
|
-
}
|
|
15969
15335
|
// ============================================================================
|
|
15970
15336
|
// READ
|
|
15971
15337
|
// ============================================================================
|
|
@@ -15997,13 +15363,6 @@ var DocumentService = class extends BaseService {
|
|
|
15997
15363
|
}
|
|
15998
15364
|
return await this.adapter.documents.findByIds(documentIds);
|
|
15999
15365
|
}
|
|
16000
|
-
/**
|
|
16001
|
-
* Get the template for a document.
|
|
16002
|
-
*/
|
|
16003
|
-
async getDocumentTemplate(documentId) {
|
|
16004
|
-
const document2 = await this.getDocumentOrThrow(documentId);
|
|
16005
|
-
return await this.templateService.getTemplateOrThrow(document2.templateId);
|
|
16006
|
-
}
|
|
16007
15366
|
// ============================================================================
|
|
16008
15367
|
// LIST
|
|
16009
15368
|
// ============================================================================
|
|
@@ -16094,27 +15453,19 @@ var DocumentService = class extends BaseService {
|
|
|
16094
15453
|
if (!this.adapter.documentSlots) {
|
|
16095
15454
|
throw new Error("Document slots repository is not configured");
|
|
16096
15455
|
}
|
|
16097
|
-
|
|
16098
|
-
const template = await this.templateService.getTemplateOrThrow(document2.templateId);
|
|
16099
|
-
const slotDef = template.slots.find((s) => s.name === data.slotName);
|
|
16100
|
-
const isAdditional = !slotDef;
|
|
16101
|
-
if (isAdditional && !template.allowAdditionalFiles) {
|
|
16102
|
-
throw new Error(
|
|
16103
|
-
`Slot "${data.slotName}" is not defined in template and additional files are not allowed`
|
|
16104
|
-
);
|
|
16105
|
-
}
|
|
15456
|
+
await this.getDocumentOrThrow(documentId);
|
|
16106
15457
|
const existingSlot = await this.adapter.documentSlots.findByDocumentAndSlot(
|
|
16107
15458
|
documentId,
|
|
16108
15459
|
data.slotName
|
|
16109
15460
|
);
|
|
16110
|
-
if (existingSlot
|
|
15461
|
+
if (existingSlot) {
|
|
16111
15462
|
await this.adapter.documentSlots.delete(existingSlot.id);
|
|
16112
15463
|
}
|
|
16113
15464
|
const slot = await this.adapter.documentSlots.create({
|
|
16114
15465
|
documentId,
|
|
16115
15466
|
slotName: data.slotName,
|
|
16116
15467
|
fileId: data.fileId,
|
|
16117
|
-
isAdditional
|
|
15468
|
+
isAdditional: data.isAdditional ?? false
|
|
16118
15469
|
});
|
|
16119
15470
|
await this.recalculateStatus(documentId);
|
|
16120
15471
|
return slot;
|
|
@@ -16148,13 +15499,9 @@ var DocumentService = class extends BaseService {
|
|
|
16148
15499
|
* - failed: At least one job failed
|
|
16149
15500
|
*/
|
|
16150
15501
|
async recalculateStatus(documentId) {
|
|
16151
|
-
|
|
16152
|
-
const template = await this.templateService.getTemplateOrThrow(document2.templateId);
|
|
15502
|
+
await this.getDocumentOrThrow(documentId);
|
|
16153
15503
|
const slots = await this.getSlots(documentId);
|
|
16154
|
-
|
|
16155
|
-
const filledSlotNames = slots.reduce((set, s) => set.add(s.slotName), /* @__PURE__ */ new Set());
|
|
16156
|
-
const allRequiredFilled = requiredSlots.every((s) => filledSlotNames.has(s.name));
|
|
16157
|
-
if (!allRequiredFilled) {
|
|
15504
|
+
if (slots.length === 0) {
|
|
16158
15505
|
return await this.updateStatus(documentId, "draft");
|
|
16159
15506
|
}
|
|
16160
15507
|
if (this.adapter.documentJobs) {
|
|
@@ -16189,13 +15536,12 @@ var DocumentService = class extends BaseService {
|
|
|
16189
15536
|
return document2?.status !== "draft";
|
|
16190
15537
|
}
|
|
16191
15538
|
/**
|
|
16192
|
-
* Get document with its
|
|
15539
|
+
* Get document with its slots.
|
|
16193
15540
|
*/
|
|
16194
15541
|
async getDocumentWithDetails(documentId) {
|
|
16195
15542
|
const document2 = await this.getDocumentOrThrow(documentId);
|
|
16196
|
-
const template = await this.templateService.getTemplateOrThrow(document2.templateId);
|
|
16197
15543
|
const slots = await this.getSlots(documentId);
|
|
16198
|
-
return { document: document2,
|
|
15544
|
+
return { document: document2, slots };
|
|
16199
15545
|
}
|
|
16200
15546
|
// ============================================================================
|
|
16201
15547
|
// RECORD DOCUMENTS
|
|
@@ -16258,7 +15604,7 @@ var DocumentService = class extends BaseService {
|
|
|
16258
15604
|
*
|
|
16259
15605
|
* This method:
|
|
16260
15606
|
* 1. Uploads the file
|
|
16261
|
-
* 2. Creates a document
|
|
15607
|
+
* 2. Creates a document
|
|
16262
15608
|
* 3. Adds the file to the document slot
|
|
16263
15609
|
*
|
|
16264
15610
|
* Note: The caller is responsible for updating record.values with the document ID.
|
|
@@ -16272,17 +15618,7 @@ var DocumentService = class extends BaseService {
|
|
|
16272
15618
|
if (!this.adapter.documents) {
|
|
16273
15619
|
throw new Error("Documents repository is not configured");
|
|
16274
15620
|
}
|
|
16275
|
-
const {
|
|
16276
|
-
objectName,
|
|
16277
|
-
recordId,
|
|
16278
|
-
fileContent,
|
|
16279
|
-
fileName,
|
|
16280
|
-
mimeType,
|
|
16281
|
-
fileSize,
|
|
16282
|
-
uploadedBy,
|
|
16283
|
-
title,
|
|
16284
|
-
templateId = SYSTEM_TEMPLATE_IDS.GENERIC_DOCUMENT
|
|
16285
|
-
} = input;
|
|
15621
|
+
const { objectName, recordId, fileContent, fileName, mimeType, fileSize, uploadedBy, title } = input;
|
|
16286
15622
|
const uploadedFile = await this.fileService.uploadFile({
|
|
16287
15623
|
content: fileContent,
|
|
16288
15624
|
fileName,
|
|
@@ -16293,7 +15629,6 @@ var DocumentService = class extends BaseService {
|
|
|
16293
15629
|
uploadedBy
|
|
16294
15630
|
});
|
|
16295
15631
|
const document2 = await this.createDocument({
|
|
16296
|
-
templateId,
|
|
16297
15632
|
title: title ?? fileName
|
|
16298
15633
|
});
|
|
16299
15634
|
const slot = await this.addSlot(document2.id, {
|
|
@@ -16313,10 +15648,7 @@ var DocumentProcessingService = class extends BaseService {
|
|
|
16313
15648
|
constructor(adapter, config) {
|
|
16314
15649
|
super(adapter);
|
|
16315
15650
|
this.config = config;
|
|
16316
|
-
this.
|
|
16317
|
-
this.documentService = new DocumentService(adapter, {
|
|
16318
|
-
templateService: this.templateService
|
|
16319
|
-
});
|
|
15651
|
+
this.documentService = new DocumentService(adapter);
|
|
16320
15652
|
}
|
|
16321
15653
|
// ============================================================================
|
|
16322
15654
|
// OCR PROCESSING
|
|
@@ -16599,18 +15931,11 @@ var DocumentProcessingService = class extends BaseService {
|
|
|
16599
15931
|
if (!this.adapter.documentJobs) {
|
|
16600
15932
|
throw new Error("Document jobs repository is not configured");
|
|
16601
15933
|
}
|
|
16602
|
-
|
|
16603
|
-
const template = await this.templateService.getTemplateOrThrow(document2.templateId);
|
|
16604
|
-
if (!template.autoProcessing?.identityVerification?.enabled) {
|
|
16605
|
-
throw new Error("Identity verification is not enabled for this document type");
|
|
16606
|
-
}
|
|
15934
|
+
await this.documentService.getDocumentOrThrow(documentId);
|
|
16607
15935
|
const job = await this.adapter.documentJobs.create({
|
|
16608
15936
|
documentId,
|
|
16609
15937
|
type: "identity_verification",
|
|
16610
|
-
provider: this.config.identityAdapter.name
|
|
16611
|
-
input: {
|
|
16612
|
-
documentType: template.autoProcessing.identityVerification.documentType
|
|
16613
|
-
}
|
|
15938
|
+
provider: this.config.identityAdapter.name
|
|
16614
15939
|
});
|
|
16615
15940
|
return job;
|
|
16616
15941
|
}
|
|
@@ -16683,17 +16008,16 @@ var DocumentProcessingService = class extends BaseService {
|
|
|
16683
16008
|
* Called after all required slots are uploaded.
|
|
16684
16009
|
*/
|
|
16685
16010
|
async triggerAutoProcessing(documentId) {
|
|
16686
|
-
|
|
16687
|
-
const template = await this.templateService.getTemplateOrThrow(document2.templateId);
|
|
16011
|
+
await this.documentService.getDocumentOrThrow(documentId);
|
|
16688
16012
|
const slots = await this.documentService.getSlots(documentId);
|
|
16689
16013
|
const jobs = [];
|
|
16690
|
-
if (
|
|
16014
|
+
if (this.config.ocrAdapter) {
|
|
16691
16015
|
for (const slot of slots) {
|
|
16692
16016
|
const job = await this.processOcr(documentId, slot.slotName);
|
|
16693
16017
|
jobs.push(job);
|
|
16694
16018
|
}
|
|
16695
16019
|
}
|
|
16696
|
-
if (
|
|
16020
|
+
if (this.config.identityAdapter) {
|
|
16697
16021
|
const job = await this.verifyIdentity(documentId);
|
|
16698
16022
|
jobs.push(job);
|
|
16699
16023
|
}
|
|
@@ -16779,6 +16103,18 @@ var DocumentProcessingService = class extends BaseService {
|
|
|
16779
16103
|
};
|
|
16780
16104
|
|
|
16781
16105
|
// src/runtime/services/file.service.ts
|
|
16106
|
+
function decodeFileName(name) {
|
|
16107
|
+
try {
|
|
16108
|
+
const bytes = Buffer.from(name, "latin1");
|
|
16109
|
+
const decoded = bytes.toString("utf-8");
|
|
16110
|
+
if (Buffer.from(decoded, "utf-8").equals(bytes)) {
|
|
16111
|
+
return decoded;
|
|
16112
|
+
}
|
|
16113
|
+
return name;
|
|
16114
|
+
} catch {
|
|
16115
|
+
return name;
|
|
16116
|
+
}
|
|
16117
|
+
}
|
|
16782
16118
|
var FileService = class extends BaseService {
|
|
16783
16119
|
constructor(adapter, options) {
|
|
16784
16120
|
super(adapter);
|
|
@@ -16828,17 +16164,18 @@ var FileService = class extends BaseService {
|
|
|
16828
16164
|
}
|
|
16829
16165
|
input.folderPath = sanitized.replace(/^\/+/, "").replace(/\/+/g, "/");
|
|
16830
16166
|
}
|
|
16167
|
+
const fileName = decodeFileName(input.fileName);
|
|
16831
16168
|
const uploadResult = await this.adapter.storage.upload({
|
|
16832
16169
|
content: input.content,
|
|
16833
|
-
fileName
|
|
16170
|
+
fileName,
|
|
16834
16171
|
mimeType: input.mimeType,
|
|
16835
16172
|
size: input.size,
|
|
16836
16173
|
tenantId: this.tenantId,
|
|
16837
16174
|
folderPath: input.folderPath
|
|
16838
16175
|
});
|
|
16839
16176
|
const file2 = await this.adapter.files.create({
|
|
16840
|
-
name:
|
|
16841
|
-
originalName:
|
|
16177
|
+
name: fileName,
|
|
16178
|
+
originalName: fileName,
|
|
16842
16179
|
mimeType: input.mimeType,
|
|
16843
16180
|
size: input.size,
|
|
16844
16181
|
storageProvider: uploadResult.storageProvider,
|
|
@@ -18761,6 +18098,11 @@ export {
|
|
|
18761
18098
|
AttributeInUseError,
|
|
18762
18099
|
ObjectReferencedError,
|
|
18763
18100
|
getErrorMessage,
|
|
18101
|
+
isFlowFieldsRow,
|
|
18102
|
+
isLayoutRow,
|
|
18103
|
+
isFlowDefinition,
|
|
18104
|
+
isFlowPublished,
|
|
18105
|
+
isSystemFlow,
|
|
18764
18106
|
NoopGeocodingAdapter,
|
|
18765
18107
|
SYSTEM_FIELD_NAMES,
|
|
18766
18108
|
RESERVED_ATTRIBUTE_NAMES,
|
|
@@ -18770,14 +18112,19 @@ export {
|
|
|
18770
18112
|
getSystemAttributeList,
|
|
18771
18113
|
isSystemAttribute,
|
|
18772
18114
|
isSystemAttributeObject,
|
|
18115
|
+
nodeTypeRegistry,
|
|
18116
|
+
getNodeOutputs,
|
|
18117
|
+
setNodeNext,
|
|
18118
|
+
getNodeSlotIds,
|
|
18119
|
+
validateNode,
|
|
18120
|
+
getFormFieldRefs,
|
|
18773
18121
|
isSimpleFormNode,
|
|
18774
18122
|
isAdvancedFormNode,
|
|
18775
18123
|
isStartNode,
|
|
18776
18124
|
isFormNode,
|
|
18777
18125
|
isConditionNode,
|
|
18778
|
-
|
|
18126
|
+
isAssignNode,
|
|
18779
18127
|
isEndNode,
|
|
18780
|
-
getNodeOutputs,
|
|
18781
18128
|
isConditionRule,
|
|
18782
18129
|
isConditionGroup,
|
|
18783
18130
|
eq,
|
|
@@ -18803,12 +18150,18 @@ export {
|
|
|
18803
18150
|
createEmptyContext,
|
|
18804
18151
|
getContextValue,
|
|
18805
18152
|
setContextValue,
|
|
18153
|
+
isFormFieldsRow,
|
|
18806
18154
|
DEFAULT_THEME,
|
|
18807
18155
|
mergeWithDefaults,
|
|
18808
18156
|
generateCssVariables,
|
|
18809
18157
|
isInstanceEvent,
|
|
18810
18158
|
isNodeEvent,
|
|
18811
18159
|
isInvitationOrGrantEvent,
|
|
18160
|
+
ZONE_ORDER,
|
|
18161
|
+
ZONE_CONFIG,
|
|
18162
|
+
assignNodeZones,
|
|
18163
|
+
groupNodesByZone,
|
|
18164
|
+
getZoneAllowedTypes,
|
|
18812
18165
|
ConditionOperatorSchema,
|
|
18813
18166
|
ConditionRuleSchema,
|
|
18814
18167
|
ConditionGroupSchema,
|
|
@@ -18818,7 +18171,9 @@ export {
|
|
|
18818
18171
|
FlowRowSchema,
|
|
18819
18172
|
FormNodeSchema,
|
|
18820
18173
|
ConditionNodeSchema,
|
|
18821
|
-
|
|
18174
|
+
AssignmentSourceSchema,
|
|
18175
|
+
AssignmentMappingSchema,
|
|
18176
|
+
AssignNodeSchema,
|
|
18822
18177
|
EndNodeSchema,
|
|
18823
18178
|
WorkflowNodeSchema,
|
|
18824
18179
|
SlotModeSchema,
|
|
@@ -18915,21 +18270,12 @@ export {
|
|
|
18915
18270
|
WorkflowFormBuilder,
|
|
18916
18271
|
WorkflowSimpleFormBuilder,
|
|
18917
18272
|
WorkflowConditionBuilder,
|
|
18273
|
+
WorkflowAssignBuilder,
|
|
18918
18274
|
WorkflowEndBuilder,
|
|
18919
18275
|
WorkflowStartBuilder,
|
|
18920
18276
|
WorkflowBuilder,
|
|
18921
18277
|
workflow,
|
|
18922
18278
|
registry,
|
|
18923
|
-
SYSTEM_TEMPLATE_IDS,
|
|
18924
|
-
FRENCH_ID_CARD,
|
|
18925
|
-
PASSPORT,
|
|
18926
|
-
DRIVING_LICENSE,
|
|
18927
|
-
PROOF_OF_ADDRESS,
|
|
18928
|
-
SIGNABLE_CONTRACT,
|
|
18929
|
-
GENERIC_DOCUMENT,
|
|
18930
|
-
SYSTEM_TEMPLATES,
|
|
18931
|
-
getSystemTemplate,
|
|
18932
|
-
isSystemTemplate,
|
|
18933
18279
|
WorkflowJwtService,
|
|
18934
18280
|
hashOptions,
|
|
18935
18281
|
cacheKeys,
|
|
@@ -18975,9 +18321,9 @@ export {
|
|
|
18975
18321
|
complete,
|
|
18976
18322
|
error,
|
|
18977
18323
|
ConditionExecutor,
|
|
18978
|
-
DocumentExecutor,
|
|
18979
18324
|
EndExecutor,
|
|
18980
18325
|
FormExecutor,
|
|
18326
|
+
AssignExecutor,
|
|
18981
18327
|
StartExecutor,
|
|
18982
18328
|
createDefaultExecutorRegistry,
|
|
18983
18329
|
getDefaultExecutorRegistry,
|
|
@@ -19040,10 +18386,6 @@ export {
|
|
|
19040
18386
|
RecordService,
|
|
19041
18387
|
FormulaResolverService,
|
|
19042
18388
|
RollupScheduler,
|
|
19043
|
-
DocumentRenderError,
|
|
19044
|
-
StorageDownloadNotSupportedError,
|
|
19045
|
-
DocumentRendererService,
|
|
19046
|
-
DocumentProcessingHook,
|
|
19047
18389
|
GrantNotFoundError,
|
|
19048
18390
|
GrantExpiredError,
|
|
19049
18391
|
GrantRevokedError,
|
|
@@ -19058,10 +18400,6 @@ export {
|
|
|
19058
18400
|
WorkflowRelationService,
|
|
19059
18401
|
WorkflowService,
|
|
19060
18402
|
UserProfileService,
|
|
19061
|
-
DocumentGenerationTemplateNotFoundError,
|
|
19062
|
-
DocumentGenerationNotConfiguredError,
|
|
19063
|
-
DocumentGenerationService,
|
|
19064
|
-
DocumentTemplateService,
|
|
19065
18403
|
DocumentService,
|
|
19066
18404
|
DocumentProcessingService,
|
|
19067
18405
|
FileService,
|