@stndrds/schema 0.1.0-alpha.15 → 0.1.0-alpha.16
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-BTVPMGYW.mjs +6327 -0
- package/dist/chunk-C7HZ55GJ.mjs +6882 -0
- package/dist/chunk-JDUTFH3J.mjs +6940 -0
- package/dist/chunk-QAGN53HE.mjs +6327 -0
- package/dist/chunk-UUOAVXAI.mjs +6327 -0
- package/dist/chunk-VYZ3BOSB.mjs +6946 -0
- package/dist/chunk-XAX75NSP.mjs +6321 -0
- package/dist/chunk-ZG3PT3RL.mjs +6944 -0
- package/dist/index.d.mts +229 -4
- package/dist/index.d.ts +229 -4
- package/dist/index.js +769 -134
- package/dist/index.mjs +25 -1
- package/dist/runtime-BPYJWZfC.d.mts +5130 -0
- package/dist/runtime-BPYJWZfC.d.ts +5130 -0
- package/dist/runtime-DRc_OhnM.d.mts +5142 -0
- package/dist/runtime-DRc_OhnM.d.ts +5142 -0
- package/dist/runtime-D_S3m9jf.d.mts +5142 -0
- package/dist/runtime-D_S3m9jf.d.ts +5142 -0
- package/dist/runtime-Db3rRxbJ.d.mts +4821 -0
- package/dist/runtime-Db3rRxbJ.d.ts +4821 -0
- package/dist/runtime-DlCiIIfr.d.mts +5130 -0
- package/dist/runtime-DlCiIIfr.d.ts +5130 -0
- package/dist/runtime-WqYxjhjJ.d.mts +5125 -0
- package/dist/runtime-WqYxjhjJ.d.ts +5125 -0
- package/dist/runtime.d.mts +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +389 -129
- package/dist/runtime.mjs +3 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -131,6 +131,10 @@ __export(index_exports, {
|
|
|
131
131
|
DuplicateError: () => DuplicateError,
|
|
132
132
|
FileNotFoundError: () => FileNotFoundError,
|
|
133
133
|
FileService: () => FileService,
|
|
134
|
+
FlowBuilder: () => FlowBuilder,
|
|
135
|
+
FlowPageBuilder: () => FlowPageBuilder,
|
|
136
|
+
FlowRowBuilder: () => FlowRowBuilder,
|
|
137
|
+
FlowService: () => FlowService,
|
|
134
138
|
ForbiddenError: () => ForbiddenError,
|
|
135
139
|
GeocodingService: () => GeocodingService,
|
|
136
140
|
GlobalSearchService: () => GlobalSearchService,
|
|
@@ -198,6 +202,7 @@ __export(index_exports, {
|
|
|
198
202
|
extractAttributeNames: () => extractAttributeNames,
|
|
199
203
|
file: () => file,
|
|
200
204
|
fileConfigSchema: () => fileConfigSchema,
|
|
205
|
+
flow: () => flow,
|
|
201
206
|
generateId: () => generateId,
|
|
202
207
|
generatePrefixedId: () => generatePrefixedId,
|
|
203
208
|
getAttributeConfigSchema: () => getAttributeConfigSchema,
|
|
@@ -208,6 +213,8 @@ __export(index_exports, {
|
|
|
208
213
|
isAdvancedFilterState: () => isAdvancedFilterState,
|
|
209
214
|
isCustomTab: () => isCustomTab,
|
|
210
215
|
isDefaultRole: () => isDefaultRole,
|
|
216
|
+
isFlowDefinition: () => isFlowDefinition,
|
|
217
|
+
isFlowPublished: () => isFlowPublished,
|
|
211
218
|
isForbiddenError: () => isForbiddenError,
|
|
212
219
|
isFormTab: () => isFormTab,
|
|
213
220
|
isLabelExpression: () => isLabelExpression,
|
|
@@ -216,6 +223,7 @@ __export(index_exports, {
|
|
|
216
223
|
isProtectedResourceError: () => isProtectedResourceError,
|
|
217
224
|
isRecordComplete: () => isRecordComplete,
|
|
218
225
|
isSchemaError: () => isSchemaError,
|
|
226
|
+
isSystemFlow: () => isSystemFlow,
|
|
219
227
|
isTableTab: () => isTableTab,
|
|
220
228
|
isValidationError: () => isValidationError,
|
|
221
229
|
location: () => location,
|
|
@@ -392,6 +400,17 @@ function isNoValueOperator(operator) {
|
|
|
392
400
|
return NO_VALUE_OPERATORS.includes(operator);
|
|
393
401
|
}
|
|
394
402
|
|
|
403
|
+
// src/types/flows.ts
|
|
404
|
+
function isFlowDefinition(obj) {
|
|
405
|
+
return typeof obj === "object" && obj !== null && "slots" in obj && "pages" in obj && "relations" in obj && "status" in obj;
|
|
406
|
+
}
|
|
407
|
+
function isFlowPublished(flow2) {
|
|
408
|
+
return flow2.status === "published";
|
|
409
|
+
}
|
|
410
|
+
function isSystemFlow(flow2) {
|
|
411
|
+
return flow2.system === true;
|
|
412
|
+
}
|
|
413
|
+
|
|
395
414
|
// src/types/geocoding.ts
|
|
396
415
|
var NoopGeocodingAdapter = class {
|
|
397
416
|
async autocomplete() {
|
|
@@ -1284,8 +1303,361 @@ function rating(config) {
|
|
|
1284
1303
|
return new RatingAttributeBuilder(config.name, config.label);
|
|
1285
1304
|
}
|
|
1286
1305
|
|
|
1306
|
+
// src/builders/flow-builder.ts
|
|
1307
|
+
var import_zod = require("zod");
|
|
1308
|
+
var FlowRowBuilder = class {
|
|
1309
|
+
/** @internal */
|
|
1310
|
+
constructor(pageBuilder, id, order) {
|
|
1311
|
+
this.pageBuilder = pageBuilder;
|
|
1312
|
+
this.rowData = {
|
|
1313
|
+
id,
|
|
1314
|
+
order,
|
|
1315
|
+
fields: []
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Add a field to the current row
|
|
1320
|
+
*
|
|
1321
|
+
* @param slotId - Reference to a slot defined with .slot()
|
|
1322
|
+
* @param attribute - Attribute name on the object
|
|
1323
|
+
* @param options - Optional field configuration
|
|
1324
|
+
*
|
|
1325
|
+
* @example
|
|
1326
|
+
* ```typescript
|
|
1327
|
+
* .row("row-1")
|
|
1328
|
+
* .field("mr", "firstName")
|
|
1329
|
+
* .field("mr", "lastName")
|
|
1330
|
+
* ```
|
|
1331
|
+
*/
|
|
1332
|
+
field(slotId, attribute, options) {
|
|
1333
|
+
const field = {
|
|
1334
|
+
id: `${this.rowData.id}-${slotId}-${attribute}`,
|
|
1335
|
+
slotId,
|
|
1336
|
+
attribute,
|
|
1337
|
+
label: options?.label,
|
|
1338
|
+
required: options?.required
|
|
1339
|
+
};
|
|
1340
|
+
this.rowData.fields.push(field);
|
|
1341
|
+
return this;
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Start a new row on the same page
|
|
1345
|
+
*/
|
|
1346
|
+
row(id) {
|
|
1347
|
+
return this.pageBuilder._finalizeRow(this.rowData).row(id);
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Start a new page
|
|
1351
|
+
*/
|
|
1352
|
+
page(id, label, description) {
|
|
1353
|
+
return this.pageBuilder._finalizeRow(this.rowData)._getFlow().page(id, label, description);
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Build the final flow definition
|
|
1357
|
+
*/
|
|
1358
|
+
build() {
|
|
1359
|
+
return this.pageBuilder._finalizeRow(this.rowData)._getFlow().build();
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
var FlowPageBuilder = class {
|
|
1363
|
+
/** @internal */
|
|
1364
|
+
constructor(flow2, id, label, description) {
|
|
1365
|
+
this.rowOrder = 0;
|
|
1366
|
+
this.flow = flow2;
|
|
1367
|
+
this.pageData = {
|
|
1368
|
+
id,
|
|
1369
|
+
label,
|
|
1370
|
+
description,
|
|
1371
|
+
order: 0,
|
|
1372
|
+
// Will be set when finalized
|
|
1373
|
+
rows: []
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Add a row to the current page
|
|
1378
|
+
*
|
|
1379
|
+
* @param id - Unique row identifier
|
|
1380
|
+
*
|
|
1381
|
+
* @example
|
|
1382
|
+
* ```typescript
|
|
1383
|
+
* .page("identity", "Identité")
|
|
1384
|
+
* .row("name-row")
|
|
1385
|
+
* .field("mr", "firstName")
|
|
1386
|
+
* .field("mr", "lastName")
|
|
1387
|
+
* .row("email-row")
|
|
1388
|
+
* .field("mr", "email")
|
|
1389
|
+
* ```
|
|
1390
|
+
*/
|
|
1391
|
+
row(id) {
|
|
1392
|
+
this.rowOrder++;
|
|
1393
|
+
return new FlowRowBuilder(this, id, this.rowOrder);
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Start a new page
|
|
1397
|
+
*/
|
|
1398
|
+
page(id, label, description) {
|
|
1399
|
+
return this.flow._finalizePage(this.pageData).page(id, label, description);
|
|
1400
|
+
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Build the final flow definition
|
|
1403
|
+
*/
|
|
1404
|
+
build() {
|
|
1405
|
+
return this.flow._finalizePage(this.pageData).build();
|
|
1406
|
+
}
|
|
1407
|
+
/** @internal */
|
|
1408
|
+
_finalizeRow(row) {
|
|
1409
|
+
if (row.id && row.fields.length > 0) {
|
|
1410
|
+
this.pageData.rows?.push(row);
|
|
1411
|
+
}
|
|
1412
|
+
return this;
|
|
1413
|
+
}
|
|
1414
|
+
/** @internal */
|
|
1415
|
+
_getFlow() {
|
|
1416
|
+
return this.flow._finalizePage(this.pageData);
|
|
1417
|
+
}
|
|
1418
|
+
/** @internal */
|
|
1419
|
+
_getPageData() {
|
|
1420
|
+
return this.pageData;
|
|
1421
|
+
}
|
|
1422
|
+
};
|
|
1423
|
+
var FlowBuilder = class {
|
|
1424
|
+
constructor(name, label) {
|
|
1425
|
+
this.data = {
|
|
1426
|
+
slots: [],
|
|
1427
|
+
pages: [],
|
|
1428
|
+
relations: [],
|
|
1429
|
+
status: "published",
|
|
1430
|
+
// System flows are always published
|
|
1431
|
+
version: 1,
|
|
1432
|
+
system: true
|
|
1433
|
+
};
|
|
1434
|
+
this.validateName(name);
|
|
1435
|
+
this.data.name = name;
|
|
1436
|
+
this.data.label = label;
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Set flow description
|
|
1440
|
+
*/
|
|
1441
|
+
description(value) {
|
|
1442
|
+
this.data.description = value;
|
|
1443
|
+
return this;
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Set flow icon
|
|
1447
|
+
*/
|
|
1448
|
+
icon(value) {
|
|
1449
|
+
this.data.icon = value;
|
|
1450
|
+
return this;
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Set extensible metadata
|
|
1454
|
+
*/
|
|
1455
|
+
metadata(value) {
|
|
1456
|
+
this.data.metadata = value;
|
|
1457
|
+
return this;
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Add a slot (object to create in the flow)
|
|
1461
|
+
*
|
|
1462
|
+
* @param id - Unique slot identifier (used to reference in fields and relations)
|
|
1463
|
+
* @param objectName - Name of the object to create
|
|
1464
|
+
* @param label - Display label for the slot
|
|
1465
|
+
* @param options - Optional slot configuration
|
|
1466
|
+
*
|
|
1467
|
+
* @example
|
|
1468
|
+
* ```typescript
|
|
1469
|
+
* .slot("mr", "contacts", "Monsieur", { color: "blue", icon: "user" })
|
|
1470
|
+
* .slot("company", "companies", "Entreprise", { color: "green" })
|
|
1471
|
+
* ```
|
|
1472
|
+
*/
|
|
1473
|
+
slot(id, objectName, label, options) {
|
|
1474
|
+
if (this.data.slots?.some((s) => s.id === id)) {
|
|
1475
|
+
throw new Error(`[FlowBuilder] Duplicate slot id: "${id}"`);
|
|
1476
|
+
}
|
|
1477
|
+
this.data.slots?.push({
|
|
1478
|
+
id,
|
|
1479
|
+
objectName,
|
|
1480
|
+
label,
|
|
1481
|
+
color: options?.color,
|
|
1482
|
+
icon: options?.icon
|
|
1483
|
+
});
|
|
1484
|
+
return this;
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* Define a relation between two slots
|
|
1488
|
+
* The source slot will be linked to the target slot via the specified attribute
|
|
1489
|
+
*
|
|
1490
|
+
* @param sourceSlotId - Slot that has the relation attribute
|
|
1491
|
+
* @param sourceAttribute - Relation attribute name on the source
|
|
1492
|
+
* @param targetSlotId - Target slot to link to
|
|
1493
|
+
*
|
|
1494
|
+
* @example
|
|
1495
|
+
* ```typescript
|
|
1496
|
+
* // mr.company → company (the contact's company field will be set to the created company)
|
|
1497
|
+
* .relation("mr", "company", "company")
|
|
1498
|
+
* ```
|
|
1499
|
+
*/
|
|
1500
|
+
relation(sourceSlotId, sourceAttribute, targetSlotId) {
|
|
1501
|
+
this.data.relations?.push({
|
|
1502
|
+
id: `${sourceSlotId}-${sourceAttribute}-${targetSlotId}`,
|
|
1503
|
+
sourceSlotId,
|
|
1504
|
+
sourceAttribute,
|
|
1505
|
+
targetSlotId
|
|
1506
|
+
});
|
|
1507
|
+
return this;
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Start a new page/step in the flow
|
|
1511
|
+
*
|
|
1512
|
+
* @param id - Unique page identifier
|
|
1513
|
+
* @param label - Display label
|
|
1514
|
+
* @param description - Optional description
|
|
1515
|
+
*
|
|
1516
|
+
* @example
|
|
1517
|
+
* ```typescript
|
|
1518
|
+
* .page("identity", "Identité", "Informations personnelles")
|
|
1519
|
+
* .row("name")
|
|
1520
|
+
* .field("mr", "firstName")
|
|
1521
|
+
* .field("mr", "lastName")
|
|
1522
|
+
* ```
|
|
1523
|
+
*/
|
|
1524
|
+
page(id, label, description) {
|
|
1525
|
+
return new FlowPageBuilder(this, id, label, description);
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Build and validate the flow definition
|
|
1529
|
+
*
|
|
1530
|
+
* @throws Error if validation fails
|
|
1531
|
+
*/
|
|
1532
|
+
build() {
|
|
1533
|
+
this.validateRequiredData();
|
|
1534
|
+
const slotIds = new Set(this.data.slots?.map((s) => s.id) ?? []);
|
|
1535
|
+
this.validateFieldReferences(slotIds);
|
|
1536
|
+
this.validateRelationReferences(slotIds);
|
|
1537
|
+
this.validateNoCircularDependencies();
|
|
1538
|
+
return this.data;
|
|
1539
|
+
}
|
|
1540
|
+
/**
|
|
1541
|
+
* Validate that required data is present
|
|
1542
|
+
*/
|
|
1543
|
+
validateRequiredData() {
|
|
1544
|
+
if (!this.data.slots || this.data.slots.length === 0) {
|
|
1545
|
+
throw new Error("[FlowBuilder] At least one slot is required. Use .slot() to add slots.");
|
|
1546
|
+
}
|
|
1547
|
+
if (!this.data.pages || this.data.pages.length === 0) {
|
|
1548
|
+
throw new Error("[FlowBuilder] At least one page is required. Use .page() to add pages.");
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
/**
|
|
1552
|
+
* Validate that all field slotId references exist
|
|
1553
|
+
*/
|
|
1554
|
+
validateFieldReferences(slotIds) {
|
|
1555
|
+
for (const page of this.data.pages ?? []) {
|
|
1556
|
+
for (const row of page.rows) {
|
|
1557
|
+
for (const field of row.fields) {
|
|
1558
|
+
if (!slotIds.has(field.slotId)) {
|
|
1559
|
+
throw new Error(
|
|
1560
|
+
`[FlowBuilder] Field "${field.id}" references unknown slot "${field.slotId}" in page "${page.id}". Available slots: ${[...slotIds].join(", ")}`
|
|
1561
|
+
);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1568
|
+
* Validate that all relation references exist and are valid
|
|
1569
|
+
*/
|
|
1570
|
+
validateRelationReferences(slotIds) {
|
|
1571
|
+
for (const relation2 of this.data.relations ?? []) {
|
|
1572
|
+
if (!slotIds.has(relation2.sourceSlotId)) {
|
|
1573
|
+
throw new Error(
|
|
1574
|
+
`[FlowBuilder] Relation references unknown source slot "${relation2.sourceSlotId}". Available slots: ${[...slotIds].join(", ")}`
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1577
|
+
if (!slotIds.has(relation2.targetSlotId)) {
|
|
1578
|
+
throw new Error(
|
|
1579
|
+
`[FlowBuilder] Relation references unknown target slot "${relation2.targetSlotId}". Available slots: ${[...slotIds].join(", ")}`
|
|
1580
|
+
);
|
|
1581
|
+
}
|
|
1582
|
+
if (relation2.sourceSlotId === relation2.targetSlotId) {
|
|
1583
|
+
throw new Error(
|
|
1584
|
+
`[FlowBuilder] Relation cannot link a slot to itself: "${relation2.sourceSlotId}"`
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* @internal Used by FlowPageBuilder to finalize a page
|
|
1591
|
+
*/
|
|
1592
|
+
_finalizePage(page) {
|
|
1593
|
+
if (page.id) {
|
|
1594
|
+
page.order = (this.data.pages?.length ?? 0) + 1;
|
|
1595
|
+
this.data.pages?.push(page);
|
|
1596
|
+
}
|
|
1597
|
+
return this;
|
|
1598
|
+
}
|
|
1599
|
+
/**
|
|
1600
|
+
* Validate flow name format (kebab-case)
|
|
1601
|
+
*/
|
|
1602
|
+
validateName(name) {
|
|
1603
|
+
const flowNameSchema = import_zod.z.string().min(1, "Flow name cannot be empty").max(63, "Flow name is too long (max 63 characters)").regex(/^[a-z][a-z0-9-]*$/, {
|
|
1604
|
+
message: "Invalid flow name format.\nName must be in kebab-case:\n \u2705 Valid: 'couple-creation', 'new-contact', 'onboarding-flow'\n \u274C Invalid: 'CoupleCreation', 'new_contact', 'Onboarding Flow'"
|
|
1605
|
+
});
|
|
1606
|
+
try {
|
|
1607
|
+
flowNameSchema.parse(name);
|
|
1608
|
+
} catch (error) {
|
|
1609
|
+
if (error instanceof import_zod.z.ZodError) {
|
|
1610
|
+
throw new Error(`[FlowBuilder] ${error.issues[0].message}`);
|
|
1611
|
+
}
|
|
1612
|
+
throw error;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Validate that there are no circular dependencies in relations
|
|
1617
|
+
* Uses DFS to detect cycles in the dependency graph
|
|
1618
|
+
*/
|
|
1619
|
+
validateNoCircularDependencies() {
|
|
1620
|
+
const relations = this.data.relations ?? [];
|
|
1621
|
+
if (relations.length === 0) return;
|
|
1622
|
+
const dependsOn = /* @__PURE__ */ new Map();
|
|
1623
|
+
for (const slot of this.data.slots ?? []) {
|
|
1624
|
+
dependsOn.set(slot.id, /* @__PURE__ */ new Set());
|
|
1625
|
+
}
|
|
1626
|
+
for (const rel of relations) {
|
|
1627
|
+
dependsOn.get(rel.sourceSlotId)?.add(rel.targetSlotId);
|
|
1628
|
+
}
|
|
1629
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1630
|
+
const recursionStack = /* @__PURE__ */ new Set();
|
|
1631
|
+
const hasCycle = (slotId) => {
|
|
1632
|
+
visited.add(slotId);
|
|
1633
|
+
recursionStack.add(slotId);
|
|
1634
|
+
for (const dependency of dependsOn.get(slotId) ?? []) {
|
|
1635
|
+
if (!visited.has(dependency)) {
|
|
1636
|
+
if (hasCycle(dependency)) return true;
|
|
1637
|
+
} else if (recursionStack.has(dependency)) {
|
|
1638
|
+
return true;
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
recursionStack.delete(slotId);
|
|
1642
|
+
return false;
|
|
1643
|
+
};
|
|
1644
|
+
for (const slot of this.data.slots ?? []) {
|
|
1645
|
+
if (!visited.has(slot.id)) {
|
|
1646
|
+
if (hasCycle(slot.id)) {
|
|
1647
|
+
throw new Error(
|
|
1648
|
+
"[FlowBuilder] Circular dependency detected in relations. Slots cannot have circular dependencies."
|
|
1649
|
+
);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
};
|
|
1655
|
+
function flow(name, label) {
|
|
1656
|
+
return new FlowBuilder(name, label);
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1287
1659
|
// src/builders/object-builder.ts
|
|
1288
|
-
var
|
|
1660
|
+
var import_zod2 = __toESM(require("zod"));
|
|
1289
1661
|
var ObjectBuilder = class {
|
|
1290
1662
|
constructor(config) {
|
|
1291
1663
|
this.validateName(config.name);
|
|
@@ -1418,14 +1790,14 @@ The labelExpression defines how records are displayed in lists and relations.
|
|
|
1418
1790
|
if (name === void 0) {
|
|
1419
1791
|
return;
|
|
1420
1792
|
}
|
|
1421
|
-
const objectNameSchema =
|
|
1793
|
+
const objectNameSchema = import_zod2.default.string().min(1, "Object name cannot be empty").max(63, "Object name is too long (max 63 characters)").regex(/^[a-z][a-z0-9-]*$/, {
|
|
1422
1794
|
message: "Invalid object name format.\nName must be in kebab-case (slug format):\n \u2705 Valid: 'products', 'user-profiles', 'order-items'\n \u274C Invalid: 'Products', 'userProfiles', 'user_profiles'"
|
|
1423
1795
|
});
|
|
1424
1796
|
try {
|
|
1425
1797
|
objectNameSchema.parse(name);
|
|
1426
1798
|
} catch (error) {
|
|
1427
|
-
if (error instanceof
|
|
1428
|
-
throw new Error(`[ObjectBuilder] ${error.
|
|
1799
|
+
if (error instanceof import_zod2.default.ZodError) {
|
|
1800
|
+
throw new Error(`[ObjectBuilder] ${error.issues[0].message}`);
|
|
1429
1801
|
}
|
|
1430
1802
|
throw error;
|
|
1431
1803
|
}
|
|
@@ -1436,7 +1808,7 @@ function object(config) {
|
|
|
1436
1808
|
}
|
|
1437
1809
|
|
|
1438
1810
|
// src/builders/view-builder.ts
|
|
1439
|
-
var
|
|
1811
|
+
var import_zod3 = require("zod");
|
|
1440
1812
|
var GroupBuilder = class {
|
|
1441
1813
|
constructor(id, label) {
|
|
1442
1814
|
this.data = { fields: [] };
|
|
@@ -1792,14 +2164,14 @@ var ViewBuilder = class {
|
|
|
1792
2164
|
* Validate view name format (kebab-case)
|
|
1793
2165
|
*/
|
|
1794
2166
|
validateName(name) {
|
|
1795
|
-
const viewNameSchema =
|
|
2167
|
+
const viewNameSchema = import_zod3.z.string().min(1, "View name cannot be empty").max(63, "View name is too long (max 63 characters)").regex(/^[a-z][a-z0-9-]*$/, {
|
|
1796
2168
|
message: "Invalid view name format.\nName must be in kebab-case:\n \u2705 Valid: 'detail', 'list-view', 'company-detail'\n \u274C Invalid: 'Detail', 'listView', 'list_view'"
|
|
1797
2169
|
});
|
|
1798
2170
|
try {
|
|
1799
2171
|
viewNameSchema.parse(name);
|
|
1800
2172
|
} catch (error) {
|
|
1801
|
-
if (error instanceof
|
|
1802
|
-
throw new Error(`[ViewBuilder] ${error.
|
|
2173
|
+
if (error instanceof import_zod3.z.ZodError) {
|
|
2174
|
+
throw new Error(`[ViewBuilder] ${error.issues[0].message}`);
|
|
1803
2175
|
}
|
|
1804
2176
|
throw error;
|
|
1805
2177
|
}
|
|
@@ -2097,35 +2469,35 @@ Available views: ${availableNames}`
|
|
|
2097
2469
|
var viewRegistry = new ViewRegistry();
|
|
2098
2470
|
|
|
2099
2471
|
// src/validation/validators.ts
|
|
2100
|
-
var
|
|
2101
|
-
var baseConfigSchema =
|
|
2102
|
-
disabled:
|
|
2103
|
-
placeholder:
|
|
2104
|
-
description:
|
|
2105
|
-
defaultValue:
|
|
2106
|
-
icon:
|
|
2107
|
-
order:
|
|
2108
|
-
hidden:
|
|
2109
|
-
archived:
|
|
2110
|
-
deprecated:
|
|
2111
|
-
metadata:
|
|
2472
|
+
var import_zod4 = require("zod");
|
|
2473
|
+
var baseConfigSchema = import_zod4.z.object({
|
|
2474
|
+
disabled: import_zod4.z.boolean().optional(),
|
|
2475
|
+
placeholder: import_zod4.z.string().optional(),
|
|
2476
|
+
description: import_zod4.z.string().optional(),
|
|
2477
|
+
defaultValue: import_zod4.z.unknown().optional(),
|
|
2478
|
+
icon: import_zod4.z.string().optional(),
|
|
2479
|
+
order: import_zod4.z.number().int().optional(),
|
|
2480
|
+
hidden: import_zod4.z.boolean().optional(),
|
|
2481
|
+
archived: import_zod4.z.boolean().optional(),
|
|
2482
|
+
deprecated: import_zod4.z.boolean().optional(),
|
|
2483
|
+
metadata: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()).optional()
|
|
2112
2484
|
});
|
|
2113
|
-
var optionSchema =
|
|
2114
|
-
id:
|
|
2115
|
-
label:
|
|
2116
|
-
value:
|
|
2117
|
-
color:
|
|
2118
|
-
icon:
|
|
2119
|
-
description:
|
|
2120
|
-
group:
|
|
2485
|
+
var optionSchema = import_zod4.z.object({
|
|
2486
|
+
id: import_zod4.z.string().min(1),
|
|
2487
|
+
label: import_zod4.z.string().min(1),
|
|
2488
|
+
value: import_zod4.z.string().min(1),
|
|
2489
|
+
color: import_zod4.z.string().optional(),
|
|
2490
|
+
icon: import_zod4.z.string().optional(),
|
|
2491
|
+
description: import_zod4.z.string().optional(),
|
|
2492
|
+
group: import_zod4.z.enum(["idle", "in_progress", "finished"]).optional()
|
|
2121
2493
|
});
|
|
2122
|
-
var relationTargetSchema =
|
|
2123
|
-
object:
|
|
2124
|
-
displayTemplate:
|
|
2125
|
-
filter:
|
|
2494
|
+
var relationTargetSchema = import_zod4.z.object({
|
|
2495
|
+
object: import_zod4.z.string().min(1),
|
|
2496
|
+
displayTemplate: import_zod4.z.string().optional(),
|
|
2497
|
+
filter: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()).optional()
|
|
2126
2498
|
});
|
|
2127
|
-
var documentTypeConfigSchema =
|
|
2128
|
-
type:
|
|
2499
|
+
var documentTypeConfigSchema = import_zod4.z.object({
|
|
2500
|
+
type: import_zod4.z.enum([
|
|
2129
2501
|
"id_card",
|
|
2130
2502
|
"passport",
|
|
2131
2503
|
"incorporation_certificate",
|
|
@@ -2136,86 +2508,86 @@ var documentTypeConfigSchema = import_zod3.z.object({
|
|
|
2136
2508
|
"proof_of_address",
|
|
2137
2509
|
"custom"
|
|
2138
2510
|
]),
|
|
2139
|
-
label:
|
|
2140
|
-
faces:
|
|
2141
|
-
attributeMapping:
|
|
2142
|
-
|
|
2143
|
-
attributeId:
|
|
2144
|
-
extractedKey:
|
|
2145
|
-
face:
|
|
2146
|
-
required:
|
|
2511
|
+
label: import_zod4.z.string(),
|
|
2512
|
+
faces: import_zod4.z.array(import_zod4.z.enum(["front", "back", "single"])),
|
|
2513
|
+
attributeMapping: import_zod4.z.array(
|
|
2514
|
+
import_zod4.z.object({
|
|
2515
|
+
attributeId: import_zod4.z.string(),
|
|
2516
|
+
extractedKey: import_zod4.z.string(),
|
|
2517
|
+
face: import_zod4.z.enum(["front", "back", "single"]).optional(),
|
|
2518
|
+
required: import_zod4.z.boolean().optional()
|
|
2147
2519
|
})
|
|
2148
2520
|
).optional()
|
|
2149
2521
|
});
|
|
2150
|
-
var fileVerificationConfigSchema =
|
|
2151
|
-
enabled:
|
|
2152
|
-
documentTypes:
|
|
2153
|
-
autoExtract:
|
|
2154
|
-
autoValidate:
|
|
2522
|
+
var fileVerificationConfigSchema = import_zod4.z.object({
|
|
2523
|
+
enabled: import_zod4.z.boolean(),
|
|
2524
|
+
documentTypes: import_zod4.z.array(documentTypeConfigSchema),
|
|
2525
|
+
autoExtract: import_zod4.z.boolean().optional(),
|
|
2526
|
+
autoValidate: import_zod4.z.boolean().optional()
|
|
2155
2527
|
});
|
|
2156
2528
|
var textConfigSchema = baseConfigSchema.extend({
|
|
2157
|
-
minLength:
|
|
2158
|
-
maxLength:
|
|
2159
|
-
pattern:
|
|
2529
|
+
minLength: import_zod4.z.number().int().min(0).optional(),
|
|
2530
|
+
maxLength: import_zod4.z.number().int().min(1).optional(),
|
|
2531
|
+
pattern: import_zod4.z.string().optional()
|
|
2160
2532
|
});
|
|
2161
2533
|
var textareaConfigSchema = baseConfigSchema;
|
|
2162
2534
|
var numberConfigSchema = baseConfigSchema.extend({
|
|
2163
|
-
min:
|
|
2164
|
-
max:
|
|
2165
|
-
unit:
|
|
2166
|
-
decimals:
|
|
2535
|
+
min: import_zod4.z.number().optional(),
|
|
2536
|
+
max: import_zod4.z.number().optional(),
|
|
2537
|
+
unit: import_zod4.z.enum(["integer", "decimal", "percentage"]).optional(),
|
|
2538
|
+
decimals: import_zod4.z.number().int().min(0).optional()
|
|
2167
2539
|
});
|
|
2168
2540
|
var checkboxConfigSchema = baseConfigSchema;
|
|
2169
2541
|
var dateConfigSchema = baseConfigSchema.extend({
|
|
2170
|
-
dateFormat:
|
|
2171
|
-
minDate:
|
|
2172
|
-
maxDate:
|
|
2542
|
+
dateFormat: import_zod4.z.enum(["short", "long", "full", "relative"]).optional(),
|
|
2543
|
+
minDate: import_zod4.z.string().optional(),
|
|
2544
|
+
maxDate: import_zod4.z.string().optional()
|
|
2173
2545
|
});
|
|
2174
2546
|
var phoneConfigSchema = baseConfigSchema.extend({
|
|
2175
|
-
defaultCountryCode:
|
|
2547
|
+
defaultCountryCode: import_zod4.z.string().length(3).optional()
|
|
2176
2548
|
});
|
|
2177
2549
|
var currencyConfigSchema = baseConfigSchema.extend({
|
|
2178
|
-
defaultCurrency:
|
|
2179
|
-
allowedCurrencies:
|
|
2550
|
+
defaultCurrency: import_zod4.z.string().length(3).optional(),
|
|
2551
|
+
allowedCurrencies: import_zod4.z.array(import_zod4.z.string().length(3)).optional()
|
|
2180
2552
|
});
|
|
2181
2553
|
var statusConfigSchema = baseConfigSchema.extend({
|
|
2182
|
-
options:
|
|
2554
|
+
options: import_zod4.z.array(optionSchema).min(1)
|
|
2183
2555
|
});
|
|
2184
2556
|
var locationConfigSchema = baseConfigSchema.extend({
|
|
2185
|
-
granularity:
|
|
2186
|
-
enableAutocomplete:
|
|
2187
|
-
enableMap:
|
|
2188
|
-
defaultCountry:
|
|
2189
|
-
allowedCountries:
|
|
2190
|
-
displayFormat:
|
|
2557
|
+
granularity: import_zod4.z.enum(["full", "address", "city", "state", "country", "coordinates"]),
|
|
2558
|
+
enableAutocomplete: import_zod4.z.boolean().optional(),
|
|
2559
|
+
enableMap: import_zod4.z.boolean().optional(),
|
|
2560
|
+
defaultCountry: import_zod4.z.string().length(3).optional(),
|
|
2561
|
+
allowedCountries: import_zod4.z.array(import_zod4.z.string().length(3)).optional(),
|
|
2562
|
+
displayFormat: import_zod4.z.enum(["single_line", "multi_line", "compact"]).optional()
|
|
2191
2563
|
});
|
|
2192
2564
|
var timestampConfigSchema = baseConfigSchema.extend({
|
|
2193
|
-
autoUpdate:
|
|
2565
|
+
autoUpdate: import_zod4.z.boolean().optional()
|
|
2194
2566
|
});
|
|
2195
2567
|
var selectConfigSchema = baseConfigSchema.extend({
|
|
2196
|
-
options:
|
|
2568
|
+
options: import_zod4.z.array(optionSchema).min(1)
|
|
2197
2569
|
});
|
|
2198
2570
|
var multiselectConfigSchema = baseConfigSchema.extend({
|
|
2199
|
-
options:
|
|
2571
|
+
options: import_zod4.z.array(optionSchema).min(1)
|
|
2200
2572
|
});
|
|
2201
2573
|
var fileConfigSchema = baseConfigSchema.extend({
|
|
2202
|
-
maxFiles:
|
|
2203
|
-
maxSize:
|
|
2204
|
-
allowedTypes:
|
|
2574
|
+
maxFiles: import_zod4.z.number().int().min(1).optional(),
|
|
2575
|
+
maxSize: import_zod4.z.number().int().min(1).optional(),
|
|
2576
|
+
allowedTypes: import_zod4.z.array(import_zod4.z.string()).optional(),
|
|
2205
2577
|
verification: fileVerificationConfigSchema.optional()
|
|
2206
2578
|
});
|
|
2207
2579
|
var userConfigSchema = baseConfigSchema.extend({
|
|
2208
|
-
allowedRoles:
|
|
2580
|
+
allowedRoles: import_zod4.z.array(import_zod4.z.string()).optional()
|
|
2209
2581
|
});
|
|
2210
2582
|
var relationConfigSchema = baseConfigSchema.extend({
|
|
2211
|
-
targets:
|
|
2212
|
-
cardinality:
|
|
2213
|
-
minItems:
|
|
2214
|
-
maxItems:
|
|
2583
|
+
targets: import_zod4.z.array(relationTargetSchema).min(1),
|
|
2584
|
+
cardinality: import_zod4.z.enum(["one", "many"]),
|
|
2585
|
+
minItems: import_zod4.z.number().int().min(0).optional(),
|
|
2586
|
+
maxItems: import_zod4.z.number().int().min(1).optional()
|
|
2215
2587
|
});
|
|
2216
2588
|
var ratingConfigSchema = baseConfigSchema.extend({
|
|
2217
|
-
max:
|
|
2218
|
-
iconType:
|
|
2589
|
+
max: import_zod4.z.number().int().min(1).optional(),
|
|
2590
|
+
iconType: import_zod4.z.enum(["star", "heart", "thumbs", "number"]).optional()
|
|
2219
2591
|
});
|
|
2220
2592
|
var attributeConfigSchemas = {
|
|
2221
2593
|
text: textConfigSchema,
|
|
@@ -2246,7 +2618,7 @@ function validateAttributeConfig(type, config) {
|
|
|
2246
2618
|
}
|
|
2247
2619
|
return {
|
|
2248
2620
|
success: false,
|
|
2249
|
-
errors: result.error.
|
|
2621
|
+
errors: result.error.issues.map((err) => `${err.path.join(".")}: ${err.message}`)
|
|
2250
2622
|
};
|
|
2251
2623
|
}
|
|
2252
2624
|
function parseAttributeConfig(type, config) {
|
|
@@ -2259,7 +2631,7 @@ function safeParseAttributeConfig(type, config) {
|
|
|
2259
2631
|
return result.success ? result.data : void 0;
|
|
2260
2632
|
}
|
|
2261
2633
|
function createTextValidator(attr) {
|
|
2262
|
-
let schema =
|
|
2634
|
+
let schema = import_zod4.z.string();
|
|
2263
2635
|
if (attr.minLength !== void 0) {
|
|
2264
2636
|
schema = schema.min(
|
|
2265
2637
|
attr.minLength,
|
|
@@ -2278,7 +2650,7 @@ function createTextValidator(attr) {
|
|
|
2278
2650
|
return schema;
|
|
2279
2651
|
}
|
|
2280
2652
|
function createNumberValidator(attr) {
|
|
2281
|
-
let schema =
|
|
2653
|
+
let schema = import_zod4.z.number();
|
|
2282
2654
|
if (attr.min !== void 0) {
|
|
2283
2655
|
schema = schema.min(attr.min, `${attr.label} must be at least ${attr.min}`);
|
|
2284
2656
|
}
|
|
@@ -2291,81 +2663,75 @@ function createNumberValidator(attr) {
|
|
|
2291
2663
|
return schema;
|
|
2292
2664
|
}
|
|
2293
2665
|
function createCheckboxValidator(_attr) {
|
|
2294
|
-
return
|
|
2666
|
+
return import_zod4.z.boolean();
|
|
2295
2667
|
}
|
|
2296
2668
|
function createDateValidator(attr) {
|
|
2297
|
-
return
|
|
2669
|
+
return import_zod4.z.string().datetime({ message: `${attr.label} must be a valid ISO date` });
|
|
2298
2670
|
}
|
|
2299
2671
|
function createPhoneValidator(_attr) {
|
|
2300
|
-
return
|
|
2301
|
-
countryCode:
|
|
2302
|
-
phoneNumber:
|
|
2672
|
+
return import_zod4.z.object({
|
|
2673
|
+
countryCode: import_zod4.z.string().length(3),
|
|
2674
|
+
phoneNumber: import_zod4.z.string().min(1)
|
|
2303
2675
|
});
|
|
2304
2676
|
}
|
|
2305
2677
|
function createCurrencyValidator(_attr) {
|
|
2306
|
-
return
|
|
2307
|
-
code:
|
|
2308
|
-
value:
|
|
2678
|
+
return import_zod4.z.object({
|
|
2679
|
+
code: import_zod4.z.string().length(3),
|
|
2680
|
+
value: import_zod4.z.number().min(0)
|
|
2309
2681
|
});
|
|
2310
2682
|
}
|
|
2311
2683
|
function createStatusValidator(attr) {
|
|
2312
2684
|
const validValues = attr.options.map((opt) => opt.value);
|
|
2313
|
-
return
|
|
2314
|
-
|
|
2315
|
-
message: `${attr.label} must be one of: ${validValues.join(", ")}`
|
|
2316
|
-
})
|
|
2685
|
+
return import_zod4.z.enum(validValues, {
|
|
2686
|
+
error: `${attr.label} must be one of: ${validValues.join(", ")}`
|
|
2317
2687
|
});
|
|
2318
2688
|
}
|
|
2319
2689
|
function createSelectValidator(attr) {
|
|
2320
2690
|
const validValues = attr.options.map((opt) => opt.value);
|
|
2321
|
-
return
|
|
2322
|
-
|
|
2323
|
-
message: `${attr.label} must be one of: ${validValues.join(", ")}`
|
|
2324
|
-
})
|
|
2691
|
+
return import_zod4.z.enum(validValues, {
|
|
2692
|
+
error: `${attr.label} must be one of: ${validValues.join(", ")}`
|
|
2325
2693
|
});
|
|
2326
2694
|
}
|
|
2327
2695
|
function createMultiselectValidator(attr) {
|
|
2328
2696
|
const validValues = attr.options.map((opt) => opt.value);
|
|
2329
|
-
return
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
message: `Each value must be one of: ${validValues.join(", ")}`
|
|
2333
|
-
})
|
|
2697
|
+
return import_zod4.z.array(
|
|
2698
|
+
import_zod4.z.enum(validValues, {
|
|
2699
|
+
error: `Each value must be one of: ${validValues.join(", ")}`
|
|
2334
2700
|
})
|
|
2335
2701
|
);
|
|
2336
2702
|
}
|
|
2337
2703
|
function createLocationValidator(_attr) {
|
|
2338
|
-
return
|
|
2339
|
-
address:
|
|
2340
|
-
address2:
|
|
2341
|
-
city:
|
|
2342
|
-
state:
|
|
2343
|
-
postalCode:
|
|
2344
|
-
country:
|
|
2345
|
-
latitude:
|
|
2346
|
-
longitude:
|
|
2704
|
+
return import_zod4.z.object({
|
|
2705
|
+
address: import_zod4.z.string().optional(),
|
|
2706
|
+
address2: import_zod4.z.string().optional(),
|
|
2707
|
+
city: import_zod4.z.string().optional(),
|
|
2708
|
+
state: import_zod4.z.string().optional(),
|
|
2709
|
+
postalCode: import_zod4.z.string().optional(),
|
|
2710
|
+
country: import_zod4.z.string().length(3).optional(),
|
|
2711
|
+
latitude: import_zod4.z.number().optional(),
|
|
2712
|
+
longitude: import_zod4.z.number().optional()
|
|
2347
2713
|
});
|
|
2348
2714
|
}
|
|
2349
2715
|
function createTimestampValidator(_attr) {
|
|
2350
|
-
return
|
|
2716
|
+
return import_zod4.z.number().int().positive();
|
|
2351
2717
|
}
|
|
2352
2718
|
function createFileValidator(_attr) {
|
|
2353
|
-
return
|
|
2719
|
+
return import_zod4.z.string().uuid();
|
|
2354
2720
|
}
|
|
2355
2721
|
function createUserValidator(_attr) {
|
|
2356
|
-
return
|
|
2722
|
+
return import_zod4.z.string().uuid();
|
|
2357
2723
|
}
|
|
2358
2724
|
function createSingleRelationValidator(attr) {
|
|
2359
|
-
const uuidSchema =
|
|
2725
|
+
const uuidSchema = import_zod4.z.string().uuid({
|
|
2360
2726
|
message: `${attr.label} must be a valid record ID`
|
|
2361
2727
|
});
|
|
2362
|
-
return
|
|
2728
|
+
return import_zod4.z.union([uuidSchema, import_zod4.z.null()]);
|
|
2363
2729
|
}
|
|
2364
2730
|
function createMultiRelationValidator(attr) {
|
|
2365
|
-
const uuidSchema =
|
|
2731
|
+
const uuidSchema = import_zod4.z.string().uuid({
|
|
2366
2732
|
message: `Each ${attr.label} item must be a valid record ID`
|
|
2367
2733
|
});
|
|
2368
|
-
let arraySchema =
|
|
2734
|
+
let arraySchema = import_zod4.z.array(uuidSchema);
|
|
2369
2735
|
if (attr.minItems !== void 0) {
|
|
2370
2736
|
arraySchema = arraySchema.min(
|
|
2371
2737
|
attr.minItems,
|
|
@@ -2387,7 +2753,7 @@ function createRelationValidator(attr) {
|
|
|
2387
2753
|
return createSingleRelationValidator(attr);
|
|
2388
2754
|
}
|
|
2389
2755
|
function createRatingValidator(attr) {
|
|
2390
|
-
let schema =
|
|
2756
|
+
let schema = import_zod4.z.number().int().min(0);
|
|
2391
2757
|
if (attr.max !== void 0) {
|
|
2392
2758
|
schema = schema.max(attr.max, `${attr.label} must be at most ${attr.max}`);
|
|
2393
2759
|
}
|
|
@@ -2426,7 +2792,7 @@ function createAttributeValidator(attr) {
|
|
|
2426
2792
|
case "rating":
|
|
2427
2793
|
return createRatingValidator(attr);
|
|
2428
2794
|
default:
|
|
2429
|
-
return
|
|
2795
|
+
return import_zod4.z.unknown();
|
|
2430
2796
|
}
|
|
2431
2797
|
}
|
|
2432
2798
|
function createObjectValidator(objectDef) {
|
|
@@ -2438,7 +2804,7 @@ function createObjectValidator(objectDef) {
|
|
|
2438
2804
|
}
|
|
2439
2805
|
shape[attr.name] = validator;
|
|
2440
2806
|
}
|
|
2441
|
-
return
|
|
2807
|
+
return import_zod4.z.object(shape);
|
|
2442
2808
|
}
|
|
2443
2809
|
function validateAttribute(attr, value) {
|
|
2444
2810
|
const validator = createAttributeValidator(attr);
|
|
@@ -2454,7 +2820,7 @@ function validateAttribute(attr, value) {
|
|
|
2454
2820
|
}
|
|
2455
2821
|
return {
|
|
2456
2822
|
success: false,
|
|
2457
|
-
errors: result.error.
|
|
2823
|
+
errors: result.error.issues.map((err) => ({
|
|
2458
2824
|
path: [attr.name, ...err.path.map(String)],
|
|
2459
2825
|
message: err.message
|
|
2460
2826
|
}))
|
|
@@ -2471,7 +2837,7 @@ function validateObject(objectDef, data) {
|
|
|
2471
2837
|
}
|
|
2472
2838
|
return {
|
|
2473
2839
|
success: false,
|
|
2474
|
-
errors: result.error.
|
|
2840
|
+
errors: result.error.issues.map((err) => ({
|
|
2475
2841
|
path: err.path.map(String),
|
|
2476
2842
|
message: err.message
|
|
2477
2843
|
}))
|
|
@@ -2492,7 +2858,7 @@ function createDraftValidator(objectDef) {
|
|
|
2492
2858
|
const validator = createAttributeValidator(attr).optional();
|
|
2493
2859
|
shape[attr.name] = validator;
|
|
2494
2860
|
}
|
|
2495
|
-
return
|
|
2861
|
+
return import_zod4.z.object(shape);
|
|
2496
2862
|
}
|
|
2497
2863
|
function validateDraft(objectDef, data) {
|
|
2498
2864
|
const validator = createDraftValidator(objectDef);
|
|
@@ -2505,7 +2871,7 @@ function validateDraft(objectDef, data) {
|
|
|
2505
2871
|
}
|
|
2506
2872
|
return {
|
|
2507
2873
|
success: false,
|
|
2508
|
-
errors: result.error.
|
|
2874
|
+
errors: result.error.issues.map((err) => ({
|
|
2509
2875
|
path: err.path.map(String),
|
|
2510
2876
|
message: err.message
|
|
2511
2877
|
}))
|
|
@@ -3925,6 +4291,273 @@ var FileService = class {
|
|
|
3925
4291
|
}
|
|
3926
4292
|
};
|
|
3927
4293
|
|
|
4294
|
+
// src/runtime/services/flow.service.ts
|
|
4295
|
+
var FlowService = class {
|
|
4296
|
+
constructor(adapter, systemFlows = []) {
|
|
4297
|
+
this.adapter = adapter;
|
|
4298
|
+
this.systemFlows = new Map(systemFlows.map((f) => [f.name, f]));
|
|
4299
|
+
}
|
|
4300
|
+
/**
|
|
4301
|
+
* Get all flows for a tenant (system + custom)
|
|
4302
|
+
*/
|
|
4303
|
+
async getAllFlows(tenantId) {
|
|
4304
|
+
const systemFlowsList = Array.from(this.systemFlows.values());
|
|
4305
|
+
if (!this.adapter.flows) {
|
|
4306
|
+
return systemFlowsList;
|
|
4307
|
+
}
|
|
4308
|
+
const dbFlows = await this.adapter.flows.findAllForTenant(tenantId);
|
|
4309
|
+
const customFlows = dbFlows.filter((f) => !f.system).map(this.convertDBFlowToDefinition);
|
|
4310
|
+
return [...systemFlowsList, ...customFlows];
|
|
4311
|
+
}
|
|
4312
|
+
/**
|
|
4313
|
+
* Get published flows only
|
|
4314
|
+
*/
|
|
4315
|
+
async getPublishedFlows(tenantId) {
|
|
4316
|
+
const allFlows = await this.getAllFlows(tenantId);
|
|
4317
|
+
return allFlows.filter((f) => f.status === "published");
|
|
4318
|
+
}
|
|
4319
|
+
/**
|
|
4320
|
+
* Get a specific flow by name
|
|
4321
|
+
*/
|
|
4322
|
+
async getFlow(name, tenantId) {
|
|
4323
|
+
const systemFlow = this.systemFlows.get(name);
|
|
4324
|
+
if (systemFlow) {
|
|
4325
|
+
return systemFlow;
|
|
4326
|
+
}
|
|
4327
|
+
if (!this.adapter.flows) {
|
|
4328
|
+
return null;
|
|
4329
|
+
}
|
|
4330
|
+
const dbFlow = await this.adapter.flows.findByName(tenantId, name);
|
|
4331
|
+
if (dbFlow) {
|
|
4332
|
+
return this.convertDBFlowToDefinition(dbFlow);
|
|
4333
|
+
}
|
|
4334
|
+
return null;
|
|
4335
|
+
}
|
|
4336
|
+
/**
|
|
4337
|
+
* Get a flow by ID
|
|
4338
|
+
*/
|
|
4339
|
+
async getFlowById(flowId) {
|
|
4340
|
+
if (!this.adapter.flows) {
|
|
4341
|
+
return null;
|
|
4342
|
+
}
|
|
4343
|
+
const dbFlow = await this.adapter.flows.findById(flowId);
|
|
4344
|
+
if (dbFlow) {
|
|
4345
|
+
return this.convertDBFlowToDefinition(dbFlow);
|
|
4346
|
+
}
|
|
4347
|
+
return null;
|
|
4348
|
+
}
|
|
4349
|
+
/**
|
|
4350
|
+
* Create a new custom flow (as draft)
|
|
4351
|
+
*/
|
|
4352
|
+
async createFlow(input, tenantId) {
|
|
4353
|
+
if (!this.adapter.flows) {
|
|
4354
|
+
throw new Error("Flows feature is not enabled. Database adapter does not support flows.");
|
|
4355
|
+
}
|
|
4356
|
+
this.validateFlowName(input.name);
|
|
4357
|
+
const existing = await this.adapter.flows.findByName(tenantId, input.name);
|
|
4358
|
+
if (existing) {
|
|
4359
|
+
throw new Error(`Flow "${input.name}" already exists`);
|
|
4360
|
+
}
|
|
4361
|
+
if (this.systemFlows.has(input.name)) {
|
|
4362
|
+
throw new Error(
|
|
4363
|
+
`Cannot create flow "${input.name}": a system flow with this name already exists`
|
|
4364
|
+
);
|
|
4365
|
+
}
|
|
4366
|
+
this.validateFlowStructure(input);
|
|
4367
|
+
const dbFlow = await this.adapter.flows.create({
|
|
4368
|
+
tenantId,
|
|
4369
|
+
name: input.name,
|
|
4370
|
+
label: input.label,
|
|
4371
|
+
description: input.description,
|
|
4372
|
+
icon: input.icon,
|
|
4373
|
+
status: "draft",
|
|
4374
|
+
version: 1,
|
|
4375
|
+
slots: input.slots,
|
|
4376
|
+
pages: input.pages,
|
|
4377
|
+
relations: input.relations,
|
|
4378
|
+
system: false,
|
|
4379
|
+
metadata: input.metadata
|
|
4380
|
+
});
|
|
4381
|
+
return this.convertDBFlowToDefinition(dbFlow);
|
|
4382
|
+
}
|
|
4383
|
+
/**
|
|
4384
|
+
* Update a custom flow
|
|
4385
|
+
*/
|
|
4386
|
+
async updateFlow(flowId, input) {
|
|
4387
|
+
if (!this.adapter.flows) {
|
|
4388
|
+
throw new Error("Flows feature is not enabled.");
|
|
4389
|
+
}
|
|
4390
|
+
const dbFlow = await this.adapter.flows.findById(flowId);
|
|
4391
|
+
if (!dbFlow) {
|
|
4392
|
+
throw new Error(`Flow with id "${flowId}" not found`);
|
|
4393
|
+
}
|
|
4394
|
+
if (dbFlow.system) {
|
|
4395
|
+
throw new Error("Cannot modify system flows. System flows are protected.");
|
|
4396
|
+
}
|
|
4397
|
+
if (input.slots || input.pages || input.relations) {
|
|
4398
|
+
this.validateFlowStructure({
|
|
4399
|
+
name: dbFlow.name,
|
|
4400
|
+
label: input.label ?? dbFlow.label,
|
|
4401
|
+
slots: input.slots ?? dbFlow.slots,
|
|
4402
|
+
pages: input.pages ?? dbFlow.pages,
|
|
4403
|
+
relations: input.relations ?? dbFlow.relations
|
|
4404
|
+
});
|
|
4405
|
+
}
|
|
4406
|
+
const updated = await this.adapter.flows.update(flowId, {
|
|
4407
|
+
label: input.label,
|
|
4408
|
+
description: input.description,
|
|
4409
|
+
icon: input.icon,
|
|
4410
|
+
slots: input.slots,
|
|
4411
|
+
pages: input.pages,
|
|
4412
|
+
relations: input.relations,
|
|
4413
|
+
metadata: input.metadata
|
|
4414
|
+
});
|
|
4415
|
+
return this.convertDBFlowToDefinition(updated);
|
|
4416
|
+
}
|
|
4417
|
+
/**
|
|
4418
|
+
* Publish a flow
|
|
4419
|
+
*/
|
|
4420
|
+
async publishFlow(flowId) {
|
|
4421
|
+
if (!this.adapter.flows) {
|
|
4422
|
+
throw new Error("Flows feature is not enabled.");
|
|
4423
|
+
}
|
|
4424
|
+
const dbFlow = await this.adapter.flows.findById(flowId);
|
|
4425
|
+
if (!dbFlow) {
|
|
4426
|
+
throw new Error(`Flow with id "${flowId}" not found`);
|
|
4427
|
+
}
|
|
4428
|
+
if (dbFlow.system) {
|
|
4429
|
+
throw new Error("Cannot publish system flows. They are always published.");
|
|
4430
|
+
}
|
|
4431
|
+
if (dbFlow.status === "published") {
|
|
4432
|
+
return this.convertDBFlowToDefinition(dbFlow);
|
|
4433
|
+
}
|
|
4434
|
+
this.validateFlowStructure({
|
|
4435
|
+
name: dbFlow.name,
|
|
4436
|
+
label: dbFlow.label,
|
|
4437
|
+
slots: dbFlow.slots,
|
|
4438
|
+
pages: dbFlow.pages,
|
|
4439
|
+
relations: dbFlow.relations
|
|
4440
|
+
});
|
|
4441
|
+
const updated = await this.adapter.flows.update(flowId, {
|
|
4442
|
+
status: "published",
|
|
4443
|
+
version: dbFlow.version + 1
|
|
4444
|
+
});
|
|
4445
|
+
return this.convertDBFlowToDefinition(updated);
|
|
4446
|
+
}
|
|
4447
|
+
/**
|
|
4448
|
+
* Archive a flow
|
|
4449
|
+
*/
|
|
4450
|
+
async archiveFlow(flowId) {
|
|
4451
|
+
if (!this.adapter.flows) {
|
|
4452
|
+
throw new Error("Flows feature is not enabled.");
|
|
4453
|
+
}
|
|
4454
|
+
const dbFlow = await this.adapter.flows.findById(flowId);
|
|
4455
|
+
if (!dbFlow) {
|
|
4456
|
+
throw new Error(`Flow with id "${flowId}" not found`);
|
|
4457
|
+
}
|
|
4458
|
+
if (dbFlow.system) {
|
|
4459
|
+
throw new Error("Cannot archive system flows.");
|
|
4460
|
+
}
|
|
4461
|
+
const updated = await this.adapter.flows.update(flowId, {
|
|
4462
|
+
status: "archived"
|
|
4463
|
+
});
|
|
4464
|
+
return this.convertDBFlowToDefinition(updated);
|
|
4465
|
+
}
|
|
4466
|
+
/**
|
|
4467
|
+
* Delete a custom flow
|
|
4468
|
+
*/
|
|
4469
|
+
async deleteFlow(flowId) {
|
|
4470
|
+
if (!this.adapter.flows) {
|
|
4471
|
+
throw new Error("Flows feature is not enabled.");
|
|
4472
|
+
}
|
|
4473
|
+
const dbFlow = await this.adapter.flows.findById(flowId);
|
|
4474
|
+
if (!dbFlow) {
|
|
4475
|
+
throw new Error(`Flow with id "${flowId}" not found`);
|
|
4476
|
+
}
|
|
4477
|
+
if (dbFlow.system) {
|
|
4478
|
+
throw new Error("Cannot delete system flows. System flows are protected.");
|
|
4479
|
+
}
|
|
4480
|
+
await this.adapter.flows.delete(flowId);
|
|
4481
|
+
}
|
|
4482
|
+
// ============================================================================
|
|
4483
|
+
// PRIVATE HELPERS
|
|
4484
|
+
// ============================================================================
|
|
4485
|
+
/**
|
|
4486
|
+
* Validate flow name format (kebab-case)
|
|
4487
|
+
*/
|
|
4488
|
+
validateFlowName(name) {
|
|
4489
|
+
if (!name || name.length === 0) {
|
|
4490
|
+
throw new Error("Flow name cannot be empty");
|
|
4491
|
+
}
|
|
4492
|
+
if (name.length > 63) {
|
|
4493
|
+
throw new Error("Flow name is too long (max 63 characters)");
|
|
4494
|
+
}
|
|
4495
|
+
const kebabCaseRegex = /^[a-z][a-z0-9-]*$/;
|
|
4496
|
+
if (!kebabCaseRegex.test(name)) {
|
|
4497
|
+
throw new Error(
|
|
4498
|
+
"Invalid flow name format. Name must be in kebab-case (e.g., 'couple-creation', 'new-contact')"
|
|
4499
|
+
);
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4502
|
+
/**
|
|
4503
|
+
* Validate flow structure (slots, pages, relations)
|
|
4504
|
+
*/
|
|
4505
|
+
validateFlowStructure(input) {
|
|
4506
|
+
if (!input.slots || input.slots.length === 0) {
|
|
4507
|
+
throw new Error("Flow must have at least one slot");
|
|
4508
|
+
}
|
|
4509
|
+
if (!input.pages || input.pages.length === 0) {
|
|
4510
|
+
throw new Error("Flow must have at least one page");
|
|
4511
|
+
}
|
|
4512
|
+
const slotIds = new Set(input.slots.map((s) => s.id));
|
|
4513
|
+
if (slotIds.size !== input.slots.length) {
|
|
4514
|
+
throw new Error("Duplicate slot IDs detected");
|
|
4515
|
+
}
|
|
4516
|
+
for (const page of input.pages) {
|
|
4517
|
+
for (const row of page.rows) {
|
|
4518
|
+
for (const field of row.fields) {
|
|
4519
|
+
if (!slotIds.has(field.slotId)) {
|
|
4520
|
+
throw new Error(`Field "${field.id}" references unknown slot "${field.slotId}"`);
|
|
4521
|
+
}
|
|
4522
|
+
}
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
for (const relation2 of input.relations) {
|
|
4526
|
+
if (!slotIds.has(relation2.sourceSlotId)) {
|
|
4527
|
+
throw new Error(`Relation references unknown source slot "${relation2.sourceSlotId}"`);
|
|
4528
|
+
}
|
|
4529
|
+
if (!slotIds.has(relation2.targetSlotId)) {
|
|
4530
|
+
throw new Error(`Relation references unknown target slot "${relation2.targetSlotId}"`);
|
|
4531
|
+
}
|
|
4532
|
+
if (relation2.sourceSlotId === relation2.targetSlotId) {
|
|
4533
|
+
throw new Error(`Relation cannot link a slot to itself: "${relation2.sourceSlotId}"`);
|
|
4534
|
+
}
|
|
4535
|
+
}
|
|
4536
|
+
}
|
|
4537
|
+
/**
|
|
4538
|
+
* Convert database flow to FlowDefinition
|
|
4539
|
+
*/
|
|
4540
|
+
convertDBFlowToDefinition(dbFlow) {
|
|
4541
|
+
return {
|
|
4542
|
+
id: dbFlow.id,
|
|
4543
|
+
name: dbFlow.name,
|
|
4544
|
+
label: dbFlow.label,
|
|
4545
|
+
description: dbFlow.description,
|
|
4546
|
+
icon: dbFlow.icon,
|
|
4547
|
+
status: dbFlow.status,
|
|
4548
|
+
version: dbFlow.version,
|
|
4549
|
+
slots: dbFlow.slots,
|
|
4550
|
+
pages: dbFlow.pages,
|
|
4551
|
+
relations: dbFlow.relations,
|
|
4552
|
+
system: dbFlow.system,
|
|
4553
|
+
tenantId: dbFlow.tenantId,
|
|
4554
|
+
metadata: dbFlow.metadata,
|
|
4555
|
+
createdAt: dbFlow.createdAt,
|
|
4556
|
+
updatedAt: dbFlow.updatedAt
|
|
4557
|
+
};
|
|
4558
|
+
}
|
|
4559
|
+
};
|
|
4560
|
+
|
|
3928
4561
|
// src/runtime/services/geocoding.service.ts
|
|
3929
4562
|
var GeocodingService = class {
|
|
3930
4563
|
constructor(adapter) {
|
|
@@ -6262,9 +6895,6 @@ var ViewService = class {
|
|
|
6262
6895
|
`Cannot create view "${input.name}": a system view with this name already exists`
|
|
6263
6896
|
);
|
|
6264
6897
|
}
|
|
6265
|
-
if (!input.tabs || input.tabs.length === 0) {
|
|
6266
|
-
throw new Error("View must have at least one tab");
|
|
6267
|
-
}
|
|
6268
6898
|
const dbView = await this.adapter.views.create({
|
|
6269
6899
|
tenantId,
|
|
6270
6900
|
objectName: input.objectName,
|
|
@@ -6272,7 +6902,7 @@ var ViewService = class {
|
|
|
6272
6902
|
label: input.label,
|
|
6273
6903
|
description: input.description,
|
|
6274
6904
|
icon: input.icon,
|
|
6275
|
-
tabs: input.tabs,
|
|
6905
|
+
tabs: input.tabs ?? [],
|
|
6276
6906
|
default: input.default ?? false,
|
|
6277
6907
|
system: false,
|
|
6278
6908
|
// Custom views are never system
|
|
@@ -6295,9 +6925,6 @@ var ViewService = class {
|
|
|
6295
6925
|
if (dbView.system) {
|
|
6296
6926
|
throw new Error("Cannot modify system views. System views are protected.");
|
|
6297
6927
|
}
|
|
6298
|
-
if (input.tabs && input.tabs.length === 0) {
|
|
6299
|
-
throw new Error("View must have at least one tab");
|
|
6300
|
-
}
|
|
6301
6928
|
const updated = await this.adapter.views.update(viewId, {
|
|
6302
6929
|
label: input.label,
|
|
6303
6930
|
description: input.description,
|
|
@@ -6725,6 +7352,10 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
|
|
|
6725
7352
|
DuplicateError,
|
|
6726
7353
|
FileNotFoundError,
|
|
6727
7354
|
FileService,
|
|
7355
|
+
FlowBuilder,
|
|
7356
|
+
FlowPageBuilder,
|
|
7357
|
+
FlowRowBuilder,
|
|
7358
|
+
FlowService,
|
|
6728
7359
|
ForbiddenError,
|
|
6729
7360
|
GeocodingService,
|
|
6730
7361
|
GlobalSearchService,
|
|
@@ -6792,6 +7423,7 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
|
|
|
6792
7423
|
extractAttributeNames,
|
|
6793
7424
|
file,
|
|
6794
7425
|
fileConfigSchema,
|
|
7426
|
+
flow,
|
|
6795
7427
|
generateId,
|
|
6796
7428
|
generatePrefixedId,
|
|
6797
7429
|
getAttributeConfigSchema,
|
|
@@ -6802,6 +7434,8 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
|
|
|
6802
7434
|
isAdvancedFilterState,
|
|
6803
7435
|
isCustomTab,
|
|
6804
7436
|
isDefaultRole,
|
|
7437
|
+
isFlowDefinition,
|
|
7438
|
+
isFlowPublished,
|
|
6805
7439
|
isForbiddenError,
|
|
6806
7440
|
isFormTab,
|
|
6807
7441
|
isLabelExpression,
|
|
@@ -6810,6 +7444,7 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
|
|
|
6810
7444
|
isProtectedResourceError,
|
|
6811
7445
|
isRecordComplete,
|
|
6812
7446
|
isSchemaError,
|
|
7447
|
+
isSystemFlow,
|
|
6813
7448
|
isTableTab,
|
|
6814
7449
|
isValidationError,
|
|
6815
7450
|
location,
|