@stndrds/schema 0.1.0-alpha.56 → 0.1.0-alpha.57
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-DPRLHGPO.js} +1485 -539
- package/dist/{chunk-LOOGQL6M.mjs → chunk-YVUSATKC.mjs} +1240 -294
- package/dist/index.d.mts +919 -54
- package/dist/index.d.ts +919 -54
- package/dist/index.js +976 -33
- package/dist/index.mjs +970 -27
- package/dist/{runtime-D5MuaVQs.d.mts → runtime-BqJdmg_4.d.mts} +896 -276
- package/dist/{runtime-D5MuaVQs.d.ts → runtime-BqJdmg_4.d.ts} +896 -276
- package/dist/runtime.d.mts +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +24 -2
- package/dist/runtime.mjs +23 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
AttributeNotFoundError,
|
|
5
5
|
AuditService,
|
|
6
6
|
AuthMethodSchema,
|
|
7
|
+
BEHAVIOR_PROPERTIES,
|
|
7
8
|
BaseRepository,
|
|
8
9
|
BaseService,
|
|
9
10
|
ConcurrentModificationError,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
DEFAULT_THEME,
|
|
19
20
|
DEFAULT_VALIDATION_MESSAGES,
|
|
20
21
|
DRIVING_LICENSE,
|
|
22
|
+
DetailViewBuilder,
|
|
21
23
|
DirectTableTabConfig,
|
|
22
24
|
DocumentExecutor,
|
|
23
25
|
DocumentGenerationNotConfiguredError,
|
|
@@ -37,6 +39,7 @@ import {
|
|
|
37
39
|
EndNodeSchema,
|
|
38
40
|
ExecutorRegistry,
|
|
39
41
|
FRENCH_ID_CARD,
|
|
42
|
+
FeatureFlagsContextError,
|
|
40
43
|
FileNotFoundError,
|
|
41
44
|
FileService,
|
|
42
45
|
FlowRowFieldSchema,
|
|
@@ -54,12 +57,15 @@ import {
|
|
|
54
57
|
GrantNotFoundError,
|
|
55
58
|
GrantRevokedError,
|
|
56
59
|
GroupBuilder,
|
|
60
|
+
IDENTITY_PROPERTIES,
|
|
57
61
|
InvalidPathError,
|
|
58
62
|
InverseTableTabConfig,
|
|
59
63
|
InvitationAlreadyAcceptedError,
|
|
60
64
|
InvitationExpiredError,
|
|
61
65
|
InvitationNotFoundError,
|
|
62
66
|
InvitationRevokedError,
|
|
67
|
+
ListViewBuilder,
|
|
68
|
+
ListViewTabConfigBuilder,
|
|
63
69
|
MaxDepthExceededError,
|
|
64
70
|
NodePositionSchema,
|
|
65
71
|
NoopCacheAdapter,
|
|
@@ -73,6 +79,7 @@ import {
|
|
|
73
79
|
ObjectReferencedError,
|
|
74
80
|
ObjectSchemaService,
|
|
75
81
|
PASSPORT,
|
|
82
|
+
PRESENTATION_PROPERTIES,
|
|
76
83
|
PROOF_OF_ADDRESS,
|
|
77
84
|
PermissionService,
|
|
78
85
|
PolicyRegistry,
|
|
@@ -204,6 +211,7 @@ import {
|
|
|
204
211
|
dateConfigSchema,
|
|
205
212
|
defaultPolicyRegistry,
|
|
206
213
|
defaultTtl,
|
|
214
|
+
detailView,
|
|
207
215
|
document,
|
|
208
216
|
documentConfigSchema,
|
|
209
217
|
enrichRecordsWithFormulas,
|
|
@@ -227,6 +235,7 @@ import {
|
|
|
227
235
|
extractRelationReferences,
|
|
228
236
|
file,
|
|
229
237
|
fileConfigSchema,
|
|
238
|
+
filterPropertiesByCategory,
|
|
230
239
|
flattenRelationsForEval,
|
|
231
240
|
formatAttributeValue,
|
|
232
241
|
formatFormulaResult,
|
|
@@ -242,10 +251,13 @@ import {
|
|
|
242
251
|
getContext,
|
|
243
252
|
getContextValue,
|
|
244
253
|
getDefaultExecutorRegistry,
|
|
254
|
+
getFeatureFlags,
|
|
255
|
+
getFeatureValue,
|
|
245
256
|
getMissingRequiredAttributes,
|
|
246
257
|
getNodeOutputs,
|
|
247
258
|
getPathDepth,
|
|
248
259
|
getPolicy,
|
|
260
|
+
getPropertyProtectionLevel,
|
|
249
261
|
getRelationPath,
|
|
250
262
|
getSchemaByNameFromContext,
|
|
251
263
|
getSchemaContext,
|
|
@@ -256,14 +268,17 @@ import {
|
|
|
256
268
|
getTargetAttributeName,
|
|
257
269
|
getTenantId,
|
|
258
270
|
getUserId,
|
|
271
|
+
getViewSeedPreview,
|
|
259
272
|
getViewSyncPreview,
|
|
260
273
|
group,
|
|
261
274
|
hasContext,
|
|
275
|
+
hasFeatureFlagsContext,
|
|
262
276
|
hasRelationReferences,
|
|
263
277
|
hasSchemaContext,
|
|
264
278
|
hashOptions,
|
|
265
279
|
inValues,
|
|
266
280
|
isAdvancedFormNode,
|
|
281
|
+
isBehaviorProperty,
|
|
267
282
|
isConcurrentModificationError,
|
|
268
283
|
isConditionGroup,
|
|
269
284
|
isConditionNode,
|
|
@@ -271,11 +286,13 @@ import {
|
|
|
271
286
|
isDocumentNode,
|
|
272
287
|
isEmpty,
|
|
273
288
|
isEndNode,
|
|
289
|
+
isFeatureEnabled,
|
|
274
290
|
isForbiddenError,
|
|
275
291
|
isFormNode,
|
|
276
292
|
isGrantExpired,
|
|
277
293
|
isGrantRevoked,
|
|
278
294
|
isGrantValid,
|
|
295
|
+
isIdentityProperty,
|
|
279
296
|
isInstanceEvent,
|
|
280
297
|
isInstanceTerminal,
|
|
281
298
|
isInstanceWaiting,
|
|
@@ -287,6 +304,7 @@ import {
|
|
|
287
304
|
isNodeEvent,
|
|
288
305
|
isNotEmpty,
|
|
289
306
|
isNotFoundError,
|
|
307
|
+
isPresentationProperty,
|
|
290
308
|
isProtectedResourceError,
|
|
291
309
|
isRecordComplete,
|
|
292
310
|
isSchemaError,
|
|
@@ -301,6 +319,7 @@ import {
|
|
|
301
319
|
isValidationError,
|
|
302
320
|
isWorkflowDefinition,
|
|
303
321
|
isWorkflowPublished,
|
|
322
|
+
listView,
|
|
304
323
|
location,
|
|
305
324
|
locationConfigSchema,
|
|
306
325
|
mergeFormToSlot,
|
|
@@ -332,9 +351,11 @@ import {
|
|
|
332
351
|
rollup,
|
|
333
352
|
rollupConfigSchema,
|
|
334
353
|
runWithContext,
|
|
354
|
+
runWithFeatureFlags,
|
|
335
355
|
runWithMergedSchemaContext,
|
|
336
356
|
runWithSchemaContext,
|
|
337
357
|
safeParseAttributeConfig,
|
|
358
|
+
seedRegistryViews,
|
|
338
359
|
select,
|
|
339
360
|
selectConfigSchema,
|
|
340
361
|
setContextValue,
|
|
@@ -350,6 +371,7 @@ import {
|
|
|
350
371
|
textarea,
|
|
351
372
|
textareaConfigSchema,
|
|
352
373
|
traversePath,
|
|
374
|
+
tryGetFeatureValue,
|
|
353
375
|
user,
|
|
354
376
|
userConfigSchema,
|
|
355
377
|
validateAttribute,
|
|
@@ -362,11 +384,13 @@ import {
|
|
|
362
384
|
validatePath,
|
|
363
385
|
verifyNativeObjectsSync,
|
|
364
386
|
verifyNativeViewsSync,
|
|
387
|
+
verifyRegistryViewsSeeded,
|
|
365
388
|
view,
|
|
366
389
|
wait,
|
|
390
|
+
withFeatureFlags,
|
|
367
391
|
withTenantContext,
|
|
368
392
|
workflow
|
|
369
|
-
} from "./chunk-
|
|
393
|
+
} from "./chunk-YVUSATKC.mjs";
|
|
370
394
|
import {
|
|
371
395
|
ALL_SYSTEM_RESOURCES,
|
|
372
396
|
DEFAULT_ROLES,
|
|
@@ -503,6 +527,21 @@ function isSystemFlow(flow) {
|
|
|
503
527
|
}
|
|
504
528
|
|
|
505
529
|
// src/types/views.ts
|
|
530
|
+
function isDetailView(view2) {
|
|
531
|
+
return view2.type === "detail";
|
|
532
|
+
}
|
|
533
|
+
function isListView(view2) {
|
|
534
|
+
return view2.type === "list";
|
|
535
|
+
}
|
|
536
|
+
function isCalendarView(view2) {
|
|
537
|
+
return view2.type === "calendar";
|
|
538
|
+
}
|
|
539
|
+
function isTimelineView(view2) {
|
|
540
|
+
return view2.type === "timeline";
|
|
541
|
+
}
|
|
542
|
+
function isGalleryView(view2) {
|
|
543
|
+
return view2.type === "gallery";
|
|
544
|
+
}
|
|
506
545
|
function isFormTab(tab) {
|
|
507
546
|
return tab.type === "form";
|
|
508
547
|
}
|
|
@@ -531,6 +570,523 @@ function isDocumentsTab(tab) {
|
|
|
531
570
|
return tab.type === "documents";
|
|
532
571
|
}
|
|
533
572
|
|
|
573
|
+
// src/feature-flags/flag-builder.ts
|
|
574
|
+
function validateFlagName(name) {
|
|
575
|
+
if (!/^[a-z][a-z0-9-]*[a-z0-9]$|^[a-z]$/.test(name)) {
|
|
576
|
+
throw new Error(
|
|
577
|
+
`Invalid flag name "${name}". Flag names must be kebab-case (e.g., "architect-mode", "ai-features").`
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
var BaseFlagBuilder = class {
|
|
582
|
+
constructor(name, valueType, defaultValue) {
|
|
583
|
+
validateFlagName(name);
|
|
584
|
+
this.flag = {
|
|
585
|
+
name,
|
|
586
|
+
valueType,
|
|
587
|
+
defaultValue,
|
|
588
|
+
allowedLevels: ["global", "tenant", "user"]
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Set the human-readable label for the flag.
|
|
593
|
+
* Required - build() will throw if not set.
|
|
594
|
+
*/
|
|
595
|
+
label(value) {
|
|
596
|
+
this.flag.label = value;
|
|
597
|
+
return this;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Set an optional description for the flag.
|
|
601
|
+
*/
|
|
602
|
+
description(value) {
|
|
603
|
+
this.flag.description = value;
|
|
604
|
+
return this;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Set a category for grouping flags in UI.
|
|
608
|
+
*/
|
|
609
|
+
category(value) {
|
|
610
|
+
this.flag.category = value;
|
|
611
|
+
return this;
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Mark this flag as a system flag.
|
|
615
|
+
* System flags cannot be modified via API.
|
|
616
|
+
*/
|
|
617
|
+
system() {
|
|
618
|
+
this.flag.system = true;
|
|
619
|
+
return this;
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Restrict the flag to global and tenant levels only.
|
|
623
|
+
* Per-user overrides will not be allowed.
|
|
624
|
+
*/
|
|
625
|
+
tenantScoped() {
|
|
626
|
+
this.flag.allowedLevels = ["global", "tenant"];
|
|
627
|
+
return this;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Restrict the flag to global level only.
|
|
631
|
+
* No tenant or user overrides will be allowed.
|
|
632
|
+
*/
|
|
633
|
+
globalOnly() {
|
|
634
|
+
this.flag.allowedLevels = ["global"];
|
|
635
|
+
return this;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Set custom allowed levels.
|
|
639
|
+
*/
|
|
640
|
+
allowedLevels(levels) {
|
|
641
|
+
this.flag.allowedLevels = levels;
|
|
642
|
+
return this;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Build the flag definition.
|
|
646
|
+
* @throws {Error} If label is not set
|
|
647
|
+
*/
|
|
648
|
+
build() {
|
|
649
|
+
if (!this.flag.label) {
|
|
650
|
+
throw new Error(
|
|
651
|
+
`Flag "${this.flag.name}" must have a label. Use .label("Human-readable label").`
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
return this.flag;
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
var BooleanFlagBuilder = class extends BaseFlagBuilder {
|
|
658
|
+
constructor(name) {
|
|
659
|
+
super(name, "boolean", false);
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Set the default value (default: false).
|
|
663
|
+
*/
|
|
664
|
+
defaultValue(value) {
|
|
665
|
+
this.flag.defaultValue = value;
|
|
666
|
+
return this;
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Set the default to true.
|
|
670
|
+
* Shorthand for .defaultValue(true).
|
|
671
|
+
*/
|
|
672
|
+
enabledByDefault() {
|
|
673
|
+
this.flag.defaultValue = true;
|
|
674
|
+
return this;
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
var StringFlagBuilder = class extends BaseFlagBuilder {
|
|
678
|
+
constructor(name) {
|
|
679
|
+
super(name, "string", "");
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Set the default value.
|
|
683
|
+
*/
|
|
684
|
+
defaultValue(value) {
|
|
685
|
+
this.flag.defaultValue = value;
|
|
686
|
+
return this;
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
var NumberFlagBuilder = class extends BaseFlagBuilder {
|
|
690
|
+
constructor(name) {
|
|
691
|
+
super(name, "number", 0);
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Set the default value.
|
|
695
|
+
*/
|
|
696
|
+
defaultValue(value) {
|
|
697
|
+
this.flag.defaultValue = value;
|
|
698
|
+
return this;
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
var JsonFlagBuilder = class extends BaseFlagBuilder {
|
|
702
|
+
constructor(name, defaultValue) {
|
|
703
|
+
super(name, "json", defaultValue);
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Set the default value.
|
|
707
|
+
*/
|
|
708
|
+
defaultValue(value) {
|
|
709
|
+
this.flag.defaultValue = value;
|
|
710
|
+
return this;
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
function booleanFlag(name) {
|
|
714
|
+
return new BooleanFlagBuilder(name);
|
|
715
|
+
}
|
|
716
|
+
function stringFlag(name) {
|
|
717
|
+
return new StringFlagBuilder(name);
|
|
718
|
+
}
|
|
719
|
+
function numberFlag(name) {
|
|
720
|
+
return new NumberFlagBuilder(name);
|
|
721
|
+
}
|
|
722
|
+
function jsonFlag(name, defaultValue) {
|
|
723
|
+
return new JsonFlagBuilder(name, defaultValue);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// src/feature-flags/flag-registry.ts
|
|
727
|
+
var FlagRegistry = class {
|
|
728
|
+
constructor() {
|
|
729
|
+
this.flags = /* @__PURE__ */ new Map();
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Register a feature flag definition.
|
|
733
|
+
*
|
|
734
|
+
* @param flag - The flag definition to register
|
|
735
|
+
* @throws {Error} If a flag with the same name is already registered
|
|
736
|
+
*
|
|
737
|
+
* @example
|
|
738
|
+
* ```typescript
|
|
739
|
+
* registry.register(booleanFlag("my-feature")
|
|
740
|
+
* .label("My Feature")
|
|
741
|
+
* .build()
|
|
742
|
+
* );
|
|
743
|
+
* ```
|
|
744
|
+
*/
|
|
745
|
+
register(flag) {
|
|
746
|
+
if (this.flags.has(flag.name)) {
|
|
747
|
+
throw new Error(`Flag "${flag.name}" is already registered. Each flag name must be unique.`);
|
|
748
|
+
}
|
|
749
|
+
this.flags.set(flag.name, flag);
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Register multiple flags at once.
|
|
753
|
+
*
|
|
754
|
+
* @param flags - Array of flag definitions to register
|
|
755
|
+
*/
|
|
756
|
+
registerAll(flags) {
|
|
757
|
+
for (const flag of flags) {
|
|
758
|
+
this.register(flag);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Get a flag definition by name.
|
|
763
|
+
*
|
|
764
|
+
* @param name - The flag name
|
|
765
|
+
* @returns The flag definition or undefined if not found
|
|
766
|
+
*/
|
|
767
|
+
get(name) {
|
|
768
|
+
return this.flags.get(name);
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Get a flag definition by name, throwing if not found.
|
|
772
|
+
*
|
|
773
|
+
* @param name - The flag name
|
|
774
|
+
* @returns The flag definition
|
|
775
|
+
* @throws {Error} If the flag is not registered
|
|
776
|
+
*/
|
|
777
|
+
getOrThrow(name) {
|
|
778
|
+
const flag = this.get(name);
|
|
779
|
+
if (!flag) {
|
|
780
|
+
throw new Error(
|
|
781
|
+
`Flag "${name}" is not registered. Available flags: ${this.listNames().join(", ")}`
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
return flag;
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Check if a flag is registered.
|
|
788
|
+
*
|
|
789
|
+
* @param name - The flag name
|
|
790
|
+
* @returns true if the flag is registered
|
|
791
|
+
*/
|
|
792
|
+
has(name) {
|
|
793
|
+
return this.flags.has(name);
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* List all registered flag definitions.
|
|
797
|
+
*
|
|
798
|
+
* @returns Array of all flag definitions
|
|
799
|
+
*/
|
|
800
|
+
list() {
|
|
801
|
+
return Array.from(this.flags.values());
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* List all registered flag names.
|
|
805
|
+
*
|
|
806
|
+
* @returns Array of flag names
|
|
807
|
+
*/
|
|
808
|
+
listNames() {
|
|
809
|
+
return Array.from(this.flags.keys());
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* List flags filtered by category.
|
|
813
|
+
*
|
|
814
|
+
* @param category - The category to filter by
|
|
815
|
+
* @returns Array of flag definitions in the category
|
|
816
|
+
*/
|
|
817
|
+
listByCategory(category) {
|
|
818
|
+
return this.list().filter((flag) => flag.category === category);
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Get all unique categories.
|
|
822
|
+
*
|
|
823
|
+
* @returns Array of unique category names
|
|
824
|
+
*/
|
|
825
|
+
getCategories() {
|
|
826
|
+
const categories = /* @__PURE__ */ new Set();
|
|
827
|
+
for (const flag of this.flags.values()) {
|
|
828
|
+
if (flag.category) {
|
|
829
|
+
categories.add(flag.category);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return Array.from(categories);
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Get the default value for a flag.
|
|
836
|
+
*
|
|
837
|
+
* @param name - The flag name
|
|
838
|
+
* @returns The default value or undefined if flag not found
|
|
839
|
+
*/
|
|
840
|
+
getDefaultValue(name) {
|
|
841
|
+
return this.get(name)?.defaultValue;
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Get a map of all flag names to their default values.
|
|
845
|
+
*
|
|
846
|
+
* @returns Map of flag names to default values
|
|
847
|
+
*/
|
|
848
|
+
getDefaults() {
|
|
849
|
+
const defaults = /* @__PURE__ */ new Map();
|
|
850
|
+
for (const [name, flag] of this.flags) {
|
|
851
|
+
defaults.set(name, flag.defaultValue);
|
|
852
|
+
}
|
|
853
|
+
return defaults;
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Unregister a flag (mainly for testing).
|
|
857
|
+
*
|
|
858
|
+
* @param name - The flag name to unregister
|
|
859
|
+
* @returns true if the flag was removed, false if it didn't exist
|
|
860
|
+
*/
|
|
861
|
+
unregister(name) {
|
|
862
|
+
return this.flags.delete(name);
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Clear all registered flags (mainly for testing).
|
|
866
|
+
*/
|
|
867
|
+
clear() {
|
|
868
|
+
this.flags.clear();
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Get the number of registered flags.
|
|
872
|
+
*/
|
|
873
|
+
get size() {
|
|
874
|
+
return this.flags.size;
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
var flagRegistry = new FlagRegistry();
|
|
878
|
+
function createFlagRegistry() {
|
|
879
|
+
return new FlagRegistry();
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// src/feature-flags/flag-service.ts
|
|
883
|
+
var FlagService = class {
|
|
884
|
+
constructor(options) {
|
|
885
|
+
this.repository = options.repository;
|
|
886
|
+
this.registry = options.registry;
|
|
887
|
+
this.staticDefaults = new Map(options.staticDefaults?.map((d) => [d.name, d.value]) ?? []);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Resolve all flags for the current context.
|
|
891
|
+
* Returns a Map suitable for runWithFeatureFlags().
|
|
892
|
+
*
|
|
893
|
+
* @param context - Resolution context with tenant/user IDs
|
|
894
|
+
* @returns Map of flag names to resolved values
|
|
895
|
+
*/
|
|
896
|
+
async resolveAll(context = {}) {
|
|
897
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
898
|
+
const flagNames = /* @__PURE__ */ new Set();
|
|
899
|
+
for (const name of this.registry.listNames()) {
|
|
900
|
+
flagNames.add(name);
|
|
901
|
+
}
|
|
902
|
+
for (const name of this.staticDefaults.keys()) {
|
|
903
|
+
flagNames.add(name);
|
|
904
|
+
}
|
|
905
|
+
const overrides = this.repository ? await this.getAllOverrides(context) : { global: [], tenant: [], user: [] };
|
|
906
|
+
for (const name of flagNames) {
|
|
907
|
+
const value = this.resolveValue(name, overrides, context);
|
|
908
|
+
resolved.set(name, value);
|
|
909
|
+
}
|
|
910
|
+
return resolved;
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Resolve a single flag with full metadata.
|
|
914
|
+
*
|
|
915
|
+
* @param flagName - The flag to resolve
|
|
916
|
+
* @param context - Resolution context
|
|
917
|
+
* @returns Resolved flag with source information
|
|
918
|
+
*/
|
|
919
|
+
async resolve(flagName, context = {}) {
|
|
920
|
+
const overrides = this.repository ? await this.getAllOverrides(context) : { global: [], tenant: [], user: [] };
|
|
921
|
+
return this.resolveWithSource(flagName, overrides, context);
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* Quick check if a boolean flag is enabled.
|
|
925
|
+
*
|
|
926
|
+
* @param flagName - The flag to check
|
|
927
|
+
* @param context - Resolution context
|
|
928
|
+
* @returns true if the flag value is exactly true
|
|
929
|
+
*/
|
|
930
|
+
async isEnabled(flagName, context = {}) {
|
|
931
|
+
const resolved = await this.resolve(flagName, context);
|
|
932
|
+
return resolved.value === true;
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Get a flag value with type inference.
|
|
936
|
+
*
|
|
937
|
+
* @param flagName - The flag to get
|
|
938
|
+
* @param context - Resolution context
|
|
939
|
+
* @returns The resolved flag value
|
|
940
|
+
*/
|
|
941
|
+
async getValue(flagName, context = {}) {
|
|
942
|
+
const resolved = await this.resolve(flagName, context);
|
|
943
|
+
return resolved.value;
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Set an override for a flag.
|
|
947
|
+
*
|
|
948
|
+
* @param flagName - The flag to override
|
|
949
|
+
* @param level - The level of the override
|
|
950
|
+
* @param value - The override value
|
|
951
|
+
* @param options - Additional options
|
|
952
|
+
*/
|
|
953
|
+
async setOverride(flagName, level, value, options = {}) {
|
|
954
|
+
if (!this.repository) {
|
|
955
|
+
throw new Error(
|
|
956
|
+
"Cannot set override: no FeatureFlagsRepository configured. Add featureFlags to your DatabaseAdapter to enable runtime overrides."
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
const flag = this.registry.get(flagName);
|
|
960
|
+
if (flag && !flag.allowedLevels.includes(level)) {
|
|
961
|
+
throw new Error(
|
|
962
|
+
`Flag "${flagName}" does not allow ${level}-level overrides. Allowed levels: ${flag.allowedLevels.join(", ")}`
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
if (flag?.system) {
|
|
966
|
+
throw new Error(`Flag "${flagName}" is a system flag and cannot be overridden via API.`);
|
|
967
|
+
}
|
|
968
|
+
await this.repository.setOverride({
|
|
969
|
+
flagName,
|
|
970
|
+
level,
|
|
971
|
+
value,
|
|
972
|
+
targetId: options.targetId,
|
|
973
|
+
expiresAt: options.expiresAt,
|
|
974
|
+
createdBy: options.createdBy
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Delete an override.
|
|
979
|
+
*
|
|
980
|
+
* @param flagName - The flag name
|
|
981
|
+
* @param level - The level to delete
|
|
982
|
+
* @param targetId - Optional target ID for tenant/user level
|
|
983
|
+
*/
|
|
984
|
+
async deleteOverride(flagName, level, targetId) {
|
|
985
|
+
if (!this.repository) {
|
|
986
|
+
throw new Error("Cannot delete override: no FeatureFlagsRepository configured.");
|
|
987
|
+
}
|
|
988
|
+
await this.repository.deleteOverride(flagName, level, targetId);
|
|
989
|
+
}
|
|
990
|
+
// ============================================================================
|
|
991
|
+
// PRIVATE HELPERS
|
|
992
|
+
// ============================================================================
|
|
993
|
+
/**
|
|
994
|
+
* Get all overrides organized by level.
|
|
995
|
+
*/
|
|
996
|
+
async getAllOverrides(context) {
|
|
997
|
+
if (!this.repository) {
|
|
998
|
+
return { global: [], tenant: [], user: [] };
|
|
999
|
+
}
|
|
1000
|
+
const [globalOverrides, tenantOverrides, userOverrides] = await Promise.all([
|
|
1001
|
+
this.repository.getOverrides({ level: "global" }),
|
|
1002
|
+
context.tenantId ? this.repository.getOverrides({ level: "tenant", targetId: context.tenantId }) : Promise.resolve([]),
|
|
1003
|
+
context.userId ? this.repository.getOverrides({ level: "user", targetId: context.userId }) : Promise.resolve([])
|
|
1004
|
+
]);
|
|
1005
|
+
return {
|
|
1006
|
+
global: globalOverrides,
|
|
1007
|
+
tenant: tenantOverrides,
|
|
1008
|
+
user: userOverrides
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Resolve a flag value from overrides and defaults.
|
|
1013
|
+
*/
|
|
1014
|
+
resolveValue(flagName, overrides, context) {
|
|
1015
|
+
const flag = this.registry.get(flagName);
|
|
1016
|
+
const allowedLevels = flag?.allowedLevels ?? ["global", "tenant", "user"];
|
|
1017
|
+
if (context.userId && allowedLevels.includes("user")) {
|
|
1018
|
+
const userOverride = overrides.user.find((o) => o.flagName === flagName);
|
|
1019
|
+
if (userOverride) return userOverride.value;
|
|
1020
|
+
}
|
|
1021
|
+
if (context.tenantId && allowedLevels.includes("tenant")) {
|
|
1022
|
+
const tenantOverride = overrides.tenant.find((o) => o.flagName === flagName);
|
|
1023
|
+
if (tenantOverride) return tenantOverride.value;
|
|
1024
|
+
}
|
|
1025
|
+
if (allowedLevels.includes("global")) {
|
|
1026
|
+
const globalOverride = overrides.global.find((o) => o.flagName === flagName);
|
|
1027
|
+
if (globalOverride) return globalOverride.value;
|
|
1028
|
+
}
|
|
1029
|
+
if (this.staticDefaults.has(flagName)) {
|
|
1030
|
+
return this.staticDefaults.get(flagName);
|
|
1031
|
+
}
|
|
1032
|
+
return flag?.defaultValue;
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Resolve a flag value with source information.
|
|
1036
|
+
*/
|
|
1037
|
+
resolveWithSource(flagName, overrides, context) {
|
|
1038
|
+
const flag = this.registry.get(flagName);
|
|
1039
|
+
const allowedLevels = flag?.allowedLevels ?? ["global", "tenant", "user"];
|
|
1040
|
+
if (context.userId && allowedLevels.includes("user")) {
|
|
1041
|
+
const userOverride = overrides.user.find((o) => o.flagName === flagName);
|
|
1042
|
+
if (userOverride) {
|
|
1043
|
+
return {
|
|
1044
|
+
name: flagName,
|
|
1045
|
+
value: userOverride.value,
|
|
1046
|
+
source: "user",
|
|
1047
|
+
sourceId: context.userId
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
if (context.tenantId && allowedLevels.includes("tenant")) {
|
|
1052
|
+
const tenantOverride = overrides.tenant.find((o) => o.flagName === flagName);
|
|
1053
|
+
if (tenantOverride) {
|
|
1054
|
+
return {
|
|
1055
|
+
name: flagName,
|
|
1056
|
+
value: tenantOverride.value,
|
|
1057
|
+
source: "tenant",
|
|
1058
|
+
sourceId: context.tenantId
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
if (allowedLevels.includes("global")) {
|
|
1063
|
+
const globalOverride = overrides.global.find((o) => o.flagName === flagName);
|
|
1064
|
+
if (globalOverride) {
|
|
1065
|
+
return {
|
|
1066
|
+
name: flagName,
|
|
1067
|
+
value: globalOverride.value,
|
|
1068
|
+
source: "global"
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
if (this.staticDefaults.has(flagName)) {
|
|
1073
|
+
return {
|
|
1074
|
+
name: flagName,
|
|
1075
|
+
value: this.staticDefaults.get(flagName),
|
|
1076
|
+
source: "default"
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
return {
|
|
1080
|
+
name: flagName,
|
|
1081
|
+
value: flag?.defaultValue ?? void 0,
|
|
1082
|
+
source: "default"
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
};
|
|
1086
|
+
function createFlagService(options) {
|
|
1087
|
+
return new FlagService(options);
|
|
1088
|
+
}
|
|
1089
|
+
|
|
534
1090
|
// src/native/notes.ts
|
|
535
1091
|
var NOTES = object({ name: "notes", label: "Note" }).pluralLabel("Notes").icon("file-text").description("Notes that can be linked to any record or used globally").system().labelExpression("{{ title }}").attribute(text({ name: "title", label: "Title" }).placeholder("Untitled").required()).attribute(richtext({ name: "content", label: "Content" }).required()).attribute(
|
|
536
1092
|
select({ name: "visibility", label: "Visibility" }).options([
|
|
@@ -545,12 +1101,12 @@ var ViewRegistry = class {
|
|
|
545
1101
|
constructor() {
|
|
546
1102
|
this.views = /* @__PURE__ */ new Map();
|
|
547
1103
|
this.byObject = /* @__PURE__ */ new Map();
|
|
1104
|
+
this.byObjectAndType = /* @__PURE__ */ new Map();
|
|
548
1105
|
}
|
|
549
1106
|
/**
|
|
550
|
-
* Register a
|
|
1107
|
+
* Register a view
|
|
551
1108
|
* @param viewOrViews - Single view or array of views
|
|
552
|
-
* @throws Error if view
|
|
553
|
-
* @throws Error if view with same name already exists for the object
|
|
1109
|
+
* @throws Error if view with same name and type already exists for the object
|
|
554
1110
|
*/
|
|
555
1111
|
register(viewOrViews) {
|
|
556
1112
|
const views = Array.isArray(viewOrViews) ? viewOrViews : [viewOrViews];
|
|
@@ -560,22 +1116,20 @@ var ViewRegistry = class {
|
|
|
560
1116
|
return this;
|
|
561
1117
|
}
|
|
562
1118
|
registerSingle(view2) {
|
|
563
|
-
|
|
564
|
-
throw new Error(
|
|
565
|
-
`[ViewRegistry] View "${view2.name}" for object "${view2.object}" is missing system flag.
|
|
566
|
-
Only system views can be registered. Add .system() to your view builder.`
|
|
567
|
-
);
|
|
568
|
-
}
|
|
569
|
-
const key = this.makeKey(view2.object, view2.name);
|
|
1119
|
+
const key = this.makeKey(view2.object, view2.name, view2.type);
|
|
570
1120
|
if (this.views.has(key)) {
|
|
571
1121
|
throw new Error(
|
|
572
|
-
`[ViewRegistry] Duplicate view
|
|
1122
|
+
`[ViewRegistry] Duplicate view: "${view2.name}" (${view2.type}) for object "${view2.object}"`
|
|
573
1123
|
);
|
|
574
1124
|
}
|
|
575
1125
|
this.views.set(key, view2);
|
|
576
1126
|
const objectViews = this.byObject.get(view2.object) ?? [];
|
|
577
1127
|
objectViews.push(view2);
|
|
578
1128
|
this.byObject.set(view2.object, objectViews);
|
|
1129
|
+
const typeKey = `${view2.object}:${view2.type}`;
|
|
1130
|
+
const typeViews = this.byObjectAndType.get(typeKey) ?? [];
|
|
1131
|
+
typeViews.push(view2);
|
|
1132
|
+
this.byObjectAndType.set(typeKey, typeViews);
|
|
579
1133
|
}
|
|
580
1134
|
/**
|
|
581
1135
|
* Get all views for an object
|
|
@@ -584,19 +1138,29 @@ Only system views can be registered. Add .system() to your view builder.`
|
|
|
584
1138
|
return this.byObject.get(objectName) ?? [];
|
|
585
1139
|
}
|
|
586
1140
|
/**
|
|
587
|
-
* Get
|
|
1141
|
+
* Get views for an object filtered by type
|
|
588
1142
|
*/
|
|
589
|
-
|
|
590
|
-
return this.
|
|
1143
|
+
getByObjectNameAndType(objectName, type) {
|
|
1144
|
+
return this.byObjectAndType.get(`${objectName}:${type}`) ?? [];
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Get a specific view by object, name, and type
|
|
1148
|
+
*/
|
|
1149
|
+
get(objectName, viewName, type) {
|
|
1150
|
+
if (type) {
|
|
1151
|
+
return this.views.get(this.makeKey(objectName, viewName, type));
|
|
1152
|
+
}
|
|
1153
|
+
const objectViews = this.getByObjectName(objectName);
|
|
1154
|
+
return objectViews.find((v) => v.name === viewName);
|
|
591
1155
|
}
|
|
592
1156
|
/**
|
|
593
1157
|
* Get a view or throw if not found
|
|
594
1158
|
*/
|
|
595
|
-
getOrThrow(objectName, viewName) {
|
|
596
|
-
const view2 = this.get(objectName, viewName);
|
|
1159
|
+
getOrThrow(objectName, viewName, type) {
|
|
1160
|
+
const view2 = this.get(objectName, viewName, type);
|
|
597
1161
|
if (!view2) {
|
|
598
1162
|
const available = this.getByObjectName(objectName);
|
|
599
|
-
const availableNames = available.map((v) => v.name).join(", ") || "(none)";
|
|
1163
|
+
const availableNames = available.map((v) => `${v.name} (${v.type})`).join(", ") || "(none)";
|
|
600
1164
|
throw new Error(
|
|
601
1165
|
`[ViewRegistry] View "${viewName}" not found for object "${objectName}".
|
|
602
1166
|
Available views: ${availableNames}`
|
|
@@ -613,8 +1177,12 @@ Available views: ${availableNames}`
|
|
|
613
1177
|
/**
|
|
614
1178
|
* Check if a view exists
|
|
615
1179
|
*/
|
|
616
|
-
has(objectName, viewName) {
|
|
617
|
-
|
|
1180
|
+
has(objectName, viewName, type) {
|
|
1181
|
+
if (type) {
|
|
1182
|
+
return this.views.has(this.makeKey(objectName, viewName, type));
|
|
1183
|
+
}
|
|
1184
|
+
const objectViews = this.getByObjectName(objectName);
|
|
1185
|
+
return objectViews.some((v) => v.name === viewName);
|
|
618
1186
|
}
|
|
619
1187
|
/**
|
|
620
1188
|
* Check if any views exist for an object
|
|
@@ -635,10 +1203,10 @@ Available views: ${availableNames}`
|
|
|
635
1203
|
return Array.from(this.byObject.keys());
|
|
636
1204
|
}
|
|
637
1205
|
/**
|
|
638
|
-
* Get default view for an object
|
|
1206
|
+
* Get default view for an object and type
|
|
639
1207
|
*/
|
|
640
|
-
getDefault(objectName) {
|
|
641
|
-
const views = this.
|
|
1208
|
+
getDefault(objectName, type) {
|
|
1209
|
+
const views = this.getByObjectNameAndType(objectName, type);
|
|
642
1210
|
return views.find((v) => v.default) ?? views[0];
|
|
643
1211
|
}
|
|
644
1212
|
/**
|
|
@@ -647,20 +1215,21 @@ Available views: ${availableNames}`
|
|
|
647
1215
|
clear() {
|
|
648
1216
|
this.views.clear();
|
|
649
1217
|
this.byObject.clear();
|
|
1218
|
+
this.byObjectAndType.clear();
|
|
650
1219
|
}
|
|
651
1220
|
/**
|
|
652
1221
|
* Generate summary string for debugging
|
|
653
1222
|
*/
|
|
654
1223
|
summary() {
|
|
655
1224
|
const lines = [];
|
|
656
|
-
lines.push(`[ViewRegistry] ${this.size}
|
|
1225
|
+
lines.push(`[ViewRegistry] ${this.size} view(s) registered:`);
|
|
657
1226
|
for (const objectName of this.listObjectNames()) {
|
|
658
1227
|
const views = this.getByObjectName(objectName);
|
|
659
1228
|
const viewNames = views.map((v) => {
|
|
660
1229
|
const flags = [];
|
|
661
1230
|
if (v.default) flags.push("default");
|
|
662
1231
|
const flagStr = flags.length > 0 ? ` (${flags.join(", ")})` : "";
|
|
663
|
-
return `${v.name}${flagStr}`;
|
|
1232
|
+
return `${v.name}:${v.type}${flagStr}`;
|
|
664
1233
|
});
|
|
665
1234
|
lines.push(` ${objectName}: ${viewNames.join(", ")}`);
|
|
666
1235
|
}
|
|
@@ -672,11 +1241,340 @@ Available views: ${availableNames}`
|
|
|
672
1241
|
debug() {
|
|
673
1242
|
console.info(this.summary());
|
|
674
1243
|
}
|
|
675
|
-
makeKey(objectName, viewName) {
|
|
676
|
-
return `${objectName}:${viewName}`;
|
|
1244
|
+
makeKey(objectName, viewName, type) {
|
|
1245
|
+
return `${objectName}:${viewName}:${type}`;
|
|
677
1246
|
}
|
|
678
1247
|
};
|
|
679
1248
|
var viewRegistry = new ViewRegistry();
|
|
1249
|
+
|
|
1250
|
+
// src/views/auto-generator.ts
|
|
1251
|
+
function getVisibleAttributes(attributes, excludeNames = []) {
|
|
1252
|
+
return attributes.filter(
|
|
1253
|
+
(attr) => !(attr.hidden || attr.archived || attr.system || excludeNames.includes(attr.name))
|
|
1254
|
+
);
|
|
1255
|
+
}
|
|
1256
|
+
function sortAttributes(attributes) {
|
|
1257
|
+
return [...attributes].sort((a, b) => {
|
|
1258
|
+
const orderA = a.order ?? 999;
|
|
1259
|
+
const orderB = b.order ?? 999;
|
|
1260
|
+
if (orderA !== orderB) return orderA - orderB;
|
|
1261
|
+
return a.name.localeCompare(b.name);
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
function getDefaultFieldSpan(type) {
|
|
1265
|
+
switch (type) {
|
|
1266
|
+
case "textarea":
|
|
1267
|
+
case "richtext":
|
|
1268
|
+
case "location":
|
|
1269
|
+
return 12;
|
|
1270
|
+
case "checkbox":
|
|
1271
|
+
return 4;
|
|
1272
|
+
default:
|
|
1273
|
+
return 6;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
function attributeToField(attr) {
|
|
1277
|
+
return {
|
|
1278
|
+
attribute: attr.name,
|
|
1279
|
+
span: getDefaultFieldSpan(attr.type)
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
function generateDefaultDetailView(object2, options = {}) {
|
|
1283
|
+
const {
|
|
1284
|
+
includeActivityTab = true,
|
|
1285
|
+
includeNotesTab = true,
|
|
1286
|
+
includeDocumentsTab = true,
|
|
1287
|
+
includeFlowsTab = false,
|
|
1288
|
+
excludeAttributes = []
|
|
1289
|
+
} = options;
|
|
1290
|
+
const visibleAttrs = getVisibleAttributes(object2.attributes, excludeAttributes);
|
|
1291
|
+
const sortedAttrs = sortAttributes(visibleAttrs);
|
|
1292
|
+
const formTab = {
|
|
1293
|
+
id: "form",
|
|
1294
|
+
name: "form",
|
|
1295
|
+
label: "Details",
|
|
1296
|
+
type: "form",
|
|
1297
|
+
groups: [
|
|
1298
|
+
{
|
|
1299
|
+
id: "general",
|
|
1300
|
+
label: "General",
|
|
1301
|
+
fields: sortedAttrs.map(attributeToField),
|
|
1302
|
+
order: 0
|
|
1303
|
+
}
|
|
1304
|
+
],
|
|
1305
|
+
order: 0
|
|
1306
|
+
};
|
|
1307
|
+
const tabs = [formTab];
|
|
1308
|
+
let tabOrder = 1;
|
|
1309
|
+
const hasDocuments = object2.attributes.some((attr) => attr.type === "document");
|
|
1310
|
+
if (includeDocumentsTab && hasDocuments) {
|
|
1311
|
+
tabs.push({
|
|
1312
|
+
id: "documents",
|
|
1313
|
+
name: "documents",
|
|
1314
|
+
label: "Documents",
|
|
1315
|
+
type: "documents",
|
|
1316
|
+
order: tabOrder++,
|
|
1317
|
+
allowUpload: true,
|
|
1318
|
+
allowRemove: true,
|
|
1319
|
+
showProcessing: true
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
if (includeActivityTab) {
|
|
1323
|
+
tabs.push({
|
|
1324
|
+
id: "activity",
|
|
1325
|
+
name: "activity",
|
|
1326
|
+
label: "Activity",
|
|
1327
|
+
type: "activity",
|
|
1328
|
+
order: tabOrder++
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
if (includeNotesTab) {
|
|
1332
|
+
tabs.push({
|
|
1333
|
+
id: "notes",
|
|
1334
|
+
name: "notes",
|
|
1335
|
+
label: "Notes",
|
|
1336
|
+
type: "notes",
|
|
1337
|
+
order: tabOrder++,
|
|
1338
|
+
allowCreate: true
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
if (includeFlowsTab) {
|
|
1342
|
+
tabs.push({
|
|
1343
|
+
id: "flows",
|
|
1344
|
+
name: "flows",
|
|
1345
|
+
label: "Workflows",
|
|
1346
|
+
type: "flows",
|
|
1347
|
+
order: tabOrder++,
|
|
1348
|
+
allowStart: true,
|
|
1349
|
+
allowCancel: true
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
const config = {
|
|
1353
|
+
layout: "page",
|
|
1354
|
+
tabs
|
|
1355
|
+
};
|
|
1356
|
+
return {
|
|
1357
|
+
name: "default",
|
|
1358
|
+
label: "Default View",
|
|
1359
|
+
object: object2.name,
|
|
1360
|
+
type: "detail",
|
|
1361
|
+
config,
|
|
1362
|
+
default: true
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
function generateDefaultListView(object2, options = {}) {
|
|
1366
|
+
const { excludeAttributes = [], maxListColumns = 5 } = options;
|
|
1367
|
+
const visibleAttrs = getVisibleAttributes(object2.attributes, excludeAttributes);
|
|
1368
|
+
const sortedAttrs = sortAttributes(visibleAttrs);
|
|
1369
|
+
const columns = sortedAttrs.slice(0, maxListColumns).map((attr) => attr.name);
|
|
1370
|
+
const config = {
|
|
1371
|
+
layout: "table",
|
|
1372
|
+
columns
|
|
1373
|
+
};
|
|
1374
|
+
return {
|
|
1375
|
+
name: "default",
|
|
1376
|
+
label: "All Records",
|
|
1377
|
+
object: object2.name,
|
|
1378
|
+
type: "list",
|
|
1379
|
+
config,
|
|
1380
|
+
default: true
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
function generateFallbackView(object2, type, options = {}) {
|
|
1384
|
+
switch (type) {
|
|
1385
|
+
case "detail":
|
|
1386
|
+
return generateDefaultDetailView(object2, options);
|
|
1387
|
+
case "list":
|
|
1388
|
+
return generateDefaultListView(object2, options);
|
|
1389
|
+
default:
|
|
1390
|
+
throw new Error(`Fallback generation not supported for view type: ${type}`);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
function generateModalDetailView(object2, attributeNames) {
|
|
1394
|
+
const excludeAttributes = attributeNames ? object2.attributes.filter((attr) => !attributeNames.includes(attr.name)).map((attr) => attr.name) : [];
|
|
1395
|
+
const visibleAttrs = getVisibleAttributes(object2.attributes, excludeAttributes);
|
|
1396
|
+
const sortedAttrs = sortAttributes(visibleAttrs);
|
|
1397
|
+
const formTab = {
|
|
1398
|
+
id: "form",
|
|
1399
|
+
name: "form",
|
|
1400
|
+
label: "Details",
|
|
1401
|
+
type: "form",
|
|
1402
|
+
groups: [
|
|
1403
|
+
{
|
|
1404
|
+
id: "general",
|
|
1405
|
+
label: "General",
|
|
1406
|
+
fields: sortedAttrs.map(attributeToField),
|
|
1407
|
+
order: 0
|
|
1408
|
+
}
|
|
1409
|
+
],
|
|
1410
|
+
order: 0
|
|
1411
|
+
};
|
|
1412
|
+
const config = {
|
|
1413
|
+
layout: "modal",
|
|
1414
|
+
tabs: [formTab]
|
|
1415
|
+
};
|
|
1416
|
+
return {
|
|
1417
|
+
name: "default-modal",
|
|
1418
|
+
label: "Default Modal View",
|
|
1419
|
+
object: object2.name,
|
|
1420
|
+
type: "detail",
|
|
1421
|
+
config,
|
|
1422
|
+
default: true
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
function generateKanbanListView(object2, groupByAttribute, options = {}) {
|
|
1426
|
+
const { excludeAttributes = [], maxListColumns = 5 } = options;
|
|
1427
|
+
const visibleAttrs = getVisibleAttributes(object2.attributes, excludeAttributes);
|
|
1428
|
+
const sortedAttrs = sortAttributes(visibleAttrs);
|
|
1429
|
+
const columns = sortedAttrs.filter((attr) => attr.name !== groupByAttribute).slice(0, maxListColumns).map((attr) => attr.name);
|
|
1430
|
+
const config = {
|
|
1431
|
+
layout: "kanban",
|
|
1432
|
+
columns,
|
|
1433
|
+
groupByAttribute
|
|
1434
|
+
};
|
|
1435
|
+
return {
|
|
1436
|
+
name: "kanban",
|
|
1437
|
+
label: "Kanban View",
|
|
1438
|
+
object: object2.name,
|
|
1439
|
+
type: "list",
|
|
1440
|
+
config,
|
|
1441
|
+
default: false
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
// src/views/view-reset.ts
|
|
1446
|
+
function getPreservedDetailTabs(view2, options) {
|
|
1447
|
+
const { preserveCustomTabs = true, preserveTableTabs = true } = options;
|
|
1448
|
+
const preserved = [];
|
|
1449
|
+
for (const tab of view2.config.tabs) {
|
|
1450
|
+
if (preserveCustomTabs && tab.type === "custom") {
|
|
1451
|
+
preserved.push(tab);
|
|
1452
|
+
}
|
|
1453
|
+
if (preserveTableTabs && tab.type === "table") {
|
|
1454
|
+
preserved.push(tab);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
return preserved;
|
|
1458
|
+
}
|
|
1459
|
+
function mergeDetailTabs(generatedTabs, preservedTabs) {
|
|
1460
|
+
if (preservedTabs.length === 0) {
|
|
1461
|
+
return generatedTabs;
|
|
1462
|
+
}
|
|
1463
|
+
const formTab = generatedTabs.find((t) => t.type === "form");
|
|
1464
|
+
const otherTabs = generatedTabs.filter((t) => t.type !== "form");
|
|
1465
|
+
const result = [];
|
|
1466
|
+
if (formTab) {
|
|
1467
|
+
result.push(formTab);
|
|
1468
|
+
}
|
|
1469
|
+
let order = 1;
|
|
1470
|
+
for (const tab of preservedTabs) {
|
|
1471
|
+
result.push({ ...tab, order: order++ });
|
|
1472
|
+
}
|
|
1473
|
+
for (const tab of otherTabs) {
|
|
1474
|
+
result.push({ ...tab, order: order++ });
|
|
1475
|
+
}
|
|
1476
|
+
return result;
|
|
1477
|
+
}
|
|
1478
|
+
function resetDetailViewToDefault(view2, object2, options) {
|
|
1479
|
+
const { preserveMetadata = true, ...generateOptions } = options;
|
|
1480
|
+
const defaultView = generateDefaultDetailView(object2, generateOptions);
|
|
1481
|
+
const preservedTabs = getPreservedDetailTabs(view2, options);
|
|
1482
|
+
const mergedTabs = mergeDetailTabs(defaultView.config.tabs, preservedTabs);
|
|
1483
|
+
const config = {
|
|
1484
|
+
...defaultView.config,
|
|
1485
|
+
tabs: mergedTabs
|
|
1486
|
+
};
|
|
1487
|
+
const resetView = {
|
|
1488
|
+
...defaultView,
|
|
1489
|
+
config
|
|
1490
|
+
};
|
|
1491
|
+
if (preserveMetadata) {
|
|
1492
|
+
resetView.id = view2.id;
|
|
1493
|
+
resetView.name = view2.name;
|
|
1494
|
+
resetView.label = view2.label;
|
|
1495
|
+
resetView.description = view2.description;
|
|
1496
|
+
resetView.icon = view2.icon;
|
|
1497
|
+
}
|
|
1498
|
+
return resetView;
|
|
1499
|
+
}
|
|
1500
|
+
function resetListViewToDefault(view2, object2, options) {
|
|
1501
|
+
const { preserveMetadata = true, ...generateOptions } = options;
|
|
1502
|
+
const defaultView = generateDefaultListView(object2, generateOptions);
|
|
1503
|
+
const resetView = {
|
|
1504
|
+
...defaultView
|
|
1505
|
+
};
|
|
1506
|
+
if (preserveMetadata) {
|
|
1507
|
+
resetView.id = view2.id;
|
|
1508
|
+
resetView.name = view2.name;
|
|
1509
|
+
resetView.label = view2.label;
|
|
1510
|
+
resetView.description = view2.description;
|
|
1511
|
+
resetView.icon = view2.icon;
|
|
1512
|
+
}
|
|
1513
|
+
return resetView;
|
|
1514
|
+
}
|
|
1515
|
+
function resetViewToDefault(currentView, object2, options = {}) {
|
|
1516
|
+
if (isDetailView(currentView)) {
|
|
1517
|
+
return resetDetailViewToDefault(currentView, object2, options);
|
|
1518
|
+
}
|
|
1519
|
+
if (isListView(currentView)) {
|
|
1520
|
+
return resetListViewToDefault(currentView, object2, options);
|
|
1521
|
+
}
|
|
1522
|
+
throw new Error(`Reset not supported for view type: ${currentView.type}`);
|
|
1523
|
+
}
|
|
1524
|
+
function isDetailViewCustomized(view2, object2) {
|
|
1525
|
+
const defaultView = generateDefaultDetailView(object2);
|
|
1526
|
+
const viewFormTab = view2.config.tabs.find((t) => t.type === "form");
|
|
1527
|
+
const defaultFormTab = defaultView.config.tabs.find((t) => t.type === "form");
|
|
1528
|
+
if (!(viewFormTab && defaultFormTab)) {
|
|
1529
|
+
return true;
|
|
1530
|
+
}
|
|
1531
|
+
if (viewFormTab.type !== "form" || defaultFormTab.type !== "form") {
|
|
1532
|
+
return true;
|
|
1533
|
+
}
|
|
1534
|
+
if (viewFormTab.groups.length !== defaultFormTab.groups.length) {
|
|
1535
|
+
return true;
|
|
1536
|
+
}
|
|
1537
|
+
for (let i = 0; i < viewFormTab.groups.length; i++) {
|
|
1538
|
+
const viewGroup = viewFormTab.groups[i];
|
|
1539
|
+
const defaultGroup = defaultFormTab.groups[i];
|
|
1540
|
+
if (viewGroup.id !== defaultGroup.id) {
|
|
1541
|
+
return true;
|
|
1542
|
+
}
|
|
1543
|
+
if (viewGroup.fields.length !== defaultGroup.fields.length) {
|
|
1544
|
+
return true;
|
|
1545
|
+
}
|
|
1546
|
+
for (let j = 0; j < viewGroup.fields.length; j++) {
|
|
1547
|
+
if (viewGroup.fields[j].attribute !== defaultGroup.fields[j].attribute) {
|
|
1548
|
+
return true;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
return false;
|
|
1553
|
+
}
|
|
1554
|
+
function isListViewCustomized(view2, object2) {
|
|
1555
|
+
const defaultView = generateDefaultListView(object2);
|
|
1556
|
+
if (view2.config.columns.length !== defaultView.config.columns.length) {
|
|
1557
|
+
return true;
|
|
1558
|
+
}
|
|
1559
|
+
for (let i = 0; i < view2.config.columns.length; i++) {
|
|
1560
|
+
if (view2.config.columns[i] !== defaultView.config.columns[i]) {
|
|
1561
|
+
return true;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
if (view2.config.layout !== defaultView.config.layout) {
|
|
1565
|
+
return true;
|
|
1566
|
+
}
|
|
1567
|
+
return false;
|
|
1568
|
+
}
|
|
1569
|
+
function isViewCustomized(view2, object2) {
|
|
1570
|
+
if (isDetailView(view2)) {
|
|
1571
|
+
return isDetailViewCustomized(view2, object2);
|
|
1572
|
+
}
|
|
1573
|
+
if (isListView(view2)) {
|
|
1574
|
+
return isListViewCustomized(view2, object2);
|
|
1575
|
+
}
|
|
1576
|
+
return true;
|
|
1577
|
+
}
|
|
680
1578
|
export {
|
|
681
1579
|
ALL_SYSTEM_RESOURCES,
|
|
682
1580
|
ActivityTabConfig,
|
|
@@ -684,6 +1582,7 @@ export {
|
|
|
684
1582
|
AttributeNotFoundError,
|
|
685
1583
|
AuditService,
|
|
686
1584
|
AuthMethodSchema,
|
|
1585
|
+
BEHAVIOR_PROPERTIES,
|
|
687
1586
|
BaseRepository,
|
|
688
1587
|
BaseService,
|
|
689
1588
|
ConcurrentModificationError,
|
|
@@ -702,6 +1601,7 @@ export {
|
|
|
702
1601
|
DEFAULT_THEME,
|
|
703
1602
|
DEFAULT_VALIDATION_MESSAGES,
|
|
704
1603
|
DRIVING_LICENSE,
|
|
1604
|
+
DetailViewBuilder,
|
|
705
1605
|
DirectTableTabConfig,
|
|
706
1606
|
DocumentExecutor,
|
|
707
1607
|
DocumentGenerationNotConfiguredError,
|
|
@@ -721,8 +1621,11 @@ export {
|
|
|
721
1621
|
EndNodeSchema,
|
|
722
1622
|
ExecutorRegistry,
|
|
723
1623
|
FRENCH_ID_CARD,
|
|
1624
|
+
FeatureFlagsContextError,
|
|
724
1625
|
FileNotFoundError,
|
|
725
1626
|
FileService,
|
|
1627
|
+
FlagRegistry,
|
|
1628
|
+
FlagService,
|
|
726
1629
|
FlowRowFieldSchema,
|
|
727
1630
|
FlowRowSchema,
|
|
728
1631
|
FlowsTabConfig,
|
|
@@ -738,12 +1641,15 @@ export {
|
|
|
738
1641
|
GrantNotFoundError,
|
|
739
1642
|
GrantRevokedError,
|
|
740
1643
|
GroupBuilder,
|
|
1644
|
+
IDENTITY_PROPERTIES,
|
|
741
1645
|
InvalidPathError,
|
|
742
1646
|
InverseTableTabConfig,
|
|
743
1647
|
InvitationAlreadyAcceptedError,
|
|
744
1648
|
InvitationExpiredError,
|
|
745
1649
|
InvitationNotFoundError,
|
|
746
1650
|
InvitationRevokedError,
|
|
1651
|
+
ListViewBuilder,
|
|
1652
|
+
ListViewTabConfigBuilder,
|
|
747
1653
|
MaxDepthExceededError,
|
|
748
1654
|
NOTES,
|
|
749
1655
|
NO_VALUE_OPERATORS,
|
|
@@ -760,6 +1666,7 @@ export {
|
|
|
760
1666
|
ObjectReferencedError,
|
|
761
1667
|
ObjectSchemaService,
|
|
762
1668
|
PASSPORT,
|
|
1669
|
+
PRESENTATION_PROPERTIES,
|
|
763
1670
|
PROOF_OF_ADDRESS,
|
|
764
1671
|
PermissionService,
|
|
765
1672
|
PolicyRegistry,
|
|
@@ -836,6 +1743,7 @@ export {
|
|
|
836
1743
|
asTenantId,
|
|
837
1744
|
asUserId,
|
|
838
1745
|
attributeConfigSchemas,
|
|
1746
|
+
booleanFlag,
|
|
839
1747
|
buildAuditChanges,
|
|
840
1748
|
buildPolicyContext,
|
|
841
1749
|
cacheKeys,
|
|
@@ -866,6 +1774,8 @@ export {
|
|
|
866
1774
|
createDraftValidator,
|
|
867
1775
|
createEmptyContext,
|
|
868
1776
|
createFileValidator,
|
|
1777
|
+
createFlagRegistry,
|
|
1778
|
+
createFlagService,
|
|
869
1779
|
createFormAttributeValidator,
|
|
870
1780
|
createFormulaValidator,
|
|
871
1781
|
createLocationValidator,
|
|
@@ -893,6 +1803,7 @@ export {
|
|
|
893
1803
|
dateConfigSchema,
|
|
894
1804
|
defaultPolicyRegistry,
|
|
895
1805
|
defaultTtl,
|
|
1806
|
+
detailView,
|
|
896
1807
|
document,
|
|
897
1808
|
documentConfigSchema,
|
|
898
1809
|
enrichRecordsWithFormulas,
|
|
@@ -916,6 +1827,8 @@ export {
|
|
|
916
1827
|
extractRelationReferences,
|
|
917
1828
|
file,
|
|
918
1829
|
fileConfigSchema,
|
|
1830
|
+
filterPropertiesByCategory,
|
|
1831
|
+
flagRegistry,
|
|
919
1832
|
flattenRelationsForEval,
|
|
920
1833
|
formatAttributeValue,
|
|
921
1834
|
formatFormulaResult,
|
|
@@ -924,17 +1837,25 @@ export {
|
|
|
924
1837
|
formula,
|
|
925
1838
|
formulaConfigSchema,
|
|
926
1839
|
generateCssVariables,
|
|
1840
|
+
generateDefaultDetailView,
|
|
1841
|
+
generateDefaultListView,
|
|
1842
|
+
generateFallbackView,
|
|
927
1843
|
generateId,
|
|
1844
|
+
generateKanbanListView,
|
|
1845
|
+
generateModalDetailView,
|
|
928
1846
|
generatePrefixedId,
|
|
929
1847
|
generateTemplateName,
|
|
930
1848
|
getAttributeConfigSchema,
|
|
931
1849
|
getContext,
|
|
932
1850
|
getContextValue,
|
|
933
1851
|
getDefaultExecutorRegistry,
|
|
1852
|
+
getFeatureFlags,
|
|
1853
|
+
getFeatureValue,
|
|
934
1854
|
getMissingRequiredAttributes,
|
|
935
1855
|
getNodeOutputs,
|
|
936
1856
|
getPathDepth,
|
|
937
1857
|
getPolicy,
|
|
1858
|
+
getPropertyProtectionLevel,
|
|
938
1859
|
getRelationPath,
|
|
939
1860
|
getSchemaByNameFromContext,
|
|
940
1861
|
getSchemaContext,
|
|
@@ -945,9 +1866,11 @@ export {
|
|
|
945
1866
|
getTargetAttributeName,
|
|
946
1867
|
getTenantId,
|
|
947
1868
|
getUserId,
|
|
1869
|
+
getViewSeedPreview,
|
|
948
1870
|
getViewSyncPreview,
|
|
949
1871
|
group,
|
|
950
1872
|
hasContext,
|
|
1873
|
+
hasFeatureFlagsContext,
|
|
951
1874
|
hasRelationReferences,
|
|
952
1875
|
hasSchemaContext,
|
|
953
1876
|
hashOptions,
|
|
@@ -955,27 +1878,33 @@ export {
|
|
|
955
1878
|
isActivityTab,
|
|
956
1879
|
isAdvancedFilterState,
|
|
957
1880
|
isAdvancedFormNode,
|
|
1881
|
+
isBehaviorProperty,
|
|
1882
|
+
isCalendarView,
|
|
958
1883
|
isConcurrentModificationError,
|
|
959
1884
|
isConditionGroup,
|
|
960
1885
|
isConditionNode,
|
|
961
1886
|
isConditionRule,
|
|
962
1887
|
isCustomTab,
|
|
963
1888
|
isDefaultRole,
|
|
1889
|
+
isDetailView,
|
|
964
1890
|
isDirectTableTab,
|
|
965
1891
|
isDocumentNode,
|
|
966
1892
|
isDocumentsTab,
|
|
967
1893
|
isDocxTemplateSource,
|
|
968
1894
|
isEmpty,
|
|
969
1895
|
isEndNode,
|
|
1896
|
+
isFeatureEnabled,
|
|
970
1897
|
isFlowDefinition,
|
|
971
1898
|
isFlowPublished,
|
|
972
1899
|
isFlowsTab,
|
|
973
1900
|
isForbiddenError,
|
|
974
1901
|
isFormNode,
|
|
975
1902
|
isFormTab,
|
|
1903
|
+
isGalleryView,
|
|
976
1904
|
isGrantExpired,
|
|
977
1905
|
isGrantRevoked,
|
|
978
1906
|
isGrantValid,
|
|
1907
|
+
isIdentityProperty,
|
|
979
1908
|
isInstanceEvent,
|
|
980
1909
|
isInstanceTerminal,
|
|
981
1910
|
isInstanceWaiting,
|
|
@@ -985,12 +1914,14 @@ export {
|
|
|
985
1914
|
isInvitationOrGrantEvent,
|
|
986
1915
|
isInvitationValid,
|
|
987
1916
|
isLabelExpression,
|
|
1917
|
+
isListView,
|
|
988
1918
|
isNoValueOperator,
|
|
989
1919
|
isNodeEvent,
|
|
990
1920
|
isNotEmpty,
|
|
991
1921
|
isNotFoundError,
|
|
992
1922
|
isNotesTab,
|
|
993
1923
|
isPdfTemplateSource,
|
|
1924
|
+
isPresentationProperty,
|
|
994
1925
|
isProtectedResourceError,
|
|
995
1926
|
isRecordComplete,
|
|
996
1927
|
isSchemaError,
|
|
@@ -1002,11 +1933,15 @@ export {
|
|
|
1002
1933
|
isSystemTemplate,
|
|
1003
1934
|
isSystemWorkflow,
|
|
1004
1935
|
isTableTab,
|
|
1936
|
+
isTimelineView,
|
|
1005
1937
|
isTokenRevoked,
|
|
1006
1938
|
isUniversalRelation,
|
|
1007
1939
|
isValidationError,
|
|
1940
|
+
isViewCustomized,
|
|
1008
1941
|
isWorkflowDefinition,
|
|
1009
1942
|
isWorkflowPublished,
|
|
1943
|
+
jsonFlag,
|
|
1944
|
+
listView,
|
|
1010
1945
|
location,
|
|
1011
1946
|
locationConfigSchema,
|
|
1012
1947
|
mergeFormToSlot,
|
|
@@ -1017,6 +1952,7 @@ export {
|
|
|
1017
1952
|
notesPolicy,
|
|
1018
1953
|
number,
|
|
1019
1954
|
numberConfigSchema,
|
|
1955
|
+
numberFlag,
|
|
1020
1956
|
object,
|
|
1021
1957
|
or,
|
|
1022
1958
|
parseAttributeConfig,
|
|
@@ -1031,6 +1967,7 @@ export {
|
|
|
1031
1967
|
relation,
|
|
1032
1968
|
relationConfigSchema,
|
|
1033
1969
|
renderLabelExpression,
|
|
1970
|
+
resetViewToDefault,
|
|
1034
1971
|
resolveMultiplePaths,
|
|
1035
1972
|
resolveSingleValue,
|
|
1036
1973
|
richtext,
|
|
@@ -1038,15 +1975,18 @@ export {
|
|
|
1038
1975
|
rollup,
|
|
1039
1976
|
rollupConfigSchema,
|
|
1040
1977
|
runWithContext,
|
|
1978
|
+
runWithFeatureFlags,
|
|
1041
1979
|
runWithMergedSchemaContext,
|
|
1042
1980
|
runWithSchemaContext,
|
|
1043
1981
|
safeParseAttributeConfig,
|
|
1982
|
+
seedRegistryViews,
|
|
1044
1983
|
select,
|
|
1045
1984
|
selectConfigSchema,
|
|
1046
1985
|
setContextValue,
|
|
1047
1986
|
slugify,
|
|
1048
1987
|
status,
|
|
1049
1988
|
statusConfigSchema,
|
|
1989
|
+
stringFlag,
|
|
1050
1990
|
success,
|
|
1051
1991
|
syncAll,
|
|
1052
1992
|
syncNativeObjects,
|
|
@@ -1058,6 +1998,7 @@ export {
|
|
|
1058
1998
|
toAdvancedFilterState,
|
|
1059
1999
|
toSimpleFilterState,
|
|
1060
2000
|
traversePath,
|
|
2001
|
+
tryGetFeatureValue,
|
|
1061
2002
|
user,
|
|
1062
2003
|
userConfigSchema,
|
|
1063
2004
|
validateAttribute,
|
|
@@ -1070,9 +2011,11 @@ export {
|
|
|
1070
2011
|
validatePath,
|
|
1071
2012
|
verifyNativeObjectsSync,
|
|
1072
2013
|
verifyNativeViewsSync,
|
|
2014
|
+
verifyRegistryViewsSeeded,
|
|
1073
2015
|
view,
|
|
1074
2016
|
viewRegistry,
|
|
1075
2017
|
wait,
|
|
2018
|
+
withFeatureFlags,
|
|
1076
2019
|
withTenantContext,
|
|
1077
2020
|
workflow
|
|
1078
2021
|
};
|