@stndrds/schema 0.1.0-alpha.16 → 0.1.0-alpha.17
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-4D23X5XN.mjs +7257 -0
- package/dist/chunk-6RPWHMTH.mjs +7101 -0
- package/dist/chunk-CMO3JEJL.mjs +7259 -0
- package/dist/chunk-JWRGX3UW.mjs +7265 -0
- package/dist/chunk-OUSLILLE.mjs +7259 -0
- package/dist/chunk-PMJZPJDP.mjs +7265 -0
- package/dist/chunk-YEV46K3M.mjs +7259 -0
- package/dist/index.d.mts +42 -3
- package/dist/index.d.ts +42 -3
- package/dist/index.js +398 -65
- package/dist/index.mjs +11 -1
- package/dist/runtime--rp-TG-h.d.mts +5181 -0
- package/dist/runtime--rp-TG-h.d.ts +5181 -0
- package/dist/runtime.d.mts +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +497 -124
- package/dist/runtime.mjs +3 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -129,6 +129,7 @@ __export(index_exports, {
|
|
|
129
129
|
DEFAULT_ROLE_LABELS: () => DEFAULT_ROLE_LABELS,
|
|
130
130
|
DEFAULT_ROLE_PERMISSIONS: () => DEFAULT_ROLE_PERMISSIONS,
|
|
131
131
|
DuplicateError: () => DuplicateError,
|
|
132
|
+
EMPTY_VALUE_PLACEHOLDER: () => EMPTY_VALUE_PLACEHOLDER,
|
|
132
133
|
FileNotFoundError: () => FileNotFoundError,
|
|
133
134
|
FileService: () => FileService,
|
|
134
135
|
FlowBuilder: () => FlowBuilder,
|
|
@@ -199,10 +200,12 @@ __export(index_exports, {
|
|
|
199
200
|
currencyConfigSchema: () => currencyConfigSchema,
|
|
200
201
|
date: () => date,
|
|
201
202
|
dateConfigSchema: () => dateConfigSchema,
|
|
203
|
+
enrichValuesWithSelectLabels: () => enrichValuesWithSelectLabels,
|
|
202
204
|
extractAttributeNames: () => extractAttributeNames,
|
|
203
205
|
file: () => file,
|
|
204
206
|
fileConfigSchema: () => fileConfigSchema,
|
|
205
207
|
flow: () => flow,
|
|
208
|
+
formatAttributeValue: () => formatAttributeValue,
|
|
206
209
|
generateId: () => generateId,
|
|
207
210
|
generatePrefixedId: () => generatePrefixedId,
|
|
208
211
|
getAttributeConfigSchema: () => getAttributeConfigSchema,
|
|
@@ -210,6 +213,7 @@ __export(index_exports, {
|
|
|
210
213
|
getSyncPreview: () => getSyncPreview,
|
|
211
214
|
getViewSyncPreview: () => getViewSyncPreview,
|
|
212
215
|
group: () => group,
|
|
216
|
+
isActivityTab: () => isActivityTab,
|
|
213
217
|
isAdvancedFilterState: () => isAdvancedFilterState,
|
|
214
218
|
isCustomTab: () => isCustomTab,
|
|
215
219
|
isDefaultRole: () => isDefaultRole,
|
|
@@ -434,6 +438,9 @@ function isTableTab(tab) {
|
|
|
434
438
|
function isCustomTab(tab) {
|
|
435
439
|
return tab.type === "custom";
|
|
436
440
|
}
|
|
441
|
+
function isActivityTab(tab) {
|
|
442
|
+
return tab.type === "activity";
|
|
443
|
+
}
|
|
437
444
|
|
|
438
445
|
// src/utils.ts
|
|
439
446
|
function generateId() {
|
|
@@ -450,6 +457,168 @@ function generatePrefixedId(prefix) {
|
|
|
450
457
|
return `${prefix}_${generateId()}`;
|
|
451
458
|
}
|
|
452
459
|
|
|
460
|
+
// src/format.ts
|
|
461
|
+
var import_constants = require("@stndrds/constants");
|
|
462
|
+
var EMPTY_VALUE_PLACEHOLDER = "\u2014";
|
|
463
|
+
function formatText(value) {
|
|
464
|
+
return String(value);
|
|
465
|
+
}
|
|
466
|
+
function formatCheckbox(value) {
|
|
467
|
+
return value ? "Yes" : "No";
|
|
468
|
+
}
|
|
469
|
+
function formatNumber(value, attribute) {
|
|
470
|
+
if (typeof value !== "number") return String(value);
|
|
471
|
+
const decimals = attribute.decimals;
|
|
472
|
+
return value.toLocaleString(void 0, {
|
|
473
|
+
minimumFractionDigits: decimals,
|
|
474
|
+
maximumFractionDigits: decimals
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
function formatCurrency(value, _attribute) {
|
|
478
|
+
if (typeof value !== "object" || value === null) return String(value);
|
|
479
|
+
const currency2 = value;
|
|
480
|
+
if (!("value" in currency2 && "code" in currency2)) return String(value);
|
|
481
|
+
const formattedValue = currency2.value.toLocaleString(void 0, {
|
|
482
|
+
minimumFractionDigits: 2,
|
|
483
|
+
maximumFractionDigits: 2
|
|
484
|
+
});
|
|
485
|
+
return `${formattedValue} ${currency2.code}`;
|
|
486
|
+
}
|
|
487
|
+
function formatDate(value) {
|
|
488
|
+
if (value instanceof Date) {
|
|
489
|
+
return value.toISOString().split("T")[0];
|
|
490
|
+
}
|
|
491
|
+
if (typeof value === "string") {
|
|
492
|
+
const date2 = new Date(value);
|
|
493
|
+
if (!Number.isNaN(date2.getTime())) {
|
|
494
|
+
return date2.toISOString().split("T")[0];
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return String(value);
|
|
498
|
+
}
|
|
499
|
+
function formatTimestamp(value) {
|
|
500
|
+
if (typeof value === "number") {
|
|
501
|
+
return new Date(value).toISOString();
|
|
502
|
+
}
|
|
503
|
+
if (value instanceof Date) {
|
|
504
|
+
return value.toISOString();
|
|
505
|
+
}
|
|
506
|
+
return String(value);
|
|
507
|
+
}
|
|
508
|
+
function formatPhone(value) {
|
|
509
|
+
if (typeof value !== "object" || value === null) return String(value);
|
|
510
|
+
const phone2 = value;
|
|
511
|
+
if (!("phoneNumber" in phone2)) return String(value);
|
|
512
|
+
if (phone2.countryCode) {
|
|
513
|
+
const country = (0, import_constants.getCountryByIso3)(phone2.countryCode);
|
|
514
|
+
const dial = country?.phoneCode ?? "";
|
|
515
|
+
return `${dial} ${phone2.phoneNumber}`.trim();
|
|
516
|
+
}
|
|
517
|
+
return phone2.phoneNumber;
|
|
518
|
+
}
|
|
519
|
+
function formatLocation(value, attribute) {
|
|
520
|
+
if (typeof value !== "object" || value === null) return String(value);
|
|
521
|
+
const loc = value;
|
|
522
|
+
const granularity = attribute.granularity ?? "full";
|
|
523
|
+
const parts = [];
|
|
524
|
+
switch (granularity) {
|
|
525
|
+
case "country":
|
|
526
|
+
if (loc.country) parts.push(loc.country);
|
|
527
|
+
break;
|
|
528
|
+
case "state":
|
|
529
|
+
if (loc.state) parts.push(loc.state);
|
|
530
|
+
if (loc.country) parts.push(loc.country);
|
|
531
|
+
break;
|
|
532
|
+
case "city":
|
|
533
|
+
if (loc.city) parts.push(loc.city);
|
|
534
|
+
if (loc.state) parts.push(loc.state);
|
|
535
|
+
if (loc.country) parts.push(loc.country);
|
|
536
|
+
break;
|
|
537
|
+
case "coordinates":
|
|
538
|
+
if (loc.latitude !== void 0 && loc.longitude !== void 0) {
|
|
539
|
+
parts.push(`${loc.latitude}, ${loc.longitude}`);
|
|
540
|
+
}
|
|
541
|
+
break;
|
|
542
|
+
case "address":
|
|
543
|
+
if (loc.address) parts.push(loc.address);
|
|
544
|
+
if (loc.city) parts.push(loc.city);
|
|
545
|
+
if (loc.state) parts.push(loc.state);
|
|
546
|
+
if (loc.country) parts.push(loc.country);
|
|
547
|
+
break;
|
|
548
|
+
default:
|
|
549
|
+
if (loc.address) parts.push(loc.address);
|
|
550
|
+
if (loc.city) parts.push(loc.city);
|
|
551
|
+
if (loc.state) parts.push(loc.state);
|
|
552
|
+
if (loc.postalCode) parts.push(loc.postalCode);
|
|
553
|
+
if (loc.country) parts.push(loc.country);
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
return parts.join(", ") || EMPTY_VALUE_PLACEHOLDER;
|
|
557
|
+
}
|
|
558
|
+
function formatSelect(value, attribute) {
|
|
559
|
+
if (typeof value !== "string") return String(value);
|
|
560
|
+
const option = attribute.options?.find((o) => o.value === value);
|
|
561
|
+
return option?.label ?? String(value);
|
|
562
|
+
}
|
|
563
|
+
function formatMultiselect(value, attribute) {
|
|
564
|
+
if (!Array.isArray(value)) return String(value);
|
|
565
|
+
if (attribute.options) {
|
|
566
|
+
const labels = value.map((v) => attribute.options.find((o) => o.value === v)?.label).filter(Boolean);
|
|
567
|
+
return labels.join(", ");
|
|
568
|
+
}
|
|
569
|
+
return value.join(", ");
|
|
570
|
+
}
|
|
571
|
+
function formatRating(value, attribute) {
|
|
572
|
+
if (typeof value !== "number") return String(value);
|
|
573
|
+
const max = attribute.max ?? 5;
|
|
574
|
+
return `${value}/${max}`;
|
|
575
|
+
}
|
|
576
|
+
function formatAttributeValue(value, attribute) {
|
|
577
|
+
if (value === null || value === void 0 || value === "") {
|
|
578
|
+
return EMPTY_VALUE_PLACEHOLDER;
|
|
579
|
+
}
|
|
580
|
+
switch (attribute.type) {
|
|
581
|
+
case "text":
|
|
582
|
+
case "textarea":
|
|
583
|
+
return formatText(value);
|
|
584
|
+
case "checkbox":
|
|
585
|
+
return formatCheckbox(value);
|
|
586
|
+
case "number":
|
|
587
|
+
return formatNumber(value, attribute);
|
|
588
|
+
case "currency":
|
|
589
|
+
return formatCurrency(value, attribute);
|
|
590
|
+
case "date":
|
|
591
|
+
return formatDate(value);
|
|
592
|
+
case "timestamp":
|
|
593
|
+
return formatTimestamp(value);
|
|
594
|
+
case "phone":
|
|
595
|
+
return formatPhone(value);
|
|
596
|
+
case "location":
|
|
597
|
+
return formatLocation(value, attribute);
|
|
598
|
+
case "select":
|
|
599
|
+
case "status":
|
|
600
|
+
return formatSelect(value, attribute);
|
|
601
|
+
case "multiselect":
|
|
602
|
+
return formatMultiselect(value, attribute);
|
|
603
|
+
case "rating":
|
|
604
|
+
return formatRating(value, attribute);
|
|
605
|
+
// Unsupported types - return value as-is or placeholder
|
|
606
|
+
case "file":
|
|
607
|
+
case "user":
|
|
608
|
+
case "relation":
|
|
609
|
+
if (Array.isArray(value)) {
|
|
610
|
+
return value.join(", ");
|
|
611
|
+
}
|
|
612
|
+
return String(value);
|
|
613
|
+
default: {
|
|
614
|
+
if (Array.isArray(value)) {
|
|
615
|
+
return value.join(", ");
|
|
616
|
+
}
|
|
617
|
+
return String(value);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
453
622
|
// src/constants/index.ts
|
|
454
623
|
init_default_roles();
|
|
455
624
|
|
|
@@ -653,8 +822,8 @@ function isForbiddenError(error) {
|
|
|
653
822
|
}
|
|
654
823
|
|
|
655
824
|
// src/builders/attribute-validators.ts
|
|
656
|
-
var
|
|
657
|
-
var VALID_ICONS = new Set(
|
|
825
|
+
var import_constants2 = require("@stndrds/constants");
|
|
826
|
+
var VALID_ICONS = new Set(import_constants2.ICONS);
|
|
658
827
|
var ATTRIBUTE_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
659
828
|
var MAX_NAME_LENGTH = 63;
|
|
660
829
|
var MAX_LABEL_LENGTH = 128;
|
|
@@ -2491,6 +2660,13 @@ var optionSchema = import_zod4.z.object({
|
|
|
2491
2660
|
description: import_zod4.z.string().optional(),
|
|
2492
2661
|
group: import_zod4.z.enum(["idle", "in_progress", "finished"]).optional()
|
|
2493
2662
|
});
|
|
2663
|
+
var optionsArraySchema = import_zod4.z.array(optionSchema).min(1).refine(
|
|
2664
|
+
(options) => {
|
|
2665
|
+
const values = options.map((o) => o.value);
|
|
2666
|
+
return new Set(values).size === values.length;
|
|
2667
|
+
},
|
|
2668
|
+
{ message: "Duplicate option values are not allowed" }
|
|
2669
|
+
);
|
|
2494
2670
|
var relationTargetSchema = import_zod4.z.object({
|
|
2495
2671
|
object: import_zod4.z.string().min(1),
|
|
2496
2672
|
displayTemplate: import_zod4.z.string().optional(),
|
|
@@ -2551,7 +2727,7 @@ var currencyConfigSchema = baseConfigSchema.extend({
|
|
|
2551
2727
|
allowedCurrencies: import_zod4.z.array(import_zod4.z.string().length(3)).optional()
|
|
2552
2728
|
});
|
|
2553
2729
|
var statusConfigSchema = baseConfigSchema.extend({
|
|
2554
|
-
options:
|
|
2730
|
+
options: optionsArraySchema
|
|
2555
2731
|
});
|
|
2556
2732
|
var locationConfigSchema = baseConfigSchema.extend({
|
|
2557
2733
|
granularity: import_zod4.z.enum(["full", "address", "city", "state", "country", "coordinates"]),
|
|
@@ -2565,10 +2741,10 @@ var timestampConfigSchema = baseConfigSchema.extend({
|
|
|
2565
2741
|
autoUpdate: import_zod4.z.boolean().optional()
|
|
2566
2742
|
});
|
|
2567
2743
|
var selectConfigSchema = baseConfigSchema.extend({
|
|
2568
|
-
options:
|
|
2744
|
+
options: optionsArraySchema
|
|
2569
2745
|
});
|
|
2570
2746
|
var multiselectConfigSchema = baseConfigSchema.extend({
|
|
2571
|
-
options:
|
|
2747
|
+
options: optionsArraySchema
|
|
2572
2748
|
});
|
|
2573
2749
|
var fileConfigSchema = baseConfigSchema.extend({
|
|
2574
2750
|
maxFiles: import_zod4.z.number().int().min(1).optional(),
|
|
@@ -2973,6 +3149,24 @@ function extractAttributeNames(template) {
|
|
|
2973
3149
|
}
|
|
2974
3150
|
return names;
|
|
2975
3151
|
}
|
|
3152
|
+
function hasOptions(attr) {
|
|
3153
|
+
return "options" in attr && Array.isArray(attr.options) && attr.options.length > 0;
|
|
3154
|
+
}
|
|
3155
|
+
function enrichValuesWithSelectLabels(values, attributes) {
|
|
3156
|
+
const enriched = { ...values };
|
|
3157
|
+
for (const attr of attributes) {
|
|
3158
|
+
const value = values[attr.name];
|
|
3159
|
+
if (value == null) continue;
|
|
3160
|
+
const isSelectLike = attr.type === "select" || attr.type === "status" || attr.type === "multiselect";
|
|
3161
|
+
if (!(isSelectLike && hasOptions(attr))) continue;
|
|
3162
|
+
if (attr.type === "multiselect" && Array.isArray(value) && value.length === 0) continue;
|
|
3163
|
+
const formatted = formatAttributeValue(value, attr);
|
|
3164
|
+
if (formatted && formatted !== EMPTY_VALUE_PLACEHOLDER) {
|
|
3165
|
+
enriched[attr.name] = formatted;
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
return enriched;
|
|
3169
|
+
}
|
|
2976
3170
|
|
|
2977
3171
|
// src/runtime/mock-adapter.ts
|
|
2978
3172
|
function createMockObjectsRepository(stores) {
|
|
@@ -3449,14 +3643,31 @@ function createMockObjectRecordsRepository(stores) {
|
|
|
3449
3643
|
(options.offset ?? 0) + options.limit
|
|
3450
3644
|
);
|
|
3451
3645
|
}
|
|
3646
|
+
const attributesByObjectId = /* @__PURE__ */ new Map();
|
|
3647
|
+
for (const attr of stores.attributes.values()) {
|
|
3648
|
+
if (!attributesByObjectId.has(attr.objectId)) {
|
|
3649
|
+
attributesByObjectId.set(attr.objectId, []);
|
|
3650
|
+
}
|
|
3651
|
+
attributesByObjectId.get(attr.objectId)?.push(attr);
|
|
3652
|
+
}
|
|
3452
3653
|
const results = matchingRecords.map((r) => {
|
|
3453
3654
|
const obj = objectsMap.get(r.objectId);
|
|
3454
3655
|
const labelExpression = obj?.labelExpression ?? "{{ name }}";
|
|
3656
|
+
const dbAttrs = attributesByObjectId.get(r.objectId) ?? [];
|
|
3657
|
+
const attrs = dbAttrs.map((a) => ({
|
|
3658
|
+
...a.config,
|
|
3659
|
+
id: a.id,
|
|
3660
|
+
name: a.name,
|
|
3661
|
+
type: a.type,
|
|
3662
|
+
label: a.config.label ?? a.name,
|
|
3663
|
+
required: a.config.required ?? false
|
|
3664
|
+
}));
|
|
3665
|
+
const enrichedValues = enrichValuesWithSelectLabels(r.values, attrs);
|
|
3455
3666
|
return {
|
|
3456
3667
|
objectId: r.objectId,
|
|
3457
3668
|
objectName: obj?.name ?? "unknown",
|
|
3458
3669
|
objectLabel: obj?.label ?? "Unknown",
|
|
3459
|
-
label: renderLabelExpression(labelExpression,
|
|
3670
|
+
label: renderLabelExpression(labelExpression, enrichedValues),
|
|
3460
3671
|
recordId: r.id,
|
|
3461
3672
|
values: r.values,
|
|
3462
3673
|
completionStatus: r.completionStatus,
|
|
@@ -3873,6 +4084,10 @@ var AuditService = class {
|
|
|
3873
4084
|
this.options = options;
|
|
3874
4085
|
this.buffer = [];
|
|
3875
4086
|
this.flushTimer = null;
|
|
4087
|
+
/** Prevents concurrent flush operations */
|
|
4088
|
+
this.isFlushing = false;
|
|
4089
|
+
/** Pending flush promise to allow waiting on concurrent flush */
|
|
4090
|
+
this.flushPromise = null;
|
|
3876
4091
|
if (options?.async && options.flushIntervalMs) {
|
|
3877
4092
|
this.startFlushTimer();
|
|
3878
4093
|
}
|
|
@@ -4025,14 +4240,23 @@ var AuditService = class {
|
|
|
4025
4240
|
// ============================================================================
|
|
4026
4241
|
/**
|
|
4027
4242
|
* Flush buffered logs to the database
|
|
4243
|
+
* Protected against concurrent flush calls
|
|
4028
4244
|
*/
|
|
4029
4245
|
async flush() {
|
|
4246
|
+
if (this.isFlushing && this.flushPromise) {
|
|
4247
|
+
return this.flushPromise;
|
|
4248
|
+
}
|
|
4030
4249
|
if (this.buffer.length === 0 || !this.adapter.audit) {
|
|
4031
4250
|
return;
|
|
4032
4251
|
}
|
|
4252
|
+
this.isFlushing = true;
|
|
4033
4253
|
const entries = [...this.buffer];
|
|
4034
4254
|
this.buffer = [];
|
|
4035
|
-
|
|
4255
|
+
this.flushPromise = this.adapter.audit.createMany(entries).finally(() => {
|
|
4256
|
+
this.isFlushing = false;
|
|
4257
|
+
this.flushPromise = null;
|
|
4258
|
+
});
|
|
4259
|
+
return this.flushPromise;
|
|
4036
4260
|
}
|
|
4037
4261
|
/**
|
|
4038
4262
|
* Clean up resources (stop timer, flush remaining logs)
|
|
@@ -4054,6 +4278,12 @@ var AuditService = class {
|
|
|
4054
4278
|
if (!this.adapter.audit) {
|
|
4055
4279
|
return;
|
|
4056
4280
|
}
|
|
4281
|
+
if (entry.actorId && !entry.actorEmail && entry.actorType === "user") {
|
|
4282
|
+
const profile = await this.adapter.userProfiles.findById(entry.actorId);
|
|
4283
|
+
if (profile) {
|
|
4284
|
+
entry.actorEmail = profile.email;
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4057
4287
|
if (this.options?.async) {
|
|
4058
4288
|
this.buffer.push(entry);
|
|
4059
4289
|
const batchSize = this.options.batchSize ?? 10;
|
|
@@ -4351,16 +4581,26 @@ var FlowService = class {
|
|
|
4351
4581
|
*/
|
|
4352
4582
|
async createFlow(input, tenantId) {
|
|
4353
4583
|
if (!this.adapter.flows) {
|
|
4354
|
-
throw new
|
|
4584
|
+
throw new SchemaError(
|
|
4585
|
+
"Flows feature is not enabled. Database adapter does not support flows.",
|
|
4586
|
+
SchemaErrorCode.UNKNOWN,
|
|
4587
|
+
{ feature: "flows" }
|
|
4588
|
+
);
|
|
4355
4589
|
}
|
|
4356
4590
|
this.validateFlowName(input.name);
|
|
4357
4591
|
const existing = await this.adapter.flows.findByName(tenantId, input.name);
|
|
4358
4592
|
if (existing) {
|
|
4359
|
-
throw new
|
|
4593
|
+
throw new SchemaError(
|
|
4594
|
+
`Flow "${input.name}" already exists`,
|
|
4595
|
+
SchemaErrorCode.DUPLICATE_OBJECT,
|
|
4596
|
+
{ flowName: input.name }
|
|
4597
|
+
);
|
|
4360
4598
|
}
|
|
4361
4599
|
if (this.systemFlows.has(input.name)) {
|
|
4362
|
-
throw new
|
|
4363
|
-
`Cannot create flow "${input.name}": a system flow with this name already exists
|
|
4600
|
+
throw new SchemaError(
|
|
4601
|
+
`Cannot create flow "${input.name}": a system flow with this name already exists`,
|
|
4602
|
+
SchemaErrorCode.DUPLICATE_OBJECT,
|
|
4603
|
+
{ flowName: input.name, system: true }
|
|
4364
4604
|
);
|
|
4365
4605
|
}
|
|
4366
4606
|
this.validateFlowStructure(input);
|
|
@@ -4385,14 +4625,16 @@ var FlowService = class {
|
|
|
4385
4625
|
*/
|
|
4386
4626
|
async updateFlow(flowId, input) {
|
|
4387
4627
|
if (!this.adapter.flows) {
|
|
4388
|
-
throw new
|
|
4628
|
+
throw new SchemaError("Flows feature is not enabled.", SchemaErrorCode.UNKNOWN, {
|
|
4629
|
+
feature: "flows"
|
|
4630
|
+
});
|
|
4389
4631
|
}
|
|
4390
4632
|
const dbFlow = await this.adapter.flows.findById(flowId);
|
|
4391
4633
|
if (!dbFlow) {
|
|
4392
|
-
throw new
|
|
4634
|
+
throw new NotFoundError("Flow", flowId);
|
|
4393
4635
|
}
|
|
4394
4636
|
if (dbFlow.system) {
|
|
4395
|
-
throw new
|
|
4637
|
+
throw new ProtectedResourceError("object", dbFlow.name, "modify");
|
|
4396
4638
|
}
|
|
4397
4639
|
if (input.slots || input.pages || input.relations) {
|
|
4398
4640
|
this.validateFlowStructure({
|
|
@@ -4419,14 +4661,16 @@ var FlowService = class {
|
|
|
4419
4661
|
*/
|
|
4420
4662
|
async publishFlow(flowId) {
|
|
4421
4663
|
if (!this.adapter.flows) {
|
|
4422
|
-
throw new
|
|
4664
|
+
throw new SchemaError("Flows feature is not enabled.", SchemaErrorCode.UNKNOWN, {
|
|
4665
|
+
feature: "flows"
|
|
4666
|
+
});
|
|
4423
4667
|
}
|
|
4424
4668
|
const dbFlow = await this.adapter.flows.findById(flowId);
|
|
4425
4669
|
if (!dbFlow) {
|
|
4426
|
-
throw new
|
|
4670
|
+
throw new NotFoundError("Flow", flowId);
|
|
4427
4671
|
}
|
|
4428
4672
|
if (dbFlow.system) {
|
|
4429
|
-
throw new
|
|
4673
|
+
throw new ProtectedResourceError("object", dbFlow.name, "modify");
|
|
4430
4674
|
}
|
|
4431
4675
|
if (dbFlow.status === "published") {
|
|
4432
4676
|
return this.convertDBFlowToDefinition(dbFlow);
|
|
@@ -4449,14 +4693,16 @@ var FlowService = class {
|
|
|
4449
4693
|
*/
|
|
4450
4694
|
async archiveFlow(flowId) {
|
|
4451
4695
|
if (!this.adapter.flows) {
|
|
4452
|
-
throw new
|
|
4696
|
+
throw new SchemaError("Flows feature is not enabled.", SchemaErrorCode.UNKNOWN, {
|
|
4697
|
+
feature: "flows"
|
|
4698
|
+
});
|
|
4453
4699
|
}
|
|
4454
4700
|
const dbFlow = await this.adapter.flows.findById(flowId);
|
|
4455
4701
|
if (!dbFlow) {
|
|
4456
|
-
throw new
|
|
4702
|
+
throw new NotFoundError("Flow", flowId);
|
|
4457
4703
|
}
|
|
4458
4704
|
if (dbFlow.system) {
|
|
4459
|
-
throw new
|
|
4705
|
+
throw new ProtectedResourceError("object", dbFlow.name, "modify");
|
|
4460
4706
|
}
|
|
4461
4707
|
const updated = await this.adapter.flows.update(flowId, {
|
|
4462
4708
|
status: "archived"
|
|
@@ -4468,14 +4714,16 @@ var FlowService = class {
|
|
|
4468
4714
|
*/
|
|
4469
4715
|
async deleteFlow(flowId) {
|
|
4470
4716
|
if (!this.adapter.flows) {
|
|
4471
|
-
throw new
|
|
4717
|
+
throw new SchemaError("Flows feature is not enabled.", SchemaErrorCode.UNKNOWN, {
|
|
4718
|
+
feature: "flows"
|
|
4719
|
+
});
|
|
4472
4720
|
}
|
|
4473
4721
|
const dbFlow = await this.adapter.flows.findById(flowId);
|
|
4474
4722
|
if (!dbFlow) {
|
|
4475
|
-
throw new
|
|
4723
|
+
throw new NotFoundError("Flow", flowId);
|
|
4476
4724
|
}
|
|
4477
4725
|
if (dbFlow.system) {
|
|
4478
|
-
throw new
|
|
4726
|
+
throw new ProtectedResourceError("object", dbFlow.name, "delete");
|
|
4479
4727
|
}
|
|
4480
4728
|
await this.adapter.flows.delete(flowId);
|
|
4481
4729
|
}
|
|
@@ -4487,16 +4735,23 @@ var FlowService = class {
|
|
|
4487
4735
|
*/
|
|
4488
4736
|
validateFlowName(name) {
|
|
4489
4737
|
if (!name || name.length === 0) {
|
|
4490
|
-
throw new
|
|
4738
|
+
throw new ValidationError("Flow name cannot be empty", [
|
|
4739
|
+
{ path: ["name"], message: "Flow name cannot be empty" }
|
|
4740
|
+
]);
|
|
4491
4741
|
}
|
|
4492
4742
|
if (name.length > 63) {
|
|
4493
|
-
throw new
|
|
4743
|
+
throw new ValidationError("Flow name is too long", [
|
|
4744
|
+
{ path: ["name"], message: "Flow name is too long (max 63 characters)" }
|
|
4745
|
+
]);
|
|
4494
4746
|
}
|
|
4495
4747
|
const kebabCaseRegex = /^[a-z][a-z0-9-]*$/;
|
|
4496
4748
|
if (!kebabCaseRegex.test(name)) {
|
|
4497
|
-
throw new
|
|
4498
|
-
|
|
4499
|
-
|
|
4749
|
+
throw new ValidationError("Invalid flow name format", [
|
|
4750
|
+
{
|
|
4751
|
+
path: ["name"],
|
|
4752
|
+
message: "Flow name must be in kebab-case (e.g., 'couple-creation', 'new-contact')"
|
|
4753
|
+
}
|
|
4754
|
+
]);
|
|
4500
4755
|
}
|
|
4501
4756
|
}
|
|
4502
4757
|
/**
|
|
@@ -4504,33 +4759,59 @@ var FlowService = class {
|
|
|
4504
4759
|
*/
|
|
4505
4760
|
validateFlowStructure(input) {
|
|
4506
4761
|
if (!input.slots || input.slots.length === 0) {
|
|
4507
|
-
throw new
|
|
4762
|
+
throw new ValidationError("Flow must have at least one slot", [
|
|
4763
|
+
{ path: ["slots"], message: "Flow must have at least one slot" }
|
|
4764
|
+
]);
|
|
4508
4765
|
}
|
|
4509
4766
|
if (!input.pages || input.pages.length === 0) {
|
|
4510
|
-
throw new
|
|
4767
|
+
throw new ValidationError("Flow must have at least one page", [
|
|
4768
|
+
{ path: ["pages"], message: "Flow must have at least one page" }
|
|
4769
|
+
]);
|
|
4511
4770
|
}
|
|
4512
4771
|
const slotIds = new Set(input.slots.map((s) => s.id));
|
|
4513
4772
|
if (slotIds.size !== input.slots.length) {
|
|
4514
|
-
throw new
|
|
4773
|
+
throw new ValidationError("Duplicate slot IDs detected", [
|
|
4774
|
+
{ path: ["slots"], message: "Duplicate slot IDs detected" }
|
|
4775
|
+
]);
|
|
4515
4776
|
}
|
|
4516
4777
|
for (const page of input.pages) {
|
|
4517
4778
|
for (const row of page.rows) {
|
|
4518
4779
|
for (const field of row.fields) {
|
|
4519
4780
|
if (!slotIds.has(field.slotId)) {
|
|
4520
|
-
throw new
|
|
4781
|
+
throw new ValidationError("Field references unknown slot", [
|
|
4782
|
+
{
|
|
4783
|
+
path: ["pages", page.id, "rows", row.id, "fields", field.id],
|
|
4784
|
+
message: `Field "${field.id}" references unknown slot "${field.slotId}"`
|
|
4785
|
+
}
|
|
4786
|
+
]);
|
|
4521
4787
|
}
|
|
4522
4788
|
}
|
|
4523
4789
|
}
|
|
4524
4790
|
}
|
|
4525
4791
|
for (const relation2 of input.relations) {
|
|
4526
4792
|
if (!slotIds.has(relation2.sourceSlotId)) {
|
|
4527
|
-
throw new
|
|
4793
|
+
throw new ValidationError("Relation references unknown source slot", [
|
|
4794
|
+
{
|
|
4795
|
+
path: ["relations", relation2.id, "sourceSlotId"],
|
|
4796
|
+
message: `Relation references unknown source slot "${relation2.sourceSlotId}"`
|
|
4797
|
+
}
|
|
4798
|
+
]);
|
|
4528
4799
|
}
|
|
4529
4800
|
if (!slotIds.has(relation2.targetSlotId)) {
|
|
4530
|
-
throw new
|
|
4801
|
+
throw new ValidationError("Relation references unknown target slot", [
|
|
4802
|
+
{
|
|
4803
|
+
path: ["relations", relation2.id, "targetSlotId"],
|
|
4804
|
+
message: `Relation references unknown target slot "${relation2.targetSlotId}"`
|
|
4805
|
+
}
|
|
4806
|
+
]);
|
|
4531
4807
|
}
|
|
4532
4808
|
if (relation2.sourceSlotId === relation2.targetSlotId) {
|
|
4533
|
-
throw new
|
|
4809
|
+
throw new ValidationError("Relation cannot link a slot to itself", [
|
|
4810
|
+
{
|
|
4811
|
+
path: ["relations", relation2.id],
|
|
4812
|
+
message: `Relation cannot link a slot to itself: "${relation2.sourceSlotId}"`
|
|
4813
|
+
}
|
|
4814
|
+
]);
|
|
4534
4815
|
}
|
|
4535
4816
|
}
|
|
4536
4817
|
}
|
|
@@ -5023,14 +5304,15 @@ var ObjectSchemaService = class {
|
|
|
5023
5304
|
});
|
|
5024
5305
|
}
|
|
5025
5306
|
}
|
|
5307
|
+
const dbAttributes = await this.adapter.attributes.findByObjectId(objectId);
|
|
5308
|
+
const attributes = dbAttributes.map((attr) => this.convertDBAttributeToAttribute(attr));
|
|
5026
5309
|
if (updates.labelExpression !== void 0 && updates.labelExpression !== oldValues.labelExpression) {
|
|
5027
5310
|
const newExpression = updates.labelExpression;
|
|
5028
|
-
await this.adapter.objectRecords.batchRefreshLabels(
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
);
|
|
5311
|
+
await this.adapter.objectRecords.batchRefreshLabels(objectId, (values) => {
|
|
5312
|
+
const enrichedValues = enrichValuesWithSelectLabels(values, attributes);
|
|
5313
|
+
return renderLabelExpression(newExpression, enrichedValues);
|
|
5314
|
+
});
|
|
5032
5315
|
}
|
|
5033
|
-
const dbAttributes = await this.adapter.attributes.findByObjectId(objectId);
|
|
5034
5316
|
return this.convertDBObjectToDefinition(updatedDbObject, dbAttributes);
|
|
5035
5317
|
}
|
|
5036
5318
|
/**
|
|
@@ -5349,6 +5631,8 @@ var PermissionService = class {
|
|
|
5349
5631
|
this.adapter = adapter;
|
|
5350
5632
|
this.tenantId = tenantId;
|
|
5351
5633
|
this.cache = /* @__PURE__ */ new Map();
|
|
5634
|
+
/** Track pending permission fetches to prevent duplicate concurrent requests */
|
|
5635
|
+
this.pendingFetches = /* @__PURE__ */ new Map();
|
|
5352
5636
|
if (!adapter.permissions) {
|
|
5353
5637
|
throw new Error(
|
|
5354
5638
|
"PermissionService requires a DatabaseAdapter with permissions repository. Make sure your adapter implements the permissions property."
|
|
@@ -5497,6 +5781,23 @@ var PermissionService = class {
|
|
|
5497
5781
|
if (cached && cached.expiresAt > Date.now()) {
|
|
5498
5782
|
return cached.permissions;
|
|
5499
5783
|
}
|
|
5784
|
+
const pendingFetch = this.pendingFetches.get(cacheKey);
|
|
5785
|
+
if (pendingFetch) {
|
|
5786
|
+
return pendingFetch;
|
|
5787
|
+
}
|
|
5788
|
+
const fetchPromise = this.fetchAndCachePermissions(userProfileId, cacheKey);
|
|
5789
|
+
this.pendingFetches.set(cacheKey, fetchPromise);
|
|
5790
|
+
try {
|
|
5791
|
+
return await fetchPromise;
|
|
5792
|
+
} finally {
|
|
5793
|
+
this.pendingFetches.delete(cacheKey);
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
/**
|
|
5797
|
+
* Fetch permissions from database and cache the result
|
|
5798
|
+
* @internal
|
|
5799
|
+
*/
|
|
5800
|
+
async fetchAndCachePermissions(userProfileId, cacheKey) {
|
|
5500
5801
|
const permissions = await this.permissionsRepo.getEffectivePermissions(
|
|
5501
5802
|
userProfileId,
|
|
5502
5803
|
this.tenantId
|
|
@@ -5879,8 +6180,11 @@ var RelationService = class {
|
|
|
5879
6180
|
async validateRelationsOrThrow(schema, data) {
|
|
5880
6181
|
const result = await this.validateRelations(schema, data);
|
|
5881
6182
|
if (!result.valid) {
|
|
5882
|
-
const
|
|
5883
|
-
|
|
6183
|
+
const errors = result.errors.map((e) => ({
|
|
6184
|
+
path: [e.attribute],
|
|
6185
|
+
message: e.message
|
|
6186
|
+
}));
|
|
6187
|
+
throw new ValidationError("Relation validation failed", errors);
|
|
5884
6188
|
}
|
|
5885
6189
|
}
|
|
5886
6190
|
// ============================================================================
|
|
@@ -5928,7 +6232,8 @@ var RelationService = class {
|
|
|
5928
6232
|
totalCount += result.total;
|
|
5929
6233
|
for (const record of result.records) {
|
|
5930
6234
|
const template = target.displayTemplate || objectSchema.labelExpression;
|
|
5931
|
-
const
|
|
6235
|
+
const enrichedValues = enrichValuesWithSelectLabels(record.values, objectSchema.attributes);
|
|
6236
|
+
const label = renderLabelExpression(template, enrichedValues);
|
|
5932
6237
|
allOptions.push({
|
|
5933
6238
|
id: record.id,
|
|
5934
6239
|
objectId: objectSchema.id,
|
|
@@ -5992,7 +6297,8 @@ var RelationService = class {
|
|
|
5992
6297
|
}
|
|
5993
6298
|
}
|
|
5994
6299
|
for (const record of objectRecords) {
|
|
5995
|
-
const
|
|
6300
|
+
const enrichedValues = enrichValuesWithSelectLabels(record.values, objectSchema.attributes);
|
|
6301
|
+
const label = renderLabelExpression(template, enrichedValues);
|
|
5996
6302
|
resolved.push({
|
|
5997
6303
|
id: record.id,
|
|
5998
6304
|
objectId: record.objectId,
|
|
@@ -6013,10 +6319,14 @@ var RelationService = class {
|
|
|
6013
6319
|
if (!attribute || attribute.type !== "relation") {
|
|
6014
6320
|
return null;
|
|
6015
6321
|
}
|
|
6016
|
-
|
|
6322
|
+
const merged = {
|
|
6017
6323
|
...attribute.config,
|
|
6018
6324
|
...attribute
|
|
6019
6325
|
};
|
|
6326
|
+
if (!("targets" in merged && Array.isArray(merged.targets) && "cardinality" in merged)) {
|
|
6327
|
+
return null;
|
|
6328
|
+
}
|
|
6329
|
+
return merged;
|
|
6020
6330
|
}
|
|
6021
6331
|
};
|
|
6022
6332
|
|
|
@@ -6029,7 +6339,7 @@ var RecordService = class {
|
|
|
6029
6339
|
this.relationService = new RelationService(adapter, registry);
|
|
6030
6340
|
this.hookRegistry = options?.hookRegistry ?? new NoopHookRegistry();
|
|
6031
6341
|
this.permissionService = options?.permissionService;
|
|
6032
|
-
this.auditService = options?.auditService;
|
|
6342
|
+
this.auditService = options?.auditService ?? (adapter.audit ? new AuditService(adapter, tenantId) : void 0);
|
|
6033
6343
|
this.userId = options?.userId;
|
|
6034
6344
|
this.userEmail = options?.userEmail;
|
|
6035
6345
|
}
|
|
@@ -6096,21 +6406,23 @@ var RecordService = class {
|
|
|
6096
6406
|
/**
|
|
6097
6407
|
* Compute display label from schema expression
|
|
6098
6408
|
* Automatically resolves relation attribute values to their labels
|
|
6409
|
+
* and select/multiselect values to their option labels
|
|
6099
6410
|
* @internal
|
|
6100
6411
|
*/
|
|
6101
6412
|
async computeLabel(schema, values) {
|
|
6102
6413
|
const attrNames = extractAttributeNames(schema.labelExpression);
|
|
6414
|
+
let enrichedValues = enrichValuesWithSelectLabels(values, schema.attributes);
|
|
6103
6415
|
const relationAttrs = schema.attributes.filter(
|
|
6104
6416
|
(attr) => attr.type === "relation" && attrNames.includes(attr.name)
|
|
6105
6417
|
);
|
|
6106
6418
|
if (relationAttrs.length === 0) {
|
|
6107
|
-
return renderLabelExpression(schema.labelExpression,
|
|
6419
|
+
return renderLabelExpression(schema.labelExpression, enrichedValues);
|
|
6108
6420
|
}
|
|
6109
6421
|
const resolvedMap = await this.resolveRelationLabels(relationAttrs, values);
|
|
6110
6422
|
if (resolvedMap.size === 0) {
|
|
6111
|
-
return renderLabelExpression(schema.labelExpression,
|
|
6423
|
+
return renderLabelExpression(schema.labelExpression, enrichedValues);
|
|
6112
6424
|
}
|
|
6113
|
-
|
|
6425
|
+
enrichedValues = { ...enrichedValues };
|
|
6114
6426
|
for (const attr of relationAttrs) {
|
|
6115
6427
|
const val = values[attr.name];
|
|
6116
6428
|
const ids = this.extractRelationIds(val);
|
|
@@ -6223,7 +6535,7 @@ var RecordService = class {
|
|
|
6223
6535
|
async getRecordOrThrow(recordId) {
|
|
6224
6536
|
const record = await this.getRecord(recordId);
|
|
6225
6537
|
if (!record) {
|
|
6226
|
-
throw new
|
|
6538
|
+
throw new RecordNotFoundError(recordId);
|
|
6227
6539
|
}
|
|
6228
6540
|
return record;
|
|
6229
6541
|
}
|
|
@@ -6401,7 +6713,7 @@ var RecordService = class {
|
|
|
6401
6713
|
await this.checkPermission(schema.name, "delete");
|
|
6402
6714
|
if (options?.checkSystem) {
|
|
6403
6715
|
if (schema.system) {
|
|
6404
|
-
throw new
|
|
6716
|
+
throw new ProtectedResourceError("object", schema.name, "delete");
|
|
6405
6717
|
}
|
|
6406
6718
|
}
|
|
6407
6719
|
if (!options?.skipReferenceCheck) {
|
|
@@ -6450,7 +6762,11 @@ var RecordService = class {
|
|
|
6450
6762
|
async restoreRecord(recordId, options) {
|
|
6451
6763
|
const record = await this.getRecordOrThrow(recordId);
|
|
6452
6764
|
if (!record.deletedAt) {
|
|
6453
|
-
throw new
|
|
6765
|
+
throw new SchemaError(
|
|
6766
|
+
`Record "${recordId}" is not deleted`,
|
|
6767
|
+
SchemaErrorCode.VALIDATION_FAILED,
|
|
6768
|
+
{ recordId, reason: "not_deleted" }
|
|
6769
|
+
);
|
|
6454
6770
|
}
|
|
6455
6771
|
const schema = await this.schemaService.getObjectSchema(record.objectId);
|
|
6456
6772
|
await this.checkPermission(schema.name, "update");
|
|
@@ -6888,11 +7204,17 @@ var ViewService = class {
|
|
|
6888
7204
|
this.validateViewName(input.name);
|
|
6889
7205
|
const existing = await this.adapter.views.findByName(tenantId, input.objectName, input.name);
|
|
6890
7206
|
if (existing) {
|
|
6891
|
-
throw new
|
|
7207
|
+
throw new SchemaError(
|
|
7208
|
+
`View "${input.name}" already exists for object "${input.objectName}"`,
|
|
7209
|
+
SchemaErrorCode.DUPLICATE_ATTRIBUTE,
|
|
7210
|
+
{ viewName: input.name, objectName: input.objectName }
|
|
7211
|
+
);
|
|
6892
7212
|
}
|
|
6893
7213
|
if (this.nativeViews.has(input.objectName, input.name)) {
|
|
6894
|
-
throw new
|
|
6895
|
-
`Cannot create view "${input.name}": a system view with this name already exists
|
|
7214
|
+
throw new SchemaError(
|
|
7215
|
+
`Cannot create view "${input.name}": a system view with this name already exists`,
|
|
7216
|
+
SchemaErrorCode.DUPLICATE_ATTRIBUTE,
|
|
7217
|
+
{ viewName: input.name, objectName: input.objectName, system: true }
|
|
6896
7218
|
);
|
|
6897
7219
|
}
|
|
6898
7220
|
const dbView = await this.adapter.views.create({
|
|
@@ -6920,10 +7242,10 @@ var ViewService = class {
|
|
|
6920
7242
|
async updateView(viewId, input) {
|
|
6921
7243
|
const dbView = await this.adapter.views.findById(viewId);
|
|
6922
7244
|
if (!dbView) {
|
|
6923
|
-
throw new
|
|
7245
|
+
throw new NotFoundError("View", viewId);
|
|
6924
7246
|
}
|
|
6925
7247
|
if (dbView.system) {
|
|
6926
|
-
throw new
|
|
7248
|
+
throw new ProtectedResourceError("attribute", dbView.name, "modify");
|
|
6927
7249
|
}
|
|
6928
7250
|
const updated = await this.adapter.views.update(viewId, {
|
|
6929
7251
|
label: input.label,
|
|
@@ -6943,10 +7265,10 @@ var ViewService = class {
|
|
|
6943
7265
|
async deleteView(viewId) {
|
|
6944
7266
|
const dbView = await this.adapter.views.findById(viewId);
|
|
6945
7267
|
if (!dbView) {
|
|
6946
|
-
throw new
|
|
7268
|
+
throw new NotFoundError("View", viewId);
|
|
6947
7269
|
}
|
|
6948
7270
|
if (dbView.system) {
|
|
6949
|
-
throw new
|
|
7271
|
+
throw new ProtectedResourceError("attribute", dbView.name, "delete");
|
|
6950
7272
|
}
|
|
6951
7273
|
await this.adapter.views.delete(viewId);
|
|
6952
7274
|
}
|
|
@@ -6960,7 +7282,7 @@ var ViewService = class {
|
|
|
6960
7282
|
async setDefaultView(viewId, tenantId) {
|
|
6961
7283
|
const dbView = await this.adapter.views.findById(viewId);
|
|
6962
7284
|
if (!dbView) {
|
|
6963
|
-
throw new
|
|
7285
|
+
throw new NotFoundError("View", viewId);
|
|
6964
7286
|
}
|
|
6965
7287
|
const currentViews = await this.adapter.views.findByObjectName(tenantId, dbView.objectName);
|
|
6966
7288
|
for (const v of currentViews) {
|
|
@@ -6979,16 +7301,23 @@ var ViewService = class {
|
|
|
6979
7301
|
*/
|
|
6980
7302
|
validateViewName(name) {
|
|
6981
7303
|
if (!name || name.length === 0) {
|
|
6982
|
-
throw new
|
|
7304
|
+
throw new ValidationError("View name cannot be empty", [
|
|
7305
|
+
{ path: ["name"], message: "View name cannot be empty" }
|
|
7306
|
+
]);
|
|
6983
7307
|
}
|
|
6984
7308
|
if (name.length > 63) {
|
|
6985
|
-
throw new
|
|
7309
|
+
throw new ValidationError("View name is too long", [
|
|
7310
|
+
{ path: ["name"], message: "View name is too long (max 63 characters)" }
|
|
7311
|
+
]);
|
|
6986
7312
|
}
|
|
6987
7313
|
const kebabCaseRegex = /^[a-z][a-z0-9-]*$/;
|
|
6988
7314
|
if (!kebabCaseRegex.test(name)) {
|
|
6989
|
-
throw new
|
|
6990
|
-
|
|
6991
|
-
|
|
7315
|
+
throw new ValidationError("Invalid view name format", [
|
|
7316
|
+
{
|
|
7317
|
+
path: ["name"],
|
|
7318
|
+
message: "View name must be in kebab-case (e.g., 'detail', 'list-view')"
|
|
7319
|
+
}
|
|
7320
|
+
]);
|
|
6992
7321
|
}
|
|
6993
7322
|
}
|
|
6994
7323
|
/**
|
|
@@ -7350,6 +7679,7 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
|
|
|
7350
7679
|
DEFAULT_ROLE_LABELS,
|
|
7351
7680
|
DEFAULT_ROLE_PERMISSIONS,
|
|
7352
7681
|
DuplicateError,
|
|
7682
|
+
EMPTY_VALUE_PLACEHOLDER,
|
|
7353
7683
|
FileNotFoundError,
|
|
7354
7684
|
FileService,
|
|
7355
7685
|
FlowBuilder,
|
|
@@ -7420,10 +7750,12 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
|
|
|
7420
7750
|
currencyConfigSchema,
|
|
7421
7751
|
date,
|
|
7422
7752
|
dateConfigSchema,
|
|
7753
|
+
enrichValuesWithSelectLabels,
|
|
7423
7754
|
extractAttributeNames,
|
|
7424
7755
|
file,
|
|
7425
7756
|
fileConfigSchema,
|
|
7426
7757
|
flow,
|
|
7758
|
+
formatAttributeValue,
|
|
7427
7759
|
generateId,
|
|
7428
7760
|
generatePrefixedId,
|
|
7429
7761
|
getAttributeConfigSchema,
|
|
@@ -7431,6 +7763,7 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
|
|
|
7431
7763
|
getSyncPreview,
|
|
7432
7764
|
getViewSyncPreview,
|
|
7433
7765
|
group,
|
|
7766
|
+
isActivityTab,
|
|
7434
7767
|
isAdvancedFilterState,
|
|
7435
7768
|
isCustomTab,
|
|
7436
7769
|
isDefaultRole,
|