@stndrds/schema 0.1.0-alpha.56 → 0.1.0-alpha.58
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-XBF4EUC4.js → chunk-2OS63USQ.js} +1550 -1250
- package/dist/{chunk-LOOGQL6M.mjs → chunk-JRY236AO.mjs} +1307 -1007
- package/dist/chunk-NEVERCM3.js +41 -0
- package/dist/chunk-SV4BCGQU.mjs +617 -0
- package/dist/chunk-U4AB53AM.js +617 -0
- package/dist/chunk-V2RPPE2Y.mjs +41 -0
- package/dist/index.d.mts +942 -55
- package/dist/index.d.ts +942 -55
- package/dist/index.js +922 -69
- package/dist/index.mjs +988 -135
- package/dist/{runtime-D5MuaVQs.d.mts → runtime-C4vB_EcQ.d.ts} +759 -1779
- package/dist/{runtime-D5MuaVQs.d.ts → runtime-SCsDOQi0.d.mts} +759 -1779
- package/dist/runtime.d.mts +3 -1
- package/dist/runtime.d.ts +3 -1
- package/dist/runtime.js +26 -2
- package/dist/runtime.mjs +25 -1
- package/dist/utils.d.mts +154 -0
- package/dist/utils.d.ts +154 -0
- package/dist/utils.js +19 -0
- package/dist/utils.mjs +19 -0
- package/dist/validation/validators.d.mts +4 -0
- package/dist/validation/validators.d.ts +4 -0
- package/dist/validation/validators.js +121 -0
- package/dist/validation/validators.mjs +121 -0
- package/dist/validators-BgmWgr-G.d.mts +1488 -0
- package/dist/validators-miARInAq.d.ts +1488 -0
- package/package.json +12 -2
|
@@ -1,40 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
asTenantId,
|
|
3
|
+
asUserId,
|
|
4
|
+
generateId
|
|
5
|
+
} from "./chunk-V2RPPE2Y.mjs";
|
|
6
|
+
import {
|
|
7
|
+
computeRecordStatus,
|
|
8
|
+
formatZodErrors,
|
|
9
|
+
parseAttributeConfig,
|
|
10
|
+
validateDraftOrThrow,
|
|
11
|
+
validateObject,
|
|
12
|
+
validateObjectOrThrow
|
|
13
|
+
} from "./chunk-SV4BCGQU.mjs";
|
|
1
14
|
import {
|
|
2
15
|
__require
|
|
3
16
|
} from "./chunk-Y6FXYEAI.mjs";
|
|
4
17
|
|
|
5
18
|
// src/runtime/auth/workflow-jwt.service.ts
|
|
6
19
|
import { SignJWT, importPKCS8, importSPKI, jwtVerify } from "jose";
|
|
7
|
-
|
|
8
|
-
// src/utils.ts
|
|
9
|
-
function asTenantId(id) {
|
|
10
|
-
return id;
|
|
11
|
-
}
|
|
12
|
-
function asUserId(id) {
|
|
13
|
-
return id;
|
|
14
|
-
}
|
|
15
|
-
function generateId() {
|
|
16
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
17
|
-
return crypto.randomUUID();
|
|
18
|
-
}
|
|
19
|
-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
20
|
-
const r = Math.random() * 16 | 0;
|
|
21
|
-
const v = c === "x" ? r : r & 3 | 8;
|
|
22
|
-
return v.toString(16);
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
function generatePrefixedId(prefix) {
|
|
26
|
-
return `${prefix}_${generateId()}`;
|
|
27
|
-
}
|
|
28
|
-
function slugify(input) {
|
|
29
|
-
return input.normalize("NFD").replace(/\p{M}/gu, "").toLowerCase().replace(/[^a-z0-9\s_-]/g, "").trim().replace(/\s+/g, "-").replace(/-+/g, "-");
|
|
30
|
-
}
|
|
31
|
-
function generateTemplateName(label) {
|
|
32
|
-
const slug = slugify(label) || "template";
|
|
33
|
-
const suffix = generateId().slice(0, 8);
|
|
34
|
-
return `${slug}-${suffix}`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// src/runtime/auth/workflow-jwt.service.ts
|
|
38
20
|
var WorkflowJwtService = class _WorkflowJwtService {
|
|
39
21
|
constructor(config) {
|
|
40
22
|
this.config = config;
|
|
@@ -212,6 +194,26 @@ var WorkflowJwtService = class _WorkflowJwtService {
|
|
|
212
194
|
}
|
|
213
195
|
};
|
|
214
196
|
|
|
197
|
+
// src/lib/object-helpers.ts
|
|
198
|
+
function isEmpty(obj) {
|
|
199
|
+
if (obj === null || obj === void 0) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
if (typeof obj !== "object") {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
return Object.keys(obj).length === 0;
|
|
206
|
+
}
|
|
207
|
+
function isNotEmpty(obj) {
|
|
208
|
+
return obj !== null && obj !== void 0 && typeof obj === "object" && Object.keys(obj).length > 0;
|
|
209
|
+
}
|
|
210
|
+
function toUndefinedIfEmpty(obj) {
|
|
211
|
+
return isNotEmpty(obj) ? obj : void 0;
|
|
212
|
+
}
|
|
213
|
+
function hasProperties(obj) {
|
|
214
|
+
return isNotEmpty(obj);
|
|
215
|
+
}
|
|
216
|
+
|
|
215
217
|
// src/runtime/cache.ts
|
|
216
218
|
function fnv1aHash(str) {
|
|
217
219
|
let hash = 2166136261;
|
|
@@ -222,7 +224,7 @@ function fnv1aHash(str) {
|
|
|
222
224
|
return hash.toString(16).padStart(8, "0");
|
|
223
225
|
}
|
|
224
226
|
function hashOptions(options) {
|
|
225
|
-
if (
|
|
227
|
+
if (isEmpty(options)) {
|
|
226
228
|
return "default";
|
|
227
229
|
}
|
|
228
230
|
const sortedJson = JSON.stringify(options, (_, value) => {
|
|
@@ -543,7 +545,7 @@ var TenantContextError = class _TenantContextError extends Error {
|
|
|
543
545
|
}
|
|
544
546
|
};
|
|
545
547
|
|
|
546
|
-
// src/runtime/context/
|
|
548
|
+
// src/runtime/context/feature-flags-context.ts
|
|
547
549
|
var browserStub = {
|
|
548
550
|
getStore: () => void 0,
|
|
549
551
|
run: (_store, callback) => callback()
|
|
@@ -581,22 +583,106 @@ function getStorage() {
|
|
|
581
583
|
storageInstance = browserStub;
|
|
582
584
|
return storageInstance;
|
|
583
585
|
}
|
|
584
|
-
|
|
586
|
+
var FeatureFlagsContextError = class _FeatureFlagsContextError extends Error {
|
|
587
|
+
constructor(message) {
|
|
588
|
+
super(
|
|
589
|
+
message ?? "No feature flags context found. This usually means:\n - HTTP request: Missing FeatureFlagsInterceptor\n - Background job: Wrap with runWithFeatureFlags()\n - Test: Wrap test body with runWithFeatureFlags()\n\nTip: FeatureFlagsInterceptor must run AFTER TenantContextInterceptor."
|
|
590
|
+
);
|
|
591
|
+
this.name = "FeatureFlagsContextError";
|
|
592
|
+
if ("captureStackTrace" in Error) {
|
|
593
|
+
Error.captureStackTrace(
|
|
594
|
+
this,
|
|
595
|
+
_FeatureFlagsContextError
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
function getContext() {
|
|
601
|
+
const ctx = getStorage().getStore();
|
|
602
|
+
if (!ctx) {
|
|
603
|
+
throw new FeatureFlagsContextError();
|
|
604
|
+
}
|
|
605
|
+
return ctx;
|
|
606
|
+
}
|
|
607
|
+
function isFeatureEnabled(flagName) {
|
|
608
|
+
return getContext().flags.get(flagName) === true;
|
|
609
|
+
}
|
|
610
|
+
function getFeatureValue(flagName, defaultValue) {
|
|
611
|
+
const value = getContext().flags.get(flagName);
|
|
612
|
+
return value !== void 0 ? value : defaultValue;
|
|
613
|
+
}
|
|
614
|
+
function getFeatureFlags() {
|
|
615
|
+
return getContext().flags;
|
|
616
|
+
}
|
|
617
|
+
function tryGetFeatureValue(flagName) {
|
|
585
618
|
const ctx = getStorage().getStore();
|
|
619
|
+
return ctx?.flags.get(flagName);
|
|
620
|
+
}
|
|
621
|
+
function hasFeatureFlagsContext() {
|
|
622
|
+
return getStorage().getStore() !== void 0;
|
|
623
|
+
}
|
|
624
|
+
function runWithFeatureFlags(flags, fn) {
|
|
625
|
+
const frozenContext = Object.freeze({ flags });
|
|
626
|
+
return getStorage().run(frozenContext, fn);
|
|
627
|
+
}
|
|
628
|
+
function withFeatureFlags(resolvedFlags, fn) {
|
|
629
|
+
return runWithFeatureFlags(resolvedFlags, fn);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// src/runtime/context/schema-context.ts
|
|
633
|
+
var browserStub2 = {
|
|
634
|
+
getStore: () => void 0,
|
|
635
|
+
run: (_store, callback) => callback()
|
|
636
|
+
};
|
|
637
|
+
var AsyncLocalStorageClass2 = null;
|
|
638
|
+
if (typeof process !== "undefined" && process.versions?.node) {
|
|
639
|
+
try {
|
|
640
|
+
if (typeof __require !== "undefined") {
|
|
641
|
+
const asyncHooks = __require("async_hooks");
|
|
642
|
+
AsyncLocalStorageClass2 = asyncHooks.AsyncLocalStorage;
|
|
643
|
+
}
|
|
644
|
+
} catch {
|
|
645
|
+
try {
|
|
646
|
+
const dynamicRequire = new Function(
|
|
647
|
+
"m",
|
|
648
|
+
'return typeof require!=="undefined"?require(m):null'
|
|
649
|
+
);
|
|
650
|
+
const asyncHooks = dynamicRequire("node:async_hooks");
|
|
651
|
+
if (asyncHooks) {
|
|
652
|
+
AsyncLocalStorageClass2 = asyncHooks.AsyncLocalStorage;
|
|
653
|
+
}
|
|
654
|
+
} catch {
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
var storageInstance2 = null;
|
|
659
|
+
function getStorage2() {
|
|
660
|
+
if (storageInstance2 !== null) {
|
|
661
|
+
return storageInstance2;
|
|
662
|
+
}
|
|
663
|
+
if (AsyncLocalStorageClass2) {
|
|
664
|
+
storageInstance2 = new AsyncLocalStorageClass2();
|
|
665
|
+
return storageInstance2;
|
|
666
|
+
}
|
|
667
|
+
storageInstance2 = browserStub2;
|
|
668
|
+
return storageInstance2;
|
|
669
|
+
}
|
|
670
|
+
function getSchemaFromContext(objectId) {
|
|
671
|
+
const ctx = getStorage2().getStore();
|
|
586
672
|
return ctx?.objectsById.get(objectId);
|
|
587
673
|
}
|
|
588
674
|
function getSchemaByNameFromContext(objectName) {
|
|
589
|
-
const ctx =
|
|
675
|
+
const ctx = getStorage2().getStore();
|
|
590
676
|
return ctx?.objectsByName.get(objectName);
|
|
591
677
|
}
|
|
592
678
|
function hasSchemaContext() {
|
|
593
|
-
return
|
|
679
|
+
return getStorage2().getStore() !== void 0;
|
|
594
680
|
}
|
|
595
681
|
function getSchemaContext() {
|
|
596
|
-
return
|
|
682
|
+
return getStorage2().getStore();
|
|
597
683
|
}
|
|
598
684
|
function addSchemaToContext(schema) {
|
|
599
|
-
const ctx =
|
|
685
|
+
const ctx = getStorage2().getStore();
|
|
600
686
|
if (!ctx) {
|
|
601
687
|
return;
|
|
602
688
|
}
|
|
@@ -618,10 +704,10 @@ function buildSchemaContext(schemas) {
|
|
|
618
704
|
}
|
|
619
705
|
function runWithSchemaContext(schemas, fn) {
|
|
620
706
|
const context = buildSchemaContext(schemas);
|
|
621
|
-
return
|
|
707
|
+
return getStorage2().run(context, fn);
|
|
622
708
|
}
|
|
623
709
|
function runWithMergedSchemaContext(schemas, fn) {
|
|
624
|
-
const existing =
|
|
710
|
+
const existing = getStorage2().getStore();
|
|
625
711
|
const objectsById = new Map(existing?.objectsById);
|
|
626
712
|
const objectsByName = new Map(existing?.objectsByName);
|
|
627
713
|
for (const schema of schemas) {
|
|
@@ -634,20 +720,20 @@ function runWithMergedSchemaContext(schemas, fn) {
|
|
|
634
720
|
objectsById,
|
|
635
721
|
objectsByName
|
|
636
722
|
};
|
|
637
|
-
return
|
|
723
|
+
return getStorage2().run(context, fn);
|
|
638
724
|
}
|
|
639
725
|
|
|
640
726
|
// src/runtime/context/tenant-context.ts
|
|
641
|
-
var
|
|
727
|
+
var browserStub3 = {
|
|
642
728
|
getStore: () => void 0,
|
|
643
729
|
run: (_store, callback) => callback()
|
|
644
730
|
};
|
|
645
|
-
var
|
|
731
|
+
var AsyncLocalStorageClass3 = null;
|
|
646
732
|
if (typeof process !== "undefined" && process.versions?.node) {
|
|
647
733
|
try {
|
|
648
734
|
if (typeof __require !== "undefined") {
|
|
649
735
|
const asyncHooks = __require("async_hooks");
|
|
650
|
-
|
|
736
|
+
AsyncLocalStorageClass3 = asyncHooks.AsyncLocalStorage;
|
|
651
737
|
}
|
|
652
738
|
} catch {
|
|
653
739
|
try {
|
|
@@ -657,43 +743,43 @@ if (typeof process !== "undefined" && process.versions?.node) {
|
|
|
657
743
|
);
|
|
658
744
|
const asyncHooks = dynamicRequire("node:async_hooks");
|
|
659
745
|
if (asyncHooks) {
|
|
660
|
-
|
|
746
|
+
AsyncLocalStorageClass3 = asyncHooks.AsyncLocalStorage;
|
|
661
747
|
}
|
|
662
748
|
} catch {
|
|
663
749
|
}
|
|
664
750
|
}
|
|
665
751
|
}
|
|
666
|
-
var
|
|
667
|
-
function
|
|
668
|
-
if (
|
|
669
|
-
return
|
|
752
|
+
var storageInstance3 = null;
|
|
753
|
+
function getStorage3() {
|
|
754
|
+
if (storageInstance3 !== null) {
|
|
755
|
+
return storageInstance3;
|
|
670
756
|
}
|
|
671
|
-
if (
|
|
672
|
-
|
|
673
|
-
return
|
|
757
|
+
if (AsyncLocalStorageClass3) {
|
|
758
|
+
storageInstance3 = new AsyncLocalStorageClass3();
|
|
759
|
+
return storageInstance3;
|
|
674
760
|
}
|
|
675
|
-
|
|
676
|
-
return
|
|
761
|
+
storageInstance3 = browserStub3;
|
|
762
|
+
return storageInstance3;
|
|
677
763
|
}
|
|
678
|
-
function
|
|
679
|
-
const ctx =
|
|
764
|
+
function getContext2() {
|
|
765
|
+
const ctx = getStorage3().getStore();
|
|
680
766
|
if (!ctx) {
|
|
681
767
|
throw new TenantContextError();
|
|
682
768
|
}
|
|
683
769
|
return ctx;
|
|
684
770
|
}
|
|
685
771
|
function getTenantId() {
|
|
686
|
-
return
|
|
772
|
+
return getContext2().tenantId;
|
|
687
773
|
}
|
|
688
774
|
function getUserId() {
|
|
689
|
-
return
|
|
775
|
+
return getContext2().userId;
|
|
690
776
|
}
|
|
691
777
|
function hasContext() {
|
|
692
|
-
return
|
|
778
|
+
return getStorage3().getStore() !== void 0;
|
|
693
779
|
}
|
|
694
780
|
function runWithContext(context, fn) {
|
|
695
781
|
const frozenContext = Object.freeze({ ...context });
|
|
696
|
-
return
|
|
782
|
+
return getStorage3().run(frozenContext, fn);
|
|
697
783
|
}
|
|
698
784
|
function withTenantContext(tenantId, fn, userId) {
|
|
699
785
|
return runWithContext({ tenantId, userId }, fn);
|
|
@@ -1261,12 +1347,6 @@ function or(...rules) {
|
|
|
1261
1347
|
function inValues(field, values) {
|
|
1262
1348
|
return { field, operator: "in", value: values };
|
|
1263
1349
|
}
|
|
1264
|
-
function isEmpty(field) {
|
|
1265
|
-
return { field, operator: "isEmpty", value: null };
|
|
1266
|
-
}
|
|
1267
|
-
function isNotEmpty(field) {
|
|
1268
|
-
return { field, operator: "isNotEmpty", value: null };
|
|
1269
|
-
}
|
|
1270
1350
|
|
|
1271
1351
|
// src/types/workflows/definition.ts
|
|
1272
1352
|
function isWorkflowDefinition(obj) {
|
|
@@ -1376,16 +1456,6 @@ function setContextValue(context, path, value) {
|
|
|
1376
1456
|
}
|
|
1377
1457
|
current[parts[parts.length - 1]] = value;
|
|
1378
1458
|
}
|
|
1379
|
-
function mergeFormToSlot(context, nodeId, slotId) {
|
|
1380
|
-
const formData = context.forms[nodeId];
|
|
1381
|
-
if (!formData) {
|
|
1382
|
-
return;
|
|
1383
|
-
}
|
|
1384
|
-
if (!context.slots[slotId]) {
|
|
1385
|
-
context.slots[slotId] = {};
|
|
1386
|
-
}
|
|
1387
|
-
Object.assign(context.slots[slotId], formData);
|
|
1388
|
-
}
|
|
1389
1459
|
|
|
1390
1460
|
// src/types/workflows/theme.ts
|
|
1391
1461
|
var DEFAULT_THEME = {
|
|
@@ -1739,9 +1809,9 @@ function compareValues(actual, operator, expected) {
|
|
|
1739
1809
|
return typeof actual === "string" && typeof expected === "string" ? actual.endsWith(expected) : false;
|
|
1740
1810
|
// Presence
|
|
1741
1811
|
case "isEmpty":
|
|
1742
|
-
return
|
|
1812
|
+
return isEmptyValue(actual);
|
|
1743
1813
|
case "isNotEmpty":
|
|
1744
|
-
return !
|
|
1814
|
+
return !isEmptyValue(actual);
|
|
1745
1815
|
// Set membership
|
|
1746
1816
|
case "in":
|
|
1747
1817
|
if (Array.isArray(expected)) {
|
|
@@ -1757,7 +1827,7 @@ function compareValues(actual, operator, expected) {
|
|
|
1757
1827
|
return false;
|
|
1758
1828
|
}
|
|
1759
1829
|
}
|
|
1760
|
-
function
|
|
1830
|
+
function isEmptyValue(value) {
|
|
1761
1831
|
if (value === null || value === void 0) {
|
|
1762
1832
|
return true;
|
|
1763
1833
|
}
|
|
@@ -1767,7 +1837,7 @@ function isEmpty2(value) {
|
|
|
1767
1837
|
if (Array.isArray(value) && value.length === 0) {
|
|
1768
1838
|
return true;
|
|
1769
1839
|
}
|
|
1770
|
-
if (typeof value === "object" &&
|
|
1840
|
+
if (typeof value === "object" && isEmpty(value)) {
|
|
1771
1841
|
return true;
|
|
1772
1842
|
}
|
|
1773
1843
|
return false;
|
|
@@ -2012,7 +2082,7 @@ var DocumentExecutor = class {
|
|
|
2012
2082
|
validateSlotReferences(node, workflowSlots) {
|
|
2013
2083
|
const errors = [];
|
|
2014
2084
|
if (node.targetSlotIds && node.targetSlotIds.length > 0) {
|
|
2015
|
-
const slotIdSet =
|
|
2085
|
+
const slotIdSet = workflowSlots.reduce((set, s) => set.add(s.id), /* @__PURE__ */ new Set());
|
|
2016
2086
|
for (const slotId of node.targetSlotIds) {
|
|
2017
2087
|
if (!slotIdSet.has(slotId)) {
|
|
2018
2088
|
errors.push(
|
|
@@ -2048,24 +2118,25 @@ var FormExecutor = class {
|
|
|
2048
2118
|
}
|
|
2049
2119
|
execute(node, context) {
|
|
2050
2120
|
const { input } = context;
|
|
2051
|
-
if (
|
|
2121
|
+
if (isEmpty(input)) {
|
|
2052
2122
|
const requiredParticipationId = node.participantId ?? void 0;
|
|
2053
2123
|
return wait(`Waiting for form submission: ${node.label}`, {
|
|
2054
2124
|
requiredParticipationId
|
|
2055
2125
|
});
|
|
2056
2126
|
}
|
|
2127
|
+
const formInput = input;
|
|
2057
2128
|
const slotIds = this.extractSlotIds(node);
|
|
2058
2129
|
if (slotIds.size === 0) {
|
|
2059
2130
|
return error("MISSING_FIELDS", "FormNode must have fields or rows with slot references");
|
|
2060
2131
|
}
|
|
2061
2132
|
const contextUpdates = {
|
|
2062
2133
|
forms: {
|
|
2063
|
-
[node.id]:
|
|
2134
|
+
[node.id]: formInput
|
|
2064
2135
|
},
|
|
2065
2136
|
slots: {}
|
|
2066
2137
|
};
|
|
2067
2138
|
for (const slotId of slotIds) {
|
|
2068
|
-
const slotInput =
|
|
2139
|
+
const slotInput = formInput[slotId] ?? {};
|
|
2069
2140
|
if (!contextUpdates.slots) {
|
|
2070
2141
|
contextUpdates.slots = {};
|
|
2071
2142
|
}
|
|
@@ -2093,9 +2164,9 @@ var FormExecutor = class {
|
|
|
2093
2164
|
}
|
|
2094
2165
|
canExecute(node, context) {
|
|
2095
2166
|
if (node.participantId && context.executorId) {
|
|
2096
|
-
return
|
|
2167
|
+
return hasProperties(context.input);
|
|
2097
2168
|
}
|
|
2098
|
-
return
|
|
2169
|
+
return hasProperties(context.input);
|
|
2099
2170
|
}
|
|
2100
2171
|
validate(node) {
|
|
2101
2172
|
const errors = [];
|
|
@@ -3387,9 +3458,6 @@ var ConcurrentModificationError = class extends SchemaError {
|
|
|
3387
3458
|
);
|
|
3388
3459
|
}
|
|
3389
3460
|
};
|
|
3390
|
-
function isConcurrentModificationError(error2) {
|
|
3391
|
-
return error2 instanceof ConcurrentModificationError;
|
|
3392
|
-
}
|
|
3393
3461
|
|
|
3394
3462
|
// src/format.ts
|
|
3395
3463
|
import { getCountryByIso3 } from "@stndrds/constants";
|
|
@@ -4000,6 +4068,7 @@ function createEmptyStores() {
|
|
|
4000
4068
|
files: /* @__PURE__ */ new Map(),
|
|
4001
4069
|
objectRecords: /* @__PURE__ */ new Map(),
|
|
4002
4070
|
views: /* @__PURE__ */ new Map(),
|
|
4071
|
+
viewOverlays: /* @__PURE__ */ new Map(),
|
|
4003
4072
|
roles: /* @__PURE__ */ new Map(),
|
|
4004
4073
|
permissions: /* @__PURE__ */ new Map(),
|
|
4005
4074
|
userRoles: /* @__PURE__ */ new Map(),
|
|
@@ -4217,7 +4286,8 @@ function createMockPermissionsRepository(stores) {
|
|
|
4217
4286
|
getUserRoles(userProfileId) {
|
|
4218
4287
|
const tenantId = getTenantId();
|
|
4219
4288
|
const roleIds = Array.from(stores.userRoles.values()).filter((ur) => ur.userProfileId === userProfileId && ur.tenantId === tenantId).map((ur) => ur.roleId);
|
|
4220
|
-
const
|
|
4289
|
+
const roleIdsSet = new Set(roleIds);
|
|
4290
|
+
const roles = Array.from(stores.roles.values()).filter((r) => roleIdsSet.has(r.id));
|
|
4221
4291
|
return Promise.resolve(roles);
|
|
4222
4292
|
},
|
|
4223
4293
|
assignRole(input) {
|
|
@@ -4297,32 +4367,38 @@ function createMockViewsRepository(stores) {
|
|
|
4297
4367
|
) ?? null
|
|
4298
4368
|
);
|
|
4299
4369
|
},
|
|
4300
|
-
|
|
4370
|
+
findByNameAndType(objectName, viewName, type) {
|
|
4371
|
+
const tenantId = getTenantId();
|
|
4372
|
+
return Promise.resolve(
|
|
4373
|
+
Array.from(stores.views.values()).find(
|
|
4374
|
+
(v) => v.tenantId === tenantId && v.objectName === objectName && v.name === viewName && v.type === type
|
|
4375
|
+
) ?? null
|
|
4376
|
+
);
|
|
4377
|
+
},
|
|
4378
|
+
findByObjectName(objectName, type) {
|
|
4301
4379
|
const tenantId = getTenantId();
|
|
4302
4380
|
return Promise.resolve(
|
|
4303
4381
|
Array.from(stores.views.values()).filter(
|
|
4304
|
-
(v) => v.tenantId === tenantId && v.objectName === objectName
|
|
4382
|
+
(v) => v.tenantId === tenantId && v.objectName === objectName && (type === void 0 || v.type === type)
|
|
4305
4383
|
)
|
|
4306
4384
|
);
|
|
4307
4385
|
},
|
|
4308
|
-
findAllForTenant() {
|
|
4386
|
+
findAllForTenant(type) {
|
|
4309
4387
|
const tenantId = getTenantId();
|
|
4310
4388
|
return Promise.resolve(
|
|
4311
|
-
Array.from(stores.views.values()).filter(
|
|
4389
|
+
Array.from(stores.views.values()).filter(
|
|
4390
|
+
(v) => v.tenantId === tenantId && (type === void 0 || v.type === type)
|
|
4391
|
+
)
|
|
4312
4392
|
);
|
|
4313
4393
|
},
|
|
4314
|
-
|
|
4394
|
+
findDefault(objectName, type) {
|
|
4395
|
+
const tenantId = getTenantId();
|
|
4315
4396
|
return Promise.resolve(
|
|
4316
4397
|
Array.from(stores.views.values()).find(
|
|
4317
|
-
(v) => v.objectName === objectName && v.
|
|
4398
|
+
(v) => v.tenantId === tenantId && v.objectName === objectName && v.type === type && v.default === true
|
|
4318
4399
|
) ?? null
|
|
4319
4400
|
);
|
|
4320
4401
|
},
|
|
4321
|
-
findSystemByObjectName(objectName) {
|
|
4322
|
-
return Promise.resolve(
|
|
4323
|
-
Array.from(stores.views.values()).filter((v) => v.objectName === objectName && v.system)
|
|
4324
|
-
);
|
|
4325
|
-
},
|
|
4326
4402
|
create(data) {
|
|
4327
4403
|
const tenantId = getTenantId();
|
|
4328
4404
|
const id = generateId();
|
|
@@ -4331,13 +4407,13 @@ function createMockViewsRepository(stores) {
|
|
|
4331
4407
|
id,
|
|
4332
4408
|
tenantId,
|
|
4333
4409
|
objectName: data.objectName,
|
|
4410
|
+
type: data.type,
|
|
4334
4411
|
name: data.name,
|
|
4335
4412
|
label: data.label,
|
|
4336
4413
|
description: data.description,
|
|
4337
4414
|
icon: data.icon,
|
|
4338
|
-
|
|
4415
|
+
config: data.config,
|
|
4339
4416
|
default: data.default ?? false,
|
|
4340
|
-
system: data.system ?? false,
|
|
4341
4417
|
metadata: data.metadata,
|
|
4342
4418
|
createdAt: now,
|
|
4343
4419
|
updatedAt: now
|
|
@@ -4362,10 +4438,11 @@ function createMockViewsRepository(stores) {
|
|
|
4362
4438
|
stores.views.delete(id);
|
|
4363
4439
|
return Promise.resolve();
|
|
4364
4440
|
},
|
|
4365
|
-
deleteNotIn(objectName, keepViewNames) {
|
|
4441
|
+
deleteNotIn(objectName, type, keepViewNames) {
|
|
4442
|
+
const tenantId = getTenantId();
|
|
4366
4443
|
let deleted = 0;
|
|
4367
4444
|
for (const [id, view2] of stores.views.entries()) {
|
|
4368
|
-
if (view2.objectName === objectName && view2.
|
|
4445
|
+
if (view2.tenantId === tenantId && view2.objectName === objectName && view2.type === type && !keepViewNames.includes(view2.name)) {
|
|
4369
4446
|
stores.views.delete(id);
|
|
4370
4447
|
deleted++;
|
|
4371
4448
|
}
|
|
@@ -4373,18 +4450,151 @@ function createMockViewsRepository(stores) {
|
|
|
4373
4450
|
return Promise.resolve(deleted);
|
|
4374
4451
|
},
|
|
4375
4452
|
async upsert(data) {
|
|
4376
|
-
const existing =
|
|
4453
|
+
const existing = await this.findByNameAndType(data.objectName, data.name, data.type);
|
|
4377
4454
|
if (existing) {
|
|
4378
4455
|
return this.update(existing.id, {
|
|
4379
4456
|
label: data.label,
|
|
4380
4457
|
description: data.description,
|
|
4381
4458
|
icon: data.icon,
|
|
4382
|
-
|
|
4459
|
+
config: data.config,
|
|
4383
4460
|
default: data.default,
|
|
4384
4461
|
metadata: data.metadata
|
|
4385
4462
|
});
|
|
4386
4463
|
}
|
|
4387
4464
|
return this.create(data);
|
|
4465
|
+
},
|
|
4466
|
+
async exists(objectName, viewName, type) {
|
|
4467
|
+
const existing = await this.findByNameAndType(objectName, viewName, type);
|
|
4468
|
+
return existing !== null;
|
|
4469
|
+
}
|
|
4470
|
+
};
|
|
4471
|
+
}
|
|
4472
|
+
function createMockViewOverlaysRepository(stores) {
|
|
4473
|
+
return {
|
|
4474
|
+
findById(id) {
|
|
4475
|
+
return Promise.resolve(stores.viewOverlays.get(id) ?? null);
|
|
4476
|
+
},
|
|
4477
|
+
findByViewAndUser(viewId, userId) {
|
|
4478
|
+
const tenantId = getTenantId();
|
|
4479
|
+
return Promise.resolve(
|
|
4480
|
+
Array.from(stores.viewOverlays.values()).find(
|
|
4481
|
+
(o) => o.tenantId === tenantId && o.viewId === viewId && o.userId === userId
|
|
4482
|
+
) ?? null
|
|
4483
|
+
);
|
|
4484
|
+
},
|
|
4485
|
+
findByUser(userId) {
|
|
4486
|
+
const tenantId = getTenantId();
|
|
4487
|
+
return Promise.resolve(
|
|
4488
|
+
Array.from(stores.viewOverlays.values()).filter(
|
|
4489
|
+
(o) => o.tenantId === tenantId && o.userId === userId
|
|
4490
|
+
)
|
|
4491
|
+
);
|
|
4492
|
+
},
|
|
4493
|
+
findByView(viewId) {
|
|
4494
|
+
const tenantId = getTenantId();
|
|
4495
|
+
return Promise.resolve(
|
|
4496
|
+
Array.from(stores.viewOverlays.values()).filter(
|
|
4497
|
+
(o) => o.tenantId === tenantId && o.viewId === viewId
|
|
4498
|
+
)
|
|
4499
|
+
);
|
|
4500
|
+
},
|
|
4501
|
+
async findUserDefault(userId, objectName, type) {
|
|
4502
|
+
const tenantId = getTenantId();
|
|
4503
|
+
const views = Array.from(stores.views.values()).filter(
|
|
4504
|
+
(v) => v.tenantId === tenantId && v.objectName === objectName && v.type === type
|
|
4505
|
+
);
|
|
4506
|
+
const viewIds = views.reduce((set, v) => set.add(v.id), /* @__PURE__ */ new Set());
|
|
4507
|
+
return Promise.resolve(
|
|
4508
|
+
Array.from(stores.viewOverlays.values()).find(
|
|
4509
|
+
(o) => o.tenantId === tenantId && o.userId === userId && o.isUserDefault === true && viewIds.has(o.viewId)
|
|
4510
|
+
) ?? null
|
|
4511
|
+
);
|
|
4512
|
+
},
|
|
4513
|
+
create(data) {
|
|
4514
|
+
const tenantId = getTenantId();
|
|
4515
|
+
const id = generateId();
|
|
4516
|
+
const now = /* @__PURE__ */ new Date();
|
|
4517
|
+
const overlay = {
|
|
4518
|
+
id,
|
|
4519
|
+
tenantId,
|
|
4520
|
+
viewId: data.viewId,
|
|
4521
|
+
userId: data.userId,
|
|
4522
|
+
configOverrides: data.configOverrides,
|
|
4523
|
+
isUserDefault: data.isUserDefault,
|
|
4524
|
+
createdAt: now,
|
|
4525
|
+
updatedAt: now
|
|
4526
|
+
};
|
|
4527
|
+
stores.viewOverlays.set(id, overlay);
|
|
4528
|
+
return Promise.resolve(overlay);
|
|
4529
|
+
},
|
|
4530
|
+
update(id, data) {
|
|
4531
|
+
const overlay = stores.viewOverlays.get(id);
|
|
4532
|
+
if (!overlay) {
|
|
4533
|
+
return Promise.reject(new Error(`ViewOverlay not found: ${id}`));
|
|
4534
|
+
}
|
|
4535
|
+
const updated = {
|
|
4536
|
+
...overlay,
|
|
4537
|
+
...data,
|
|
4538
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
4539
|
+
};
|
|
4540
|
+
stores.viewOverlays.set(id, updated);
|
|
4541
|
+
return Promise.resolve(updated);
|
|
4542
|
+
},
|
|
4543
|
+
delete(id) {
|
|
4544
|
+
stores.viewOverlays.delete(id);
|
|
4545
|
+
return Promise.resolve();
|
|
4546
|
+
},
|
|
4547
|
+
async deleteByViewAndUser(viewId, userId) {
|
|
4548
|
+
const overlay = await this.findByViewAndUser(viewId, userId);
|
|
4549
|
+
if (overlay) {
|
|
4550
|
+
stores.viewOverlays.delete(overlay.id);
|
|
4551
|
+
}
|
|
4552
|
+
},
|
|
4553
|
+
deleteByView(viewId) {
|
|
4554
|
+
const tenantId = getTenantId();
|
|
4555
|
+
let deleted = 0;
|
|
4556
|
+
for (const [id, overlay] of stores.viewOverlays.entries()) {
|
|
4557
|
+
if (overlay.tenantId === tenantId && overlay.viewId === viewId) {
|
|
4558
|
+
stores.viewOverlays.delete(id);
|
|
4559
|
+
deleted++;
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4562
|
+
return Promise.resolve(deleted);
|
|
4563
|
+
},
|
|
4564
|
+
migrateViewId(fromViewId, toViewId) {
|
|
4565
|
+
const tenantId = getTenantId();
|
|
4566
|
+
let migrated = 0;
|
|
4567
|
+
for (const overlay of stores.viewOverlays.values()) {
|
|
4568
|
+
if (overlay.tenantId === tenantId && overlay.viewId === fromViewId) {
|
|
4569
|
+
overlay.viewId = toViewId;
|
|
4570
|
+
overlay.updatedAt = /* @__PURE__ */ new Date();
|
|
4571
|
+
migrated++;
|
|
4572
|
+
}
|
|
4573
|
+
}
|
|
4574
|
+
return Promise.resolve(migrated);
|
|
4575
|
+
},
|
|
4576
|
+
async upsert(data) {
|
|
4577
|
+
const existing = await this.findByViewAndUser(data.viewId, data.userId);
|
|
4578
|
+
if (existing) {
|
|
4579
|
+
return this.update(existing.id, {
|
|
4580
|
+
configOverrides: data.configOverrides,
|
|
4581
|
+
isUserDefault: data.isUserDefault
|
|
4582
|
+
});
|
|
4583
|
+
}
|
|
4584
|
+
return this.create(data);
|
|
4585
|
+
},
|
|
4586
|
+
async clearUserDefault(userId, objectName, type) {
|
|
4587
|
+
const tenantId = getTenantId();
|
|
4588
|
+
const views = Array.from(stores.views.values()).filter(
|
|
4589
|
+
(v) => v.tenantId === tenantId && v.objectName === objectName && v.type === type
|
|
4590
|
+
);
|
|
4591
|
+
const viewIds = views.reduce((set, v) => set.add(v.id), /* @__PURE__ */ new Set());
|
|
4592
|
+
for (const overlay of stores.viewOverlays.values()) {
|
|
4593
|
+
if (overlay.tenantId === tenantId && overlay.userId === userId && overlay.isUserDefault === true && viewIds.has(overlay.viewId)) {
|
|
4594
|
+
overlay.isUserDefault = false;
|
|
4595
|
+
overlay.updatedAt = /* @__PURE__ */ new Date();
|
|
4596
|
+
}
|
|
4597
|
+
}
|
|
4388
4598
|
}
|
|
4389
4599
|
};
|
|
4390
4600
|
}
|
|
@@ -4774,6 +4984,7 @@ function createMockAdapter() {
|
|
|
4774
4984
|
files: createMockFilesRepository(stores),
|
|
4775
4985
|
objectRecords: createMockObjectRecordsRepository(stores),
|
|
4776
4986
|
views: createMockViewsRepository(stores),
|
|
4987
|
+
viewOverlays: createMockViewOverlaysRepository(stores),
|
|
4777
4988
|
permissions: createMockPermissionsRepository(stores),
|
|
4778
4989
|
workflows: createMockWorkflowsRepository(stores),
|
|
4779
4990
|
workflowInstances: createMockWorkflowInstancesRepository(stores),
|
|
@@ -4796,6 +5007,7 @@ function createMockAdapter() {
|
|
|
4796
5007
|
stores.files.clear();
|
|
4797
5008
|
stores.objectRecords.clear();
|
|
4798
5009
|
stores.views.clear();
|
|
5010
|
+
stores.viewOverlays.clear();
|
|
4799
5011
|
stores.roles.clear();
|
|
4800
5012
|
stores.permissions.clear();
|
|
4801
5013
|
stores.userRoles.clear();
|
|
@@ -5339,6 +5551,32 @@ var BaseAttributeBuilder = class {
|
|
|
5339
5551
|
this.attr.metadata = value;
|
|
5340
5552
|
return this;
|
|
5341
5553
|
}
|
|
5554
|
+
/**
|
|
5555
|
+
* Add a feature gate to conditionally show/hide/disable this attribute.
|
|
5556
|
+
*
|
|
5557
|
+
* @param flagName - Name of the feature flag to check
|
|
5558
|
+
* @param options - Optional configuration for expected value and fallback behavior
|
|
5559
|
+
*
|
|
5560
|
+
* @example Hide attribute when flag is disabled
|
|
5561
|
+
* ```typescript
|
|
5562
|
+
* text({ name: "aiSummary", label: "AI Summary" })
|
|
5563
|
+
* .featureGate("ai-features")
|
|
5564
|
+
* ```
|
|
5565
|
+
*
|
|
5566
|
+
* @example Disable attribute when tier is not enterprise
|
|
5567
|
+
* ```typescript
|
|
5568
|
+
* text({ name: "advancedField", label: "Advanced Field" })
|
|
5569
|
+
* .featureGate("tier", { expectedValue: "enterprise", fallback: "disable" })
|
|
5570
|
+
* ```
|
|
5571
|
+
*/
|
|
5572
|
+
featureGate(flagName, options) {
|
|
5573
|
+
this.attr.featureGate = {
|
|
5574
|
+
flag: flagName,
|
|
5575
|
+
expectedValue: options?.expectedValue,
|
|
5576
|
+
fallback: options?.fallback
|
|
5577
|
+
};
|
|
5578
|
+
return this;
|
|
5579
|
+
}
|
|
5342
5580
|
build() {
|
|
5343
5581
|
return this.attr;
|
|
5344
5582
|
}
|
|
@@ -6261,6 +6499,7 @@ function object(config) {
|
|
|
6261
6499
|
}
|
|
6262
6500
|
|
|
6263
6501
|
// src/builders/view-builder.ts
|
|
6502
|
+
import { randomUUID } from "crypto";
|
|
6264
6503
|
import { z as z3 } from "zod";
|
|
6265
6504
|
var GroupBuilder = class {
|
|
6266
6505
|
constructor(id, label) {
|
|
@@ -6411,7 +6650,7 @@ var BaseTableTabConfig = class {
|
|
|
6411
6650
|
return new TabBuilder(this.view, name, label);
|
|
6412
6651
|
}
|
|
6413
6652
|
/**
|
|
6414
|
-
* Finish this tab and return to
|
|
6653
|
+
* Finish this tab and return to DetailViewBuilder
|
|
6415
6654
|
*/
|
|
6416
6655
|
done() {
|
|
6417
6656
|
this.finalize();
|
|
@@ -6477,7 +6716,7 @@ var CustomTabConfig = class {
|
|
|
6477
6716
|
return new TabBuilder(this.view, name, label);
|
|
6478
6717
|
}
|
|
6479
6718
|
/**
|
|
6480
|
-
* Finish this tab and return to
|
|
6719
|
+
* Finish this tab and return to DetailViewBuilder
|
|
6481
6720
|
*/
|
|
6482
6721
|
done() {
|
|
6483
6722
|
this.view._addTab(this.tabData);
|
|
@@ -6519,7 +6758,7 @@ var NotesTabConfig = class {
|
|
|
6519
6758
|
return new TabBuilder(this.view, name, label);
|
|
6520
6759
|
}
|
|
6521
6760
|
/**
|
|
6522
|
-
* Finish this tab and return to
|
|
6761
|
+
* Finish this tab and return to DetailViewBuilder
|
|
6523
6762
|
*/
|
|
6524
6763
|
done() {
|
|
6525
6764
|
this.view._addTab(this.tabData);
|
|
@@ -6554,7 +6793,7 @@ var ActivityTabConfig = class {
|
|
|
6554
6793
|
return new TabBuilder(this.view, name, label);
|
|
6555
6794
|
}
|
|
6556
6795
|
/**
|
|
6557
|
-
* Finish this tab and return to
|
|
6796
|
+
* Finish this tab and return to DetailViewBuilder
|
|
6558
6797
|
*/
|
|
6559
6798
|
done() {
|
|
6560
6799
|
this.view._addTab(this.tabData);
|
|
@@ -6617,7 +6856,7 @@ var FlowsTabConfig = class {
|
|
|
6617
6856
|
return new TabBuilder(this.view, name, label);
|
|
6618
6857
|
}
|
|
6619
6858
|
/**
|
|
6620
|
-
* Finish this tab and return to
|
|
6859
|
+
* Finish this tab and return to DetailViewBuilder
|
|
6621
6860
|
*/
|
|
6622
6861
|
done() {
|
|
6623
6862
|
this.view._addTab(this.tabData);
|
|
@@ -6688,7 +6927,7 @@ var DocumentsTabConfig = class {
|
|
|
6688
6927
|
return new TabBuilder(this.view, name, label);
|
|
6689
6928
|
}
|
|
6690
6929
|
/**
|
|
6691
|
-
* Finish this tab and return to
|
|
6930
|
+
* Finish this tab and return to DetailViewBuilder
|
|
6692
6931
|
*/
|
|
6693
6932
|
done() {
|
|
6694
6933
|
this.view._addTab(this.tabData);
|
|
@@ -6726,13 +6965,6 @@ var TabBuilder = class {
|
|
|
6726
6965
|
this.base.order = value;
|
|
6727
6966
|
return this;
|
|
6728
6967
|
}
|
|
6729
|
-
/**
|
|
6730
|
-
* Mark as system tab (protected)
|
|
6731
|
-
*/
|
|
6732
|
-
system() {
|
|
6733
|
-
this.base.system = true;
|
|
6734
|
-
return this;
|
|
6735
|
-
}
|
|
6736
6968
|
// ─────────────────────────────────────────────────────────────────────────
|
|
6737
6969
|
// TYPE DISCRIMINATORS
|
|
6738
6970
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -6812,13 +7044,15 @@ var TabBuilder = class {
|
|
|
6812
7044
|
return new DocumentsTabConfig(this.view, this.base);
|
|
6813
7045
|
}
|
|
6814
7046
|
};
|
|
6815
|
-
var
|
|
7047
|
+
var DetailViewBuilder = class {
|
|
6816
7048
|
constructor(name, label) {
|
|
6817
|
-
this.data = { tabs: [] };
|
|
6818
|
-
this.validated = false;
|
|
6819
7049
|
this.validateName(name);
|
|
6820
|
-
this.data
|
|
6821
|
-
|
|
7050
|
+
this.data = {
|
|
7051
|
+
name,
|
|
7052
|
+
label,
|
|
7053
|
+
layout: "page",
|
|
7054
|
+
tabs: []
|
|
7055
|
+
};
|
|
6822
7056
|
}
|
|
6823
7057
|
/**
|
|
6824
7058
|
* Set view description
|
|
@@ -6843,19 +7077,12 @@ var ViewBuilder = class {
|
|
|
6843
7077
|
return this;
|
|
6844
7078
|
}
|
|
6845
7079
|
/**
|
|
6846
|
-
* Mark as default view for the object
|
|
7080
|
+
* Mark as default view for the object
|
|
6847
7081
|
*/
|
|
6848
7082
|
default() {
|
|
6849
7083
|
this.data.default = true;
|
|
6850
7084
|
return this;
|
|
6851
7085
|
}
|
|
6852
|
-
/**
|
|
6853
|
-
* Mark as system view (protected, defined by developer)
|
|
6854
|
-
*/
|
|
6855
|
-
system() {
|
|
6856
|
-
this.data.system = true;
|
|
6857
|
-
return this;
|
|
6858
|
-
}
|
|
6859
7086
|
/**
|
|
6860
7087
|
* Set the layout mode for the view
|
|
6861
7088
|
* @param value - "page" for full tabs, "modal" for single form
|
|
@@ -6890,14 +7117,14 @@ var ViewBuilder = class {
|
|
|
6890
7117
|
* Add a pre-built tab
|
|
6891
7118
|
*/
|
|
6892
7119
|
addTab(tab) {
|
|
6893
|
-
this.data.tabs
|
|
7120
|
+
this.data.tabs.push(tab);
|
|
6894
7121
|
return this;
|
|
6895
7122
|
}
|
|
6896
7123
|
/**
|
|
6897
7124
|
* @internal Used by TabBuilder to add tabs
|
|
6898
7125
|
*/
|
|
6899
7126
|
_addTab(tab) {
|
|
6900
|
-
this.data.tabs
|
|
7127
|
+
this.data.tabs.push(tab);
|
|
6901
7128
|
return this;
|
|
6902
7129
|
}
|
|
6903
7130
|
/**
|
|
@@ -6905,28 +7132,41 @@ var ViewBuilder = class {
|
|
|
6905
7132
|
*/
|
|
6906
7133
|
build() {
|
|
6907
7134
|
if (!this.data.object) {
|
|
6908
|
-
throw new Error("[
|
|
7135
|
+
throw new Error("[DetailViewBuilder] for() is required - specify the target object");
|
|
6909
7136
|
}
|
|
6910
|
-
if (
|
|
6911
|
-
throw new Error("[
|
|
7137
|
+
if (this.data.tabs.length === 0) {
|
|
7138
|
+
throw new Error("[DetailViewBuilder] At least one tab is required");
|
|
6912
7139
|
}
|
|
6913
7140
|
const tabNames = /* @__PURE__ */ new Set();
|
|
6914
7141
|
for (const tab of this.data.tabs) {
|
|
6915
7142
|
if (tabNames.has(tab.name)) {
|
|
6916
|
-
throw new Error(`[
|
|
7143
|
+
throw new Error(`[DetailViewBuilder] Duplicate tab name "${tab.name}"`);
|
|
6917
7144
|
}
|
|
6918
7145
|
tabNames.add(tab.name);
|
|
6919
7146
|
}
|
|
6920
7147
|
if (this.data.layout === "modal") {
|
|
6921
7148
|
if (this.data.tabs.length !== 1) {
|
|
6922
|
-
throw new Error("[
|
|
7149
|
+
throw new Error("[DetailViewBuilder] Modal views must have exactly one tab");
|
|
6923
7150
|
}
|
|
6924
7151
|
if (this.data.tabs[0].type !== "form") {
|
|
6925
|
-
throw new Error("[
|
|
7152
|
+
throw new Error("[DetailViewBuilder] Modal views must have a form tab");
|
|
6926
7153
|
}
|
|
6927
7154
|
}
|
|
6928
|
-
|
|
6929
|
-
|
|
7155
|
+
const config = {
|
|
7156
|
+
layout: this.data.layout,
|
|
7157
|
+
tabs: this.data.tabs
|
|
7158
|
+
};
|
|
7159
|
+
return {
|
|
7160
|
+
name: this.data.name,
|
|
7161
|
+
label: this.data.label,
|
|
7162
|
+
description: this.data.description,
|
|
7163
|
+
icon: this.data.icon,
|
|
7164
|
+
object: this.data.object,
|
|
7165
|
+
type: "detail",
|
|
7166
|
+
config,
|
|
7167
|
+
default: this.data.default,
|
|
7168
|
+
metadata: this.data.metadata
|
|
7169
|
+
};
|
|
6930
7170
|
}
|
|
6931
7171
|
/**
|
|
6932
7172
|
* Validate view name format (kebab-case)
|
|
@@ -6939,24 +7179,295 @@ var ViewBuilder = class {
|
|
|
6939
7179
|
viewNameSchema.parse(name);
|
|
6940
7180
|
} catch (error2) {
|
|
6941
7181
|
if (error2 instanceof z3.ZodError) {
|
|
6942
|
-
throw new Error(`[
|
|
7182
|
+
throw new Error(`[DetailViewBuilder] ${error2.issues[0].message}`);
|
|
6943
7183
|
}
|
|
6944
7184
|
throw error2;
|
|
6945
7185
|
}
|
|
6946
7186
|
}
|
|
6947
7187
|
};
|
|
6948
|
-
|
|
6949
|
-
|
|
7188
|
+
var ViewBuilder = DetailViewBuilder;
|
|
7189
|
+
function detailView(name, label) {
|
|
7190
|
+
return new DetailViewBuilder(name, label);
|
|
6950
7191
|
}
|
|
6951
|
-
function
|
|
6952
|
-
return new
|
|
7192
|
+
function view(name, label) {
|
|
7193
|
+
return new DetailViewBuilder(name, label);
|
|
6953
7194
|
}
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
7195
|
+
var ListViewBuilder = class {
|
|
7196
|
+
constructor(name, label) {
|
|
7197
|
+
this.validateName(name);
|
|
7198
|
+
this.data = {
|
|
7199
|
+
name,
|
|
7200
|
+
label,
|
|
7201
|
+
layout: "table",
|
|
7202
|
+
columns: [],
|
|
7203
|
+
tabs: []
|
|
7204
|
+
};
|
|
7205
|
+
}
|
|
7206
|
+
/**
|
|
7207
|
+
* Set view description
|
|
7208
|
+
*/
|
|
7209
|
+
description(value) {
|
|
7210
|
+
this.data.description = value;
|
|
7211
|
+
return this;
|
|
7212
|
+
}
|
|
7213
|
+
/**
|
|
7214
|
+
* Set view icon
|
|
7215
|
+
*/
|
|
7216
|
+
icon(value) {
|
|
7217
|
+
this.data.icon = value;
|
|
7218
|
+
return this;
|
|
7219
|
+
}
|
|
7220
|
+
/**
|
|
7221
|
+
* Associate view with an object
|
|
7222
|
+
* @param objectName - Object name (kebab-case)
|
|
7223
|
+
*/
|
|
7224
|
+
for(objectName) {
|
|
7225
|
+
this.data.object = objectName;
|
|
7226
|
+
return this;
|
|
7227
|
+
}
|
|
7228
|
+
/**
|
|
7229
|
+
* Mark as default view for the object
|
|
7230
|
+
*/
|
|
7231
|
+
default() {
|
|
7232
|
+
this.data.default = true;
|
|
7233
|
+
return this;
|
|
7234
|
+
}
|
|
7235
|
+
/**
|
|
7236
|
+
* Set metadata
|
|
7237
|
+
*/
|
|
7238
|
+
metadata(value) {
|
|
7239
|
+
this.data.metadata = value;
|
|
7240
|
+
return this;
|
|
7241
|
+
}
|
|
7242
|
+
/**
|
|
7243
|
+
* Set the layout to table (default)
|
|
7244
|
+
*/
|
|
7245
|
+
table() {
|
|
7246
|
+
this.data.layout = "table";
|
|
7247
|
+
return this;
|
|
7248
|
+
}
|
|
7249
|
+
/**
|
|
7250
|
+
* Set the layout to kanban and specify the grouping attribute
|
|
7251
|
+
* @param groupByAttribute - Attribute to group by (must be a select/status type)
|
|
7252
|
+
*/
|
|
7253
|
+
kanban(groupByAttribute) {
|
|
7254
|
+
this.data.layout = "kanban";
|
|
7255
|
+
this.data.groupByAttribute = groupByAttribute;
|
|
7256
|
+
return this;
|
|
7257
|
+
}
|
|
7258
|
+
/**
|
|
7259
|
+
* Set columns to display
|
|
7260
|
+
* @example .columns("name", "email", "status", "createdAt")
|
|
7261
|
+
*/
|
|
7262
|
+
columns(...names) {
|
|
7263
|
+
this.data.columns = names;
|
|
7264
|
+
return this;
|
|
7265
|
+
}
|
|
7266
|
+
/**
|
|
7267
|
+
* Set width for a specific column in pixels
|
|
7268
|
+
* @example .columnWidth("email", 200)
|
|
7269
|
+
*/
|
|
7270
|
+
columnWidth(columnName, width) {
|
|
7271
|
+
if (!this.data.columnSizing) {
|
|
7272
|
+
this.data.columnSizing = {};
|
|
7273
|
+
}
|
|
7274
|
+
this.data.columnSizing[columnName] = width;
|
|
7275
|
+
return this;
|
|
7276
|
+
}
|
|
7277
|
+
/**
|
|
7278
|
+
* Set widths for multiple columns
|
|
7279
|
+
* @example .columnWidths({ email: 200, name: 300, status: 100 })
|
|
7280
|
+
*/
|
|
7281
|
+
columnWidths(widths) {
|
|
7282
|
+
this.data.columnSizing = { ...this.data.columnSizing, ...widths };
|
|
7283
|
+
return this;
|
|
7284
|
+
}
|
|
7285
|
+
/**
|
|
7286
|
+
* Set default filters
|
|
7287
|
+
* @example .filter({ combinator: "and", rules: [{ attribute: "status", operator: "is", value: "active" }] })
|
|
7288
|
+
*/
|
|
7289
|
+
filter(filters) {
|
|
7290
|
+
this.data.defaultFilters = filters;
|
|
7291
|
+
return this;
|
|
7292
|
+
}
|
|
7293
|
+
/**
|
|
7294
|
+
* Add a single sort rule
|
|
7295
|
+
* @example .sort("lastName", "asc")
|
|
7296
|
+
*/
|
|
7297
|
+
sort(attribute, direction = "asc") {
|
|
7298
|
+
if (!this.data.defaultSorts) {
|
|
7299
|
+
this.data.defaultSorts = [];
|
|
7300
|
+
}
|
|
7301
|
+
this.data.defaultSorts.push({ attribute, direction });
|
|
7302
|
+
return this;
|
|
7303
|
+
}
|
|
7304
|
+
/**
|
|
7305
|
+
* Set multiple sort rules
|
|
7306
|
+
* @example .sorts([{ attribute: "lastName", direction: "asc" }, { attribute: "firstName", direction: "asc" }])
|
|
7307
|
+
*/
|
|
7308
|
+
sorts(rules) {
|
|
7309
|
+
this.data.defaultSorts = rules;
|
|
7310
|
+
return this;
|
|
7311
|
+
}
|
|
7312
|
+
/**
|
|
7313
|
+
* Add an internal tab (filter preset)
|
|
7314
|
+
* Returns a TabConfigBuilder for chaining tab configuration
|
|
7315
|
+
* @example
|
|
7316
|
+
* ```typescript
|
|
7317
|
+
* .tab("all", "All Contacts").icon("users").default()
|
|
7318
|
+
* .tab("active", "Active").icon("check").filter({ ... })
|
|
7319
|
+
* ```
|
|
7320
|
+
*/
|
|
7321
|
+
tab(id, label) {
|
|
7322
|
+
return new ListViewTabConfigBuilder(this, id, label);
|
|
7323
|
+
}
|
|
7324
|
+
/**
|
|
7325
|
+
* @internal Used by ListViewTabConfigBuilder to add tabs
|
|
7326
|
+
*/
|
|
7327
|
+
_addTab(tab) {
|
|
7328
|
+
this.data.tabs.push(tab);
|
|
7329
|
+
return this;
|
|
7330
|
+
}
|
|
7331
|
+
/**
|
|
7332
|
+
* Build the final list view definition
|
|
7333
|
+
*/
|
|
7334
|
+
build() {
|
|
7335
|
+
if (!this.data.object) {
|
|
7336
|
+
throw new Error("[ListViewBuilder] for() is required - specify the target object");
|
|
7337
|
+
}
|
|
7338
|
+
if (this.data.columns.length === 0) {
|
|
7339
|
+
throw new Error("[ListViewBuilder] columns() is required - specify at least one column");
|
|
7340
|
+
}
|
|
7341
|
+
if (this.data.layout === "kanban" && !this.data.groupByAttribute) {
|
|
7342
|
+
throw new Error(
|
|
7343
|
+
"[ListViewBuilder] kanban() requires a groupByAttribute - specify the attribute to group by"
|
|
7344
|
+
);
|
|
7345
|
+
}
|
|
7346
|
+
const tabIds = /* @__PURE__ */ new Set();
|
|
7347
|
+
for (const tab of this.data.tabs) {
|
|
7348
|
+
if (tabIds.has(tab.id)) {
|
|
7349
|
+
throw new Error(`[ListViewBuilder] Duplicate tab id "${tab.id}"`);
|
|
7350
|
+
}
|
|
7351
|
+
tabIds.add(tab.id);
|
|
7352
|
+
}
|
|
7353
|
+
const defaultTabs = this.data.tabs.filter((t) => t.default);
|
|
7354
|
+
if (defaultTabs.length > 1) {
|
|
7355
|
+
throw new Error("[ListViewBuilder] Only one tab can be marked as default");
|
|
7356
|
+
}
|
|
7357
|
+
const config = {
|
|
7358
|
+
layout: this.data.layout,
|
|
7359
|
+
columns: this.data.columns,
|
|
7360
|
+
columnSizing: this.data.columnSizing,
|
|
7361
|
+
defaultFilters: this.data.defaultFilters ? {
|
|
7362
|
+
id: randomUUID(),
|
|
7363
|
+
combinator: this.data.defaultFilters.combinator,
|
|
7364
|
+
rules: this.data.defaultFilters.rules
|
|
7365
|
+
} : void 0,
|
|
7366
|
+
defaultSorts: this.data.defaultSorts,
|
|
7367
|
+
groupByAttribute: this.data.groupByAttribute,
|
|
7368
|
+
tabs: this.data.tabs.length > 0 ? this.data.tabs.map((tab) => ({
|
|
7369
|
+
id: tab.id,
|
|
7370
|
+
label: tab.label,
|
|
7371
|
+
icon: tab.icon,
|
|
7372
|
+
filters: tab.filters ? {
|
|
7373
|
+
id: randomUUID(),
|
|
7374
|
+
combinator: tab.filters.combinator,
|
|
7375
|
+
rules: tab.filters.rules
|
|
7376
|
+
} : void 0,
|
|
7377
|
+
default: tab.default
|
|
7378
|
+
})) : void 0
|
|
7379
|
+
};
|
|
7380
|
+
return {
|
|
7381
|
+
name: this.data.name,
|
|
7382
|
+
label: this.data.label,
|
|
7383
|
+
description: this.data.description,
|
|
7384
|
+
icon: this.data.icon,
|
|
7385
|
+
object: this.data.object,
|
|
7386
|
+
type: "list",
|
|
7387
|
+
config,
|
|
7388
|
+
default: this.data.default,
|
|
7389
|
+
metadata: this.data.metadata
|
|
7390
|
+
};
|
|
7391
|
+
}
|
|
7392
|
+
/**
|
|
7393
|
+
* Validate view name format (kebab-case)
|
|
7394
|
+
*/
|
|
7395
|
+
validateName(name) {
|
|
7396
|
+
const viewNameSchema = z3.string().min(1, "View name cannot be empty").max(63, "View name is too long (max 63 characters)").regex(/^[a-z][a-z0-9-]*$/, {
|
|
7397
|
+
message: "Invalid view name format.\nName must be in kebab-case:\n \u2705 Valid: 'default', 'list-view', 'active-contacts'\n \u274C Invalid: 'Default', 'listView', 'list_view'"
|
|
7398
|
+
});
|
|
7399
|
+
try {
|
|
7400
|
+
viewNameSchema.parse(name);
|
|
7401
|
+
} catch (error2) {
|
|
7402
|
+
if (error2 instanceof z3.ZodError) {
|
|
7403
|
+
throw new Error(`[ListViewBuilder] ${error2.issues[0].message}`);
|
|
7404
|
+
}
|
|
7405
|
+
throw error2;
|
|
7406
|
+
}
|
|
7407
|
+
}
|
|
7408
|
+
};
|
|
7409
|
+
var ListViewTabConfigBuilder = class _ListViewTabConfigBuilder {
|
|
7410
|
+
/** @internal */
|
|
7411
|
+
constructor(view2, id, label) {
|
|
7412
|
+
this.view = view2;
|
|
7413
|
+
this.tabData = { id, label };
|
|
7414
|
+
}
|
|
7415
|
+
/**
|
|
7416
|
+
* Set tab icon
|
|
7417
|
+
*/
|
|
7418
|
+
icon(value) {
|
|
7419
|
+
this.tabData.icon = value;
|
|
7420
|
+
return this;
|
|
7421
|
+
}
|
|
7422
|
+
/**
|
|
7423
|
+
* Set filters for this tab (filter preset)
|
|
7424
|
+
* @example .filter({ combinator: "and", rules: [{ attribute: "status", operator: "is", value: "active" }] })
|
|
7425
|
+
*/
|
|
7426
|
+
filter(filters) {
|
|
7427
|
+
this.tabData.filters = filters;
|
|
7428
|
+
return this;
|
|
7429
|
+
}
|
|
7430
|
+
/**
|
|
7431
|
+
* Mark this tab as the default tab
|
|
7432
|
+
*/
|
|
7433
|
+
default() {
|
|
7434
|
+
this.tabData.default = true;
|
|
7435
|
+
return this;
|
|
7436
|
+
}
|
|
7437
|
+
/**
|
|
7438
|
+
* Continue building with a new tab
|
|
7439
|
+
*/
|
|
7440
|
+
tab(id, label) {
|
|
7441
|
+
this.view._addTab(this.tabData);
|
|
7442
|
+
return new _ListViewTabConfigBuilder(this.view, id, label);
|
|
7443
|
+
}
|
|
7444
|
+
/**
|
|
7445
|
+
* Finish this tab and return to ListViewBuilder
|
|
7446
|
+
*/
|
|
7447
|
+
done() {
|
|
7448
|
+
this.view._addTab(this.tabData);
|
|
7449
|
+
return this.view;
|
|
7450
|
+
}
|
|
7451
|
+
/**
|
|
7452
|
+
* Build the final view definition
|
|
7453
|
+
*/
|
|
7454
|
+
build() {
|
|
7455
|
+
this.view._addTab(this.tabData);
|
|
7456
|
+
return this.view.build();
|
|
7457
|
+
}
|
|
7458
|
+
};
|
|
7459
|
+
function listView(name, label) {
|
|
7460
|
+
return new ListViewBuilder(name, label);
|
|
7461
|
+
}
|
|
7462
|
+
function group(id, label) {
|
|
7463
|
+
return new GroupBuilder(id, label);
|
|
7464
|
+
}
|
|
7465
|
+
|
|
7466
|
+
// src/builders/workflow-builder.ts
|
|
7467
|
+
import { z as z4 } from "zod";
|
|
7468
|
+
var WorkflowFormRowBuilder = class {
|
|
7469
|
+
/** @internal */
|
|
7470
|
+
constructor(formBuilder, rowId, order) {
|
|
6960
7471
|
this.formBuilder = formBuilder;
|
|
6961
7472
|
this.rowData = {
|
|
6962
7473
|
id: rowId,
|
|
@@ -7373,13 +7884,13 @@ var WorkflowBuilder = class {
|
|
|
7373
7884
|
if (!this.startNodeId) {
|
|
7374
7885
|
throw new Error("[WorkflowBuilder] A start node is required. Use .start() to add one.");
|
|
7375
7886
|
}
|
|
7376
|
-
if (
|
|
7887
|
+
if (isEmpty(this.data.nodes)) {
|
|
7377
7888
|
throw new Error("[WorkflowBuilder] At least one node is required.");
|
|
7378
7889
|
}
|
|
7379
7890
|
if (!this.data.slots || this.data.slots.length === 0) {
|
|
7380
7891
|
throw new Error("[WorkflowBuilder] At least one slot is required. Use .slot() to add slots.");
|
|
7381
7892
|
}
|
|
7382
|
-
const hasEndNode = Object.values(this.data.nodes).some((n) => n.type === "end");
|
|
7893
|
+
const hasEndNode = this.data.nodes ? Object.values(this.data.nodes).some((n) => n.type === "end") : false;
|
|
7383
7894
|
if (!hasEndNode) {
|
|
7384
7895
|
throw new Error(
|
|
7385
7896
|
"[WorkflowBuilder] At least one end node is required. Use .end() to add one."
|
|
@@ -7410,7 +7921,7 @@ var WorkflowBuilder = class {
|
|
|
7410
7921
|
}
|
|
7411
7922
|
}
|
|
7412
7923
|
validateSlotReferences() {
|
|
7413
|
-
const slotIds =
|
|
7924
|
+
const slotIds = this.data.slots?.reduce((set, s) => set.add(s.id), /* @__PURE__ */ new Set()) ?? /* @__PURE__ */ new Set();
|
|
7414
7925
|
for (const node of Object.values(this.data.nodes ?? {})) {
|
|
7415
7926
|
if (node.type === "form") {
|
|
7416
7927
|
const referencedSlots = /* @__PURE__ */ new Set();
|
|
@@ -7492,6 +8003,30 @@ function workflow(name, label) {
|
|
|
7492
8003
|
return new WorkflowBuilder(name, label);
|
|
7493
8004
|
}
|
|
7494
8005
|
|
|
8006
|
+
// src/types/attribute-protection.ts
|
|
8007
|
+
var IDENTITY_PROPERTIES = ["name", "type"];
|
|
8008
|
+
var BEHAVIOR_PROPERTIES = [
|
|
8009
|
+
"required",
|
|
8010
|
+
"disabled",
|
|
8011
|
+
"hidden",
|
|
8012
|
+
"archived",
|
|
8013
|
+
"deprecated",
|
|
8014
|
+
"defaultValue",
|
|
8015
|
+
"config",
|
|
8016
|
+
"order",
|
|
8017
|
+
"unique"
|
|
8018
|
+
];
|
|
8019
|
+
var PRESENTATION_PROPERTIES = ["label", "description", "placeholder", "icon"];
|
|
8020
|
+
function isIdentityProperty(property) {
|
|
8021
|
+
return IDENTITY_PROPERTIES.includes(property);
|
|
8022
|
+
}
|
|
8023
|
+
function isBehaviorProperty(property) {
|
|
8024
|
+
return BEHAVIOR_PROPERTIES.includes(property);
|
|
8025
|
+
}
|
|
8026
|
+
function isPresentationProperty(property) {
|
|
8027
|
+
return PRESENTATION_PROPERTIES.includes(property);
|
|
8028
|
+
}
|
|
8029
|
+
|
|
7495
8030
|
// src/types/errors.ts
|
|
7496
8031
|
var RecordReferencedError = class extends Error {
|
|
7497
8032
|
constructor(recordId, references) {
|
|
@@ -7583,582 +8118,25 @@ var SYSTEM_ATTRIBUTES = {
|
|
|
7583
8118
|
label: "Pi\xE8ces jointes",
|
|
7584
8119
|
type: "document",
|
|
7585
8120
|
required: false,
|
|
7586
|
-
disabled: false,
|
|
7587
|
-
// Editable via Documents tab
|
|
7588
|
-
system: true,
|
|
7589
|
-
hidden: true,
|
|
7590
|
-
// Hidden from regular form views
|
|
7591
|
-
multiple: true,
|
|
7592
|
-
icon: "paperclip",
|
|
7593
|
-
description: "Free-form document attachments"
|
|
7594
|
-
// No templateId = all templates allowed
|
|
7595
|
-
}
|
|
7596
|
-
};
|
|
7597
|
-
function getSystemAttributeList() {
|
|
7598
|
-
return Object.values(SYSTEM_ATTRIBUTES);
|
|
7599
|
-
}
|
|
7600
|
-
function isSystemAttribute(name) {
|
|
7601
|
-
return name in SYSTEM_ATTRIBUTES;
|
|
7602
|
-
}
|
|
7603
|
-
function isSystemAttributeObject(attr) {
|
|
7604
|
-
return attr.system === true;
|
|
7605
|
-
}
|
|
7606
|
-
|
|
7607
|
-
// src/validation/validators.ts
|
|
7608
|
-
import { z as z5 } from "zod";
|
|
7609
|
-
var regexPatternCache = /* @__PURE__ */ new Map();
|
|
7610
|
-
function getCachedRegex(pattern) {
|
|
7611
|
-
let cached = regexPatternCache.get(pattern);
|
|
7612
|
-
if (!cached) {
|
|
7613
|
-
cached = new RegExp(pattern);
|
|
7614
|
-
regexPatternCache.set(pattern, cached);
|
|
7615
|
-
}
|
|
7616
|
-
return cached;
|
|
7617
|
-
}
|
|
7618
|
-
var DEFAULT_VALIDATION_MESSAGES = {
|
|
7619
|
-
required: (attr) => `${attr.label} is required`,
|
|
7620
|
-
invalidType: (attr, expected) => `${attr.label} must be a ${expected}`,
|
|
7621
|
-
minLength: (attr, min) => `${attr.label} must be at least ${min} characters`,
|
|
7622
|
-
maxLength: (attr, max) => `${attr.label} must be at most ${max} characters`,
|
|
7623
|
-
invalidPattern: (attr) => `${attr.label} format is invalid`,
|
|
7624
|
-
minValue: (attr, min) => `${attr.label} must be at least ${min}`,
|
|
7625
|
-
maxValue: (attr, max) => `${attr.label} must be at most ${max}`,
|
|
7626
|
-
mustBeInteger: (attr) => `${attr.label} must be an integer`,
|
|
7627
|
-
invalidDate: (attr) => `${attr.label} must be a valid date`,
|
|
7628
|
-
invalidOption: (attr, options) => `${attr.label} must be one of: ${options.join(", ")}`,
|
|
7629
|
-
invalidId: (attr) => `${attr.label} must be a valid ID`,
|
|
7630
|
-
minItems: (attr, min) => `${attr.label} must have at least ${min} item${min > 1 ? "s" : ""}`,
|
|
7631
|
-
maxItems: (attr, max) => `${attr.label} must have at most ${max} item${max > 1 ? "s" : ""}`,
|
|
7632
|
-
invalidRichtext: (attr) => `${attr.label} must be valid rich text content`,
|
|
7633
|
-
invalidPhone: (attr) => `${attr.label} must be a valid phone number`,
|
|
7634
|
-
invalidCurrency: (attr) => `${attr.label} must be a valid currency value`,
|
|
7635
|
-
invalidLocation: (attr) => `${attr.label} must be a valid location`
|
|
7636
|
-
};
|
|
7637
|
-
var baseConfigSchema = z5.object({
|
|
7638
|
-
disabled: z5.boolean().optional(),
|
|
7639
|
-
placeholder: z5.string().optional(),
|
|
7640
|
-
description: z5.string().optional(),
|
|
7641
|
-
defaultValue: z5.unknown().optional(),
|
|
7642
|
-
icon: z5.string().optional(),
|
|
7643
|
-
order: z5.number().int().optional(),
|
|
7644
|
-
hidden: z5.boolean().optional(),
|
|
7645
|
-
archived: z5.boolean().optional(),
|
|
7646
|
-
deprecated: z5.boolean().optional(),
|
|
7647
|
-
metadata: z5.record(z5.string(), z5.unknown()).optional()
|
|
7648
|
-
});
|
|
7649
|
-
var optionSchema = z5.object({
|
|
7650
|
-
id: z5.string().min(1),
|
|
7651
|
-
label: z5.string().min(1),
|
|
7652
|
-
value: z5.string().min(1),
|
|
7653
|
-
color: z5.string().optional(),
|
|
7654
|
-
icon: z5.string().optional(),
|
|
7655
|
-
description: z5.string().optional(),
|
|
7656
|
-
group: z5.enum(["idle", "in_progress", "finished"]).optional()
|
|
7657
|
-
});
|
|
7658
|
-
var optionsArraySchema = z5.array(optionSchema).min(1).refine(
|
|
7659
|
-
(options) => {
|
|
7660
|
-
const values = options.map((o) => o.value);
|
|
7661
|
-
return new Set(values).size === values.length;
|
|
7662
|
-
},
|
|
7663
|
-
{ message: "Duplicate option values are not allowed" }
|
|
7664
|
-
);
|
|
7665
|
-
var relationTargetSchema = z5.object({
|
|
7666
|
-
object: z5.string().min(1),
|
|
7667
|
-
displayTemplate: z5.string().optional(),
|
|
7668
|
-
filter: z5.record(z5.string(), z5.unknown()).optional()
|
|
7669
|
-
});
|
|
7670
|
-
var textConfigSchema = baseConfigSchema.extend({
|
|
7671
|
-
minLength: z5.number().int().min(0).optional(),
|
|
7672
|
-
maxLength: z5.number().int().min(1).optional(),
|
|
7673
|
-
pattern: z5.string().optional()
|
|
7674
|
-
});
|
|
7675
|
-
var textareaConfigSchema = baseConfigSchema;
|
|
7676
|
-
var richtextConfigSchema = baseConfigSchema.extend({
|
|
7677
|
-
features: z5.array(
|
|
7678
|
-
z5.enum(["headings", "bold", "italic", "lists", "links", "images", "codeBlocks", "tables"])
|
|
7679
|
-
).optional()
|
|
7680
|
-
});
|
|
7681
|
-
var numberConfigSchema = baseConfigSchema.extend({
|
|
7682
|
-
min: z5.number().optional(),
|
|
7683
|
-
max: z5.number().optional(),
|
|
7684
|
-
unit: z5.enum(["integer", "decimal", "percentage"]).optional(),
|
|
7685
|
-
decimals: z5.number().int().min(0).optional()
|
|
7686
|
-
});
|
|
7687
|
-
var checkboxConfigSchema = baseConfigSchema;
|
|
7688
|
-
var dateConfigSchema = baseConfigSchema.extend({
|
|
7689
|
-
dateFormat: z5.enum(["short", "long", "full", "relative"]).optional(),
|
|
7690
|
-
minDate: z5.string().optional(),
|
|
7691
|
-
maxDate: z5.string().optional()
|
|
7692
|
-
});
|
|
7693
|
-
var phoneConfigSchema = baseConfigSchema.extend({
|
|
7694
|
-
defaultCountryCode: z5.string().length(3).optional()
|
|
7695
|
-
});
|
|
7696
|
-
var currencyConfigSchema = baseConfigSchema.extend({
|
|
7697
|
-
defaultCurrency: z5.string().length(3).optional(),
|
|
7698
|
-
allowedCurrencies: z5.array(z5.string().length(3)).optional()
|
|
7699
|
-
});
|
|
7700
|
-
var statusConfigSchema = baseConfigSchema.extend({
|
|
7701
|
-
options: optionsArraySchema
|
|
7702
|
-
});
|
|
7703
|
-
var locationConfigSchema = baseConfigSchema.extend({
|
|
7704
|
-
granularity: z5.enum(["full", "address", "city", "state", "country", "coordinates"]),
|
|
7705
|
-
enableAutocomplete: z5.boolean().optional(),
|
|
7706
|
-
enableMap: z5.boolean().optional(),
|
|
7707
|
-
defaultCountry: z5.string().length(3).optional(),
|
|
7708
|
-
allowedCountries: z5.array(z5.string().length(3)).optional(),
|
|
7709
|
-
displayFormat: z5.enum(["single_line", "multi_line", "compact"]).optional()
|
|
7710
|
-
});
|
|
7711
|
-
var selectConfigSchema = baseConfigSchema.extend({
|
|
7712
|
-
options: optionsArraySchema
|
|
7713
|
-
});
|
|
7714
|
-
var multiselectConfigSchema = baseConfigSchema.extend({
|
|
7715
|
-
options: optionsArraySchema
|
|
7716
|
-
});
|
|
7717
|
-
var fileConfigSchema = baseConfigSchema.extend({
|
|
7718
|
-
maxFiles: z5.number().int().min(1).optional(),
|
|
7719
|
-
maxSize: z5.number().int().min(1).optional(),
|
|
7720
|
-
allowedTypes: z5.array(z5.string()).optional(),
|
|
7721
|
-
multiple: z5.boolean().optional()
|
|
7722
|
-
});
|
|
7723
|
-
var userConfigSchema = baseConfigSchema.extend({
|
|
7724
|
-
allowedRoles: z5.array(z5.string()).optional(),
|
|
7725
|
-
multiple: z5.boolean().optional()
|
|
7726
|
-
});
|
|
7727
|
-
var relationConfigSchema = baseConfigSchema.extend({
|
|
7728
|
-
targets: z5.array(relationTargetSchema).min(1),
|
|
7729
|
-
cardinality: z5.enum(["one", "many"]),
|
|
7730
|
-
minItems: z5.number().int().min(0).optional(),
|
|
7731
|
-
maxItems: z5.number().int().min(1).optional()
|
|
7732
|
-
});
|
|
7733
|
-
var ratingConfigSchema = baseConfigSchema.extend({
|
|
7734
|
-
max: z5.number().int().min(1).optional(),
|
|
7735
|
-
iconType: z5.enum(["star", "heart", "thumbs", "number"]).optional()
|
|
7736
|
-
});
|
|
7737
|
-
var formulaConfigSchema = baseConfigSchema.extend({
|
|
7738
|
-
expression: z5.string().min(1),
|
|
7739
|
-
returnType: z5.enum(["text", "number", "boolean", "date"]),
|
|
7740
|
-
decimals: z5.number().int().min(0).max(10).optional(),
|
|
7741
|
-
allowRelations: z5.boolean().optional()
|
|
7742
|
-
});
|
|
7743
|
-
var rollupConfigSchema = baseConfigSchema.extend({
|
|
7744
|
-
relationAttribute: z5.string().min(1).optional(),
|
|
7745
|
-
relationPath: z5.string().optional(),
|
|
7746
|
-
targetAttribute: z5.string().min(1),
|
|
7747
|
-
function: z5.enum([
|
|
7748
|
-
// Numeric aggregates
|
|
7749
|
-
"sum",
|
|
7750
|
-
"avg",
|
|
7751
|
-
// Date aggregates
|
|
7752
|
-
"earliest",
|
|
7753
|
-
"latest",
|
|
7754
|
-
// Count (universal)
|
|
7755
|
-
"count",
|
|
7756
|
-
"countValues",
|
|
7757
|
-
"countUniqueValues",
|
|
7758
|
-
"countEmpty",
|
|
7759
|
-
// Percent (universal)
|
|
7760
|
-
"percentEmpty",
|
|
7761
|
-
"percentNotEmpty",
|
|
7762
|
-
// Lookup (universal)
|
|
7763
|
-
"original"
|
|
7764
|
-
]),
|
|
7765
|
-
decimals: z5.number().int().min(0).max(10).optional(),
|
|
7766
|
-
targetAttributeType: z5.string().optional(),
|
|
7767
|
-
targetAttributeOptions: z5.array(
|
|
7768
|
-
z5.object({
|
|
7769
|
-
id: z5.string(),
|
|
7770
|
-
label: z5.string(),
|
|
7771
|
-
value: z5.string(),
|
|
7772
|
-
color: z5.string().optional(),
|
|
7773
|
-
icon: z5.string().optional(),
|
|
7774
|
-
description: z5.string().optional(),
|
|
7775
|
-
group: z5.enum(["idle", "in_progress", "finished"]).optional()
|
|
7776
|
-
})
|
|
7777
|
-
).optional()
|
|
7778
|
-
});
|
|
7779
|
-
var documentConfigSchema = baseConfigSchema.extend({
|
|
7780
|
-
templateId: z5.string().optional(),
|
|
7781
|
-
allowedTemplates: z5.array(z5.string()).optional(),
|
|
7782
|
-
multiple: z5.boolean().optional(),
|
|
7783
|
-
maxDocuments: z5.number().int().min(1).optional(),
|
|
7784
|
-
autoProcess: z5.boolean().optional()
|
|
7785
|
-
});
|
|
7786
|
-
var attributeConfigSchemas = {
|
|
7787
|
-
text: textConfigSchema,
|
|
7788
|
-
textarea: textareaConfigSchema,
|
|
7789
|
-
richtext: richtextConfigSchema,
|
|
7790
|
-
number: numberConfigSchema,
|
|
7791
|
-
checkbox: checkboxConfigSchema,
|
|
7792
|
-
date: dateConfigSchema,
|
|
7793
|
-
phone: phoneConfigSchema,
|
|
7794
|
-
currency: currencyConfigSchema,
|
|
7795
|
-
status: statusConfigSchema,
|
|
7796
|
-
location: locationConfigSchema,
|
|
7797
|
-
select: selectConfigSchema,
|
|
7798
|
-
multiselect: multiselectConfigSchema,
|
|
7799
|
-
file: fileConfigSchema,
|
|
7800
|
-
user: userConfigSchema,
|
|
7801
|
-
relation: relationConfigSchema,
|
|
7802
|
-
rating: ratingConfigSchema,
|
|
7803
|
-
formula: formulaConfigSchema,
|
|
7804
|
-
rollup: rollupConfigSchema,
|
|
7805
|
-
document: documentConfigSchema
|
|
7806
|
-
};
|
|
7807
|
-
function getAttributeConfigSchema(type) {
|
|
7808
|
-
return attributeConfigSchemas[type];
|
|
7809
|
-
}
|
|
7810
|
-
function validateAttributeConfig(type, config) {
|
|
7811
|
-
const schema = getAttributeConfigSchema(type);
|
|
7812
|
-
const result = schema.safeParse(config);
|
|
7813
|
-
if (result.success) {
|
|
7814
|
-
return { success: true, data: result.data };
|
|
7815
|
-
}
|
|
7816
|
-
return {
|
|
7817
|
-
success: false,
|
|
7818
|
-
errors: result.error.issues.map((err) => `${err.path.join(".")}: ${err.message}`)
|
|
7819
|
-
};
|
|
7820
|
-
}
|
|
7821
|
-
function parseAttributeConfig(type, config) {
|
|
7822
|
-
const schema = getAttributeConfigSchema(type);
|
|
7823
|
-
return schema.strip().parse(config);
|
|
7824
|
-
}
|
|
7825
|
-
function safeParseAttributeConfig(type, config) {
|
|
7826
|
-
const schema = getAttributeConfigSchema(type);
|
|
7827
|
-
const result = schema.strip().safeParse(config);
|
|
7828
|
-
return result.success ? result.data : void 0;
|
|
7829
|
-
}
|
|
7830
|
-
function createTextValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7831
|
-
let schema = z5.string();
|
|
7832
|
-
if (attr.minLength !== void 0) {
|
|
7833
|
-
schema = schema.min(attr.minLength, messages.minLength(attr, attr.minLength));
|
|
7834
|
-
}
|
|
7835
|
-
if (attr.maxLength !== void 0) {
|
|
7836
|
-
schema = schema.max(attr.maxLength, messages.maxLength(attr, attr.maxLength));
|
|
7837
|
-
}
|
|
7838
|
-
if (attr.pattern) {
|
|
7839
|
-
schema = schema.regex(getCachedRegex(attr.pattern), messages.invalidPattern(attr));
|
|
7840
|
-
}
|
|
7841
|
-
return schema;
|
|
7842
|
-
}
|
|
7843
|
-
function createNumberValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7844
|
-
let schema = z5.number();
|
|
7845
|
-
if (attr.min !== void 0) {
|
|
7846
|
-
schema = schema.min(attr.min, messages.minValue(attr, attr.min));
|
|
7847
|
-
}
|
|
7848
|
-
if (attr.max !== void 0) {
|
|
7849
|
-
schema = schema.max(attr.max, messages.maxValue(attr, attr.max));
|
|
7850
|
-
}
|
|
7851
|
-
if (attr.unit === "integer") {
|
|
7852
|
-
schema = schema.int(messages.mustBeInteger(attr));
|
|
7853
|
-
}
|
|
7854
|
-
return schema;
|
|
7855
|
-
}
|
|
7856
|
-
function createCheckboxValidator(_attr, _messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7857
|
-
return z5.boolean();
|
|
7858
|
-
}
|
|
7859
|
-
function createDateValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7860
|
-
return z5.coerce.date({ message: messages.invalidDate(attr) });
|
|
7861
|
-
}
|
|
7862
|
-
function createPhoneValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7863
|
-
return z5.object(
|
|
7864
|
-
{
|
|
7865
|
-
countryCode: z5.string().length(3),
|
|
7866
|
-
phoneNumber: z5.string().min(1)
|
|
7867
|
-
},
|
|
7868
|
-
{ message: messages.invalidPhone(attr) }
|
|
7869
|
-
);
|
|
7870
|
-
}
|
|
7871
|
-
function createCurrencyValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7872
|
-
return z5.object(
|
|
7873
|
-
{
|
|
7874
|
-
code: z5.string().length(3),
|
|
7875
|
-
value: z5.number().min(0)
|
|
7876
|
-
},
|
|
7877
|
-
{ message: messages.invalidCurrency(attr) }
|
|
7878
|
-
);
|
|
7879
|
-
}
|
|
7880
|
-
function createStatusValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7881
|
-
const validValues = attr.options.map((opt) => opt.value);
|
|
7882
|
-
return z5.enum(validValues, {
|
|
7883
|
-
message: messages.invalidOption(attr, validValues)
|
|
7884
|
-
});
|
|
7885
|
-
}
|
|
7886
|
-
function createSelectValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7887
|
-
const validValues = attr.options.map((opt) => opt.value);
|
|
7888
|
-
return z5.enum(validValues, {
|
|
7889
|
-
message: messages.invalidOption(attr, validValues)
|
|
7890
|
-
});
|
|
7891
|
-
}
|
|
7892
|
-
function createMultiselectValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7893
|
-
const validValues = attr.options.map((opt) => opt.value);
|
|
7894
|
-
return z5.array(
|
|
7895
|
-
z5.enum(validValues, {
|
|
7896
|
-
message: messages.invalidOption(attr, validValues)
|
|
7897
|
-
})
|
|
7898
|
-
);
|
|
7899
|
-
}
|
|
7900
|
-
function createLocationValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7901
|
-
return z5.object(
|
|
7902
|
-
{
|
|
7903
|
-
address: z5.string().optional(),
|
|
7904
|
-
address2: z5.string().optional(),
|
|
7905
|
-
city: z5.string().optional(),
|
|
7906
|
-
state: z5.string().optional(),
|
|
7907
|
-
postalCode: z5.string().optional(),
|
|
7908
|
-
country: z5.string().length(3).optional(),
|
|
7909
|
-
latitude: z5.number().optional(),
|
|
7910
|
-
longitude: z5.number().optional()
|
|
7911
|
-
},
|
|
7912
|
-
{ message: messages.invalidLocation(attr) }
|
|
7913
|
-
);
|
|
7914
|
-
}
|
|
7915
|
-
function createFileValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7916
|
-
const uuidSchema = z5.uuid({
|
|
7917
|
-
message: messages.invalidId(attr)
|
|
7918
|
-
});
|
|
7919
|
-
if (attr.multiple) {
|
|
7920
|
-
let arraySchema = z5.array(uuidSchema);
|
|
7921
|
-
if (attr.maxFiles) {
|
|
7922
|
-
arraySchema = arraySchema.max(attr.maxFiles, messages.maxItems(attr, attr.maxFiles));
|
|
7923
|
-
}
|
|
7924
|
-
return arraySchema;
|
|
7925
|
-
}
|
|
7926
|
-
return uuidSchema;
|
|
7927
|
-
}
|
|
7928
|
-
function createUserValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7929
|
-
const uuidSchema = z5.uuid({
|
|
7930
|
-
message: messages.invalidId(attr)
|
|
7931
|
-
});
|
|
7932
|
-
if (attr.multiple) {
|
|
7933
|
-
return z5.array(uuidSchema);
|
|
7934
|
-
}
|
|
7935
|
-
return uuidSchema;
|
|
7936
|
-
}
|
|
7937
|
-
function createSingleRelationValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7938
|
-
const uuidSchema = z5.uuid({
|
|
7939
|
-
message: messages.invalidId(attr)
|
|
7940
|
-
});
|
|
7941
|
-
return z5.union([uuidSchema, z5.null()]);
|
|
7942
|
-
}
|
|
7943
|
-
function createMultiRelationValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7944
|
-
const uuidSchema = z5.uuid({
|
|
7945
|
-
message: messages.invalidId(attr)
|
|
7946
|
-
});
|
|
7947
|
-
let arraySchema = z5.array(uuidSchema);
|
|
7948
|
-
if (attr.minItems !== void 0) {
|
|
7949
|
-
arraySchema = arraySchema.min(attr.minItems, messages.minItems(attr, attr.minItems));
|
|
7950
|
-
}
|
|
7951
|
-
if (attr.maxItems !== void 0) {
|
|
7952
|
-
arraySchema = arraySchema.max(attr.maxItems, messages.maxItems(attr, attr.maxItems));
|
|
7953
|
-
}
|
|
7954
|
-
return arraySchema;
|
|
7955
|
-
}
|
|
7956
|
-
function createRelationValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7957
|
-
if (attr.cardinality === "many") {
|
|
7958
|
-
return createMultiRelationValidator(attr, messages);
|
|
7959
|
-
}
|
|
7960
|
-
return createSingleRelationValidator(attr, messages);
|
|
7961
|
-
}
|
|
7962
|
-
function createRatingValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7963
|
-
let schema = z5.number().min(0);
|
|
7964
|
-
if (attr.max !== void 0) {
|
|
7965
|
-
schema = schema.max(attr.max, messages.maxValue(attr, attr.max));
|
|
7966
|
-
}
|
|
7967
|
-
return schema;
|
|
7968
|
-
}
|
|
7969
|
-
function createFormulaValidator(_attr, _messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7970
|
-
return z5.unknown();
|
|
7971
|
-
}
|
|
7972
|
-
function createRollupValidator(_attr, _messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7973
|
-
return z5.unknown();
|
|
7974
|
-
}
|
|
7975
|
-
function createTextAreaValidator(_attr, _messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7976
|
-
return z5.string();
|
|
7977
|
-
}
|
|
7978
|
-
function createRichtextValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7979
|
-
return z5.string({
|
|
7980
|
-
message: messages.invalidRichtext(attr)
|
|
7981
|
-
});
|
|
7982
|
-
}
|
|
7983
|
-
function createAttributeValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
7984
|
-
switch (attr.type) {
|
|
7985
|
-
case "text":
|
|
7986
|
-
return createTextValidator(attr, messages);
|
|
7987
|
-
case "textarea":
|
|
7988
|
-
return createTextAreaValidator(attr, messages);
|
|
7989
|
-
case "richtext":
|
|
7990
|
-
return createRichtextValidator(attr, messages);
|
|
7991
|
-
case "number":
|
|
7992
|
-
return createNumberValidator(attr, messages);
|
|
7993
|
-
case "checkbox":
|
|
7994
|
-
return createCheckboxValidator(attr, messages);
|
|
7995
|
-
case "date":
|
|
7996
|
-
return createDateValidator(attr, messages);
|
|
7997
|
-
case "phone":
|
|
7998
|
-
return createPhoneValidator(attr, messages);
|
|
7999
|
-
case "currency":
|
|
8000
|
-
return createCurrencyValidator(attr, messages);
|
|
8001
|
-
case "status":
|
|
8002
|
-
return createStatusValidator(attr, messages);
|
|
8003
|
-
case "location":
|
|
8004
|
-
return createLocationValidator(attr, messages);
|
|
8005
|
-
case "select":
|
|
8006
|
-
return createSelectValidator(attr, messages);
|
|
8007
|
-
case "multiselect":
|
|
8008
|
-
return createMultiselectValidator(attr, messages);
|
|
8009
|
-
case "file":
|
|
8010
|
-
return createFileValidator(attr, messages);
|
|
8011
|
-
case "user":
|
|
8012
|
-
return createUserValidator(attr, messages);
|
|
8013
|
-
case "relation":
|
|
8014
|
-
return createRelationValidator(attr, messages);
|
|
8015
|
-
case "rating":
|
|
8016
|
-
return createRatingValidator(attr, messages);
|
|
8017
|
-
case "formula":
|
|
8018
|
-
return createFormulaValidator(attr, messages);
|
|
8019
|
-
case "rollup":
|
|
8020
|
-
return createRollupValidator(attr, messages);
|
|
8021
|
-
default:
|
|
8022
|
-
return z5.unknown();
|
|
8023
|
-
}
|
|
8024
|
-
}
|
|
8025
|
-
function isEmptyValue(value) {
|
|
8026
|
-
if (value === null || value === void 0) return true;
|
|
8027
|
-
if (typeof value === "string" && value.trim() === "") return true;
|
|
8028
|
-
if (value instanceof Date) return false;
|
|
8029
|
-
if (typeof value === "object" && !Array.isArray(value)) {
|
|
8030
|
-
return Object.values(value).every(
|
|
8031
|
-
(v) => v === null || v === void 0 || typeof v === "string" && v.trim() === ""
|
|
8032
|
-
);
|
|
8033
|
-
}
|
|
8034
|
-
return false;
|
|
8035
|
-
}
|
|
8036
|
-
function withEmptyToNull(validator) {
|
|
8037
|
-
return z5.preprocess((val) => isEmptyValue(val) ? null : val, validator.nullish());
|
|
8038
|
-
}
|
|
8039
|
-
function createFormAttributeValidator(attr, messages = DEFAULT_VALIDATION_MESSAGES) {
|
|
8040
|
-
const validator = createAttributeValidator(attr, messages);
|
|
8041
|
-
if (!attr.required) {
|
|
8042
|
-
return withEmptyToNull(validator);
|
|
8043
|
-
}
|
|
8044
|
-
return validator;
|
|
8045
|
-
}
|
|
8046
|
-
function createObjectValidator(objectDef) {
|
|
8047
|
-
const shape = {};
|
|
8048
|
-
for (const attr of objectDef.attributes) {
|
|
8049
|
-
const validator = createAttributeValidator(attr);
|
|
8050
|
-
shape[attr.name] = attr.required ? validator : withEmptyToNull(validator);
|
|
8051
|
-
}
|
|
8052
|
-
return z5.object(shape).passthrough();
|
|
8053
|
-
}
|
|
8054
|
-
function validateAttribute(attr, value) {
|
|
8055
|
-
const validator = createAttributeValidator(attr);
|
|
8056
|
-
if (!attr.required && (value === void 0 || value === null)) {
|
|
8057
|
-
return { success: true, data: { [attr.name]: value } };
|
|
8058
|
-
}
|
|
8059
|
-
const result = validator.safeParse(value);
|
|
8060
|
-
if (result.success) {
|
|
8061
|
-
return {
|
|
8062
|
-
success: true,
|
|
8063
|
-
data: { [attr.name]: result.data }
|
|
8064
|
-
};
|
|
8065
|
-
}
|
|
8066
|
-
return {
|
|
8067
|
-
success: false,
|
|
8068
|
-
errors: result.error.issues.map((err) => ({
|
|
8069
|
-
path: [attr.name, ...err.path.map(String)],
|
|
8070
|
-
message: err.message
|
|
8071
|
-
}))
|
|
8072
|
-
};
|
|
8073
|
-
}
|
|
8074
|
-
function validateObject(objectDef, data) {
|
|
8075
|
-
const validator = createObjectValidator(objectDef);
|
|
8076
|
-
const result = validator.safeParse(data);
|
|
8077
|
-
if (result.success) {
|
|
8078
|
-
return {
|
|
8079
|
-
success: true,
|
|
8080
|
-
data: result.data
|
|
8081
|
-
};
|
|
8082
|
-
}
|
|
8083
|
-
return {
|
|
8084
|
-
success: false,
|
|
8085
|
-
errors: result.error.issues.map((err) => ({
|
|
8086
|
-
path: err.path.map(String),
|
|
8087
|
-
message: err.message
|
|
8088
|
-
}))
|
|
8089
|
-
};
|
|
8090
|
-
}
|
|
8091
|
-
function validateObjectOrThrow(objectDef, data) {
|
|
8092
|
-
const result = validateObject(objectDef, data);
|
|
8093
|
-
if (!result.success) {
|
|
8094
|
-
const errorMessages = result.errors?.map((err) => `${err.path.join(".")}: ${err.message}`).join("\n") || "Unknown validation error";
|
|
8095
|
-
throw new Error(`Validation failed for ${objectDef.label}:
|
|
8096
|
-
${errorMessages}`);
|
|
8097
|
-
}
|
|
8098
|
-
return result.data;
|
|
8099
|
-
}
|
|
8100
|
-
function createDraftValidator(objectDef) {
|
|
8101
|
-
const shape = {};
|
|
8102
|
-
for (const attr of objectDef.attributes) {
|
|
8103
|
-
const validator = createAttributeValidator(attr);
|
|
8104
|
-
shape[attr.name] = withEmptyToNull(validator);
|
|
8105
|
-
}
|
|
8106
|
-
return z5.object(shape).passthrough();
|
|
8107
|
-
}
|
|
8108
|
-
function validateDraft(objectDef, data) {
|
|
8109
|
-
const validator = createDraftValidator(objectDef);
|
|
8110
|
-
const result = validator.safeParse(data);
|
|
8111
|
-
if (result.success) {
|
|
8112
|
-
return {
|
|
8113
|
-
success: true,
|
|
8114
|
-
data: result.data
|
|
8115
|
-
};
|
|
8116
|
-
}
|
|
8117
|
-
return {
|
|
8118
|
-
success: false,
|
|
8119
|
-
errors: result.error.issues.map((err) => ({
|
|
8120
|
-
path: err.path.map(String),
|
|
8121
|
-
message: err.message
|
|
8122
|
-
}))
|
|
8123
|
-
};
|
|
8124
|
-
}
|
|
8125
|
-
function validateDraftOrThrow(objectDef, data) {
|
|
8126
|
-
const result = validateDraft(objectDef, data);
|
|
8127
|
-
if (!result.success) {
|
|
8128
|
-
const errorMessages = result.errors?.map((err) => `${err.path.join(".")}: ${err.message}`).join("\n") || "Unknown validation error";
|
|
8129
|
-
throw new Error(`Draft validation failed for ${objectDef.label}:
|
|
8130
|
-
${errorMessages}`);
|
|
8131
|
-
}
|
|
8132
|
-
return result.data;
|
|
8133
|
-
}
|
|
8134
|
-
function isValuePresent(value) {
|
|
8135
|
-
if (value === void 0 || value === null) {
|
|
8136
|
-
return false;
|
|
8137
|
-
}
|
|
8138
|
-
if (typeof value === "string" && value.trim() === "") {
|
|
8139
|
-
return false;
|
|
8140
|
-
}
|
|
8141
|
-
return true;
|
|
8142
|
-
}
|
|
8143
|
-
function getMissingRequiredAttributes(objectDef, data) {
|
|
8144
|
-
const missing = [];
|
|
8145
|
-
for (const attr of objectDef.attributes) {
|
|
8146
|
-
if (attr.required && !isValuePresent(data[attr.name])) {
|
|
8147
|
-
missing.push(attr);
|
|
8148
|
-
}
|
|
8121
|
+
disabled: false,
|
|
8122
|
+
// Editable via Documents tab
|
|
8123
|
+
system: true,
|
|
8124
|
+
hidden: true,
|
|
8125
|
+
// Hidden from regular form views
|
|
8126
|
+
multiple: true,
|
|
8127
|
+
icon: "paperclip",
|
|
8128
|
+
description: "Free-form document attachments"
|
|
8129
|
+
// No templateId = all templates allowed
|
|
8149
8130
|
}
|
|
8150
|
-
|
|
8131
|
+
};
|
|
8132
|
+
function getSystemAttributeList() {
|
|
8133
|
+
return Object.values(SYSTEM_ATTRIBUTES);
|
|
8151
8134
|
}
|
|
8152
|
-
function
|
|
8153
|
-
|
|
8154
|
-
if (missing.length > 0) {
|
|
8155
|
-
return false;
|
|
8156
|
-
}
|
|
8157
|
-
const validation = validateObject(objectDef, data);
|
|
8158
|
-
return validation.success;
|
|
8135
|
+
function isSystemAttribute(name) {
|
|
8136
|
+
return name in SYSTEM_ATTRIBUTES;
|
|
8159
8137
|
}
|
|
8160
|
-
function
|
|
8161
|
-
return
|
|
8138
|
+
function isSystemAttributeObject(attr) {
|
|
8139
|
+
return attr.system === true;
|
|
8162
8140
|
}
|
|
8163
8141
|
|
|
8164
8142
|
// src/runtime/services/audit/helpers.ts
|
|
@@ -8321,7 +8299,8 @@ var ObjectSchemaService = class extends BaseService {
|
|
|
8321
8299
|
}
|
|
8322
8300
|
/**
|
|
8323
8301
|
* Update an attribute.
|
|
8324
|
-
*
|
|
8302
|
+
* Custom attributes can be fully updated.
|
|
8303
|
+
* System attributes can only have presentation properties modified (label, description, placeholder, icon).
|
|
8325
8304
|
* Automatically uses tenant context from AsyncLocalStorage.
|
|
8326
8305
|
*
|
|
8327
8306
|
* @param attributeId - Attribute UUID
|
|
@@ -8333,20 +8312,34 @@ var ObjectSchemaService = class extends BaseService {
|
|
|
8333
8312
|
if (!dbAttr) {
|
|
8334
8313
|
throw new Error(`Attribute with id "${attributeId}" not found`);
|
|
8335
8314
|
}
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
);
|
|
8315
|
+
const requestedKeys = Object.keys(updates);
|
|
8316
|
+
const identityChanges = requestedKeys.filter(
|
|
8317
|
+
(k) => IDENTITY_PROPERTIES.includes(k)
|
|
8318
|
+
);
|
|
8319
|
+
if (identityChanges.length > 0) {
|
|
8320
|
+
const actualChanges = identityChanges.filter((k) => {
|
|
8321
|
+
const key = k;
|
|
8322
|
+
return updates[key] !== void 0 && updates[key] !== dbAttr[key];
|
|
8323
|
+
});
|
|
8324
|
+
if (actualChanges.length > 0) {
|
|
8325
|
+
throw new Error(
|
|
8326
|
+
`Cannot modify identity properties: ${actualChanges.join(", ")}. These properties cannot be changed after creation.`
|
|
8327
|
+
);
|
|
8328
|
+
}
|
|
8345
8329
|
}
|
|
8346
|
-
if (
|
|
8347
|
-
|
|
8348
|
-
|
|
8330
|
+
if (dbAttr.system) {
|
|
8331
|
+
const behaviorChanges = requestedKeys.filter(
|
|
8332
|
+
(k) => BEHAVIOR_PROPERTIES.includes(k)
|
|
8349
8333
|
);
|
|
8334
|
+
const actualBehaviorChanges = behaviorChanges.filter((k) => {
|
|
8335
|
+
const key = k;
|
|
8336
|
+
return updates[key] !== void 0;
|
|
8337
|
+
});
|
|
8338
|
+
if (actualBehaviorChanges.length > 0) {
|
|
8339
|
+
throw new Error(
|
|
8340
|
+
`Cannot modify behavior properties on system attribute "${dbAttr.name}": ${actualBehaviorChanges.join(", ")}. Only presentation properties (${PRESENTATION_PROPERTIES.join(", ")}) are editable.`
|
|
8341
|
+
);
|
|
8342
|
+
}
|
|
8350
8343
|
}
|
|
8351
8344
|
const mergedInput = {
|
|
8352
8345
|
name: dbAttr.name,
|
|
@@ -9657,7 +9650,7 @@ async function preloadSchemas(records, schemaService) {
|
|
|
9657
9650
|
if (records.length === 0) {
|
|
9658
9651
|
return schemasByObjectId;
|
|
9659
9652
|
}
|
|
9660
|
-
const uniqueObjectIds = [...
|
|
9653
|
+
const uniqueObjectIds = [...records.reduce((set, r) => set.add(r.objectId), /* @__PURE__ */ new Set())];
|
|
9661
9654
|
await Promise.all(
|
|
9662
9655
|
uniqueObjectIds.map(async (objId) => {
|
|
9663
9656
|
const schema = await schemaService.getObjectSchema(objId);
|
|
@@ -10304,8 +10297,10 @@ var RelationService = class extends BaseService {
|
|
|
10304
10297
|
const [attributeId, recordId] = c.split(":");
|
|
10305
10298
|
return { compositeId: c, attributeId, recordId };
|
|
10306
10299
|
});
|
|
10307
|
-
const uniqueRecordIds = [...
|
|
10308
|
-
const uniqueAttributeIds = [
|
|
10300
|
+
const uniqueRecordIds = [...parsed.reduce((set, p) => set.add(p.recordId), /* @__PURE__ */ new Set())];
|
|
10301
|
+
const uniqueAttributeIds = [
|
|
10302
|
+
...parsed.reduce((set, p) => set.add(p.attributeId), /* @__PURE__ */ new Set())
|
|
10303
|
+
];
|
|
10309
10304
|
const records = await this.recordResolver.findByIds(uniqueRecordIds);
|
|
10310
10305
|
if (records.length === 0) {
|
|
10311
10306
|
return [];
|
|
@@ -10314,7 +10309,7 @@ var RelationService = class extends BaseService {
|
|
|
10314
10309
|
const attributePromises = uniqueAttributeIds.map((id) => this.findAttributeById(id));
|
|
10315
10310
|
const attributes = await Promise.all(attributePromises);
|
|
10316
10311
|
const attributeMap = new Map(uniqueAttributeIds.map((id, i) => [id, attributes[i]]));
|
|
10317
|
-
const uniqueObjectIds = [...
|
|
10312
|
+
const uniqueObjectIds = [...records.reduce((set, r) => set.add(r.objectId), /* @__PURE__ */ new Set())];
|
|
10318
10313
|
const schemaPromises = uniqueObjectIds.map((id) => this.schemaService.getObjectSchema(id));
|
|
10319
10314
|
const schemas = await Promise.all(schemaPromises);
|
|
10320
10315
|
const schemaMap = new Map(uniqueObjectIds.map((id, i) => [id, schemas[i]]));
|
|
@@ -10567,7 +10562,10 @@ var RollupService = class extends BaseService {
|
|
|
10567
10562
|
return values.filter((v) => v != null && v !== "").length;
|
|
10568
10563
|
case "countUniqueValues": {
|
|
10569
10564
|
const nonEmpty = values.filter((v) => v != null && v !== "");
|
|
10570
|
-
return
|
|
10565
|
+
return nonEmpty.reduce(
|
|
10566
|
+
(set, v) => set.add(JSON.stringify(v)),
|
|
10567
|
+
/* @__PURE__ */ new Set()
|
|
10568
|
+
).size;
|
|
10571
10569
|
}
|
|
10572
10570
|
case "countEmpty":
|
|
10573
10571
|
return values.filter((v) => v == null || v === "").length;
|
|
@@ -12231,7 +12229,6 @@ var WorkflowAccessGrantService = class extends BaseService {
|
|
|
12231
12229
|
};
|
|
12232
12230
|
|
|
12233
12231
|
// src/runtime/services/workflow/instance.service.ts
|
|
12234
|
-
import { randomUUID } from "crypto";
|
|
12235
12232
|
var WorkflowInstanceService = class extends BaseService {
|
|
12236
12233
|
constructor(adapter, workflowService, options) {
|
|
12237
12234
|
super(adapter);
|
|
@@ -12497,7 +12494,7 @@ var WorkflowInstanceService = class extends BaseService {
|
|
|
12497
12494
|
updatedAt: /* @__PURE__ */ new Date()
|
|
12498
12495
|
};
|
|
12499
12496
|
}
|
|
12500
|
-
const executionId =
|
|
12497
|
+
const executionId = generateId();
|
|
12501
12498
|
let current = {
|
|
12502
12499
|
...instance,
|
|
12503
12500
|
context: {
|
|
@@ -13407,7 +13404,7 @@ var WorkflowService = class extends BaseService {
|
|
|
13407
13404
|
};
|
|
13408
13405
|
const validationResult = WorkflowDefinitionSchema.safeParse(definition);
|
|
13409
13406
|
if (!validationResult.success) {
|
|
13410
|
-
const errors = validationResult.error.
|
|
13407
|
+
const errors = formatZodErrors(validationResult.error).map((err) => err.message);
|
|
13411
13408
|
throw new SchemaError(
|
|
13412
13409
|
`Invalid workflow definition: ${errors.join(", ")}`,
|
|
13413
13410
|
SchemaErrorCode.VALIDATION_FAILED
|
|
@@ -13449,7 +13446,7 @@ var WorkflowService = class extends BaseService {
|
|
|
13449
13446
|
};
|
|
13450
13447
|
const validationResult = WorkflowDefinitionSchema.safeParse(updated);
|
|
13451
13448
|
if (!validationResult.success) {
|
|
13452
|
-
const errors = validationResult.error.
|
|
13449
|
+
const errors = formatZodErrors(validationResult.error).map((err) => err.message);
|
|
13453
13450
|
throw new SchemaError(
|
|
13454
13451
|
`Invalid workflow definition: ${errors.join(", ")}`,
|
|
13455
13452
|
SchemaErrorCode.VALIDATION_FAILED
|
|
@@ -13478,7 +13475,7 @@ var WorkflowService = class extends BaseService {
|
|
|
13478
13475
|
}
|
|
13479
13476
|
const validationResult = WorkflowDefinitionSchema.safeParse(existing);
|
|
13480
13477
|
if (!validationResult.success) {
|
|
13481
|
-
const errors = validationResult.error.
|
|
13478
|
+
const errors = formatZodErrors(validationResult.error).map((err) => err.message);
|
|
13482
13479
|
throw new SchemaError(
|
|
13483
13480
|
`Cannot publish invalid workflow: ${errors.join(", ")}`,
|
|
13484
13481
|
SchemaErrorCode.VALIDATION_FAILED
|
|
@@ -14563,7 +14560,7 @@ var DocumentService = class extends BaseService {
|
|
|
14563
14560
|
const template = await this.templateService.getTemplateOrThrow(document2.templateId);
|
|
14564
14561
|
const slots = await this.getSlots(documentId);
|
|
14565
14562
|
const requiredSlots = template.slots.filter((s) => s.required);
|
|
14566
|
-
const filledSlotNames =
|
|
14563
|
+
const filledSlotNames = slots.reduce((set, s) => set.add(s.slotName), /* @__PURE__ */ new Set());
|
|
14567
14564
|
const allRequiredFilled = requiredSlots.every((s) => filledSlotNames.has(s.name));
|
|
14568
14565
|
if (!allRequiredFilled) {
|
|
14569
14566
|
return await this.updateStatus(documentId, "draft");
|
|
@@ -16137,7 +16134,7 @@ var PermissionService = class extends BaseService {
|
|
|
16137
16134
|
DEFAULT_ROLE_PERMISSIONS
|
|
16138
16135
|
} = await import("./default-roles-42X3TJI5.mjs");
|
|
16139
16136
|
const existingRoles = await this.getRoles();
|
|
16140
|
-
const existingRoleNames =
|
|
16137
|
+
const existingRoleNames = existingRoles.reduce((set, r) => set.add(r.name), /* @__PURE__ */ new Set());
|
|
16141
16138
|
for (const roleName of Object.values(DEFAULT_ROLES)) {
|
|
16142
16139
|
if (existingRoleNames.has(roleName)) {
|
|
16143
16140
|
continue;
|
|
@@ -16172,124 +16169,340 @@ var PermissionService = class extends BaseService {
|
|
|
16172
16169
|
|
|
16173
16170
|
// src/runtime/services/view.service.ts
|
|
16174
16171
|
var ViewService = class extends BaseService {
|
|
16175
|
-
constructor(adapter
|
|
16172
|
+
constructor(adapter) {
|
|
16176
16173
|
super(adapter);
|
|
16177
|
-
this.nativeViews = nativeViews;
|
|
16178
16174
|
}
|
|
16179
16175
|
// ============================================================================
|
|
16180
16176
|
// CACHE MANAGEMENT
|
|
16181
16177
|
// ============================================================================
|
|
16182
|
-
/**
|
|
16183
|
-
* Invalidate cached views for an object.
|
|
16184
|
-
* Called automatically after view mutations.
|
|
16185
|
-
*/
|
|
16186
16178
|
async invalidateViewCache(objectName) {
|
|
16187
16179
|
await this.invalidateCache(cacheKeys.viewsByObject(this.tenantId, objectName));
|
|
16188
16180
|
}
|
|
16181
|
+
// ============================================================================
|
|
16182
|
+
// READ METHODS
|
|
16183
|
+
// ============================================================================
|
|
16189
16184
|
/**
|
|
16190
|
-
* Get all views for
|
|
16191
|
-
*
|
|
16185
|
+
* Get all views for the current tenant.
|
|
16186
|
+
* Optionally filter by view type.
|
|
16192
16187
|
*
|
|
16193
|
-
*
|
|
16188
|
+
* @param type - Optional view type filter
|
|
16189
|
+
* @returns All views for the tenant
|
|
16190
|
+
*/
|
|
16191
|
+
async getAllViews(type) {
|
|
16192
|
+
const dbViews = await this.adapter.views.findAllForTenant(type);
|
|
16193
|
+
return dbViews.map((v) => this.convertDBViewToDefinition(v));
|
|
16194
|
+
}
|
|
16195
|
+
/**
|
|
16196
|
+
* Get a specific view by its ID.
|
|
16197
|
+
* Returns null if not found.
|
|
16194
16198
|
*
|
|
16195
|
-
* @param
|
|
16196
|
-
* @returns
|
|
16199
|
+
* @param viewId - View ID (UUID)
|
|
16200
|
+
* @returns View definition or null
|
|
16197
16201
|
*/
|
|
16198
|
-
async
|
|
16199
|
-
|
|
16202
|
+
async getViewById(viewId) {
|
|
16203
|
+
const dbView = await this.adapter.views.findById(viewId);
|
|
16204
|
+
if (!dbView) {
|
|
16205
|
+
return null;
|
|
16206
|
+
}
|
|
16207
|
+
return this.convertDBViewToDefinition(dbView);
|
|
16200
16208
|
}
|
|
16201
16209
|
/**
|
|
16202
|
-
*
|
|
16210
|
+
* Get all views for an object from the database.
|
|
16211
|
+
* Optionally filter by type and merge with user overlays.
|
|
16212
|
+
*
|
|
16213
|
+
* @param objectName - Object name
|
|
16214
|
+
* @param options - Filter and overlay options
|
|
16215
|
+
* @returns Views from database
|
|
16203
16216
|
*/
|
|
16204
|
-
async
|
|
16205
|
-
const
|
|
16206
|
-
const dbViews = await this.adapter.views.findByObjectName(objectName);
|
|
16207
|
-
const
|
|
16208
|
-
|
|
16217
|
+
async getViews(objectName, options = {}) {
|
|
16218
|
+
const { type, userId } = options;
|
|
16219
|
+
const dbViews = await this.adapter.views.findByObjectName(objectName, type);
|
|
16220
|
+
const views = dbViews.map((v) => this.convertDBViewToDefinition(v));
|
|
16221
|
+
if (!userId) {
|
|
16222
|
+
return views;
|
|
16223
|
+
}
|
|
16224
|
+
const userOverlays = await this.adapter.viewOverlays.findByUser(userId);
|
|
16225
|
+
return views.map((view2) => {
|
|
16226
|
+
const overlay = userOverlays.find((o) => o.viewId === view2.id);
|
|
16227
|
+
return overlay ? this.applyOverlay(view2, overlay) : view2;
|
|
16228
|
+
});
|
|
16209
16229
|
}
|
|
16210
16230
|
/**
|
|
16211
16231
|
* Get a specific view by name.
|
|
16212
|
-
*
|
|
16232
|
+
* Returns null if not found (use getDefaultView for fallback behavior).
|
|
16213
16233
|
*
|
|
16214
16234
|
* @param objectName - Object name
|
|
16215
16235
|
* @param viewName - View name
|
|
16216
|
-
* @
|
|
16217
|
-
|
|
16218
|
-
|
|
16219
|
-
|
|
16220
|
-
|
|
16221
|
-
|
|
16236
|
+
* @param options - Type filter and overlay options
|
|
16237
|
+
* @returns View or null
|
|
16238
|
+
*/
|
|
16239
|
+
async getView(objectName, viewName, options = {}) {
|
|
16240
|
+
const { type, userId } = options;
|
|
16241
|
+
let dbView;
|
|
16242
|
+
if (type) {
|
|
16243
|
+
dbView = await this.adapter.views.findByNameAndType(objectName, viewName, type);
|
|
16244
|
+
} else {
|
|
16245
|
+
dbView = await this.adapter.views.findByName(objectName, viewName);
|
|
16246
|
+
}
|
|
16247
|
+
if (!dbView) {
|
|
16248
|
+
return null;
|
|
16222
16249
|
}
|
|
16223
|
-
const
|
|
16224
|
-
if (
|
|
16225
|
-
return
|
|
16250
|
+
const view2 = this.convertDBViewToDefinition(dbView);
|
|
16251
|
+
if (!userId) {
|
|
16252
|
+
return view2;
|
|
16226
16253
|
}
|
|
16227
|
-
|
|
16254
|
+
const overlay = await this.adapter.viewOverlays.findByViewAndUser(dbView.id, userId);
|
|
16255
|
+
return overlay ? this.applyOverlay(view2, overlay) : view2;
|
|
16228
16256
|
}
|
|
16229
16257
|
/**
|
|
16230
|
-
* Get the default view for an object
|
|
16258
|
+
* Get the default view for an object and type.
|
|
16259
|
+
* If no view exists in DB, auto-creates and persists a default view.
|
|
16231
16260
|
*
|
|
16232
16261
|
* Priority:
|
|
16233
|
-
* 1.
|
|
16234
|
-
* 2.
|
|
16235
|
-
* 3. First available view (
|
|
16262
|
+
* 1. User's preferred view (from overlay with isUserDefault=true)
|
|
16263
|
+
* 2. View marked as default in DB (with matching layout if specified)
|
|
16264
|
+
* 3. First available view (with matching layout if specified)
|
|
16265
|
+
* 4. Auto-created default view (persisted to DB)
|
|
16236
16266
|
*
|
|
16237
16267
|
* @param objectName - Object name
|
|
16238
|
-
* @param
|
|
16239
|
-
* @
|
|
16268
|
+
* @param type - View type
|
|
16269
|
+
* @param objectDefinition - Object definition (for default generation)
|
|
16270
|
+
* @param options - Optional filters (userId, layout for detail views)
|
|
16271
|
+
* @returns View definition (existing or auto-created)
|
|
16272
|
+
*/
|
|
16273
|
+
async getDefaultView(objectName, type, objectDefinition, options) {
|
|
16274
|
+
const { userId, layout } = options ?? {};
|
|
16275
|
+
const matchesLayout = (view2) => {
|
|
16276
|
+
if (!layout || type !== "detail") return true;
|
|
16277
|
+
const detailView2 = view2;
|
|
16278
|
+
return detailView2.config.layout === layout;
|
|
16279
|
+
};
|
|
16280
|
+
const applyUserOverlay = async (view2, viewId) => {
|
|
16281
|
+
if (!userId) return view2;
|
|
16282
|
+
const overlay = await this.adapter.viewOverlays.findByViewAndUser(viewId, userId);
|
|
16283
|
+
return overlay ? this.applyOverlay(view2, overlay) : view2;
|
|
16284
|
+
};
|
|
16285
|
+
if (userId) {
|
|
16286
|
+
const userDefaultOverlay = await this.adapter.viewOverlays.findUserDefault(
|
|
16287
|
+
userId,
|
|
16288
|
+
objectName,
|
|
16289
|
+
type
|
|
16290
|
+
);
|
|
16291
|
+
if (userDefaultOverlay) {
|
|
16292
|
+
const dbView = await this.adapter.views.findById(userDefaultOverlay.viewId);
|
|
16293
|
+
if (dbView && dbView.type === type) {
|
|
16294
|
+
const view2 = this.convertDBViewToDefinition(dbView);
|
|
16295
|
+
if (matchesLayout(view2)) {
|
|
16296
|
+
return this.applyOverlay(view2, userDefaultOverlay);
|
|
16297
|
+
}
|
|
16298
|
+
}
|
|
16299
|
+
}
|
|
16300
|
+
}
|
|
16301
|
+
const defaultDbView = await this.adapter.views.findDefault(objectName, type);
|
|
16302
|
+
if (defaultDbView) {
|
|
16303
|
+
const view2 = this.convertDBViewToDefinition(defaultDbView);
|
|
16304
|
+
if (matchesLayout(view2)) {
|
|
16305
|
+
return await applyUserOverlay(view2, defaultDbView.id);
|
|
16306
|
+
}
|
|
16307
|
+
}
|
|
16308
|
+
const dbViews = await this.adapter.views.findByObjectName(objectName, type);
|
|
16309
|
+
for (const dbView of dbViews) {
|
|
16310
|
+
const view2 = this.convertDBViewToDefinition(dbView);
|
|
16311
|
+
if (matchesLayout(view2)) {
|
|
16312
|
+
return await applyUserOverlay(view2, dbView.id);
|
|
16313
|
+
}
|
|
16314
|
+
}
|
|
16315
|
+
if (dbViews.length > 0) {
|
|
16316
|
+
const firstView = dbViews[0];
|
|
16317
|
+
const view2 = this.convertDBViewToDefinition(firstView);
|
|
16318
|
+
return await applyUserOverlay(view2, firstView.id);
|
|
16319
|
+
}
|
|
16320
|
+
const created = await this.ensureDefaultView(objectName, type, objectDefinition, layout);
|
|
16321
|
+
if (userId) {
|
|
16322
|
+
if (!created.id) {
|
|
16323
|
+
throw new Error("Created view missing ID - this should never happen");
|
|
16324
|
+
}
|
|
16325
|
+
const overlay = await this.adapter.viewOverlays.findByViewAndUser(created.id, userId);
|
|
16326
|
+
return overlay ? this.applyOverlay(created, overlay) : created;
|
|
16327
|
+
}
|
|
16328
|
+
return created;
|
|
16329
|
+
}
|
|
16330
|
+
// ============================================================================
|
|
16331
|
+
// DEFAULT VIEW GENERATION
|
|
16332
|
+
// ============================================================================
|
|
16333
|
+
/**
|
|
16334
|
+
* Ensure a default view exists in DB for the given object and type.
|
|
16335
|
+
* If no view exists, generates and persists one.
|
|
16336
|
+
* Idempotent — safe to call concurrently (uses upsert).
|
|
16240
16337
|
*/
|
|
16241
|
-
async
|
|
16242
|
-
const
|
|
16243
|
-
const
|
|
16244
|
-
const
|
|
16245
|
-
|
|
16246
|
-
|
|
16247
|
-
|
|
16248
|
-
|
|
16338
|
+
async ensureDefaultView(objectName, type, objectDefinition, layout) {
|
|
16339
|
+
const generated = this.generateDefaultViewConfig(objectName, type, objectDefinition, layout);
|
|
16340
|
+
const viewName = type === "detail" && layout === "modal" ? "default-modal" : "default";
|
|
16341
|
+
const dbView = await this.adapter.views.upsert({
|
|
16342
|
+
objectName,
|
|
16343
|
+
type,
|
|
16344
|
+
name: viewName,
|
|
16345
|
+
label: generated.label,
|
|
16346
|
+
config: generated.config,
|
|
16347
|
+
default: true
|
|
16348
|
+
});
|
|
16349
|
+
const legacyFallbackId = `fallback:${objectName}:${type}`;
|
|
16350
|
+
await this.adapter.viewOverlays.migrateViewId(legacyFallbackId, dbView.id);
|
|
16351
|
+
await this.invalidateViewCache(objectName);
|
|
16352
|
+
return this.convertDBViewToDefinition(dbView);
|
|
16249
16353
|
}
|
|
16250
16354
|
/**
|
|
16251
|
-
*
|
|
16252
|
-
*
|
|
16355
|
+
* Generate default view config for an object.
|
|
16356
|
+
* Used by ensureDefaultView() and resetViewToDefault().
|
|
16357
|
+
*/
|
|
16358
|
+
generateDefaultViewConfig(objectName, type, objectDefinition, layout) {
|
|
16359
|
+
switch (type) {
|
|
16360
|
+
case "detail":
|
|
16361
|
+
return this.generateDefaultDetailConfig(objectName, objectDefinition, layout);
|
|
16362
|
+
case "list":
|
|
16363
|
+
return this.generateDefaultListConfig(objectName, objectDefinition);
|
|
16364
|
+
default:
|
|
16365
|
+
throw new SchemaError(
|
|
16366
|
+
`Default view generation not supported for type: ${type}`,
|
|
16367
|
+
SchemaErrorCode.VALIDATION_FAILED,
|
|
16368
|
+
{ type }
|
|
16369
|
+
);
|
|
16370
|
+
}
|
|
16371
|
+
}
|
|
16372
|
+
generateDefaultDetailConfig(objectName, object2, layout = "page") {
|
|
16373
|
+
const visibleAttrs = object2.attributes.filter(
|
|
16374
|
+
(attr) => !(attr.hidden || attr.archived || attr.system)
|
|
16375
|
+
);
|
|
16376
|
+
const fields = visibleAttrs.sort((a, b) => (a.order ?? 999) - (b.order ?? 999)).map((attr) => ({
|
|
16377
|
+
attribute: attr.name,
|
|
16378
|
+
span: this.getDefaultFieldSpan(attr.type)
|
|
16379
|
+
}));
|
|
16380
|
+
const formTab = {
|
|
16381
|
+
id: "form",
|
|
16382
|
+
name: "form",
|
|
16383
|
+
label: "Details",
|
|
16384
|
+
type: "form",
|
|
16385
|
+
groups: [
|
|
16386
|
+
{
|
|
16387
|
+
id: "general",
|
|
16388
|
+
label: "General",
|
|
16389
|
+
fields,
|
|
16390
|
+
order: 0
|
|
16391
|
+
}
|
|
16392
|
+
],
|
|
16393
|
+
order: 0
|
|
16394
|
+
};
|
|
16395
|
+
const tabs = [formTab];
|
|
16396
|
+
tabs.push({
|
|
16397
|
+
id: "activity",
|
|
16398
|
+
name: "activity",
|
|
16399
|
+
label: "Activity",
|
|
16400
|
+
type: "activity",
|
|
16401
|
+
order: 1
|
|
16402
|
+
});
|
|
16403
|
+
tabs.push({
|
|
16404
|
+
id: "notes",
|
|
16405
|
+
name: "notes",
|
|
16406
|
+
label: "Notes",
|
|
16407
|
+
type: "notes",
|
|
16408
|
+
order: 2,
|
|
16409
|
+
allowCreate: true
|
|
16410
|
+
});
|
|
16411
|
+
const hasDocuments = object2.attributes.some((attr) => attr.type === "document");
|
|
16412
|
+
if (hasDocuments) {
|
|
16413
|
+
tabs.push({
|
|
16414
|
+
id: "documents",
|
|
16415
|
+
name: "documents",
|
|
16416
|
+
label: "Documents",
|
|
16417
|
+
type: "documents",
|
|
16418
|
+
order: 3,
|
|
16419
|
+
allowUpload: true,
|
|
16420
|
+
allowRemove: true,
|
|
16421
|
+
showProcessing: true
|
|
16422
|
+
});
|
|
16423
|
+
}
|
|
16424
|
+
const config = {
|
|
16425
|
+
layout,
|
|
16426
|
+
tabs
|
|
16427
|
+
};
|
|
16428
|
+
return {
|
|
16429
|
+
name: "default",
|
|
16430
|
+
label: "Default View",
|
|
16431
|
+
object: objectName,
|
|
16432
|
+
type: "detail",
|
|
16433
|
+
config,
|
|
16434
|
+
default: true
|
|
16435
|
+
};
|
|
16436
|
+
}
|
|
16437
|
+
generateDefaultListConfig(objectName, object2) {
|
|
16438
|
+
const visibleAttrs = object2.attributes.filter(
|
|
16439
|
+
(attr) => !(attr.hidden || attr.archived || attr.system)
|
|
16440
|
+
);
|
|
16441
|
+
const columns = visibleAttrs.sort((a, b) => (a.order ?? 999) - (b.order ?? 999)).slice(0, 5).map((attr) => attr.name);
|
|
16442
|
+
const config = {
|
|
16443
|
+
layout: "table",
|
|
16444
|
+
columns
|
|
16445
|
+
};
|
|
16446
|
+
return {
|
|
16447
|
+
name: "default",
|
|
16448
|
+
label: "All Records",
|
|
16449
|
+
object: objectName,
|
|
16450
|
+
type: "list",
|
|
16451
|
+
config,
|
|
16452
|
+
default: true
|
|
16453
|
+
};
|
|
16454
|
+
}
|
|
16455
|
+
getDefaultFieldSpan(type) {
|
|
16456
|
+
switch (type) {
|
|
16457
|
+
case "textarea":
|
|
16458
|
+
case "richtext":
|
|
16459
|
+
case "location":
|
|
16460
|
+
return 12;
|
|
16461
|
+
case "checkbox":
|
|
16462
|
+
return 4;
|
|
16463
|
+
default:
|
|
16464
|
+
return 6;
|
|
16465
|
+
}
|
|
16466
|
+
}
|
|
16467
|
+
// ============================================================================
|
|
16468
|
+
// WRITE METHODS (Architect Mode)
|
|
16469
|
+
// ============================================================================
|
|
16470
|
+
/**
|
|
16471
|
+
* Create a new view (Architect Mode).
|
|
16253
16472
|
*
|
|
16254
16473
|
* @param input - View definition
|
|
16255
16474
|
* @returns Created view
|
|
16256
16475
|
*/
|
|
16257
16476
|
async createView(input) {
|
|
16258
16477
|
this.validateViewName(input.name);
|
|
16259
|
-
this.
|
|
16260
|
-
|
|
16478
|
+
const existing = await this.adapter.views.findByNameAndType(
|
|
16479
|
+
input.objectName,
|
|
16480
|
+
input.name,
|
|
16481
|
+
input.type
|
|
16482
|
+
);
|
|
16261
16483
|
if (existing) {
|
|
16262
16484
|
throw new SchemaError(
|
|
16263
|
-
`View "${input.name}" already exists for object "${input.objectName}"`,
|
|
16264
|
-
SchemaErrorCode.DUPLICATE_ATTRIBUTE,
|
|
16265
|
-
{ viewName: input.name, objectName: input.objectName }
|
|
16266
|
-
);
|
|
16267
|
-
}
|
|
16268
|
-
if (this.nativeViews.has(input.objectName, input.name)) {
|
|
16269
|
-
throw new SchemaError(
|
|
16270
|
-
`Cannot create view "${input.name}": a system view with this name already exists`,
|
|
16485
|
+
`View "${input.name}" of type "${input.type}" already exists for object "${input.objectName}"`,
|
|
16271
16486
|
SchemaErrorCode.DUPLICATE_ATTRIBUTE,
|
|
16272
|
-
{ viewName: input.name,
|
|
16487
|
+
{ viewName: input.name, type: input.type, objectName: input.objectName }
|
|
16273
16488
|
);
|
|
16274
16489
|
}
|
|
16275
16490
|
const dbView = await this.adapter.views.create({
|
|
16276
16491
|
objectName: input.objectName,
|
|
16492
|
+
type: input.type,
|
|
16277
16493
|
name: input.name,
|
|
16278
16494
|
label: input.label,
|
|
16279
16495
|
description: input.description,
|
|
16280
16496
|
icon: input.icon,
|
|
16281
|
-
|
|
16282
|
-
|
|
16283
|
-
default: input.default ?? false,
|
|
16284
|
-
system: false,
|
|
16285
|
-
// Custom views are never system
|
|
16497
|
+
config: input.config,
|
|
16498
|
+
default: input.default,
|
|
16286
16499
|
metadata: input.metadata
|
|
16287
16500
|
});
|
|
16288
16501
|
await this.invalidateViewCache(input.objectName);
|
|
16289
16502
|
return this.convertDBViewToDefinition(dbView);
|
|
16290
16503
|
}
|
|
16291
16504
|
/**
|
|
16292
|
-
* Update
|
|
16505
|
+
* Update an existing view.
|
|
16293
16506
|
*
|
|
16294
16507
|
* @param viewId - View ID
|
|
16295
16508
|
* @param input - Update data
|
|
@@ -16300,18 +16513,11 @@ var ViewService = class extends BaseService {
|
|
|
16300
16513
|
if (!dbView) {
|
|
16301
16514
|
throw new NotFoundError("View", viewId);
|
|
16302
16515
|
}
|
|
16303
|
-
if (dbView.system) {
|
|
16304
|
-
throw new ProtectedResourceError("view", dbView.name, "modify");
|
|
16305
|
-
}
|
|
16306
|
-
const effectiveLayout = input.layout ?? dbView.layout;
|
|
16307
|
-
const effectiveTabs = input.tabs ?? dbView.tabs;
|
|
16308
|
-
this.validateModalLayout(effectiveTabs, effectiveLayout);
|
|
16309
16516
|
const updated = await this.adapter.views.update(viewId, {
|
|
16310
16517
|
label: input.label,
|
|
16311
16518
|
description: input.description,
|
|
16312
16519
|
icon: input.icon,
|
|
16313
|
-
|
|
16314
|
-
tabs: input.tabs,
|
|
16520
|
+
config: input.config,
|
|
16315
16521
|
default: input.default,
|
|
16316
16522
|
metadata: input.metadata
|
|
16317
16523
|
});
|
|
@@ -16319,7 +16525,8 @@ var ViewService = class extends BaseService {
|
|
|
16319
16525
|
return this.convertDBViewToDefinition(updated);
|
|
16320
16526
|
}
|
|
16321
16527
|
/**
|
|
16322
|
-
* Delete a
|
|
16528
|
+
* Delete a view.
|
|
16529
|
+
* Overlays are automatically deleted (cascade).
|
|
16323
16530
|
*
|
|
16324
16531
|
* @param viewId - View ID
|
|
16325
16532
|
*/
|
|
@@ -16328,16 +16535,12 @@ var ViewService = class extends BaseService {
|
|
|
16328
16535
|
if (!dbView) {
|
|
16329
16536
|
throw new NotFoundError("View", viewId);
|
|
16330
16537
|
}
|
|
16331
|
-
|
|
16332
|
-
throw new ProtectedResourceError("view", dbView.name, "delete");
|
|
16333
|
-
}
|
|
16538
|
+
await this.adapter.viewOverlays.deleteByView(viewId);
|
|
16334
16539
|
await this.adapter.views.delete(viewId);
|
|
16335
16540
|
await this.invalidateViewCache(dbView.objectName);
|
|
16336
16541
|
}
|
|
16337
16542
|
/**
|
|
16338
|
-
* Set a view as default for its object and
|
|
16339
|
-
* Only unsets other defaults for the same layout.
|
|
16340
|
-
* Automatically uses tenant context from AsyncLocalStorage.
|
|
16543
|
+
* Set a view as default for its object and type.
|
|
16341
16544
|
*
|
|
16342
16545
|
* @param viewId - View ID
|
|
16343
16546
|
* @returns Updated view
|
|
@@ -16347,11 +16550,9 @@ var ViewService = class extends BaseService {
|
|
|
16347
16550
|
if (!dbView) {
|
|
16348
16551
|
throw new NotFoundError("View", viewId);
|
|
16349
16552
|
}
|
|
16350
|
-
const
|
|
16351
|
-
const currentViews = await this.adapter.views.findByObjectName(dbView.objectName);
|
|
16553
|
+
const currentViews = await this.adapter.views.findByObjectName(dbView.objectName, dbView.type);
|
|
16352
16554
|
for (const v of currentViews) {
|
|
16353
|
-
|
|
16354
|
-
if (v.default && v.id !== viewId && !v.system && vLayout === viewLayout) {
|
|
16555
|
+
if (v.default && v.id !== viewId) {
|
|
16355
16556
|
await this.adapter.views.update(v.id, { default: false });
|
|
16356
16557
|
}
|
|
16357
16558
|
}
|
|
@@ -16359,12 +16560,140 @@ var ViewService = class extends BaseService {
|
|
|
16359
16560
|
await this.invalidateViewCache(dbView.objectName);
|
|
16360
16561
|
return this.convertDBViewToDefinition(updated);
|
|
16361
16562
|
}
|
|
16563
|
+
/**
|
|
16564
|
+
* Reset a view to its default (auto-generated) state.
|
|
16565
|
+
* Regenerates the view config based on the object definition.
|
|
16566
|
+
*
|
|
16567
|
+
* @param viewId - View ID
|
|
16568
|
+
* @param objectDefinition - Object definition for regeneration
|
|
16569
|
+
* @returns Updated view
|
|
16570
|
+
*/
|
|
16571
|
+
async resetViewToDefault(viewId, objectDefinition) {
|
|
16572
|
+
const dbView = await this.adapter.views.findById(viewId);
|
|
16573
|
+
if (!dbView) {
|
|
16574
|
+
throw new NotFoundError("View", viewId);
|
|
16575
|
+
}
|
|
16576
|
+
const generated = this.generateDefaultViewConfig(
|
|
16577
|
+
dbView.objectName,
|
|
16578
|
+
dbView.type,
|
|
16579
|
+
objectDefinition,
|
|
16580
|
+
dbView.type === "detail" ? dbView.config?.layout ?? "page" : void 0
|
|
16581
|
+
);
|
|
16582
|
+
const newConfig = generated.config;
|
|
16583
|
+
const updated = await this.adapter.views.update(viewId, { config: newConfig });
|
|
16584
|
+
await this.invalidateViewCache(dbView.objectName);
|
|
16585
|
+
return this.convertDBViewToDefinition(updated);
|
|
16586
|
+
}
|
|
16362
16587
|
// ============================================================================
|
|
16363
|
-
//
|
|
16588
|
+
// USER CUSTOMIZATION METHODS
|
|
16364
16589
|
// ============================================================================
|
|
16365
16590
|
/**
|
|
16366
|
-
*
|
|
16591
|
+
* Reset user customizations for a view.
|
|
16592
|
+
* Deletes the overlay, returning to source/fallback view.
|
|
16593
|
+
*
|
|
16594
|
+
* @param viewId - View ID (can be UUID or fallback ID)
|
|
16595
|
+
* @param userId - User ID
|
|
16596
|
+
*/
|
|
16597
|
+
async resetUserCustomizations(viewId, userId) {
|
|
16598
|
+
await this.adapter.viewOverlays.deleteByViewAndUser(viewId, userId);
|
|
16599
|
+
}
|
|
16600
|
+
/**
|
|
16601
|
+
* Set a view as the user's default for an object and type.
|
|
16602
|
+
*
|
|
16603
|
+
* @param viewId - View ID (can be UUID or fallback ID)
|
|
16604
|
+
* @param userId - User ID
|
|
16605
|
+
* @param objectName - Object name
|
|
16606
|
+
* @param type - View type
|
|
16607
|
+
*/
|
|
16608
|
+
async setUserDefaultView(viewId, userId, objectName, type) {
|
|
16609
|
+
await this.adapter.viewOverlays.clearUserDefault(userId, objectName, type);
|
|
16610
|
+
const existingOverlay = await this.adapter.viewOverlays.findByViewAndUser(viewId, userId);
|
|
16611
|
+
if (existingOverlay) {
|
|
16612
|
+
await this.adapter.viewOverlays.update(existingOverlay.id, { isUserDefault: true });
|
|
16613
|
+
} else {
|
|
16614
|
+
await this.adapter.viewOverlays.create({
|
|
16615
|
+
viewId,
|
|
16616
|
+
userId,
|
|
16617
|
+
configOverrides: {},
|
|
16618
|
+
isUserDefault: true
|
|
16619
|
+
});
|
|
16620
|
+
}
|
|
16621
|
+
}
|
|
16622
|
+
/**
|
|
16623
|
+
* Check if a user has customized a view.
|
|
16624
|
+
*
|
|
16625
|
+
* @param viewId - View ID
|
|
16626
|
+
* @param userId - User ID
|
|
16627
|
+
* @returns True if overlay exists
|
|
16628
|
+
*/
|
|
16629
|
+
async hasUserCustomizations(viewId, userId) {
|
|
16630
|
+
const overlay = await this.adapter.viewOverlays.findByViewAndUser(viewId, userId);
|
|
16631
|
+
return overlay !== null && hasProperties(overlay.configOverrides);
|
|
16632
|
+
}
|
|
16633
|
+
// ============================================================================
|
|
16634
|
+
// OVERLAY MERGE LOGIC
|
|
16635
|
+
// ============================================================================
|
|
16636
|
+
/**
|
|
16637
|
+
* Apply an overlay to a view definition.
|
|
16638
|
+
* Implements merge semantics defined in the plan.
|
|
16639
|
+
*
|
|
16640
|
+
* @param view - Source view definition
|
|
16641
|
+
* @param overlay - User overlay
|
|
16642
|
+
* @returns Merged view definition
|
|
16643
|
+
*/
|
|
16644
|
+
applyOverlay(view2, overlay) {
|
|
16645
|
+
const overrides = overlay.configOverrides;
|
|
16646
|
+
switch (view2.type) {
|
|
16647
|
+
case "list":
|
|
16648
|
+
return this.applyListViewOverlay(view2, overrides);
|
|
16649
|
+
case "detail":
|
|
16650
|
+
return this.applyDetailViewOverlay(view2, overrides);
|
|
16651
|
+
default:
|
|
16652
|
+
return view2;
|
|
16653
|
+
}
|
|
16654
|
+
}
|
|
16655
|
+
applyListViewOverlay(view2, overrides) {
|
|
16656
|
+
const mergedConfig = {
|
|
16657
|
+
...view2.config,
|
|
16658
|
+
tabs: this.mergeViewTabs(view2.config.tabs, overrides.tabs, overrides.hiddenTabIds)
|
|
16659
|
+
};
|
|
16660
|
+
return { ...view2, config: mergedConfig };
|
|
16661
|
+
}
|
|
16662
|
+
applyDetailViewOverlay(view2, overrides) {
|
|
16663
|
+
const mergedConfig = {
|
|
16664
|
+
...view2.config,
|
|
16665
|
+
// Append for tabs (detail tabs)
|
|
16666
|
+
tabs: this.mergeDetailTabs(
|
|
16667
|
+
view2.config.tabs,
|
|
16668
|
+
overrides.detailTabs,
|
|
16669
|
+
overrides.hiddenDetailTabIds
|
|
16670
|
+
)
|
|
16671
|
+
};
|
|
16672
|
+
return {
|
|
16673
|
+
...view2,
|
|
16674
|
+
config: mergedConfig
|
|
16675
|
+
};
|
|
16676
|
+
}
|
|
16677
|
+
/**
|
|
16678
|
+
* Merge source tabs with overlay tabs.
|
|
16679
|
+
* - Source tabs are visible to all
|
|
16680
|
+
* - Overlay tabs are appended (user-private)
|
|
16681
|
+
* - hiddenTabIds allows hiding source tabs
|
|
16682
|
+
*/
|
|
16683
|
+
mergeViewTabs(sourceTabs = [], overlayTabs = [], hiddenTabIds = []) {
|
|
16684
|
+
const visibleSourceTabs = sourceTabs.filter((t) => !hiddenTabIds.includes(t.id));
|
|
16685
|
+
return [...visibleSourceTabs, ...overlayTabs];
|
|
16686
|
+
}
|
|
16687
|
+
/**
|
|
16688
|
+
* Merge detail tabs (form, activity, etc.)
|
|
16367
16689
|
*/
|
|
16690
|
+
mergeDetailTabs(sourceTabs = [], overlayTabs = [], hiddenTabIds = []) {
|
|
16691
|
+
const visibleSourceTabs = sourceTabs.filter((t) => !hiddenTabIds.includes(t.id));
|
|
16692
|
+
return [...visibleSourceTabs, ...overlayTabs];
|
|
16693
|
+
}
|
|
16694
|
+
// ============================================================================
|
|
16695
|
+
// PRIVATE HELPERS
|
|
16696
|
+
// ============================================================================
|
|
16368
16697
|
validateViewName(name) {
|
|
16369
16698
|
if (!name || name.length === 0) {
|
|
16370
16699
|
throw new ValidationError("View name cannot be empty", [
|
|
@@ -16386,66 +16715,72 @@ var ViewService = class extends BaseService {
|
|
|
16386
16715
|
]);
|
|
16387
16716
|
}
|
|
16388
16717
|
}
|
|
16389
|
-
/**
|
|
16390
|
-
* Validate modal layout constraints.
|
|
16391
|
-
* Modal views must have exactly one form tab.
|
|
16392
|
-
*/
|
|
16393
|
-
validateModalLayout(tabs, layout) {
|
|
16394
|
-
if (layout !== "modal") return;
|
|
16395
|
-
if (!tabs || tabs.length === 0) {
|
|
16396
|
-
throw new ValidationError("Modal views must have exactly one form tab", [
|
|
16397
|
-
{ path: ["tabs"], message: "Modal views must have exactly one form tab" }
|
|
16398
|
-
]);
|
|
16399
|
-
}
|
|
16400
|
-
if (tabs.length > 1) {
|
|
16401
|
-
throw new ValidationError("Modal views can only have one tab", [
|
|
16402
|
-
{ path: ["tabs"], message: "Modal views can only have one tab" }
|
|
16403
|
-
]);
|
|
16404
|
-
}
|
|
16405
|
-
if (tabs[0].type !== "form") {
|
|
16406
|
-
throw new ValidationError("Modal views must have a form tab", [
|
|
16407
|
-
{ path: ["tabs"], message: `Modal views must have a form tab, not a ${tabs[0].type}` }
|
|
16408
|
-
]);
|
|
16409
|
-
}
|
|
16410
|
-
}
|
|
16411
16718
|
/**
|
|
16412
16719
|
* Convert database view to ViewDefinition
|
|
16413
16720
|
*/
|
|
16414
16721
|
convertDBViewToDefinition(dbView) {
|
|
16415
|
-
|
|
16722
|
+
const base = {
|
|
16416
16723
|
id: dbView.id,
|
|
16417
16724
|
name: dbView.name,
|
|
16418
16725
|
label: dbView.label,
|
|
16419
16726
|
description: dbView.description,
|
|
16420
16727
|
icon: dbView.icon,
|
|
16421
16728
|
object: dbView.objectName,
|
|
16422
|
-
layout: dbView.layout,
|
|
16423
|
-
tabs: dbView.tabs,
|
|
16424
16729
|
default: dbView.default,
|
|
16425
|
-
system: dbView.system,
|
|
16426
16730
|
metadata: dbView.metadata
|
|
16427
16731
|
};
|
|
16732
|
+
switch (dbView.type) {
|
|
16733
|
+
case "detail": {
|
|
16734
|
+
const detailView2 = {
|
|
16735
|
+
...base,
|
|
16736
|
+
type: "detail",
|
|
16737
|
+
config: dbView.config
|
|
16738
|
+
};
|
|
16739
|
+
return detailView2;
|
|
16740
|
+
}
|
|
16741
|
+
case "list": {
|
|
16742
|
+
const listView2 = {
|
|
16743
|
+
...base,
|
|
16744
|
+
type: "list",
|
|
16745
|
+
config: dbView.config
|
|
16746
|
+
};
|
|
16747
|
+
return listView2;
|
|
16748
|
+
}
|
|
16749
|
+
// TODO: Add proper config types for calendar, timeline, gallery when implemented
|
|
16750
|
+
case "calendar":
|
|
16751
|
+
case "timeline":
|
|
16752
|
+
case "gallery":
|
|
16753
|
+
return { ...base, type: dbView.type, config: dbView.config };
|
|
16754
|
+
default:
|
|
16755
|
+
throw new SchemaError(
|
|
16756
|
+
`Unknown view type: ${dbView.type}`,
|
|
16757
|
+
SchemaErrorCode.VALIDATION_FAILED,
|
|
16758
|
+
{ type: dbView.type }
|
|
16759
|
+
);
|
|
16760
|
+
}
|
|
16428
16761
|
}
|
|
16429
16762
|
};
|
|
16430
16763
|
|
|
16431
16764
|
// src/runtime/view-sync.ts
|
|
16432
|
-
async function
|
|
16765
|
+
async function seedRegistryViews(adapter, registry2, options = {}) {
|
|
16433
16766
|
const result = {
|
|
16434
16767
|
success: true,
|
|
16435
16768
|
viewsSynced: 0,
|
|
16436
16769
|
viewsCreated: 0,
|
|
16437
|
-
|
|
16770
|
+
viewsSkipped: 0,
|
|
16438
16771
|
viewsDeleted: 0,
|
|
16439
16772
|
errors: []
|
|
16440
16773
|
};
|
|
16441
|
-
const
|
|
16774
|
+
const registryViews = registry2.getAll();
|
|
16442
16775
|
if (options.verbose && options.logger) {
|
|
16443
|
-
options.logger.info(`[ViewSync] Starting
|
|
16776
|
+
options.logger.info(`[ViewSync] Starting seed for ${registryViews.length} registry views...`);
|
|
16444
16777
|
}
|
|
16445
16778
|
try {
|
|
16446
16779
|
await adapter.transaction(async (tx) => {
|
|
16447
|
-
const
|
|
16448
|
-
|
|
16780
|
+
const viewsByObjectAndType = await seedAllViews(tx, registryViews, result, options);
|
|
16781
|
+
if (options.deleteOrphans) {
|
|
16782
|
+
await cleanupOrphanViews(tx, viewsByObjectAndType, result, options);
|
|
16783
|
+
}
|
|
16449
16784
|
});
|
|
16450
16785
|
} catch (error2) {
|
|
16451
16786
|
handleTransactionError(result, error2);
|
|
@@ -16453,38 +16788,78 @@ async function syncNativeViews(adapter, nativeViewRegistry, options = {}) {
|
|
|
16453
16788
|
logSyncComplete(result, options);
|
|
16454
16789
|
return result;
|
|
16455
16790
|
}
|
|
16456
|
-
|
|
16457
|
-
|
|
16458
|
-
|
|
16791
|
+
var syncNativeViews = seedRegistryViews;
|
|
16792
|
+
async function seedAllViews(tx, registryViews, result, options) {
|
|
16793
|
+
const viewsByObjectAndType = /* @__PURE__ */ new Map();
|
|
16794
|
+
for (const view2 of registryViews) {
|
|
16459
16795
|
try {
|
|
16460
|
-
await
|
|
16461
|
-
|
|
16796
|
+
await seedSingleView(tx, view2, result, options);
|
|
16797
|
+
trackViewByObjectAndType(viewsByObjectAndType, view2);
|
|
16462
16798
|
} catch (error2) {
|
|
16463
|
-
handleViewSyncError(result,
|
|
16799
|
+
handleViewSyncError(result, view2, error2);
|
|
16800
|
+
}
|
|
16801
|
+
}
|
|
16802
|
+
return viewsByObjectAndType;
|
|
16803
|
+
}
|
|
16804
|
+
async function seedSingleView(adapter, view2, result, options) {
|
|
16805
|
+
const exists = await adapter.views.exists(view2.object, view2.name, view2.type);
|
|
16806
|
+
if (exists) {
|
|
16807
|
+
result.viewsSkipped++;
|
|
16808
|
+
result.viewsSynced++;
|
|
16809
|
+
if (options.verbose && options.logger) {
|
|
16810
|
+
options.logger.info(`[ViewSync] Skipped (exists): ${view2.object}:${view2.name}:${view2.type}`);
|
|
16811
|
+
}
|
|
16812
|
+
return;
|
|
16813
|
+
}
|
|
16814
|
+
if (options.dryRun) {
|
|
16815
|
+
result.viewsCreated++;
|
|
16816
|
+
result.viewsSynced++;
|
|
16817
|
+
if (options.verbose && options.logger) {
|
|
16818
|
+
options.logger.info(`[ViewSync] Would create: ${view2.object}:${view2.name}:${view2.type}`);
|
|
16464
16819
|
}
|
|
16820
|
+
return;
|
|
16821
|
+
}
|
|
16822
|
+
await adapter.views.upsert({
|
|
16823
|
+
objectName: view2.object,
|
|
16824
|
+
type: view2.type,
|
|
16825
|
+
name: view2.name,
|
|
16826
|
+
label: view2.label,
|
|
16827
|
+
description: view2.description,
|
|
16828
|
+
icon: view2.icon,
|
|
16829
|
+
config: view2.config,
|
|
16830
|
+
default: view2.default ?? false,
|
|
16831
|
+
metadata: view2.metadata
|
|
16832
|
+
});
|
|
16833
|
+
result.viewsCreated++;
|
|
16834
|
+
result.viewsSynced++;
|
|
16835
|
+
if (options.verbose && options.logger) {
|
|
16836
|
+
options.logger.info(`[ViewSync] \u2713 Created: ${view2.object}:${view2.name}:${view2.type}`);
|
|
16465
16837
|
}
|
|
16466
|
-
return viewsByObject;
|
|
16467
16838
|
}
|
|
16468
|
-
function
|
|
16469
|
-
const
|
|
16470
|
-
|
|
16471
|
-
|
|
16839
|
+
function trackViewByObjectAndType(viewsByObjectAndType, view2) {
|
|
16840
|
+
const key = `${view2.object}:${view2.type}`;
|
|
16841
|
+
const viewNames = viewsByObjectAndType.get(key) ?? [];
|
|
16842
|
+
viewNames.push(view2.name);
|
|
16843
|
+
viewsByObjectAndType.set(key, viewNames);
|
|
16472
16844
|
}
|
|
16473
|
-
function handleViewSyncError(result,
|
|
16845
|
+
function handleViewSyncError(result, view2, error2) {
|
|
16474
16846
|
result.success = false;
|
|
16475
16847
|
result.errors.push({
|
|
16476
|
-
viewName:
|
|
16477
|
-
objectName:
|
|
16848
|
+
viewName: view2.name,
|
|
16849
|
+
objectName: view2.object,
|
|
16478
16850
|
error: error2 instanceof Error ? error2.message : String(error2)
|
|
16479
16851
|
});
|
|
16480
16852
|
}
|
|
16481
|
-
async function
|
|
16853
|
+
async function cleanupOrphanViews(tx, viewsByObjectAndType, result, options) {
|
|
16482
16854
|
if (options.dryRun) return;
|
|
16483
|
-
for (const [
|
|
16484
|
-
const
|
|
16855
|
+
for (const [key, viewNames] of viewsByObjectAndType) {
|
|
16856
|
+
const [objectName, type] = key.split(":");
|
|
16857
|
+
const deletedCount = await tx.views.deleteNotIn(objectName, type, viewNames);
|
|
16485
16858
|
result.viewsDeleted += deletedCount;
|
|
16486
16859
|
if (options.verbose && deletedCount > 0 && options.logger) {
|
|
16487
|
-
options.logger.info(
|
|
16860
|
+
options.logger.info(
|
|
16861
|
+
`[ViewSync] Deleted ${deletedCount} orphan ${type} views for ${objectName}`
|
|
16862
|
+
);
|
|
16488
16863
|
}
|
|
16489
16864
|
}
|
|
16490
16865
|
}
|
|
@@ -16499,63 +16874,27 @@ function handleTransactionError(result, error2) {
|
|
|
16499
16874
|
function logSyncComplete(result, options) {
|
|
16500
16875
|
if (options.verbose && options.logger) {
|
|
16501
16876
|
options.logger.info(
|
|
16502
|
-
`[ViewSync] ${result.success ? "\u2713" : "\u2717"}
|
|
16503
|
-
Views: ${result.viewsCreated} created, ${result.
|
|
16877
|
+
`[ViewSync] ${result.success ? "\u2713" : "\u2717"} Seed complete:
|
|
16878
|
+
Views: ${result.viewsCreated} created, ${result.viewsSkipped} skipped, ${result.viewsDeleted} deleted
|
|
16504
16879
|
Errors: ${result.errors.length}`
|
|
16505
16880
|
);
|
|
16506
16881
|
}
|
|
16507
16882
|
}
|
|
16508
|
-
async function
|
|
16509
|
-
|
|
16510
|
-
|
|
16511
|
-
|
|
16512
|
-
|
|
16513
|
-
const isNew = !existingView;
|
|
16514
|
-
if (isNew) {
|
|
16515
|
-
result.viewsCreated++;
|
|
16516
|
-
} else {
|
|
16517
|
-
result.viewsUpdated++;
|
|
16518
|
-
}
|
|
16519
|
-
result.viewsSynced++;
|
|
16520
|
-
if (options.dryRun) {
|
|
16521
|
-
if (options.verbose && options.logger) {
|
|
16522
|
-
options.logger.info(
|
|
16523
|
-
`[ViewSync] Would ${isNew ? "create" : "update"} view: ${nativeView.object}:${nativeView.name}`
|
|
16524
|
-
);
|
|
16525
|
-
}
|
|
16526
|
-
return;
|
|
16527
|
-
}
|
|
16528
|
-
await adapter.views.upsert({
|
|
16529
|
-
objectName: nativeView.object,
|
|
16530
|
-
name: nativeView.name,
|
|
16531
|
-
label: nativeView.label,
|
|
16532
|
-
description: nativeView.description,
|
|
16533
|
-
icon: nativeView.icon,
|
|
16534
|
-
tabs: nativeView.tabs,
|
|
16535
|
-
default: nativeView.default ?? false,
|
|
16536
|
-
system: true,
|
|
16537
|
-
metadata: nativeView.metadata
|
|
16538
|
-
});
|
|
16539
|
-
if (options.verbose && options.logger) {
|
|
16540
|
-
options.logger.info(
|
|
16541
|
-
`[ViewSync] \u2713 Synced ${nativeView.object}:${nativeView.name}: ${nativeView.tabs.length} tabs`
|
|
16542
|
-
);
|
|
16543
|
-
}
|
|
16544
|
-
}
|
|
16545
|
-
async function verifyNativeViewsSync(adapter, nativeViewRegistry) {
|
|
16546
|
-
const nativeViews = nativeViewRegistry.getAll();
|
|
16547
|
-
for (const view2 of nativeViews) {
|
|
16548
|
-
if (!view2.system) continue;
|
|
16549
|
-
const existing = await adapter.views.findSystemByName(view2.object, view2.name);
|
|
16550
|
-
if (!existing) {
|
|
16883
|
+
async function verifyRegistryViewsSeeded(adapter, registry2) {
|
|
16884
|
+
const registryViews = registry2.getAll();
|
|
16885
|
+
for (const view2 of registryViews) {
|
|
16886
|
+
const exists = await adapter.views.exists(view2.object, view2.name, view2.type);
|
|
16887
|
+
if (!exists) {
|
|
16551
16888
|
return false;
|
|
16552
16889
|
}
|
|
16553
16890
|
}
|
|
16554
16891
|
return true;
|
|
16555
16892
|
}
|
|
16556
|
-
|
|
16557
|
-
|
|
16893
|
+
var verifyNativeViewsSync = verifyRegistryViewsSeeded;
|
|
16894
|
+
async function getViewSeedPreview(adapter, registry2) {
|
|
16895
|
+
return await seedRegistryViews(adapter, registry2, { dryRun: true });
|
|
16558
16896
|
}
|
|
16897
|
+
var getViewSyncPreview = getViewSeedPreview;
|
|
16559
16898
|
|
|
16560
16899
|
// src/runtime/sync.ts
|
|
16561
16900
|
var BASE_ATTRIBUTE_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -16664,8 +17003,9 @@ async function handleDryRun(adapter, nativeObject, existingObject, result, optio
|
|
|
16664
17003
|
if (options.verbose) {
|
|
16665
17004
|
console.info(`[SyncService] Would ${isNew ? "create" : "update"} object: ${nativeObject.name}`);
|
|
16666
17005
|
}
|
|
17006
|
+
const existingAttrs = existingObject ? await adapter.attributes.findByObjectId(existingObject.id) : [];
|
|
16667
17007
|
for (const attr of nativeObject.attributes) {
|
|
16668
|
-
const existingAttr =
|
|
17008
|
+
const existingAttr = existingAttrs.find((a) => a.name === attr.name) ?? null;
|
|
16669
17009
|
if (existingAttr) {
|
|
16670
17010
|
result.attributesUpdated++;
|
|
16671
17011
|
} else {
|
|
@@ -16687,8 +17027,9 @@ async function upsertObject(adapter, nativeObject, _options) {
|
|
|
16687
17027
|
});
|
|
16688
17028
|
}
|
|
16689
17029
|
async function syncAttributes(adapter, nativeObject, dbObject, existingObject, result) {
|
|
17030
|
+
const existingAttrs = existingObject ? await adapter.attributes.findByObjectId(dbObject.id) : [];
|
|
16690
17031
|
for (const [index, attr] of nativeObject.attributes.entries()) {
|
|
16691
|
-
const existingAttr =
|
|
17032
|
+
const existingAttr = existingAttrs.find((a) => a.name === attr.name) ?? null;
|
|
16692
17033
|
await adapter.attributes.upsert({
|
|
16693
17034
|
objectId: dbObject.id,
|
|
16694
17035
|
name: attr.name,
|
|
@@ -16755,7 +17096,7 @@ async function syncAll(adapter, objectRegistry, nativeViewRegistry, options = {}
|
|
|
16755
17096
|
console.info(
|
|
16756
17097
|
`[SyncAll] ${result.success ? "\u2713" : "\u2717"} Full sync complete:
|
|
16757
17098
|
Objects: ${objectsResult.objectsSynced} synced (${objectsResult.objectsCreated} created, ${objectsResult.objectsUpdated} updated)
|
|
16758
|
-
Views: ${viewsResult.viewsSynced} synced (${viewsResult.viewsCreated} created, ${viewsResult.
|
|
17099
|
+
Views: ${viewsResult.viewsSynced} synced (${viewsResult.viewsCreated} created, ${viewsResult.viewsSkipped} skipped)
|
|
16759
17100
|
Errors: ${objectsResult.errors.length + viewsResult.errors.length}`
|
|
16760
17101
|
);
|
|
16761
17102
|
}
|
|
@@ -16776,6 +17117,12 @@ var NoopGeocodingAdapter = class {
|
|
|
16776
17117
|
};
|
|
16777
17118
|
|
|
16778
17119
|
export {
|
|
17120
|
+
IDENTITY_PROPERTIES,
|
|
17121
|
+
BEHAVIOR_PROPERTIES,
|
|
17122
|
+
PRESENTATION_PROPERTIES,
|
|
17123
|
+
isIdentityProperty,
|
|
17124
|
+
isBehaviorProperty,
|
|
17125
|
+
isPresentationProperty,
|
|
16779
17126
|
RELATION_TARGET_ANY,
|
|
16780
17127
|
isUniversalRelation,
|
|
16781
17128
|
RecordReferencedError,
|
|
@@ -16804,8 +17151,6 @@ export {
|
|
|
16804
17151
|
and,
|
|
16805
17152
|
or,
|
|
16806
17153
|
inValues,
|
|
16807
|
-
isEmpty,
|
|
16808
|
-
isNotEmpty,
|
|
16809
17154
|
isWorkflowDefinition,
|
|
16810
17155
|
isWorkflowPublished,
|
|
16811
17156
|
isSystemWorkflow,
|
|
@@ -16824,7 +17169,6 @@ export {
|
|
|
16824
17169
|
createEmptyContext,
|
|
16825
17170
|
getContextValue,
|
|
16826
17171
|
setContextValue,
|
|
16827
|
-
mergeFormToSlot,
|
|
16828
17172
|
DEFAULT_THEME,
|
|
16829
17173
|
mergeWithDefaults,
|
|
16830
17174
|
generateCssVariables,
|
|
@@ -16858,12 +17202,10 @@ export {
|
|
|
16858
17202
|
WorkflowConfigSchema,
|
|
16859
17203
|
WorkflowStatusSchema,
|
|
16860
17204
|
WorkflowDefinitionSchema,
|
|
16861
|
-
|
|
16862
|
-
|
|
16863
|
-
|
|
16864
|
-
|
|
16865
|
-
slugify,
|
|
16866
|
-
generateTemplateName,
|
|
17205
|
+
isEmpty,
|
|
17206
|
+
isNotEmpty,
|
|
17207
|
+
toUndefinedIfEmpty,
|
|
17208
|
+
hasProperties,
|
|
16867
17209
|
EMPTY_VALUE_PLACEHOLDER,
|
|
16868
17210
|
formatAttributeValue,
|
|
16869
17211
|
SchemaErrorCode,
|
|
@@ -16888,7 +17230,6 @@ export {
|
|
|
16888
17230
|
RoleNotFoundError,
|
|
16889
17231
|
isForbiddenError,
|
|
16890
17232
|
ConcurrentModificationError,
|
|
16891
|
-
isConcurrentModificationError,
|
|
16892
17233
|
text,
|
|
16893
17234
|
textarea,
|
|
16894
17235
|
richtext,
|
|
@@ -16919,8 +17260,13 @@ export {
|
|
|
16919
17260
|
FlowsTabConfig,
|
|
16920
17261
|
DocumentsTabConfig,
|
|
16921
17262
|
TabBuilder,
|
|
17263
|
+
DetailViewBuilder,
|
|
16922
17264
|
ViewBuilder,
|
|
17265
|
+
detailView,
|
|
16923
17266
|
view,
|
|
17267
|
+
ListViewBuilder,
|
|
17268
|
+
ListViewTabConfigBuilder,
|
|
17269
|
+
listView,
|
|
16924
17270
|
group,
|
|
16925
17271
|
WorkflowFormRowBuilder,
|
|
16926
17272
|
WorkflowFormBuilder,
|
|
@@ -16941,63 +17287,6 @@ export {
|
|
|
16941
17287
|
SYSTEM_TEMPLATES,
|
|
16942
17288
|
getSystemTemplate,
|
|
16943
17289
|
isSystemTemplate,
|
|
16944
|
-
DEFAULT_VALIDATION_MESSAGES,
|
|
16945
|
-
textConfigSchema,
|
|
16946
|
-
textareaConfigSchema,
|
|
16947
|
-
richtextConfigSchema,
|
|
16948
|
-
numberConfigSchema,
|
|
16949
|
-
checkboxConfigSchema,
|
|
16950
|
-
dateConfigSchema,
|
|
16951
|
-
phoneConfigSchema,
|
|
16952
|
-
currencyConfigSchema,
|
|
16953
|
-
statusConfigSchema,
|
|
16954
|
-
locationConfigSchema,
|
|
16955
|
-
selectConfigSchema,
|
|
16956
|
-
multiselectConfigSchema,
|
|
16957
|
-
fileConfigSchema,
|
|
16958
|
-
userConfigSchema,
|
|
16959
|
-
relationConfigSchema,
|
|
16960
|
-
ratingConfigSchema,
|
|
16961
|
-
formulaConfigSchema,
|
|
16962
|
-
rollupConfigSchema,
|
|
16963
|
-
documentConfigSchema,
|
|
16964
|
-
attributeConfigSchemas,
|
|
16965
|
-
getAttributeConfigSchema,
|
|
16966
|
-
validateAttributeConfig,
|
|
16967
|
-
parseAttributeConfig,
|
|
16968
|
-
safeParseAttributeConfig,
|
|
16969
|
-
createTextValidator,
|
|
16970
|
-
createNumberValidator,
|
|
16971
|
-
createCheckboxValidator,
|
|
16972
|
-
createDateValidator,
|
|
16973
|
-
createPhoneValidator,
|
|
16974
|
-
createCurrencyValidator,
|
|
16975
|
-
createStatusValidator,
|
|
16976
|
-
createSelectValidator,
|
|
16977
|
-
createMultiselectValidator,
|
|
16978
|
-
createLocationValidator,
|
|
16979
|
-
createFileValidator,
|
|
16980
|
-
createUserValidator,
|
|
16981
|
-
createSingleRelationValidator,
|
|
16982
|
-
createMultiRelationValidator,
|
|
16983
|
-
createRelationValidator,
|
|
16984
|
-
createRatingValidator,
|
|
16985
|
-
createFormulaValidator,
|
|
16986
|
-
createRollupValidator,
|
|
16987
|
-
createTextAreaValidator,
|
|
16988
|
-
createRichtextValidator,
|
|
16989
|
-
createAttributeValidator,
|
|
16990
|
-
createFormAttributeValidator,
|
|
16991
|
-
createObjectValidator,
|
|
16992
|
-
validateAttribute,
|
|
16993
|
-
validateObject,
|
|
16994
|
-
validateObjectOrThrow,
|
|
16995
|
-
createDraftValidator,
|
|
16996
|
-
validateDraft,
|
|
16997
|
-
validateDraftOrThrow,
|
|
16998
|
-
getMissingRequiredAttributes,
|
|
16999
|
-
isRecordComplete,
|
|
17000
|
-
computeRecordStatus,
|
|
17001
17290
|
WorkflowJwtService,
|
|
17002
17291
|
hashOptions,
|
|
17003
17292
|
cacheKeys,
|
|
@@ -17011,6 +17300,14 @@ export {
|
|
|
17011
17300
|
QueryNoResultError,
|
|
17012
17301
|
QueryMultipleResultsError,
|
|
17013
17302
|
TenantContextError,
|
|
17303
|
+
FeatureFlagsContextError,
|
|
17304
|
+
isFeatureEnabled,
|
|
17305
|
+
getFeatureValue,
|
|
17306
|
+
getFeatureFlags,
|
|
17307
|
+
tryGetFeatureValue,
|
|
17308
|
+
hasFeatureFlagsContext,
|
|
17309
|
+
runWithFeatureFlags,
|
|
17310
|
+
withFeatureFlags,
|
|
17014
17311
|
getSchemaFromContext,
|
|
17015
17312
|
getSchemaByNameFromContext,
|
|
17016
17313
|
hasSchemaContext,
|
|
@@ -17018,7 +17315,7 @@ export {
|
|
|
17018
17315
|
addSchemaToContext,
|
|
17019
17316
|
runWithSchemaContext,
|
|
17020
17317
|
runWithMergedSchemaContext,
|
|
17021
|
-
getContext,
|
|
17318
|
+
getContext2 as getContext,
|
|
17022
17319
|
getTenantId,
|
|
17023
17320
|
getUserId,
|
|
17024
17321
|
hasContext,
|
|
@@ -17136,8 +17433,11 @@ export {
|
|
|
17136
17433
|
GlobalSearchService,
|
|
17137
17434
|
PermissionService,
|
|
17138
17435
|
ViewService,
|
|
17436
|
+
seedRegistryViews,
|
|
17139
17437
|
syncNativeViews,
|
|
17438
|
+
verifyRegistryViewsSeeded,
|
|
17140
17439
|
verifyNativeViewsSync,
|
|
17440
|
+
getViewSeedPreview,
|
|
17141
17441
|
getViewSyncPreview,
|
|
17142
17442
|
syncNativeObjects,
|
|
17143
17443
|
verifyNativeObjectsSync,
|