@rippling/rippling-sdk 0.2.0-alpha.36 → 0.2.0-alpha.37

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 (130) hide show
  1. package/client.d.mts +7 -1
  2. package/client.d.mts.map +1 -1
  3. package/client.d.ts +7 -1
  4. package/client.d.ts.map +1 -1
  5. package/client.js +7 -1
  6. package/client.js.map +1 -1
  7. package/client.mjs +7 -1
  8. package/client.mjs.map +1 -1
  9. package/examples/manifest/dental-practice-management/dental-practice-management.ts +21 -34
  10. package/examples/manifest/gym-membership-management/gym-membership-management.ts +27 -38
  11. package/examples/manifest/gym-membership-management/manifest.json +1 -1
  12. package/internal/utils/log.d.mts.map +1 -1
  13. package/internal/utils/log.d.ts.map +1 -1
  14. package/internal/utils/log.js +2 -0
  15. package/internal/utils/log.js.map +1 -1
  16. package/internal/utils/log.mjs +2 -0
  17. package/internal/utils/log.mjs.map +1 -1
  18. package/lib/manifest/custom-object-field-section.d.mts +4 -0
  19. package/lib/manifest/custom-object-field-section.d.mts.map +1 -1
  20. package/lib/manifest/custom-object-field-section.d.ts +4 -0
  21. package/lib/manifest/custom-object-field-section.d.ts.map +1 -1
  22. package/lib/manifest/custom-object-field-section.js +6 -0
  23. package/lib/manifest/custom-object-field-section.js.map +1 -1
  24. package/lib/manifest/custom-object-field-section.mjs +6 -0
  25. package/lib/manifest/custom-object-field-section.mjs.map +1 -1
  26. package/lib/manifest/custom-object-page-layout.d.mts +21 -13
  27. package/lib/manifest/custom-object-page-layout.d.mts.map +1 -1
  28. package/lib/manifest/custom-object-page-layout.d.ts +21 -13
  29. package/lib/manifest/custom-object-page-layout.d.ts.map +1 -1
  30. package/lib/manifest/custom-object-page-layout.js +119 -9
  31. package/lib/manifest/custom-object-page-layout.js.map +1 -1
  32. package/lib/manifest/custom-object-page-layout.mjs +119 -9
  33. package/lib/manifest/custom-object-page-layout.mjs.map +1 -1
  34. package/lib/manifest/field.d.mts +5 -1
  35. package/lib/manifest/field.d.mts.map +1 -1
  36. package/lib/manifest/field.d.ts +5 -1
  37. package/lib/manifest/field.d.ts.map +1 -1
  38. package/lib/manifest/field.js +8 -0
  39. package/lib/manifest/field.js.map +1 -1
  40. package/lib/manifest/field.mjs +8 -0
  41. package/lib/manifest/field.mjs.map +1 -1
  42. package/lib/manifest/index.d.mts +1 -1
  43. package/lib/manifest/index.d.mts.map +1 -1
  44. package/lib/manifest/index.d.ts +1 -1
  45. package/lib/manifest/index.d.ts.map +1 -1
  46. package/lib/manifest/index.js.map +1 -1
  47. package/lib/manifest/index.mjs.map +1 -1
  48. package/package.json +1 -1
  49. package/resources/company-legal-entities.d.mts +180 -0
  50. package/resources/company-legal-entities.d.mts.map +1 -0
  51. package/resources/company-legal-entities.d.ts +180 -0
  52. package/resources/company-legal-entities.d.ts.map +1 -0
  53. package/resources/company-legal-entities.js +30 -0
  54. package/resources/company-legal-entities.js.map +1 -0
  55. package/resources/company-legal-entities.mjs +26 -0
  56. package/resources/company-legal-entities.mjs.map +1 -0
  57. package/resources/earning-types.d.mts +1 -6
  58. package/resources/earning-types.d.mts.map +1 -1
  59. package/resources/earning-types.d.ts +1 -6
  60. package/resources/earning-types.d.ts.map +1 -1
  61. package/resources/earning-types.js +1 -1
  62. package/resources/earning-types.mjs +1 -1
  63. package/resources/earnings-inputs.d.mts +1 -1
  64. package/resources/earnings-inputs.d.ts +1 -1
  65. package/resources/earnings-inputs.js +1 -1
  66. package/resources/earnings-inputs.mjs +1 -1
  67. package/resources/index.d.mts +2 -1
  68. package/resources/index.d.mts.map +1 -1
  69. package/resources/index.d.ts +2 -1
  70. package/resources/index.d.ts.map +1 -1
  71. package/resources/index.js +5 -3
  72. package/resources/index.js.map +1 -1
  73. package/resources/index.mjs +2 -1
  74. package/resources/index.mjs.map +1 -1
  75. package/resources/legal-entities.d.mts +1 -1
  76. package/resources/legal-entities.d.mts.map +1 -1
  77. package/resources/legal-entities.d.ts +1 -1
  78. package/resources/legal-entities.d.ts.map +1 -1
  79. package/resources/payroll-runs/index.d.mts +3 -0
  80. package/resources/payroll-runs/index.d.mts.map +1 -0
  81. package/resources/payroll-runs/index.d.ts +3 -0
  82. package/resources/payroll-runs/index.d.ts.map +1 -0
  83. package/resources/payroll-runs/index.js +9 -0
  84. package/resources/payroll-runs/index.js.map +1 -0
  85. package/resources/payroll-runs/index.mjs +4 -0
  86. package/resources/payroll-runs/index.mjs.map +1 -0
  87. package/resources/payroll-runs/payroll-runs.d.mts +199 -0
  88. package/resources/payroll-runs/payroll-runs.d.mts.map +1 -0
  89. package/resources/payroll-runs/payroll-runs.d.ts +199 -0
  90. package/resources/payroll-runs/payroll-runs.d.ts.map +1 -0
  91. package/resources/payroll-runs/payroll-runs.js +40 -0
  92. package/resources/payroll-runs/payroll-runs.js.map +1 -0
  93. package/resources/payroll-runs/payroll-runs.mjs +35 -0
  94. package/resources/payroll-runs/payroll-runs.mjs.map +1 -0
  95. package/resources/payroll-runs/worker-payroll-records.d.mts +200 -0
  96. package/resources/payroll-runs/worker-payroll-records.d.mts.map +1 -0
  97. package/resources/payroll-runs/worker-payroll-records.d.ts +200 -0
  98. package/resources/payroll-runs/worker-payroll-records.d.ts.map +1 -0
  99. package/resources/payroll-runs/worker-payroll-records.js +23 -0
  100. package/resources/payroll-runs/worker-payroll-records.js.map +1 -0
  101. package/resources/payroll-runs/worker-payroll-records.mjs +19 -0
  102. package/resources/payroll-runs/worker-payroll-records.mjs.map +1 -0
  103. package/resources/payroll-runs.d.mts +1 -194
  104. package/resources/payroll-runs.d.mts.map +1 -1
  105. package/resources/payroll-runs.d.ts +1 -194
  106. package/resources/payroll-runs.d.ts.map +1 -1
  107. package/resources/payroll-runs.js +2 -28
  108. package/resources/payroll-runs.js.map +1 -1
  109. package/resources/payroll-runs.mjs +1 -26
  110. package/resources/payroll-runs.mjs.map +1 -1
  111. package/src/client.ts +27 -7
  112. package/src/internal/utils/log.ts +2 -0
  113. package/src/lib/manifest/custom-object-field-section.ts +7 -0
  114. package/src/lib/manifest/custom-object-page-layout.ts +220 -18
  115. package/src/lib/manifest/field.ts +9 -1
  116. package/src/lib/manifest/index.ts +2 -0
  117. package/src/resources/company-legal-entities.ts +237 -0
  118. package/src/resources/earning-types.ts +1 -7
  119. package/src/resources/earnings-inputs.ts +1 -1
  120. package/src/resources/index.ts +8 -1
  121. package/src/resources/legal-entities.ts +1 -1
  122. package/src/resources/payroll-runs/index.ts +15 -0
  123. package/src/resources/payroll-runs/payroll-runs.ts +270 -0
  124. package/src/resources/payroll-runs/worker-payroll-records.ts +255 -0
  125. package/src/resources/payroll-runs.ts +1 -249
  126. package/src/version.ts +1 -1
  127. package/version.d.mts +1 -1
  128. package/version.d.ts +1 -1
  129. package/version.js +1 -1
  130. package/version.mjs +1 -1
@@ -1,32 +1,6 @@
1
1
  "use strict";
2
2
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.PayrollRuns = void 0;
5
- const resource_1 = require("../core/resource.js");
6
- const pagination_1 = require("../core/pagination.js");
7
- const path_1 = require("../internal/utils/path.js");
8
- /**
9
- * Payroll runs for the company
10
- */
11
- class PayrollRuns extends resource_1.APIResource {
12
- /**
13
- * A list of payroll-runs.
14
- *
15
- * - Requires: `Payroll API`
16
- * - Sortable fields: `check_date`
17
- */
18
- list(query = {}, options) {
19
- return this._client.getAPIList('/payroll-runs/', (pagination_1.PageCursorURL), {
20
- query,
21
- ...options,
22
- });
23
- }
24
- /**
25
- * Retrieve a specific payroll-run
26
- */
27
- retrieve(id, options) {
28
- return this._client.get((0, path_1.path) `/payroll-runs/${id}/`, options);
29
- }
30
- }
31
- exports.PayrollRuns = PayrollRuns;
4
+ const tslib_1 = require("../internal/tslib.js");
5
+ tslib_1.__exportStar(require("./payroll-runs/index.js"), exports);
32
6
  //# sourceMappingURL=payroll-runs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"payroll-runs.js","sourceRoot":"","sources":["../src/resources/payroll-runs.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;AAEtF,kDAA+C;AAG/C,sDAAgE;AAEhE,oDAA8C;AAE9C;;GAEG;AACH,MAAa,WAAY,SAAQ,sBAAW;IAC1C;;;;;OAKG;IACH,IAAI,CACF,QAAiD,EAAE,EACnD,OAAwB;QAExB,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,gBAAgB,EAAE,CAAA,0BAAqC,CAAA,EAAE;YACtF,KAAK;YACL,GAAG,OAAO;SACX,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,EAAU,EAAE,OAAwB;QAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAA,WAAI,EAAA,iBAAiB,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC/D,CAAC;CACF;AAvBD,kCAuBC"}
1
+ {"version":3,"file":"payroll-runs.js","sourceRoot":"","sources":["../src/resources/payroll-runs.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;AAEtF,kEAAqC"}
@@ -1,28 +1,3 @@
1
1
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
- import { APIResource } from "../core/resource.mjs";
3
- import { PageCursorURL } from "../core/pagination.mjs";
4
- import { path } from "../internal/utils/path.mjs";
5
- /**
6
- * Payroll runs for the company
7
- */
8
- export class PayrollRuns extends APIResource {
9
- /**
10
- * A list of payroll-runs.
11
- *
12
- * - Requires: `Payroll API`
13
- * - Sortable fields: `check_date`
14
- */
15
- list(query = {}, options) {
16
- return this._client.getAPIList('/payroll-runs/', (PageCursorURL), {
17
- query,
18
- ...options,
19
- });
20
- }
21
- /**
22
- * Retrieve a specific payroll-run
23
- */
24
- retrieve(id, options) {
25
- return this._client.get(path `/payroll-runs/${id}/`, options);
26
- }
27
- }
2
+ export * from "./payroll-runs/index.mjs";
28
3
  //# sourceMappingURL=payroll-runs.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"payroll-runs.mjs","sourceRoot":"","sources":["../src/resources/payroll-runs.ts"],"names":[],"mappings":"AAAA,sFAAsF;OAE/E,EAAE,WAAW,EAAE;OAGf,EAAE,aAAa,EAAe;OAE9B,EAAE,IAAI,EAAE;AAEf;;GAEG;AACH,MAAM,OAAO,WAAY,SAAQ,WAAW;IAC1C;;;;;OAKG;IACH,IAAI,CACF,QAAiD,EAAE,EACnD,OAAwB;QAExB,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,gBAAgB,EAAE,CAAA,aAAqC,CAAA,EAAE;YACtF,KAAK;YACL,GAAG,OAAO;SACX,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,EAAU,EAAE,OAAwB;QAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAA,iBAAiB,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC/D,CAAC;CACF"}
1
+ {"version":3,"file":"payroll-runs.mjs","sourceRoot":"","sources":["../src/resources/payroll-runs.ts"],"names":[],"mappings":"AAAA,sFAAsF"}
package/src/client.ts CHANGED
@@ -51,6 +51,13 @@ import {
51
51
  Candidates,
52
52
  } from './resources/candidates';
53
53
  import { Companies, CompaniesPageCursorURL, Company, CompanyListParams } from './resources/companies';
54
+ import {
55
+ CompanyLegalEntities,
56
+ CompanyLegalEntityListParams,
57
+ CompanyLegalEntityListResponse,
58
+ CompanyLegalEntityListResponsesPageCursorURL,
59
+ CompanyLegalEntityRetrieveResponse,
60
+ } from './resources/company-legal-entities';
54
61
  import {
55
62
  CompanyLegalEntityWorkerListParams,
56
63
  CompanyLegalEntityWorkerListResponse,
@@ -305,13 +312,6 @@ import {
305
312
  ObjectCategoryCreateParams,
306
313
  ObjectCategoryUpdateParams,
307
314
  } from './resources/object-categories';
308
- import {
309
- PayrollRunListParams,
310
- PayrollRunListResponse,
311
- PayrollRunListResponsesPageCursorURL,
312
- PayrollRunRetrieveResponse,
313
- PayrollRuns,
314
- } from './resources/payroll-runs';
315
315
  import {
316
316
  PlatformCapabilities,
317
317
  PlatformCapabilityListResponse,
@@ -478,6 +478,13 @@ import {
478
478
  MetadataListResponse,
479
479
  MetadataListResponsesPageCursorURL,
480
480
  } from './resources/metadata/metadata';
481
+ import {
482
+ PayrollRunListParams,
483
+ PayrollRunListResponse,
484
+ PayrollRunListResponsesPageCursorURL,
485
+ PayrollRunRetrieveResponse,
486
+ PayrollRuns,
487
+ } from './resources/payroll-runs/payroll-runs';
481
488
  import { ReportRetrieveResponse, Reports } from './resources/reports/reports';
482
489
  import {
483
490
  Supergroup,
@@ -1351,6 +1358,10 @@ export class RipplingSDK {
1351
1358
  * An application by a candidate to a specific job requisition
1352
1359
  */
1353
1360
  candidateApplications: API.CandidateApplications = new API.CandidateApplications(this);
1361
+ /**
1362
+ * Legal entities registered by the company
1363
+ */
1364
+ companyLegalEntities: API.CompanyLegalEntities = new API.CompanyLegalEntities(this);
1354
1365
  /**
1355
1366
  * Workers who work or have worked at the company
1356
1367
  */
@@ -1492,6 +1503,7 @@ RipplingSDK.JobAssignments = JobAssignments;
1492
1503
  RipplingSDK.JobPayRateExceptions = JobPayRateExceptions;
1493
1504
  RipplingSDK.Candidates = Candidates;
1494
1505
  RipplingSDK.CandidateApplications = CandidateApplications;
1506
+ RipplingSDK.CompanyLegalEntities = CompanyLegalEntities;
1495
1507
  RipplingSDK.CompanyLegalEntityWorkers = CompanyLegalEntityWorkers;
1496
1508
  RipplingSDK.CompensationBandsDetails = CompensationBandsDetails;
1497
1509
  RipplingSDK.CustomApps = CustomApps;
@@ -1835,6 +1847,14 @@ export declare namespace RipplingSDK {
1835
1847
  type CandidateApplicationListParams as CandidateApplicationListParams,
1836
1848
  };
1837
1849
 
1850
+ export {
1851
+ CompanyLegalEntities as CompanyLegalEntities,
1852
+ type CompanyLegalEntityListResponse as CompanyLegalEntityListResponse,
1853
+ type CompanyLegalEntityRetrieveResponse as CompanyLegalEntityRetrieveResponse,
1854
+ type CompanyLegalEntityListResponsesPageCursorURL as CompanyLegalEntityListResponsesPageCursorURL,
1855
+ type CompanyLegalEntityListParams as CompanyLegalEntityListParams,
1856
+ };
1857
+
1838
1858
  export {
1839
1859
  CompanyLegalEntityWorkers as CompanyLegalEntityWorkers,
1840
1860
  type CompanyLegalEntityWorkerListResponse as CompanyLegalEntityWorkerListResponse,
@@ -107,6 +107,8 @@ export const formatRequestDetails = (details: {
107
107
  name,
108
108
  (
109
109
  name.toLowerCase() === 'authorization' ||
110
+ name.toLowerCase() === 'api-key' ||
111
+ name.toLowerCase() === 'x-api-key' ||
110
112
  name.toLowerCase() === 'cookie' ||
111
113
  name.toLowerCase() === 'set-cookie'
112
114
  ) ?
@@ -70,6 +70,13 @@ export class CustomObjectFieldSection {
70
70
  return this._sectionId;
71
71
  }
72
72
 
73
+ /**
74
+ * Returns the display name of this field section.
75
+ */
76
+ getName(): string {
77
+ return this._name;
78
+ }
79
+
73
80
  /**
74
81
  * Returns the api_name of the custom object this section belongs to.
75
82
  */
@@ -1,4 +1,5 @@
1
1
  import { generateId } from './_helpers';
2
+ import { CustomObjectFieldSection } from './custom-object-field-section';
2
3
  import type { CustomObject } from './custom-object';
3
4
  import type { Field } from './field';
4
5
 
@@ -65,10 +66,8 @@ export interface HeaderEdits {
65
66
  // tab_edits — TabEditsDTO + section/tab DTOs
66
67
  // ---------------------------------------------------------------------------
67
68
 
68
- /** One field entry inside a `fields_section`. */
69
- export interface SectionField {
70
- /** RQL name of the field to display. Required. */
71
- fieldRqlName: string;
69
+ /** Options for one field entry inside a `fields_section`. */
70
+ export interface SectionFieldOptions {
72
71
  /** Hide this field from view. Optional. */
73
72
  hidden?: boolean;
74
73
  /** Allow inline editing of this field. Optional. */
@@ -77,13 +76,24 @@ export interface SectionField {
77
76
  movementOption?: 'tab' | 'free';
78
77
  }
79
78
 
79
+ /** A typed field entry inside a `fields_section`. */
80
+ export interface SectionFieldRef extends SectionFieldOptions {
81
+ /** Field to display. Required. */
82
+ field: Field;
83
+ }
84
+
85
+ /** One field entry inside a `fields_section`. */
86
+ export type SectionField = Field | SectionFieldRef;
87
+
80
88
  /** A standard field-grid section inside a tab. */
81
89
  export interface FieldsSection {
82
90
  type: 'fields_section';
83
- /** Stable key for this section. Required. */
84
- key: string;
85
- /** Display name of the section. Required. */
86
- name: string;
91
+ /** Field section metadata for the fields displayed in this layout section. Required. */
92
+ section: CustomObjectFieldSection;
93
+ /** Stable layout key for this section. Optional. Defaults to the field section id. */
94
+ key?: string;
95
+ /** Display name of the layout section. Optional. Defaults to the field section name. */
96
+ name?: string;
87
97
  /** Fields to display in this section. Required. */
88
98
  fields: SectionField[];
89
99
  }
@@ -255,6 +265,194 @@ export interface CustomObjectPageLayoutProps {
255
265
  blueprintKey?: string;
256
266
  }
257
267
 
268
+ interface SerializedSectionField extends SectionFieldOptions {
269
+ fieldRqlName: string;
270
+ }
271
+
272
+ interface SerializedFieldsSection {
273
+ type: 'fields_section';
274
+ key: string;
275
+ name: string;
276
+ fields: SerializedSectionField[];
277
+ }
278
+
279
+ interface SerializedTabWithSections {
280
+ type: 'tab_with_sections';
281
+ key: string;
282
+ name: string;
283
+ sections: SerializedFieldsSection[];
284
+ }
285
+
286
+ type SerializedPageLayoutTab = SerializedTabWithSections | CustomTab;
287
+
288
+ interface SerializedSystemSectionEdit extends Omit<SystemSectionEdit, 'newFields'> {
289
+ newFields?: SerializedSectionField[];
290
+ }
291
+
292
+ interface SerializedSystemTabEdit extends Omit<SystemTabEdit, 'newSections' | 'systemSectionEdits'> {
293
+ newSections?: SerializedFieldsSection[];
294
+ systemSectionEdits?: Record<string, SerializedSystemSectionEdit>;
295
+ }
296
+
297
+ interface SerializedTabEdits extends Omit<TabEdits, 'newTabs' | 'systemTabEdits'> {
298
+ newTabs?: SerializedPageLayoutTab[];
299
+ systemTabEdits?: Record<string, SerializedSystemTabEdit>;
300
+ }
301
+
302
+ function isField(value: unknown): value is Field {
303
+ const candidate = value as {
304
+ getApiName?: unknown;
305
+ getCustomObjectApiName?: unknown;
306
+ getSectionId?: unknown;
307
+ _assignSection?: unknown;
308
+ } | null;
309
+ return (
310
+ candidate != null &&
311
+ typeof candidate.getApiName === 'function' &&
312
+ typeof candidate.getCustomObjectApiName === 'function' &&
313
+ typeof candidate.getSectionId === 'function' &&
314
+ typeof candidate._assignSection === 'function'
315
+ );
316
+ }
317
+
318
+ function isFieldRef(value: SectionField): value is SectionFieldRef {
319
+ const candidate = value as { field?: unknown } | null;
320
+ return candidate != null && typeof candidate === 'object' && isField(candidate.field);
321
+ }
322
+
323
+ function assertSameObject(customObject: CustomObject, field: Field, sectionKey: string): void {
324
+ if (field.getCustomObjectApiName() !== customObject.getApiName()) {
325
+ throw new Error(
326
+ `CustomObjectPageLayout fields_section "${sectionKey}" references field "${field.getApiName()}" ` +
327
+ `from "${field.getCustomObjectApiName()}", but the layout belongs to "${customObject.getApiName()}".`,
328
+ );
329
+ }
330
+ }
331
+
332
+ function assertSectionBelongsTo(customObject: CustomObject, section: CustomObjectFieldSection): void {
333
+ if (section.getModelApiName() !== customObject.getApiName()) {
334
+ throw new Error(
335
+ `CustomObjectPageLayout section "${section.getSectionId()}" belongs to "${section.getModelApiName()}", ` +
336
+ `but the layout belongs to "${customObject.getApiName()}".`,
337
+ );
338
+ }
339
+ }
340
+
341
+ function toSerializedSectionField(
342
+ fieldRqlName: string,
343
+ options: SectionFieldOptions = {},
344
+ ): SerializedSectionField {
345
+ const normalized: SerializedSectionField = { fieldRqlName };
346
+ if (options.hidden != null) normalized.hidden = options.hidden;
347
+ if (options.editable != null) normalized.editable = options.editable;
348
+ if (options.movementOption != null) normalized.movementOption = options.movementOption;
349
+ return normalized;
350
+ }
351
+
352
+ function normalizeSectionField(
353
+ customObject: CustomObject,
354
+ section: CustomObjectFieldSection | string,
355
+ sectionKey: string,
356
+ entry: SectionField,
357
+ ): SerializedSectionField {
358
+ if (isField(entry)) {
359
+ assertSameObject(customObject, entry, sectionKey);
360
+ entry._assignSection(section);
361
+ return toSerializedSectionField(entry.getApiName());
362
+ }
363
+
364
+ if (isFieldRef(entry)) {
365
+ assertSameObject(customObject, entry.field, sectionKey);
366
+ entry.field._assignSection(section);
367
+ return toSerializedSectionField(entry.field.getApiName(), entry);
368
+ }
369
+
370
+ throw new Error(
371
+ `CustomObjectPageLayout fields_section "${sectionKey}" has an invalid field entry; ` +
372
+ 'pass a Field instance or { field }.',
373
+ );
374
+ }
375
+
376
+ function normalizeFieldsSection(
377
+ customObject: CustomObject,
378
+ input: PageLayoutSection,
379
+ ): SerializedFieldsSection {
380
+ assertSectionBelongsTo(customObject, input.section);
381
+ const key = input.key ?? input.section.getSectionId();
382
+ const name = input.name ?? input.section.getName();
383
+ return {
384
+ key,
385
+ name,
386
+ type: 'fields_section',
387
+ fields: input.fields.map((field) => normalizeSectionField(customObject, input.section, key, field)),
388
+ };
389
+ }
390
+
391
+ function normalizeTab(customObject: CustomObject, tab: PageLayoutTab): SerializedPageLayoutTab {
392
+ if (tab.type === 'custom_tab') return tab;
393
+ return {
394
+ ...tab,
395
+ sections: tab.sections.map((section) => normalizeFieldsSection(customObject, section)),
396
+ };
397
+ }
398
+
399
+ function normalizeSystemSectionEdit(
400
+ customObject: CustomObject,
401
+ tabKey: string,
402
+ sectionKey: string,
403
+ edit: SystemSectionEdit,
404
+ ): SerializedSystemSectionEdit {
405
+ const { newFields: _newFields, ...rest } = edit;
406
+ const normalized: SerializedSystemSectionEdit = { ...rest };
407
+ if (edit.newFields != null) {
408
+ normalized.newFields = edit.newFields.map((field) =>
409
+ normalizeSectionField(customObject, sectionKey, `${tabKey}.${sectionKey}`, field),
410
+ );
411
+ }
412
+ return normalized;
413
+ }
414
+
415
+ function normalizeSystemTabEdit(
416
+ customObject: CustomObject,
417
+ tabKey: string,
418
+ edit: SystemTabEdit,
419
+ ): SerializedSystemTabEdit {
420
+ const { newSections: _newSections, systemSectionEdits: _systemSectionEdits, ...rest } = edit;
421
+ const normalized: SerializedSystemTabEdit = { ...rest };
422
+ if (edit.newSections != null) {
423
+ normalized.newSections = edit.newSections.map((section) => normalizeFieldsSection(customObject, section));
424
+ }
425
+ if (edit.systemSectionEdits != null) {
426
+ const systemSectionEdits: Record<string, SerializedSystemSectionEdit> = {};
427
+ for (const [sectionKey, sectionEdit] of Object.entries(edit.systemSectionEdits)) {
428
+ systemSectionEdits[sectionKey] = normalizeSystemSectionEdit(
429
+ customObject,
430
+ tabKey,
431
+ sectionKey,
432
+ sectionEdit,
433
+ );
434
+ }
435
+ normalized.systemSectionEdits = systemSectionEdits;
436
+ }
437
+ return normalized;
438
+ }
439
+
440
+ function normalizeTabEdits(customObject: CustomObject, tabEdits: TabEdits): SerializedTabEdits {
441
+ const { newTabs: _newTabs, systemTabEdits: _systemTabEdits, ...rest } = tabEdits;
442
+ const normalized: SerializedTabEdits = { ...rest };
443
+ if (tabEdits.newTabs != null) {
444
+ normalized.newTabs = tabEdits.newTabs.map((tab) => normalizeTab(customObject, tab));
445
+ }
446
+ if (tabEdits.systemTabEdits != null) {
447
+ const systemTabEdits: Record<string, SerializedSystemTabEdit> = {};
448
+ for (const [tabKey, tabEdit] of Object.entries(tabEdits.systemTabEdits)) {
449
+ systemTabEdits[tabKey] = normalizeSystemTabEdit(customObject, tabKey, tabEdit);
450
+ }
451
+ normalized.systemTabEdits = systemTabEdits;
452
+ }
453
+ return normalized;
454
+ }
455
+
258
456
  // ---------------------------------------------------------------------------
259
457
  // Class
260
458
  // ---------------------------------------------------------------------------
@@ -283,11 +481,11 @@ export interface CustomObjectPageLayoutProps {
283
481
  * newTabs: [{
284
482
  * key: 'member', name: 'Member', type: 'tab_with_sections',
285
483
  * sections: [{
286
- * key: 'profile', name: 'Profile', type: 'fields_section',
287
- * fields: [
288
- * { fieldRqlName: memberFirstName.getApiName() },
289
- * { fieldRqlName: memberEmail.getApiName() },
290
- * ],
484
+ * key: 'profile',
485
+ * name: 'Profile',
486
+ * type: 'fields_section',
487
+ * section: profileSection,
488
+ * fields: [memberFirstName, memberEmail],
291
489
  * }],
292
490
  * }],
293
491
  * systemTabEdits: {},
@@ -304,7 +502,7 @@ export class CustomObjectPageLayout {
304
502
  private readonly _apiName: string;
305
503
  private readonly _customObjectApiName: string;
306
504
  private readonly _name: string;
307
- private readonly _tabEdits: TabEdits;
505
+ private readonly _tabEdits: SerializedTabEdits;
308
506
  private readonly _headerEdits: HeaderEdits;
309
507
  private readonly _visibilityConditions: VisibilityConditions;
310
508
  private readonly _blueprintKey: string | undefined;
@@ -321,7 +519,7 @@ export class CustomObjectPageLayout {
321
519
  this._apiName = props.apiName;
322
520
  this._customObjectApiName = customObject.getApiName();
323
521
  this._name = props.name;
324
- this._tabEdits = props.tabEdits ?? {};
522
+ this._tabEdits = normalizeTabEdits(customObject, props.tabEdits ?? {});
325
523
  this._headerEdits = props.headerEdits ?? {};
326
524
  this._visibilityConditions = props.visibilityConditions ?? {};
327
525
  this._blueprintKey = props.blueprintKey;
@@ -396,6 +594,11 @@ export class CustomObjectPageLayout {
396
594
  ): CustomObjectPageLayout {
397
595
  const coApiName = customObject.getApiName();
398
596
  const coName = customObject.getName();
597
+ const sectionPrefix = coApiName.replace(/__c$/, '').replace(/[^a-zA-Z0-9]+/g, '_');
598
+ const generalSection = new CustomObjectFieldSection(customObject, {
599
+ sectionId: `sec_${sectionPrefix}_general`,
600
+ name: 'General',
601
+ });
399
602
  return new CustomObjectPageLayout(customObject, {
400
603
  apiName: 'default',
401
604
  name: `${coName} Layout`,
@@ -410,9 +613,8 @@ export class CustomObjectPageLayout {
410
613
  key: 'general',
411
614
  name: 'General',
412
615
  type: 'fields_section',
413
- fields: (props.fields ?? []).map((f: Field) => ({
414
- fieldRqlName: f.getApiName(),
415
- })),
616
+ section: generalSection,
617
+ fields: props.fields ?? [],
416
618
  },
417
619
  ],
418
620
  },
@@ -130,7 +130,7 @@ export class Field {
130
130
  private readonly _rqlFormula: string | null;
131
131
  private readonly _formulaAttrMetas: Record<string, any> | null;
132
132
  private readonly _derivedFieldFormula: string | null;
133
- private readonly _section: string | null;
133
+ private _section: string | null;
134
134
  private readonly _summarySpec: SummarySpec | null;
135
135
  private readonly _derivedAggregatedFieldStatic: Record<string, any> | null;
136
136
 
@@ -202,6 +202,14 @@ export class Field {
202
202
  isSummary(): boolean {
203
203
  return this._summarySpec != null;
204
204
  }
205
+ /** Returns the field section id assigned to this field, if any. */
206
+ getSectionId(): string | null {
207
+ return this._section;
208
+ }
209
+ /** @internal Assigns this field to a field section during page-layout normalization. */
210
+ _assignSection(section: CustomObjectFieldSection | string): void {
211
+ this._section = typeof section === 'string' ? section : section.getSectionId();
212
+ }
205
213
 
206
214
  // ── Serialization ──────────────────────────────────────────────────────
207
215
 
@@ -35,6 +35,8 @@ export {
35
35
  type PageLayoutSection,
36
36
  type PageLayoutTab,
37
37
  type SectionField,
38
+ type SectionFieldOptions,
39
+ type SectionFieldRef,
38
40
  type SectionFieldsLayout,
39
41
  type SystemHeaderFieldEdit,
40
42
  type SystemSectionEdit,