@gooddata/sdk-ui-catalog 11.48.0-alpha.1 → 11.48.0-alpha.2

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.
Files changed (123) hide show
  1. package/esm/AnalyticsCatalogDetail.d.ts.map +1 -1
  2. package/esm/AnalyticsCatalogDetail.js +3 -3
  3. package/esm/asCode/AsCodeCreateDialog.d.ts +17 -0
  4. package/esm/asCode/AsCodeCreateDialog.d.ts.map +1 -0
  5. package/esm/asCode/AsCodeCreateDialog.js +51 -0
  6. package/esm/asCode/AsCodeDeleteDialog.d.ts +17 -0
  7. package/esm/asCode/AsCodeDeleteDialog.d.ts.map +1 -0
  8. package/esm/asCode/AsCodeDeleteDialog.js +55 -0
  9. package/esm/asCode/AsCodeDetailActions.d.ts +27 -0
  10. package/esm/asCode/AsCodeDetailActions.d.ts.map +1 -0
  11. package/esm/asCode/AsCodeDetailActions.js +66 -0
  12. package/esm/asCode/AsCodeDialog.d.ts +24 -0
  13. package/esm/asCode/AsCodeDialog.d.ts.map +1 -0
  14. package/esm/asCode/AsCodeDialog.js +94 -0
  15. package/esm/asCode/AsCodeEditDialog.d.ts +18 -0
  16. package/esm/asCode/AsCodeEditDialog.d.ts.map +1 -0
  17. package/esm/asCode/AsCodeEditDialog.js +38 -0
  18. package/esm/asCode/AsCodeEditorBody.d.ts +16 -0
  19. package/esm/asCode/AsCodeEditorBody.d.ts.map +1 -0
  20. package/esm/asCode/AsCodeEditorBody.js +10 -0
  21. package/esm/asCode/AsCodeMutationContext.d.ts +24 -0
  22. package/esm/asCode/AsCodeMutationContext.d.ts.map +1 -0
  23. package/esm/asCode/AsCodeMutationContext.js +40 -0
  24. package/esm/asCode/copy.d.ts +18 -0
  25. package/esm/asCode/copy.d.ts.map +1 -0
  26. package/esm/asCode/copy.js +58 -0
  27. package/esm/asCode/descriptor.d.ts +160 -0
  28. package/esm/asCode/descriptor.d.ts.map +1 -0
  29. package/esm/asCode/descriptor.js +8 -0
  30. package/esm/asCode/gate.d.ts +7 -0
  31. package/esm/asCode/gate.d.ts.map +1 -0
  32. package/esm/asCode/gate.js +11 -0
  33. package/esm/asCode/useAsCodeBase.d.ts +18 -0
  34. package/esm/asCode/useAsCodeBase.d.ts.map +1 -0
  35. package/esm/asCode/useAsCodeBase.js +27 -0
  36. package/esm/asCode/validateYaml.d.ts +39 -0
  37. package/esm/asCode/validateYaml.d.ts.map +1 -0
  38. package/esm/asCode/validateYaml.js +28 -0
  39. package/esm/asCodeRegistry.d.ts +20 -0
  40. package/esm/asCodeRegistry.d.ts.map +1 -0
  41. package/esm/asCodeRegistry.js +36 -0
  42. package/esm/catalog/Catalog.d.ts.map +1 -1
  43. package/esm/catalog/Catalog.js +4 -4
  44. package/esm/catalogDetail/CatalogDetailActions.d.ts +4 -2
  45. package/esm/catalogDetail/CatalogDetailActions.d.ts.map +1 -1
  46. package/esm/catalogDetail/CatalogDetailActions.js +22 -78
  47. package/esm/catalogDetail/CatalogDetailContent.d.ts.map +1 -1
  48. package/esm/catalogDetail/CatalogDetailContent.js +1 -3
  49. package/esm/catalogItem/query.d.ts +4 -2
  50. package/esm/catalogItem/query.d.ts.map +1 -1
  51. package/esm/catalogItem/query.js +6 -6
  52. package/esm/header/CreateObjectButton.d.ts.map +1 -1
  53. package/esm/header/CreateObjectButton.js +29 -28
  54. package/esm/localization/bundles/en-US.localization-bundle.d.ts +16 -24
  55. package/esm/localization/bundles/en-US.localization-bundle.d.ts.map +1 -1
  56. package/esm/localization/bundles/en-US.localization-bundle.js +16 -24
  57. package/esm/metric/gate.d.ts +3 -4
  58. package/esm/metric/gate.d.ts.map +1 -1
  59. package/esm/metric/gate.js +5 -10
  60. package/esm/metric/metricCopy.d.ts +0 -1
  61. package/esm/metric/metricCopy.d.ts.map +1 -1
  62. package/esm/metric/metricCopy.js +3 -45
  63. package/esm/metric/metricDescriptor.d.ts +7 -0
  64. package/esm/metric/metricDescriptor.d.ts.map +1 -0
  65. package/esm/metric/metricDescriptor.js +74 -0
  66. package/esm/metric/metricMutationPort.d.ts +7 -10
  67. package/esm/metric/metricMutationPort.d.ts.map +1 -1
  68. package/esm/metric/metricMutationPort.js +15 -17
  69. package/esm/metric/metricValidation.d.ts.map +1 -1
  70. package/esm/metric/metricValidation.js +13 -32
  71. package/esm/metric/tests/metricMutationPort.test.utils.js +1 -1
  72. package/esm/parameter/gate.d.ts +3 -0
  73. package/esm/parameter/gate.d.ts.map +1 -1
  74. package/esm/parameter/gate.js +7 -5
  75. package/esm/parameter/parameterCopy.d.ts +0 -1
  76. package/esm/parameter/parameterCopy.d.ts.map +1 -1
  77. package/esm/parameter/parameterCopy.js +4 -41
  78. package/esm/parameter/parameterDescriptor.d.ts +7 -0
  79. package/esm/parameter/parameterDescriptor.d.ts.map +1 -0
  80. package/esm/parameter/parameterDescriptor.js +91 -0
  81. package/esm/parameter/parameterMutationPort.d.ts +5 -6
  82. package/esm/parameter/parameterMutationPort.d.ts.map +1 -1
  83. package/esm/parameter/parameterMutationPort.js +15 -2
  84. package/package.json +15 -15
  85. package/esm/metric/MetricCreateDialog.d.ts +0 -11
  86. package/esm/metric/MetricCreateDialog.d.ts.map +0 -1
  87. package/esm/metric/MetricCreateDialog.js +0 -55
  88. package/esm/metric/MetricDeleteDialog.d.ts +0 -9
  89. package/esm/metric/MetricDeleteDialog.d.ts.map +0 -1
  90. package/esm/metric/MetricDeleteDialog.js +0 -68
  91. package/esm/metric/MetricDetailActions.d.ts +0 -23
  92. package/esm/metric/MetricDetailActions.d.ts.map +0 -1
  93. package/esm/metric/MetricDetailActions.js +0 -101
  94. package/esm/metric/MetricDialog.d.ts +0 -14
  95. package/esm/metric/MetricDialog.d.ts.map +0 -1
  96. package/esm/metric/MetricDialog.js +0 -100
  97. package/esm/metric/MetricEditDialog.d.ts +0 -11
  98. package/esm/metric/MetricEditDialog.d.ts.map +0 -1
  99. package/esm/metric/MetricEditDialog.js +0 -51
  100. package/esm/metric/MetricMutationContext.d.ts +0 -16
  101. package/esm/metric/MetricMutationContext.d.ts.map +0 -1
  102. package/esm/metric/MetricMutationContext.js +0 -32
  103. package/esm/metric/MetricYamlEditor.d.ts +0 -8
  104. package/esm/metric/MetricYamlEditor.d.ts.map +0 -1
  105. package/esm/metric/MetricYamlEditor.js +0 -12
  106. package/esm/parameter/ParameterCreateDialog.d.ts +0 -9
  107. package/esm/parameter/ParameterCreateDialog.d.ts.map +0 -1
  108. package/esm/parameter/ParameterCreateDialog.js +0 -56
  109. package/esm/parameter/ParameterDeleteDialog.d.ts +0 -9
  110. package/esm/parameter/ParameterDeleteDialog.d.ts.map +0 -1
  111. package/esm/parameter/ParameterDeleteDialog.js +0 -39
  112. package/esm/parameter/ParameterDialog.d.ts +0 -13
  113. package/esm/parameter/ParameterDialog.d.ts.map +0 -1
  114. package/esm/parameter/ParameterDialog.js +0 -106
  115. package/esm/parameter/ParameterEditDialog.d.ts +0 -11
  116. package/esm/parameter/ParameterEditDialog.d.ts.map +0 -1
  117. package/esm/parameter/ParameterEditDialog.js +0 -35
  118. package/esm/parameter/ParameterMutationContext.d.ts +0 -16
  119. package/esm/parameter/ParameterMutationContext.d.ts.map +0 -1
  120. package/esm/parameter/ParameterMutationContext.js +0 -32
  121. package/esm/parameter/ParameterYamlEditor.d.ts +0 -10
  122. package/esm/parameter/ParameterYamlEditor.d.ts.map +0 -1
  123. package/esm/parameter/ParameterYamlEditor.js +0 -16
@@ -0,0 +1,58 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { isUnexpectedResponseError } from "@gooddata/sdk-backend-spi";
3
+ const canonicalUuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4
+ const trailingCopySuffixRegex = /^(.*) \((\d+)\)$/;
5
+ /**
6
+ * Derives the identity (id + title) of a copied as-code object from the original; the remaining
7
+ * fields are copied by each type.
8
+ *
9
+ * The title gains a ` (2)` suffix, or increments an existing ` (N)`. The id is re-derived from that
10
+ * title or dropped (see `shouldOmitCopiedId`). Absent fields are left off so a spread does not
11
+ * introduce them.
12
+ */
13
+ export function deriveCopyIdentity(source) {
14
+ const title = getCopiedTitle(source.title);
15
+ const id = shouldOmitCopiedId(source.id) ? undefined : getCopiedId(title);
16
+ return {
17
+ ...(id === undefined ? {} : { id }),
18
+ ...(title === undefined ? {} : { title }),
19
+ };
20
+ }
21
+ function getCopiedTitle(title) {
22
+ if (title === undefined) {
23
+ return undefined;
24
+ }
25
+ if (title === "") {
26
+ return "";
27
+ }
28
+ const match = title.match(trailingCopySuffixRegex);
29
+ if (match) {
30
+ const [, prefix, suffix] = match;
31
+ return `${prefix} (${Number(suffix) + 1})`;
32
+ }
33
+ return `${title} (2)`;
34
+ }
35
+ // A UUID id was server-generated, so the copy omits its id and lets the server generate a fresh one.
36
+ // A human-authored id (e.g. "revenue.total") is instead re-derived from the copied title to stay
37
+ // readable; a resulting collision is handled by the create 409 retry-without-id.
38
+ function shouldOmitCopiedId(id) {
39
+ return id === undefined || isCanonicalUuid(id);
40
+ }
41
+ function getCopiedId(title) {
42
+ if (!title) {
43
+ return undefined;
44
+ }
45
+ const normalizedTitle = title
46
+ .normalize("NFKD")
47
+ .replace(/[\u0300-\u036f]/g, "")
48
+ .replace(/\W/g, "_")
49
+ .toLowerCase();
50
+ return normalizedTitle || undefined;
51
+ }
52
+ function isCanonicalUuid(value) {
53
+ return canonicalUuidRegex.test(value);
54
+ }
55
+ /** A create failed because the chosen id already exists (backend responds with HTTP 409). */
56
+ export function isDuplicateIdError(error) {
57
+ return isUnexpectedResponseError(error) && error.httpStatus === 409;
58
+ }
@@ -0,0 +1,160 @@
1
+ import type { CompletionSource } from "@codemirror/autocomplete";
2
+ import type { MessageDescriptor } from "react-intl";
3
+ import type { IAnalyticalBackend } from "@gooddata/sdk-backend-spi";
4
+ import type { ICatalogItem, ICatalogItemRef } from "../catalogItem/types.js";
5
+ import type { ObjectTypes } from "../objectType/constants.js";
6
+ import type { ObjectType } from "../objectType/types.js";
7
+ import type { useFeatureFlag } from "../permission/PermissionsContext.js";
8
+ /** A feature-flag name, as accepted by {@link useFeatureFlag}. */
9
+ export type AsCodeFeatureFlag = Parameters<typeof useFeatureFlag>[0];
10
+ /**
11
+ * The catalog object types editable as code. Add a member and the registry won't compile until it
12
+ * registers a descriptor for the new type, whose own type then forces every field.
13
+ */
14
+ export type AsCodeObjectType = typeof ObjectTypes.METRIC | typeof ObjectTypes.PARAMETER;
15
+ /**
16
+ * The one field the generic layer reads off any as-code definition: an optional identity. Everything
17
+ * else about a definition stays opaque to that layer.
18
+ */
19
+ export interface IAsCodeDefinition {
20
+ id?: string;
21
+ }
22
+ /**
23
+ * The entity-specific strings a generic as-code dialog needs. The generic chrome (Create/Save/Cancel/
24
+ * Duplicate, the "Error:" label) is shared and lives in the dialog, not here — these are only the
25
+ * strings that name or describe the entity.
26
+ */
27
+ export interface IAsCodeMessages {
28
+ createTitle: MessageDescriptor;
29
+ editTitle: MessageDescriptor;
30
+ /** Label of the duplicate action (edit-dialog button + detail menu); per-entity — a metric says
31
+ * "Duplicate", a parameter "Save as new". */
32
+ duplicate: MessageDescriptor;
33
+ /** Default title seeded into a blank create. */
34
+ createDefaultTitle: MessageDescriptor;
35
+ createSuccess: MessageDescriptor;
36
+ updateSuccess: MessageDescriptor;
37
+ /** Heading above the editor, e.g. "Metric definition". */
38
+ sectionHeader: MessageDescriptor;
39
+ /** Tooltip explaining what the YAML should contain. */
40
+ sectionHeaderTooltip: MessageDescriptor;
41
+ /** Text of the docs link, e.g. "How to write a metric". */
42
+ help: MessageDescriptor;
43
+ /** Fallback shown when a save fails without a backend detail. */
44
+ submitError: MessageDescriptor;
45
+ /**
46
+ * Shown when fetching the object to edit fails. A type that defines `port.load` should provide it;
47
+ * without it a load failure closes the dialog silently. A type that never loads omits it.
48
+ */
49
+ loadError?: MessageDescriptor;
50
+ deleteTitle: MessageDescriptor;
51
+ /** Confirmation body; receives the object's `name` and a `b` bold-chunk renderer. */
52
+ deleteBody: MessageDescriptor;
53
+ deleteSubmit: MessageDescriptor;
54
+ deleteSuccess: MessageDescriptor;
55
+ deleteError: MessageDescriptor;
56
+ /**
57
+ * Warns that other objects depend on this one; receives their `count`. Only types that report a
58
+ * referencing count (see `port.getReferencingObjectsCount`) provide it.
59
+ */
60
+ deleteUsageWarning?: MessageDescriptor;
61
+ }
62
+ /**
63
+ * Outcome of validating YAML: the parsed definition, or an already-localized error message. The entity
64
+ * localizes the error itself, so there is no shared error-code vocabulary.
65
+ */
66
+ export type AsCodeValidation<TDef> = {
67
+ isValid: true;
68
+ definition: TDef;
69
+ } | {
70
+ isValid: false;
71
+ error: string;
72
+ };
73
+ /**
74
+ * The per-entity editing behavior. Everything here can depend on the entity's own runtime context
75
+ * (e.g. a parameter's enabled types), which is why it is delivered through a hook rather than as static
76
+ * fields: the entity gathers its context, the generic shell never has to know about it.
77
+ */
78
+ export interface IAsCodeEditing<TDef extends IAsCodeDefinition> {
79
+ /** Autocompletion source fed to the shared `<YamlEditor>`. */
80
+ completionSource: CompletionSource;
81
+ /** Localized message the editor's inline linter shows on a YAML syntax error. */
82
+ syntaxErrorMessage: string;
83
+ /** Renders a definition to the YAML seed shown in the editor. */
84
+ serialize(definition: TDef): string;
85
+ /**
86
+ * Parses + validates YAML, returning the definition or a localized error. Identity is fixed while
87
+ * editing via `fixedIdentifier`.
88
+ */
89
+ validate(value: string, options: {
90
+ fixedIdentifier?: string;
91
+ }): AsCodeValidation<TDef>;
92
+ }
93
+ /**
94
+ * Backend operations for one catalog as-code object type. Everything flows in the definition shape:
95
+ * `load` returns the full editable definition (a metric's loaded measure already is one), and `update`
96
+ * takes that as its `base`.
97
+ *
98
+ * `load` and `getReferencingObjectsCount` are optional capabilities. A type whose catalog item already
99
+ * holds everything the editor needs omits `load` and supplies `editSeed` on the descriptor instead; a
100
+ * type that tracks no referencing objects omits `getReferencingObjectsCount` (delete shows no warning).
101
+ */
102
+ export interface IAsCodeMutationPort<TDef extends IAsCodeDefinition = IAsCodeDefinition, TItem extends ICatalogItem = ICatalogItem> {
103
+ create(definition: TDef): Promise<TItem>;
104
+ update(base: TDef, definition: TDef): Promise<TItem>;
105
+ delete(ref: ICatalogItemRef): Promise<void>;
106
+ load?(item: TItem): Promise<TDef>;
107
+ /** How many objects reference this one — used to warn before deletion. */
108
+ getReferencingObjectsCount?(item: TItem): Promise<number>;
109
+ }
110
+ /**
111
+ * The contract one entity type implements so the shared as-code shell (dialogs, mutation ports, detail
112
+ * actions) can drive it. Adding a type is "write one descriptor and register it"; the shell never
113
+ * imports the concrete type.
114
+ *
115
+ * `useEditing` is a hook and differs in shape across types, so a component that calls it must bind to
116
+ * ONE descriptor per mount and remount when the type changes (the detail dispatch keys on `objectType`
117
+ * for this) — otherwise the hook order would shift and break the rules of hooks.
118
+ */
119
+ export interface IAsCodeDescriptor<TDef extends IAsCodeDefinition = IAsCodeDefinition, TItem extends ICatalogItem = ICatalogItem> {
120
+ objectType: ObjectType;
121
+ docsUrl: string;
122
+ /**
123
+ * Gates whether this type's in-catalog editor is offered, paired with the per-item edit
124
+ * permission. Omit it for a type that is always editable (no feature-flag gate).
125
+ */
126
+ featureFlag?: AsCodeFeatureFlag;
127
+ messages: IAsCodeMessages;
128
+ /** The entity's per-mount editing behavior; a hook so it can read its own context. */
129
+ useEditing(): IAsCodeEditing<TDef>;
130
+ /** Builds this type's backend adapter; the mutation provider calls it once per backend/workspace. */
131
+ createMutationPort(backend: IAnalyticalBackend, workspace: string): IAsCodeMutationPort<TDef, TItem>;
132
+ /** A blank definition for a fresh create, seeded with a default title. */
133
+ emptyDefinition(defaultTitle: string): TDef;
134
+ /**
135
+ * Maps a catalog item to its editable definition without a fetch. Provided by types whose item
136
+ * already holds everything the editor needs; a type that must fetch (see `port.load`) omits it.
137
+ * A descriptor provides exactly one of `editSeed` / `port.load`.
138
+ */
139
+ editSeed?(item: TItem): TDef;
140
+ /** Derives a duplicate's definition from a source definition (bumps title, re-derives identity). */
141
+ toCopy(source: TDef): TDef;
142
+ /**
143
+ * Overlays the author's parsed YAML edits onto the full definition, re-adding fields the YAML
144
+ * cannot express (metric: metricType/isHiddenFromKda). Used both to seed a duplicate from the open
145
+ * editor and to reconcile a duplicate on save. Omitted — meaning `(_, parsed) => parsed` — for
146
+ * types whose YAML round-trips 1:1 to the definition (parameter).
147
+ */
148
+ applyYamlEdits?(full: TDef, parsed: TDef): TDef;
149
+ /**
150
+ * Label of the action that opens the object in its standalone editor; declaring it signals the type
151
+ * has such an editor. Omit it for a type with none (a parameter).
152
+ */
153
+ openAction?: MessageDescriptor;
154
+ }
155
+ /**
156
+ * Identity helper that keeps a descriptor's generics inferred at authoring while letting the registry
157
+ * store it widened, so the generic components never have to narrow.
158
+ */
159
+ export declare function defineAsCodeDescriptor<TDef extends IAsCodeDefinition, TItem extends ICatalogItem>(descriptor: IAsCodeDescriptor<TDef, TItem>): IAsCodeDescriptor<TDef, TItem>;
160
+ //# sourceMappingURL=descriptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"descriptor.d.ts","sourceRoot":"","sources":["../../src/asCode/descriptor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ // (C) 2026 GoodData Corporation
2
+ /**
3
+ * Identity helper that keeps a descriptor's generics inferred at authoring while letting the registry
4
+ * store it widened, so the generic components never have to narrow.
5
+ */
6
+ export function defineAsCodeDescriptor(descriptor) {
7
+ return descriptor;
8
+ }
@@ -0,0 +1,7 @@
1
+ import { useFeatureFlag } from "../permission/PermissionsContext.js";
2
+ /**
3
+ * Whether the current user can manage a catalog as-code object of a type gated by `featureFlag`:
4
+ * the feature flag is on and the user can manage the workspace.
5
+ */
6
+ export declare function useCanManageAsCode(featureFlag: Parameters<typeof useFeatureFlag>[0]): boolean;
7
+ //# sourceMappingURL=gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/asCode/gate.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAA0B,MAAM,qCAAqC,CAAC;AAE7F;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAI7F"}
@@ -0,0 +1,11 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { useFeatureFlag, useWorkspacePermission } from "../permission/PermissionsContext.js";
3
+ /**
4
+ * Whether the current user can manage a catalog as-code object of a type gated by `featureFlag`:
5
+ * the feature flag is on and the user can manage the workspace.
6
+ */
7
+ export function useCanManageAsCode(featureFlag) {
8
+ const isEnabled = useFeatureFlag(featureFlag);
9
+ const canManageProject = useWorkspacePermission("canManageProject");
10
+ return isEnabled && canManageProject;
11
+ }
@@ -0,0 +1,18 @@
1
+ import type { ICatalogItem } from "../catalogItem/types.js";
2
+ import type { IAsCodeDefinition, IAsCodeDescriptor, IAsCodeMutationPort } from "./descriptor.js";
3
+ type AsCodeBase = {
4
+ /** The editable definition for the item, or `undefined` while loading / when no item was given. */
5
+ base: IAsCodeDefinition | undefined;
6
+ isLoading: boolean;
7
+ port: IAsCodeMutationPort;
8
+ };
9
+ /**
10
+ * Resolves an existing item's editable definition for the edit dialog and for the create dialog when
11
+ * duplicating. A fetching type (metric, `port.load`) resolves asynchronously; a mapping type
12
+ * (parameter, `editSeed`) resolves synchronously, so it never flashes a loading state. `onError` fires
13
+ * on a failed fetch; an `undefined` item (a blank create) resolves to nothing.
14
+ * @internal
15
+ */
16
+ export declare function useAsCodeBase(descriptor: IAsCodeDescriptor, item: ICatalogItem | undefined, onError: () => void): AsCodeBase;
17
+ export {};
18
+ //# sourceMappingURL=useAsCodeBase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAsCodeBase.d.ts","sourceRoot":"","sources":["../../src/asCode/useAsCodeBase.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEjG,KAAK,UAAU,GAAG;IACd,mGAAmG;IACnG,IAAI,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,mBAAmB,CAAC;CAC7B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,aAAa,CACzB,UAAU,EAAE,iBAAiB,EAC7B,IAAI,EAAE,YAAY,GAAG,SAAS,EAC9B,OAAO,EAAE,MAAM,IAAI,GACpB,UAAU,CAsBZ"}
@@ -0,0 +1,27 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { useCancelablePromise } from "@gooddata/sdk-ui";
3
+ import { useAsCodePort } from "./AsCodeMutationContext.js";
4
+ /**
5
+ * Resolves an existing item's editable definition for the edit dialog and for the create dialog when
6
+ * duplicating. A fetching type (metric, `port.load`) resolves asynchronously; a mapping type
7
+ * (parameter, `editSeed`) resolves synchronously, so it never flashes a loading state. `onError` fires
8
+ * on a failed fetch; an `undefined` item (a blank create) resolves to nothing.
9
+ * @internal
10
+ */
11
+ export function useAsCodeBase(descriptor, item, onError) {
12
+ const port = useAsCodePort(descriptor.objectType);
13
+ const load = port.load;
14
+ const { result: loaded } = useCancelablePromise({ promise: item && load ? () => load(item) : undefined, onError }, [item, port]);
15
+ if (item === undefined) {
16
+ return { base: undefined, isLoading: false, port };
17
+ }
18
+ if (load) {
19
+ return { base: loaded, isLoading: loaded === undefined, port };
20
+ }
21
+ if (!descriptor.editSeed) {
22
+ // A descriptor must provide exactly one of `port.load` / `editSeed`; without either there is
23
+ // no way to seed the editor. Fail loudly rather than open a silently-blank editor.
24
+ throw new Error(`As-code descriptor "${descriptor.objectType}" provides neither port.load nor editSeed.`);
25
+ }
26
+ return { base: descriptor.editSeed(item), isLoading: false, port };
27
+ }
@@ -0,0 +1,39 @@
1
+ import type * as z from "zod/mini";
2
+ /**
3
+ * Neutral result of the shared YAML validation skeleton, before a type shapes it into its own
4
+ * result. `data` is the parsed-and-schema-validated value; the caller converts it to a definition.
5
+ */
6
+ export type YamlValidation<TData, TErrorCode extends string> = {
7
+ ok: true;
8
+ data: TData;
9
+ } | {
10
+ ok: false;
11
+ errorCode: TErrorCode | "empty" | "syntax" | "idImmutable";
12
+ };
13
+ type SafeParseSchema<TData> = {
14
+ safeParse(value: unknown): {
15
+ success: true;
16
+ data: TData;
17
+ } | {
18
+ success: false;
19
+ error: z.core.$ZodError;
20
+ };
21
+ };
22
+ type ValidateYamlOptions<TData, TErrorCode extends string> = {
23
+ schema: SafeParseSchema<TData>;
24
+ /** Maps a schema failure to a type-specific error code. */
25
+ classifyError: (error: z.core.$ZodError, parsed: unknown) => TErrorCode;
26
+ /** When set, the parsed `id` must match it, else `idImmutable` (identity is fixed while editing). */
27
+ fixedIdentifier?: string;
28
+ };
29
+ /**
30
+ * Runs the steps every as-code YAML shares: reject empty input, parse (strict) YAML, validate
31
+ * against the schema, and enforce identity immutability. Type-specific concerns — the schema, how a
32
+ * schema failure maps to an error code, and how the validated data becomes a definition — are the
33
+ * caller's.
34
+ */
35
+ export declare function validateYaml<TData extends {
36
+ id?: string;
37
+ }, TErrorCode extends string>(value: string, { schema, classifyError, fixedIdentifier }: ValidateYamlOptions<TData, TErrorCode>): YamlValidation<TData, TErrorCode>;
38
+ export {};
39
+ //# sourceMappingURL=validateYaml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateYaml.d.ts","sourceRoot":"","sources":["../../src/asCode/validateYaml.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,CAAC,MAAM,UAAU,CAAC;AAEnC;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,EAAE,UAAU,SAAS,MAAM,IACrD;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,KAAK,CAAA;CAAE,GACzB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,aAAa,CAAA;CAAE,CAAC;AAEhF,KAAK,eAAe,CAAC,KAAK,IAAI;IAC1B,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,KAAK,CAAA;KAAE,GAAG;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAA;KAAE,CAAC;CAC3G,CAAC;AAEF,KAAK,mBAAmB,CAAC,KAAK,EAAE,UAAU,SAAS,MAAM,IAAI;IACzD,MAAM,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC;IAC/B,2DAA2D;IAC3D,aAAa,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,KAAK,UAAU,CAAC;IACxE,qGAAqG;IACrG,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,SAAS;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,UAAU,SAAS,MAAM,EACjF,KAAK,EAAE,MAAM,EACb,EAAE,MAAM,EAAE,aAAa,EAAE,eAAe,EAAE,EAAE,mBAAmB,CAAC,KAAK,EAAE,UAAU,CAAC,GACnF,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC,CAsBnC"}
@@ -0,0 +1,28 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { parse as parseYaml } from "yaml";
3
+ /**
4
+ * Runs the steps every as-code YAML shares: reject empty input, parse (strict) YAML, validate
5
+ * against the schema, and enforce identity immutability. Type-specific concerns — the schema, how a
6
+ * schema failure maps to an error code, and how the validated data becomes a definition — are the
7
+ * caller's.
8
+ */
9
+ export function validateYaml(value, { schema, classifyError, fixedIdentifier }) {
10
+ if (value.trim() === "") {
11
+ return { ok: false, errorCode: "empty" };
12
+ }
13
+ let parsed;
14
+ try {
15
+ parsed = parseYaml(value, { strict: true });
16
+ }
17
+ catch {
18
+ return { ok: false, errorCode: "syntax" };
19
+ }
20
+ const result = schema.safeParse(parsed);
21
+ if (!result.success) {
22
+ return { ok: false, errorCode: classifyError(result.error, parsed) };
23
+ }
24
+ if (fixedIdentifier !== undefined && result.data.id !== fixedIdentifier) {
25
+ return { ok: false, errorCode: "idImmutable" };
26
+ }
27
+ return { ok: true, data: result.data };
28
+ }
@@ -0,0 +1,20 @@
1
+ import type { AsCodeObjectType, IAsCodeDescriptor } from "./asCode/descriptor.js";
2
+ import type { ObjectType } from "./objectType/types.js";
3
+ /**
4
+ * Every as-code descriptor, keyed by type — the one place the concrete descriptors are registered. The
5
+ * key is the full `AsCodeObjectType` union, so a missing type is a compile error.
6
+ * @internal
7
+ */
8
+ export declare const asCodeDescriptors: Record<AsCodeObjectType, IAsCodeDescriptor>;
9
+ /**
10
+ * The as-code descriptor for a catalog object type, or `undefined` if the type is not editable as code.
11
+ * @internal
12
+ */
13
+ export declare function getAsCodeDescriptor(type: ObjectType): IAsCodeDescriptor | undefined;
14
+ /**
15
+ * Whether this descriptor's editor is enabled by the feature flags. A descriptor with no `featureFlag`
16
+ * is always editable; `undefined` (not an as-code type) never is.
17
+ * @internal
18
+ */
19
+ export declare function useIsAsCodeTypeEditable(descriptor: IAsCodeDescriptor | undefined): boolean;
20
+ //# sourceMappingURL=asCodeRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asCodeRegistry.d.ts","sourceRoot":"","sources":["../src/asCodeRegistry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGlF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAIxD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,gBAAgB,EAAE,iBAAiB,CAGzE,CAAC;AAMF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,UAAU,GAAG,iBAAiB,GAAG,SAAS,CAEnF;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,iBAAiB,GAAG,SAAS,GAAG,OAAO,CAM1F"}
@@ -0,0 +1,36 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { metricDescriptor } from "./metric/metricDescriptor.js";
3
+ import { ObjectTypes } from "./objectType/constants.js";
4
+ import { parameterDescriptor } from "./parameter/parameterDescriptor.js";
5
+ import { useFeatureFlags } from "./permission/PermissionsContext.js";
6
+ /**
7
+ * Every as-code descriptor, keyed by type — the one place the concrete descriptors are registered. The
8
+ * key is the full `AsCodeObjectType` union, so a missing type is a compile error.
9
+ * @internal
10
+ */
11
+ export const asCodeDescriptors = {
12
+ [ObjectTypes.METRIC]: metricDescriptor,
13
+ [ObjectTypes.PARAMETER]: parameterDescriptor,
14
+ };
15
+ function isAsCodeObjectType(type) {
16
+ return Object.prototype.hasOwnProperty.call(asCodeDescriptors, type);
17
+ }
18
+ /**
19
+ * The as-code descriptor for a catalog object type, or `undefined` if the type is not editable as code.
20
+ * @internal
21
+ */
22
+ export function getAsCodeDescriptor(type) {
23
+ return isAsCodeObjectType(type) ? asCodeDescriptors[type] : undefined;
24
+ }
25
+ /**
26
+ * Whether this descriptor's editor is enabled by the feature flags. A descriptor with no `featureFlag`
27
+ * is always editable; `undefined` (not an as-code type) never is.
28
+ * @internal
29
+ */
30
+ export function useIsAsCodeTypeEditable(descriptor) {
31
+ const flags = useFeatureFlags();
32
+ if (descriptor === undefined) {
33
+ return false;
34
+ }
35
+ return descriptor.featureFlag === undefined || Boolean(flags?.[descriptor.featureFlag]);
36
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"Catalog.d.ts","sourceRoot":"","sources":["../../src/catalog/Catalog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AAIxC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAIpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAO/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAYtE,KAAK,KAAK,GAAG;IACT,OAAO,EAAE,kBAAkB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,eAAe,CAAC;IACrC,sBAAsB,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACnF,uBAAuB,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC/E,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;IACvD,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,uBAAuB,KAAK,IAAI,CAAC;CAClE,CAAC;AAEF,wBAAgB,OAAO,CAAC,EACpB,kBAAkB,EAClB,OAAO,EACP,SAAS,EACT,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,EACjB,EAAE,KAAK,2CAwEP"}
1
+ {"version":3,"file":"Catalog.d.ts","sourceRoot":"","sources":["../../src/catalog/Catalog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AAIxC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAMpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAM/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAWtE,KAAK,KAAK,GAAG;IACT,OAAO,EAAE,kBAAkB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,eAAe,CAAC;IACrC,sBAAsB,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACnF,uBAAuB,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC/E,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;IACvD,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,uBAAuB,KAAK,IAAI,CAAC;CAClE,CAAC;AAEF,wBAAgB,OAAO,CAAC,EACpB,kBAAkB,EAClB,OAAO,EACP,SAAS,EACT,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,EACjB,EAAE,KAAK,2CAsEP"}
@@ -4,6 +4,8 @@ import {} from "react";
4
4
  import { useIntl } from "react-intl";
5
5
  import { ErrorComponent } from "@gooddata/sdk-ui";
6
6
  import { LoadingMask } from "@gooddata/sdk-ui-kit";
7
+ import { AsCodeMutationProvider } from "../asCode/AsCodeMutationContext.js";
8
+ import { asCodeDescriptors } from "../asCodeRegistry.js";
7
9
  import { CatalogFeedProvider } from "../catalogItem/CatalogFeedContext.js";
8
10
  import {} from "../catalogItem/types.js";
9
11
  import { CreateObjectButton } from "../header/CreateObjectButton.js";
@@ -11,9 +13,7 @@ import { Header } from "../header/Header.js";
11
13
  import { useCatalogItemOpen } from "../main/hooks/useCatalogItemOpen.js";
12
14
  import { Main } from "../main/Main.js";
13
15
  import { useCanManageMetric } from "../metric/gate.js";
14
- import { MetricMutationProvider } from "../metric/MetricMutationContext.js";
15
16
  import { useCanManageParameter } from "../parameter/gate.js";
16
- import { ParameterMutationProvider } from "../parameter/ParameterMutationContext.js";
17
17
  import { PermissionsGate } from "../permission/PermissionsGate.js";
18
18
  import { useIsCatalogQualityEnabled } from "../quality/gate.js";
19
19
  import { QualityScoreCard } from "../quality/QualityScoreCard.js";
@@ -32,6 +32,6 @@ export function Catalog({ openCatalogItemRef, backend, workspace, onCatalogItemO
32
32
  id: "analyticsCatalog.error.unknown.description",
33
33
  }) }), unauthorizedNode: _jsx(ErrorComponent, { message: intl.formatMessage({ id: "analyticsCatalog.error.unauthorized.message" }), description: intl.formatMessage({
34
34
  id: "analyticsCatalog.error.unauthorized.description",
35
- }) }), children: _jsx(CatalogFeedProvider, { backend: backend, workspace: workspace, children: _jsx(ParameterMutationProvider, { children: _jsxs(MetricMutationProvider, { children: [_jsx(Header, { searchNode: _jsx(FullTextSearchInput, {}), createNode: onCreateObject ? (_jsx(CreateObjectButton, { onCreateObject: onCreateObject, showParameter: showParameter, showMetricEditor: showMetricEditor })) : null }), isTrendingEnabled ? (_jsx(CatalogTabs, { onItemClick: onOpenDetail })) : isQualityEnabled ? (_jsx(QualityScoreCard, {})) : null, _jsx(Main, { workspace: workspace, backend: backend, open: open, openedItem: openedItem, setItemOpened: setItemOpened, onOpenDetail: onOpenDetail, onCloseDetail: onCloseDetail, onOpenClick: onOpenClick, onCatalogItemNavigation: onCatalogItemNavigation })
36
- ] }) }) }) }) }));
35
+ }) }), children: _jsx(CatalogFeedProvider, { backend: backend, workspace: workspace, children: _jsxs(AsCodeMutationProvider, { descriptors: asCodeDescriptors, children: [_jsx(Header, { searchNode: _jsx(FullTextSearchInput, {}), createNode: onCreateObject ? (_jsx(CreateObjectButton, { onCreateObject: onCreateObject, showParameter: showParameter, showMetricEditor: showMetricEditor })) : null }), isTrendingEnabled ? (_jsx(CatalogTabs, { onItemClick: onOpenDetail })) : isQualityEnabled ? (_jsx(QualityScoreCard, {})) : null, _jsx(Main, { workspace: workspace, backend: backend, open: open, openedItem: openedItem, setItemOpened: setItemOpened, onOpenDetail: onOpenDetail, onCloseDetail: onCloseDetail, onOpenClick: onOpenClick, onCatalogItemNavigation: onCatalogItemNavigation })
36
+ ] }) }) }) }));
37
37
  }
@@ -7,14 +7,16 @@ import type { OpenHandlerEvent } from "./types.js";
7
7
  export interface ICatalogDetailActionsProps {
8
8
  item: ICatalogItem;
9
9
  canEdit: boolean;
10
- isMetricEditorEnabled?: boolean;
11
10
  onOpen?: (event: MouseEvent, openEvent: OpenHandlerEvent) => void;
12
11
  onCatalogItemCreate?: (item: ICatalogItem) => void;
13
12
  onCatalogItemUpdate?: (item: ICatalogItem) => void;
14
13
  onCatalogItemDelete?: (ref: ICatalogItemRef) => void;
15
14
  }
16
15
  /**
16
+ * Detail-panel actions for a catalog item. An as-code-editable type — whose in-catalog editor is
17
+ * enabled and which the user may edit — gets the full inline edit/duplicate/delete via
18
+ * {@link AsCodeDetailActions}; every other type gets the Open (+ Share) fallback.
17
19
  * @internal
18
20
  */
19
- export declare function CatalogDetailActions({ item, canEdit, isMetricEditorEnabled, onOpen, onCatalogItemCreate, onCatalogItemUpdate, onCatalogItemDelete }: ICatalogDetailActionsProps): import("react/jsx-runtime").JSX.Element | null;
21
+ export declare function CatalogDetailActions({ item, canEdit, onOpen, onCatalogItemCreate, onCatalogItemUpdate, onCatalogItemDelete }: ICatalogDetailActionsProps): import("react/jsx-runtime").JSX.Element | null;
20
22
  //# sourceMappingURL=CatalogDetailActions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogDetailActions.d.ts","sourceRoot":"","sources":["../../src/catalogDetail/CatalogDetailActions.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAkC,MAAM,OAAO,CAAC;AAUxE,OAAO,KAAK,EAAE,YAAY,EAAyB,eAAe,EAAE,MAAM,yBAAyB,CAAC;AASpG,OAAO,KAAK,EAAwB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAczE;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClE,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACnD,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACnD,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;CACxD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,EACjC,IAAI,EACJ,OAAO,EACP,qBAAqB,EACrB,MAAM,EACN,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACtB,EAAE,0BAA0B,kDA4J5B"}
1
+ {"version":3,"file":"CatalogDetailActions.d.ts","sourceRoot":"","sources":["../../src/catalogDetail/CatalogDetailActions.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AASxC,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI7E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClE,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACnD,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACnD,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;CACxD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,EACjC,IAAI,EACJ,OAAO,EACP,MAAM,EACN,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACtB,EAAE,0BAA0B,kDAqD5B"}
@@ -1,95 +1,39 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // (C) 2026 GoodData Corporation
3
- import { useCallback, useMemo, useState } from "react";
4
- import { defineMessages, useIntl } from "react-intl";
3
+ import {} from "react";
4
+ import { useIntl } from "react-intl";
5
5
  import { useWorkspaceStrict } from "@gooddata/sdk-ui";
6
6
  import { UiButton } from "@gooddata/sdk-ui-kit";
7
- import { catalogDetailActionDelete, catalogDetailActionDuplicate } from "../automation/testIds.js";
8
- import { isCatalogItemMeasure, isCatalogItemParameter } from "../catalogItem/guards.js";
9
- import { MetricDetailActions } from "../metric/MetricDetailActions.js";
10
- import { ParameterCreateDialog } from "../parameter/ParameterCreateDialog.js";
11
- import { ParameterDeleteDialog } from "../parameter/ParameterDeleteDialog.js";
12
- import { ParameterEditDialog } from "../parameter/ParameterEditDialog.js";
13
- import { CatalogDetailActionBar } from "./CatalogDetailActionBar.js";
7
+ import { AsCodeDetailActions } from "../asCode/AsCodeDetailActions.js";
8
+ import { getAsCodeDescriptor, useIsAsCodeTypeEditable } from "../asCodeRegistry.js";
14
9
  import { useCatalogItemShareActions } from "./share/CatalogItemShareProvider.js";
15
10
  import { ShareButton } from "./share/ShareButton.js";
16
- const messages = defineMessages({
17
- parameterDuplicate: { id: "analyticsCatalog.parameter.dialog.edit.duplicate" },
18
- parameterDelete: { id: "analyticsCatalog.parameter.dialog.delete.submit" },
19
- });
20
11
  /**
12
+ * Detail-panel actions for a catalog item. An as-code-editable type — whose in-catalog editor is
13
+ * enabled and which the user may edit — gets the full inline edit/duplicate/delete via
14
+ * {@link AsCodeDetailActions}; every other type gets the Open (+ Share) fallback.
21
15
  * @internal
22
16
  */
23
- export function CatalogDetailActions({ item, canEdit, isMetricEditorEnabled, onOpen, onCatalogItemCreate, onCatalogItemUpdate, onCatalogItemDelete, }) {
17
+ export function CatalogDetailActions({ item, canEdit, onOpen, onCatalogItemCreate, onCatalogItemUpdate, onCatalogItemDelete, }) {
24
18
  const intl = useIntl();
25
19
  const workspaceId = useWorkspaceStrict();
26
- const [dialog, setDialog] = useState(undefined);
27
20
  // The Share button reads the share context directly (actions only), so it never
28
21
  // re-renders as access is edited and is shown only when sharing is available.
29
22
  const share = useCatalogItemShareActions();
30
- const parameterActions = useMemo(() => [
31
- {
32
- id: "duplicate",
33
- label: intl.formatMessage(messages.parameterDuplicate),
34
- dataTestId: catalogDetailActionDuplicate,
35
- },
36
- {
37
- id: "delete",
38
- label: intl.formatMessage(messages.parameterDelete),
39
- isDestructive: true,
40
- dataTestId: catalogDetailActionDelete,
41
- },
42
- ], [intl]);
43
- const closeDialog = useCallback(() => setDialog(undefined), []);
44
- const handleEditOpen = useCallback(() => {
45
- if (isCatalogItemParameter(item)) {
46
- setDialog({ kind: "edit", item });
47
- }
48
- }, [item]);
49
- const handleSaved = useCallback((saved) => {
50
- onCatalogItemUpdate?.(saved);
51
- }, [onCatalogItemUpdate]);
52
- const handleCreated = useCallback((created) => {
53
- onCatalogItemCreate?.(created);
54
- }, [onCatalogItemCreate]);
55
- const handleDeleted = useCallback(() => {
56
- if (isCatalogItemParameter(item)) {
57
- onCatalogItemDelete?.(item);
58
- }
59
- }, [item, onCatalogItemDelete]);
60
- const handleActionsMenuSelect = useCallback((actionId) => {
61
- if (!isCatalogItemParameter(item)) {
62
- return;
63
- }
64
- if (actionId === "duplicate" || actionId === "delete") {
65
- setDialog({ kind: actionId, item });
66
- }
67
- }, [item]);
68
- const handleEditDuplicate = useCallback((parameter) => {
69
- if (!isCatalogItemParameter(item)) {
70
- return;
71
- }
72
- setDialog({
73
- kind: "duplicate",
74
- item: {
75
- ...item,
76
- identifier: parameter.id ?? item.identifier,
77
- title: parameter.title ?? item.title,
78
- description: parameter.description ?? item.description,
79
- tags: parameter.tags ?? item.tags,
80
- definition: parameter.definition,
81
- },
82
- });
83
- }, [item]);
84
- if (isCatalogItemParameter(item)) {
85
- if (!canEdit) {
86
- return null;
87
- }
88
- return (_jsxs(_Fragment, { children: [
89
- _jsx(CatalogDetailActionBar, { item: item, workspaceId: workspaceId, actions: parameterActions, onEditClick: handleEditOpen, onActionsMenuSelect: handleActionsMenuSelect }), dialog?.kind === "edit" ? (_jsx(ParameterEditDialog, { item: dialog.item, onClose: closeDialog, onSaved: handleSaved, onDuplicate: handleEditDuplicate })) : null, dialog?.kind === "duplicate" ? (_jsx(ParameterCreateDialog, { sourceItem: dialog.item, onClose: closeDialog, onCreated: handleCreated })) : null, dialog?.kind === "delete" ? (_jsx(ParameterDeleteDialog, { item: dialog.item, onClose: closeDialog, onDeleted: handleDeleted })) : null] }));
23
+ const descriptor = getAsCodeDescriptor(item.type);
24
+ const isTypeEditable = useIsAsCodeTypeEditable(descriptor);
25
+ if (descriptor && canEdit && isTypeEditable) {
26
+ return (
27
+ // Keyed by type so the descriptor a mount uses never changes under it (its hooks must stay
28
+ // stable); switching to a different as-code type remounts with the correct descriptor.
29
+ _jsx(AsCodeDetailActions, { descriptor: descriptor, item: item, onOpen: onOpen, canShare: share.active, onShare: share.open, onCatalogItemCreate: onCatalogItemCreate, onCatalogItemUpdate: onCatalogItemUpdate, onCatalogItemDelete: onCatalogItemDelete }, descriptor.objectType));
90
30
  }
91
- if (isCatalogItemMeasure(item) && isMetricEditorEnabled && canEdit) {
92
- return (_jsx(MetricDetailActions, { item: item, onOpen: onOpen, canShare: share.active, onShare: share.open, onCatalogItemCreate: onCatalogItemCreate, onCatalogItemUpdate: onCatalogItemUpdate, onCatalogItemDelete: onCatalogItemDelete }));
31
+ // Not inline-editable here. The Open button opens the item in its native editor/view, offered only
32
+ // for a type that has one: any non-as-code type, or an as-code type that declares an open action
33
+ // (a metric, not a parameter).
34
+ const canOpen = !descriptor || descriptor.openAction !== undefined;
35
+ if (!canOpen) {
36
+ return null;
93
37
  }
94
38
  return (_jsxs("div", { className: "gd-analytics-catalog-detail__header-actions", children: [share.active ? _jsx(ShareButton, { onClick: share.open }) : null, _jsx(UiButton, { label: intl.formatMessage({ id: "analyticsCatalog.catalogItem.open" }), variant: "primary", accessibilityConfig: { role: "link" }, onClick: (event) => {
95
39
  onOpen?.(event, {