@gooddata/sdk-model 11.54.0-alpha.5 → 11.54.0-alpha.7

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 (77) hide show
  1. package/esm/dashboard/alerts.test.helpers.d.ts +5 -0
  2. package/esm/dashboard/alerts.test.helpers.d.ts.map +1 -0
  3. package/esm/dashboard/alerts.test.helpers.js +37 -0
  4. package/esm/dashboard/dashboard.d.ts +6 -0
  5. package/esm/dashboard/dashboard.d.ts.map +1 -1
  6. package/esm/dashboard/dashboard.js +21 -2
  7. package/esm/dashboard/drills.test.helpers.d.ts +7 -0
  8. package/esm/dashboard/drills.test.helpers.d.ts.map +1 -0
  9. package/esm/dashboard/drills.test.helpers.js +50 -0
  10. package/esm/dashboard/filterContext.test.helpers.d.ts +9 -0
  11. package/esm/dashboard/filterContext.test.helpers.d.ts.map +1 -0
  12. package/esm/dashboard/filterContext.test.helpers.js +42 -0
  13. package/esm/dashboard/kpi.test.helpers.d.ts +4 -0
  14. package/esm/dashboard/kpi.test.helpers.d.ts.map +1 -0
  15. package/esm/dashboard/kpi.test.helpers.js +11 -0
  16. package/esm/dashboard/parameter.test.helpers.d.ts +5 -0
  17. package/esm/dashboard/parameter.test.helpers.d.ts.map +1 -0
  18. package/esm/dashboard/parameter.test.helpers.js +20 -0
  19. package/esm/dashboard/widgets.test.helpers.d.ts +4 -0
  20. package/esm/dashboard/widgets.test.helpers.d.ts.map +1 -0
  21. package/esm/dashboard/widgets.test.helpers.js +16 -0
  22. package/esm/dateFilterConfig/index.d.ts +12 -0
  23. package/esm/dateFilterConfig/index.d.ts.map +1 -1
  24. package/esm/dateFilterConfig/index.js +11 -0
  25. package/esm/dateFilterConfig/typeGuards.test.helpers.d.ts +9 -0
  26. package/esm/dateFilterConfig/typeGuards.test.helpers.d.ts.map +1 -0
  27. package/esm/dateFilterConfig/typeGuards.test.helpers.js +56 -0
  28. package/esm/exports/csvDelimiter.d.ts +15 -7
  29. package/esm/exports/csvDelimiter.d.ts.map +1 -1
  30. package/esm/exports/csvDelimiter.js +12 -6
  31. package/esm/index.d.ts +11 -1
  32. package/esm/index.d.ts.map +1 -1
  33. package/esm/index.js +11 -1
  34. package/esm/ldm/catalog/computedAttribute/index.d.ts +38 -0
  35. package/esm/ldm/catalog/computedAttribute/index.d.ts.map +1 -0
  36. package/esm/ldm/catalog/computedAttribute/index.js +10 -0
  37. package/esm/ldm/catalog/index.d.ts +4 -3
  38. package/esm/ldm/catalog/index.d.ts.map +1 -1
  39. package/esm/ldm/catalog/index.js +4 -0
  40. package/esm/ldm/catalog/types.d.ts +3 -2
  41. package/esm/ldm/catalog/types.d.ts.map +1 -1
  42. package/esm/ldm/catalog/types.js +1 -1
  43. package/esm/ldm/metadata/computedAttribute/index.d.ts +102 -0
  44. package/esm/ldm/metadata/computedAttribute/index.d.ts.map +1 -0
  45. package/esm/ldm/metadata/computedAttribute/index.js +21 -0
  46. package/esm/ldm/metadata/index.d.ts +2 -1
  47. package/esm/ldm/metadata/index.d.ts.map +1 -1
  48. package/esm/objRef/index.d.ts +1 -1
  49. package/esm/objRef/index.d.ts.map +1 -1
  50. package/esm/reports/builtinPageLayouts.d.ts +27 -0
  51. package/esm/reports/builtinPageLayouts.d.ts.map +1 -0
  52. package/esm/reports/builtinPageLayouts.js +196 -0
  53. package/esm/reports/content.d.ts +54 -0
  54. package/esm/reports/content.d.ts.map +1 -0
  55. package/esm/reports/content.js +12 -0
  56. package/esm/reports/factory.d.ts +65 -0
  57. package/esm/reports/factory.d.ts.map +1 -0
  58. package/esm/reports/factory.js +126 -0
  59. package/esm/reports/layout.d.ts +59 -0
  60. package/esm/reports/layout.d.ts.map +1 -0
  61. package/esm/reports/layout.js +18 -0
  62. package/esm/reports/pageLayout.d.ts +120 -0
  63. package/esm/reports/pageLayout.d.ts.map +1 -0
  64. package/esm/reports/pageLayout.js +83 -0
  65. package/esm/reports/report.d.ts +116 -0
  66. package/esm/reports/report.d.ts.map +1 -0
  67. package/esm/reports/report.js +35 -0
  68. package/esm/reports/slot.d.ts +213 -0
  69. package/esm/reports/slot.d.ts.map +1 -0
  70. package/esm/reports/slot.js +40 -0
  71. package/esm/reports/variables.d.ts +62 -0
  72. package/esm/reports/variables.d.ts.map +1 -0
  73. package/esm/reports/variables.js +41 -0
  74. package/esm/sdk-model.d.ts +1026 -104
  75. package/esm/settings/settings.d.ts +8 -0
  76. package/esm/settings/settings.d.ts.map +1 -1
  77. package/package.json +6 -6
@@ -0,0 +1,196 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { idRef } from "../objRef/factory.js";
3
+ const textSlot = (localIdentifier, kind, hint, content) => ({
4
+ type: "text",
5
+ localIdentifier,
6
+ kind,
7
+ ...(content === undefined ? { placeholder: { hint } } : { source: { type: "static", content } }),
8
+ });
9
+ const vizSlot = (localIdentifier) => ({
10
+ type: "visualization",
11
+ localIdentifier,
12
+ showTitle: true,
13
+ placeholder: { hint: "Add a visualization" },
14
+ });
15
+ const logoSlot = (localIdentifier) => ({
16
+ type: "image",
17
+ localIdentifier,
18
+ source: { type: "url", url: "{{logo}}" },
19
+ fit: "contain",
20
+ altText: "Logo",
21
+ });
22
+ const slot = (slotId, weight) => ({
23
+ type: "slotRef",
24
+ slotId,
25
+ ...(weight === undefined ? {} : { weight }),
26
+ });
27
+ const row = (children, weight) => ({
28
+ type: "section",
29
+ direction: "row",
30
+ children,
31
+ ...(weight === undefined ? {} : { weight }),
32
+ });
33
+ const column = (children, weight) => ({
34
+ type: "section",
35
+ direction: "column",
36
+ children,
37
+ ...(weight === undefined ? {} : { weight }),
38
+ });
39
+ const footerSlots = () => [
40
+ logoSlot("footerLogo"),
41
+ textSlot("footerPageNumber", "custom", "", "{{pageNumber}} / {{totalPages}}"),
42
+ ];
43
+ const footerRow = () => row([slot("footerLogo", 1), slot("footerPageNumber", 8)], 1);
44
+ const vizGrid = ({ vizRows, gridWeights }, weight) => vizRows.length === 1
45
+ ? row(vizRows[0].map((id, index) => slot(id, gridWeights?.[index])), weight)
46
+ : column(vizRows.map((ids) => row(ids.map((id) => slot(id)))), weight);
47
+ const contentPage = (spec) => {
48
+ const { vizRows, summary } = spec;
49
+ const body = summary ? row([vizGrid(spec, 2), slot("summary", 1)], 9) : vizGrid(spec, 9);
50
+ return {
51
+ kind: "content",
52
+ layout: column([slot("pageTitle", 2), body, footerRow()]),
53
+ slots: [
54
+ textSlot("pageTitle", "title", "Page title"),
55
+ ...vizRows.flat().map(vizSlot),
56
+ ...(summary ? [textSlot("summary", "summary", "Add a summary")] : []),
57
+ ...footerSlots(),
58
+ ],
59
+ };
60
+ };
61
+ const vizIds = (count, perRow) => {
62
+ const rows = [];
63
+ for (let index = 0; index < count; index++) {
64
+ const rowIndex = Math.floor(index / perRow);
65
+ (rows[rowIndex] ??= []).push(`widget${index + 1}`);
66
+ }
67
+ return rows;
68
+ };
69
+ // Built-ins are shared singletons served by every backend implementation;
70
+ // freezing makes accidental mutation by a consumer throw instead of corrupting them.
71
+ function deepFreeze(value) {
72
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
73
+ Object.freeze(value);
74
+ for (const key of Object.keys(value)) {
75
+ deepFreeze(value[key]);
76
+ }
77
+ }
78
+ return value;
79
+ }
80
+ const builtInPage = (id, title, description, body) => deepFreeze({
81
+ type: "reportPageLayout",
82
+ ref: idRef(`builtin.reportPageLayout.${id}`, "reportPageLayout"),
83
+ title,
84
+ description,
85
+ content: { version: "1", ...body },
86
+ isBuiltIn: true,
87
+ isLocked: true,
88
+ });
89
+ /**
90
+ * Built-in cover page: report title and subtitle.
91
+ *
92
+ * @alpha
93
+ */
94
+ export const BuiltInReportPageLayoutCover = builtInPage("cover", "Cover", "Title page", {
95
+ kind: "cover",
96
+ layout: column([slot("coverTitle", 2), slot("coverSubtitle", 1), footerRow()]),
97
+ slots: [
98
+ textSlot("coverTitle", "title", "Report title", "{{reportTitle}}"),
99
+ textSlot("coverSubtitle", "subtitle", "Subtitle", "{{periodStart}} – {{periodEnd}}"),
100
+ ...footerSlots(),
101
+ ],
102
+ });
103
+ /**
104
+ * Built-in section divider page.
105
+ *
106
+ * @alpha
107
+ */
108
+ export const BuiltInReportPageLayoutSection = builtInPage("section", "Section", "Section divider page", {
109
+ kind: "section",
110
+ layout: column([slot("sectionTitle", 9), footerRow()]),
111
+ slots: [textSlot("sectionTitle", "sectionTitle", "Section title"), ...footerSlots()],
112
+ });
113
+ const contentPages = [
114
+ ["viz1", "1 visualization", "Single full-width visualization", { vizRows: vizIds(1, 1) }],
115
+ [
116
+ "viz1SummaryRight",
117
+ "1 visualization + summary",
118
+ "Single visualization with a summary column on the right",
119
+ { vizRows: vizIds(1, 1), summary: true },
120
+ ],
121
+ ["viz2", "2 visualizations", "Two visualizations side by side", { vizRows: vizIds(2, 2) }],
122
+ [
123
+ "viz2Wide",
124
+ "2 visualizations (wide left)",
125
+ "Two visualizations, the left one twice as wide",
126
+ { vizRows: vizIds(2, 2), gridWeights: [2, 1] },
127
+ ],
128
+ [
129
+ "viz2Narrow",
130
+ "2 visualizations (wide right)",
131
+ "Two visualizations, the right one twice as wide",
132
+ { vizRows: vizIds(2, 2), gridWeights: [1, 2] },
133
+ ],
134
+ [
135
+ "viz2SummaryRight",
136
+ "2 visualizations + summary",
137
+ "Two visualizations with a summary column on the right",
138
+ { vizRows: vizIds(2, 2), summary: true },
139
+ ],
140
+ ["viz3", "3 visualizations", "Three visualizations side by side", { vizRows: vizIds(3, 3) }],
141
+ [
142
+ "viz3SummaryRight",
143
+ "3 visualizations + summary",
144
+ "Three visualizations with a summary column on the right",
145
+ { vizRows: vizIds(3, 3), summary: true },
146
+ ],
147
+ ["viz4", "4 visualizations", "Four visualizations in a 2x2 grid", { vizRows: vizIds(4, 2) }],
148
+ [
149
+ "viz4SummaryRight",
150
+ "4 visualizations + summary",
151
+ "2x2 visualization grid with a summary column on the right",
152
+ { vizRows: vizIds(4, 2), summary: true },
153
+ ],
154
+ ["viz6", "6 visualizations", "Six visualizations in a 2x3 grid", { vizRows: vizIds(6, 2) }],
155
+ [
156
+ "viz6SummaryRight",
157
+ "6 visualizations + summary",
158
+ "3x2 visualization grid with a summary column on the right",
159
+ { vizRows: vizIds(6, 2), summary: true },
160
+ ],
161
+ ];
162
+ /**
163
+ * Built-in content page with six visualizations and a text column on the left.
164
+ *
165
+ * @alpha
166
+ */
167
+ export const BuiltInReportPageLayoutViz6TextLeft = builtInPage("viz6TextLeft", "6 visualizations + text", "Two rows of three visualizations with a text column on the left", {
168
+ kind: "content",
169
+ layout: column([
170
+ slot("pageTitle", 2),
171
+ column([
172
+ row([slot("text1", 2), slot("widget1", 3), slot("widget2", 3), slot("widget3", 3)]),
173
+ row([slot("text2", 2), slot("widget4", 3), slot("widget5", 3), slot("widget6", 3)]),
174
+ ], 9),
175
+ footerRow(),
176
+ ]),
177
+ slots: [
178
+ textSlot("pageTitle", "title", "Page title"),
179
+ textSlot("text1", "body", "Add a text"),
180
+ textSlot("text2", "body", "Add a text"),
181
+ ...["widget1", "widget2", "widget3", "widget4", "widget5", "widget6"].map(vizSlot),
182
+ ...footerSlots(),
183
+ ],
184
+ });
185
+ /**
186
+ * All built-in report pages served by the SPI. Built-ins are read-only: they cannot be
187
+ * edited or deleted, and they are never persisted on the backend.
188
+ *
189
+ * @alpha
190
+ */
191
+ export const BuiltInReportPageLayouts = Object.freeze([
192
+ BuiltInReportPageLayoutCover,
193
+ BuiltInReportPageLayoutSection,
194
+ ...contentPages.map(([id, title, description, spec]) => builtInPage(id, title, description, contentPage(spec))),
195
+ BuiltInReportPageLayoutViz6TextLeft,
196
+ ]);
@@ -0,0 +1,54 @@
1
+ import { type FilterContextItem } from "../dashboard/filterContext.js";
2
+ import { type IReportPageBody } from "./pageLayout.js";
3
+ import { type IReportVariableDefinition } from "./variables.js";
4
+ /**
5
+ * Page instance embedded in template/report content: a deep clone of a page body
6
+ * plus its own identity.
7
+ *
8
+ * @remarks
9
+ * Cloning a {@link IReportPageLayout} into content assigns the localIdentifier and regenerates
10
+ * slot localIdentifiers (with the layout slotIds) so repeated use of one page stays unique.
11
+ * Deliberately carries NO reference back to the source page — later page edits never
12
+ * affect existing templates or reports.
13
+ *
14
+ * @alpha
15
+ */
16
+ export interface IReportContentPage extends IReportPageBody {
17
+ /**
18
+ * Identifier unique within the content; stable across template-to-report copy and reordering.
19
+ */
20
+ localIdentifier: string;
21
+ }
22
+ /**
23
+ * Versioned content shared verbatim by report templates and reports.
24
+ *
25
+ * @alpha
26
+ */
27
+ export interface IReportContent {
28
+ /**
29
+ * Content model version, for stored-content evolution.
30
+ */
31
+ version: "1";
32
+ /**
33
+ * Ordered pages of the document.
34
+ */
35
+ pages: IReportContentPage[];
36
+ /**
37
+ * Content-level default filters; pages and slots may extend or override them.
38
+ * The report period is NOT stored here — it is derived from periodStart/periodEnd
39
+ * at execution time.
40
+ */
41
+ filters?: FilterContextItem[];
42
+ /**
43
+ * Custom variable declarations. Values live on the report
44
+ * ({@link IReportBase.variableValues}).
45
+ */
46
+ variables?: IReportVariableDefinition[];
47
+ }
48
+ /**
49
+ * Type-guard testing whether the provided object is an instance of {@link IReportContent}.
50
+ *
51
+ * @alpha
52
+ */
53
+ export declare function isReportContentV1(obj: unknown): obj is IReportContent;
54
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/reports/content.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEvE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAEhE;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACvD;;OAEG;IACH,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC3B;;OAEG;IACH,OAAO,EAAE,GAAG,CAAC;IAEb;;OAEG;IACH,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAE5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAE9B;;;OAGG;IACH,SAAS,CAAC,EAAE,yBAAyB,EAAE,CAAC;CAC3C;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc,CAMrE"}
@@ -0,0 +1,12 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { isEmpty } from "lodash-es";
3
+ /**
4
+ * Type-guard testing whether the provided object is an instance of {@link IReportContent}.
5
+ *
6
+ * @alpha
7
+ */
8
+ export function isReportContentV1(obj) {
9
+ return (!isEmpty(obj) &&
10
+ obj.version === "1" &&
11
+ Array.isArray(obj.pages));
12
+ }
@@ -0,0 +1,65 @@
1
+ import { type IReportContent, type IReportContentPage } from "./content.js";
2
+ import { type IReportPageBody, type IReportPageLayout, type IReportPageLayoutDefinition } from "./pageLayout.js";
3
+ import { type IReport, type IReportDefinition, type IReportTemplate, type IReportTemplateDefinition } from "./report.js";
4
+ import { type ReportDateString } from "./variables.js";
5
+ /**
6
+ * Creates a new report page definition.
7
+ *
8
+ * @alpha
9
+ */
10
+ export declare function newReportPageLayoutDefinition(title: string, body: IReportPageBody, modifications?: Partial<Omit<IReportPageLayoutDefinition, "type" | "title" | "content">>): IReportPageLayoutDefinition;
11
+ /**
12
+ * Clones a report page into a page instance embeddable in template/report content.
13
+ *
14
+ * @remarks
15
+ * The body is deep-copied and detached from the source page — no reference is kept.
16
+ * Slot localIdentifiers (and the layout slotIds pointing at them) are prefixed with the
17
+ * page-instance localIdentifier, so one page used repeatedly in the same content stays unique.
18
+ *
19
+ * @alpha
20
+ */
21
+ export declare function newReportContentPageFromLayout(page: IReportPageLayout | IReportPageLayoutDefinition, localIdentifier?: string): IReportContentPage;
22
+ /**
23
+ * Creates report content from page instances.
24
+ *
25
+ * @alpha
26
+ */
27
+ export declare function newReportContent(pages: IReportContentPage[], modifications?: Partial<Omit<IReportContent, "version" | "pages">>): IReportContent;
28
+ /**
29
+ * Creates a new report template definition.
30
+ *
31
+ * @alpha
32
+ */
33
+ export declare function newReportTemplateDefinition(title: string, content: IReportContent, modifications?: Partial<Omit<IReportTemplateDefinition, "type" | "title" | "content">>): IReportTemplateDefinition;
34
+ /**
35
+ * Creates a report definition from a template.
36
+ *
37
+ * @remarks
38
+ * The template content is deep-copied; no reference to the template is kept, so the
39
+ * report stays frozen while the template evolves.
40
+ *
41
+ * @alpha
42
+ */
43
+ export declare function newReportDefinitionFromTemplate(template: IReportTemplate | IReportTemplateDefinition, options: {
44
+ title: string;
45
+ periodStart: ReportDateString;
46
+ periodEnd: ReportDateString;
47
+ }, modifications?: Partial<Omit<IReportDefinition, "type" | "title" | "periodStart" | "periodEnd" | "content">>): IReportDefinition;
48
+ /**
49
+ * Creates an ad-hoc report definition not based on any template.
50
+ *
51
+ * @alpha
52
+ */
53
+ export declare function newAdHocReportDefinition(options: {
54
+ title: string;
55
+ periodStart: ReportDateString;
56
+ periodEnd: ReportDateString;
57
+ pages?: IReportContentPage[];
58
+ }, modifications?: Partial<Omit<IReportDefinition, "type" | "title" | "periodStart" | "periodEnd" | "content">>): IReportDefinition;
59
+ /**
60
+ * Convenience accessor for a report or template page by its localIdentifier.
61
+ *
62
+ * @alpha
63
+ */
64
+ export declare function reportContentPage(reportOrTemplate: IReport | IReportDefinition | IReportTemplate | IReportTemplateDefinition, pageLocalIdentifier: string): IReportContentPage | undefined;
65
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/reports/factory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE5E,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,iBAAiB,EAEtB,KAAK,2BAA2B,EACnC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACH,KAAK,OAAO,EACZ,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,yBAAyB,EACjC,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AA2BvD;;;;GAIG;AACH,wBAAgB,6BAA6B,CACzC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,eAAe,EACrB,aAAa,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,2BAA2B,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC,GACzF,2BAA2B,CAQ7B;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC1C,IAAI,EAAE,iBAAiB,GAAG,2BAA2B,EACrD,eAAe,GAAE,MAAsC,GACxD,kBAAkB,CAapB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,kBAAkB,EAAE,EAC3B,aAAa,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC,GACnE,cAAc,CAMhB;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACvC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,cAAc,EACvB,aAAa,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,yBAAyB,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC,GACvF,yBAAyB,CAO3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAC3C,QAAQ,EAAE,eAAe,GAAG,yBAAyB,EACrD,OAAO,EAAE;IACL,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,gBAAgB,CAAC;IAC9B,SAAS,EAAE,gBAAgB,CAAC;CAC/B,EACD,aAAa,CAAC,EAAE,OAAO,CACnB,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,GAAG,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC,CACtF,GACF,iBAAiB,CASnB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACpC,OAAO,EAAE;IACL,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,gBAAgB,CAAC;IAC9B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAChC,EACD,aAAa,CAAC,EAAE,OAAO,CACnB,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,GAAG,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC,CACtF,GACF,iBAAiB,CASnB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC7B,gBAAgB,EAAE,OAAO,GAAG,iBAAiB,GAAG,eAAe,GAAG,yBAAyB,EAC3F,mBAAmB,EAAE,MAAM,GAC5B,kBAAkB,GAAG,SAAS,CAEhC"}
@@ -0,0 +1,126 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { isReportLayoutSection } from "./layout.js";
3
+ function generateReportLocalId(prefix) {
4
+ const unique = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
5
+ ? crypto.randomUUID().replace(/-/g, "").slice(0, 12)
6
+ : Math.random().toString(36).slice(2, 14);
7
+ return `${prefix}_${unique}`;
8
+ }
9
+ function deepClone(value) {
10
+ return JSON.parse(JSON.stringify(value));
11
+ }
12
+ function prefixLayoutSlotIds(node, prefix) {
13
+ if (isReportLayoutSection(node)) {
14
+ return {
15
+ ...node,
16
+ children: node.children.map((child) => prefixLayoutSlotIds(child, prefix)),
17
+ };
18
+ }
19
+ return {
20
+ ...node,
21
+ slotId: `${prefix}_${node.slotId}`,
22
+ };
23
+ }
24
+ /**
25
+ * Creates a new report page definition.
26
+ *
27
+ * @alpha
28
+ */
29
+ export function newReportPageLayoutDefinition(title, body, modifications) {
30
+ const content = { version: "1", ...body };
31
+ return {
32
+ type: "reportPageLayout",
33
+ title,
34
+ content,
35
+ ...modifications,
36
+ };
37
+ }
38
+ /**
39
+ * Clones a report page into a page instance embeddable in template/report content.
40
+ *
41
+ * @remarks
42
+ * The body is deep-copied and detached from the source page — no reference is kept.
43
+ * Slot localIdentifiers (and the layout slotIds pointing at them) are prefixed with the
44
+ * page-instance localIdentifier, so one page used repeatedly in the same content stays unique.
45
+ *
46
+ * @alpha
47
+ */
48
+ export function newReportContentPageFromLayout(page, localIdentifier = generateReportLocalId("page")) {
49
+ const { version: _version, ...body } = deepClone(page.content);
50
+ return {
51
+ ...body,
52
+ localIdentifier,
53
+ layout: prefixLayoutSlotIds(body.layout, localIdentifier),
54
+ slots: body.slots.map((slot) => ({
55
+ ...slot,
56
+ localIdentifier: `${localIdentifier}_${slot.localIdentifier}`,
57
+ })),
58
+ };
59
+ }
60
+ /**
61
+ * Creates report content from page instances.
62
+ *
63
+ * @alpha
64
+ */
65
+ export function newReportContent(pages, modifications) {
66
+ return {
67
+ version: "1",
68
+ pages,
69
+ ...modifications,
70
+ };
71
+ }
72
+ /**
73
+ * Creates a new report template definition.
74
+ *
75
+ * @alpha
76
+ */
77
+ export function newReportTemplateDefinition(title, content, modifications) {
78
+ return {
79
+ type: "reportTemplate",
80
+ title,
81
+ content,
82
+ ...modifications,
83
+ };
84
+ }
85
+ /**
86
+ * Creates a report definition from a template.
87
+ *
88
+ * @remarks
89
+ * The template content is deep-copied; no reference to the template is kept, so the
90
+ * report stays frozen while the template evolves.
91
+ *
92
+ * @alpha
93
+ */
94
+ export function newReportDefinitionFromTemplate(template, options, modifications) {
95
+ return {
96
+ type: "report",
97
+ title: options.title,
98
+ periodStart: options.periodStart,
99
+ periodEnd: options.periodEnd,
100
+ content: deepClone(template.content),
101
+ ...modifications,
102
+ };
103
+ }
104
+ /**
105
+ * Creates an ad-hoc report definition not based on any template.
106
+ *
107
+ * @alpha
108
+ */
109
+ export function newAdHocReportDefinition(options, modifications) {
110
+ return {
111
+ type: "report",
112
+ title: options.title,
113
+ periodStart: options.periodStart,
114
+ periodEnd: options.periodEnd,
115
+ content: newReportContent(options.pages ?? []),
116
+ ...modifications,
117
+ };
118
+ }
119
+ /**
120
+ * Convenience accessor for a report or template page by its localIdentifier.
121
+ *
122
+ * @alpha
123
+ */
124
+ export function reportContentPage(reportOrTemplate, pageLocalIdentifier) {
125
+ return reportOrTemplate.content.pages.find((page) => page.localIdentifier === pageLocalIdentifier);
126
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Node of a report page layout: a recursive row/column split tree (flexbox semantics)
3
+ * over a fixed page. Leaves assign their area to slots; the tree carries only geometry,
4
+ * slot content lives in {@link IReportPageBody.slots}.
5
+ *
6
+ * @alpha
7
+ */
8
+ export type ReportPageLayoutNode = IReportLayoutSection | IReportLayoutSlotRef;
9
+ /**
10
+ * Fields common to all report layout nodes.
11
+ *
12
+ * @alpha
13
+ */
14
+ export interface IReportLayoutNodeBase {
15
+ /**
16
+ * Fractional weight of this node inside its parent (flex-grow semantics).
17
+ * Defaults to 1. Example: sibling weights [2, 1] render a 2/3 + 1/3 split.
18
+ */
19
+ weight?: number;
20
+ }
21
+ /**
22
+ * A container splitting its area into children laid out along a direction.
23
+ *
24
+ * @alpha
25
+ */
26
+ export interface IReportLayoutSection extends IReportLayoutNodeBase {
27
+ type: "section";
28
+ /**
29
+ * "row" lays children out horizontally, "column" vertically.
30
+ */
31
+ direction: "row" | "column";
32
+ children: ReportPageLayoutNode[];
33
+ }
34
+ /**
35
+ * A leaf assigning its area to a slot. The slot fills the area completely.
36
+ *
37
+ * @alpha
38
+ */
39
+ export interface IReportLayoutSlotRef extends IReportLayoutNodeBase {
40
+ type: "slotRef";
41
+ /**
42
+ * Local identifier of a slot in {@link IReportPageBody.slots}.
43
+ * A slotId with no matching slot renders as an empty area.
44
+ */
45
+ slotId: string;
46
+ }
47
+ /**
48
+ * Type-guard testing whether the provided object is an instance of {@link IReportLayoutSection}.
49
+ *
50
+ * @alpha
51
+ */
52
+ export declare function isReportLayoutSection(obj: unknown): obj is IReportLayoutSection;
53
+ /**
54
+ * Type-guard testing whether the provided object is an instance of {@link IReportLayoutSlotRef}.
55
+ *
56
+ * @alpha
57
+ */
58
+ export declare function isReportLayoutSlotRef(obj: unknown): obj is IReportLayoutSlotRef;
59
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/reports/layout.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,GAAG,oBAAoB,CAAC;AAE/E;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IAClC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IAC/D,IAAI,EAAE,SAAS,CAAC;IAEhB;;OAEG;IACH,SAAS,EAAE,KAAK,GAAG,QAAQ,CAAC;IAE5B,QAAQ,EAAE,oBAAoB,EAAE,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IAC/D,IAAI,EAAE,SAAS,CAAC;IAEhB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,oBAAoB,CAE/E;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,oBAAoB,CAE/E"}
@@ -0,0 +1,18 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { isEmpty } from "lodash-es";
3
+ /**
4
+ * Type-guard testing whether the provided object is an instance of {@link IReportLayoutSection}.
5
+ *
6
+ * @alpha
7
+ */
8
+ export function isReportLayoutSection(obj) {
9
+ return !isEmpty(obj) && obj.type === "section";
10
+ }
11
+ /**
12
+ * Type-guard testing whether the provided object is an instance of {@link IReportLayoutSlotRef}.
13
+ *
14
+ * @alpha
15
+ */
16
+ export function isReportLayoutSlotRef(obj) {
17
+ return !isEmpty(obj) && obj.type === "slotRef";
18
+ }
@@ -0,0 +1,120 @@
1
+ import { type IAuditableDates, type IAuditableUsers } from "../base/metadata.js";
2
+ import { type FilterContextItem } from "../dashboard/filterContext.js";
3
+ import { type ObjRef } from "../objRef/index.js";
4
+ import { type ReportPageLayoutNode } from "./layout.js";
5
+ import { type ReportSlot } from "./slot.js";
6
+ /**
7
+ * Body of a report page: geometry (flex split tree) plus the slots it places.
8
+ *
9
+ * @remarks
10
+ * This is both the content of the standalone {@link IReportPageLayout} object and the shape
11
+ * embedded in template/report content ({@link IReportContentPage}).
12
+ *
13
+ * There is no header/footer chrome: page title, description, footer, page numbers and
14
+ * logos are ordinary slots in the layout tree (text slots with `{{pageNumber}}`/`{{totalPages}}`,
15
+ * an image slot with `{{logo}}`). Every slot fills its layout area completely.
16
+ *
17
+ * @alpha
18
+ */
19
+ export interface IReportPageBody {
20
+ /**
21
+ * Editor hint only (template galleries, default styling, AI context).
22
+ * Renderers must not branch layout logic on it — geometry always comes from `layout`.
23
+ */
24
+ kind?: "cover" | "section" | "content";
25
+ /**
26
+ * Root of the page layout tree.
27
+ */
28
+ layout: ReportPageLayoutNode;
29
+ /**
30
+ * All slots referenced by the layout, flat, keyed by localIdentifier.
31
+ */
32
+ slots: ReportSlot[];
33
+ /**
34
+ * Page-level filters: merged over content-level filters (a filter targeting the
35
+ * same object replaces the inherited one); slot filters apply on top.
36
+ */
37
+ filters?: FilterContextItem[];
38
+ }
39
+ /**
40
+ * Stored content of the reportPage entity.
41
+ *
42
+ * @alpha
43
+ */
44
+ export interface IReportPageLayoutContent extends IReportPageBody {
45
+ /**
46
+ * Content model version, for stored-content evolution.
47
+ */
48
+ version: "1";
49
+ }
50
+ /**
51
+ * Payload for creating or updating a report page.
52
+ *
53
+ * @alpha
54
+ */
55
+ export interface IReportPageLayoutDefinition {
56
+ type: "reportPageLayout";
57
+ /**
58
+ * Present when updating an existing page.
59
+ */
60
+ ref?: ObjRef;
61
+ title: string;
62
+ description?: string;
63
+ tags?: string[];
64
+ content: IReportPageLayoutContent;
65
+ }
66
+ /**
67
+ * Reusable report page metadata object.
68
+ *
69
+ * @alpha
70
+ */
71
+ export interface IReportPageLayout extends IReportPageLayoutDefinition, IAuditableDates, IAuditableUsers {
72
+ ref: ObjRef;
73
+ /**
74
+ * When true, the object comes from a parent workspace and is not editable
75
+ * in the current workspace.
76
+ */
77
+ isLocked?: boolean;
78
+ /**
79
+ * Predefined page shipped with the product, populated by the SPI. Never sent to or
80
+ * stored on the backend; UI must disable deletion and editing of built-in pages.
81
+ */
82
+ isBuiltIn?: boolean;
83
+ }
84
+ /**
85
+ * Type-guard testing whether the provided object is an instance of {@link IReportPageLayoutDefinition}.
86
+ *
87
+ * @alpha
88
+ */
89
+ export declare function isReportPageLayoutDefinition(obj: unknown): obj is IReportPageLayoutDefinition;
90
+ /**
91
+ * Type-guard testing whether the provided object is an instance of {@link IReportPageLayout}.
92
+ *
93
+ * @alpha
94
+ */
95
+ export declare function isReportPageLayout(obj: unknown): obj is IReportPageLayout;
96
+ /**
97
+ * Type-guard testing whether the provided object is an instance of {@link IReportPageLayoutContent}.
98
+ *
99
+ * @alpha
100
+ */
101
+ export declare function isReportPageLayoutContentV1(obj: unknown): obj is IReportPageLayoutContent;
102
+ /**
103
+ * Validation issue found in a report page body.
104
+ *
105
+ * @alpha
106
+ */
107
+ export interface IReportPageBodyValidationIssue {
108
+ severity: "error" | "warning";
109
+ message: string;
110
+ }
111
+ /**
112
+ * Validates the structural invariants of a page body the type system cannot express:
113
+ * slot localIdentifiers are unique, layout weights are positive, every slot is placed
114
+ * by the layout, and every layout slotId resolves (unresolved ones are warnings —
115
+ * they render as empty areas).
116
+ *
117
+ * @alpha
118
+ */
119
+ export declare function validateReportPageBody(body: IReportPageBody): IReportPageBodyValidationIssue[];
120
+ //# sourceMappingURL=pageLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pageLayout.d.ts","sourceRoot":"","sources":["../../src/reports/pageLayout.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,KAAK,MAAM,EAAY,MAAM,oBAAoB,CAAC;AAE3D,OAAO,EAAE,KAAK,oBAAoB,EAAgD,MAAM,aAAa,CAAC;AACtG,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAe;IAC5B;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;IAEvC;;OAEG;IACH,MAAM,EAAE,oBAAoB,CAAC;IAE7B;;OAEG;IACH,KAAK,EAAE,UAAU,EAAE,CAAC;IAEpB;;;OAGG;IACH,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;CACjC;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAyB,SAAQ,eAAe;IAC7D;;OAEG;IACH,OAAO,EAAE,GAAG,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IACxC,IAAI,EAAE,kBAAkB,CAAC;IAEzB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,KAAK,EAAE,MAAM,CAAC;IAEd,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB,OAAO,EAAE,wBAAwB,CAAC;CACrC;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAkB,SAAQ,2BAA2B,EAAE,eAAe,EAAE,eAAe;IACpG,GAAG,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,2BAA2B,CAE7F;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,iBAAiB,CAEzE;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,wBAAwB,CAOzF;AAED;;;;GAIG;AACH,MAAM,WAAW,8BAA8B;IAC3C,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,eAAe,GAAG,8BAA8B,EAAE,CA8C9F"}