@rippling/rippling-sdk 0.2.0-alpha.38 → 0.2.0-alpha.39

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 (132) hide show
  1. package/lib/manifest/app.d.mts +11 -1
  2. package/lib/manifest/app.d.mts.map +1 -1
  3. package/lib/manifest/app.d.ts +11 -1
  4. package/lib/manifest/app.d.ts.map +1 -1
  5. package/lib/manifest/app.js +42 -1
  6. package/lib/manifest/app.js.map +1 -1
  7. package/lib/manifest/app.mjs +42 -1
  8. package/lib/manifest/app.mjs.map +1 -1
  9. package/lib/manifest/category.d.mts +10 -1
  10. package/lib/manifest/category.d.mts.map +1 -1
  11. package/lib/manifest/category.d.ts +10 -1
  12. package/lib/manifest/category.d.ts.map +1 -1
  13. package/lib/manifest/category.js +23 -1
  14. package/lib/manifest/category.js.map +1 -1
  15. package/lib/manifest/category.mjs +23 -1
  16. package/lib/manifest/category.mjs.map +1 -1
  17. package/lib/manifest/custom-object-field-section.d.mts +13 -0
  18. package/lib/manifest/custom-object-field-section.d.mts.map +1 -1
  19. package/lib/manifest/custom-object-field-section.d.ts +13 -0
  20. package/lib/manifest/custom-object-field-section.d.ts.map +1 -1
  21. package/lib/manifest/custom-object-field-section.js +22 -1
  22. package/lib/manifest/custom-object-field-section.js.map +1 -1
  23. package/lib/manifest/custom-object-field-section.mjs +22 -1
  24. package/lib/manifest/custom-object-field-section.mjs.map +1 -1
  25. package/lib/manifest/custom-object-page-layout.d.mts +21 -0
  26. package/lib/manifest/custom-object-page-layout.d.mts.map +1 -1
  27. package/lib/manifest/custom-object-page-layout.d.ts +21 -0
  28. package/lib/manifest/custom-object-page-layout.d.ts.map +1 -1
  29. package/lib/manifest/custom-object-page-layout.js +41 -1
  30. package/lib/manifest/custom-object-page-layout.js.map +1 -1
  31. package/lib/manifest/custom-object-page-layout.mjs +41 -1
  32. package/lib/manifest/custom-object-page-layout.mjs.map +1 -1
  33. package/lib/manifest/custom-object.d.mts +11 -1
  34. package/lib/manifest/custom-object.d.mts.map +1 -1
  35. package/lib/manifest/custom-object.d.ts +11 -1
  36. package/lib/manifest/custom-object.d.ts.map +1 -1
  37. package/lib/manifest/custom-object.js +55 -2
  38. package/lib/manifest/custom-object.js.map +1 -1
  39. package/lib/manifest/custom-object.mjs +55 -2
  40. package/lib/manifest/custom-object.mjs.map +1 -1
  41. package/lib/manifest/existing-manifest-context.d.mts +87 -0
  42. package/lib/manifest/existing-manifest-context.d.mts.map +1 -0
  43. package/lib/manifest/existing-manifest-context.d.ts +87 -0
  44. package/lib/manifest/existing-manifest-context.d.ts.map +1 -0
  45. package/lib/manifest/existing-manifest-context.js +511 -0
  46. package/lib/manifest/existing-manifest-context.js.map +1 -0
  47. package/lib/manifest/existing-manifest-context.mjs +506 -0
  48. package/lib/manifest/existing-manifest-context.mjs.map +1 -0
  49. package/lib/manifest/field.d.mts +24 -0
  50. package/lib/manifest/field.d.mts.map +1 -1
  51. package/lib/manifest/field.d.ts +24 -0
  52. package/lib/manifest/field.d.ts.map +1 -1
  53. package/lib/manifest/field.js +114 -1
  54. package/lib/manifest/field.js.map +1 -1
  55. package/lib/manifest/field.mjs +114 -1
  56. package/lib/manifest/field.mjs.map +1 -1
  57. package/lib/manifest/index.d.mts +8 -7
  58. package/lib/manifest/index.d.mts.map +1 -1
  59. package/lib/manifest/index.d.ts +8 -7
  60. package/lib/manifest/index.d.ts.map +1 -1
  61. package/lib/manifest/index.js +3 -1
  62. package/lib/manifest/index.js.map +1 -1
  63. package/lib/manifest/index.mjs +5 -4
  64. package/lib/manifest/index.mjs.map +1 -1
  65. package/lib/manifest/list-view.d.mts +17 -0
  66. package/lib/manifest/list-view.d.mts.map +1 -1
  67. package/lib/manifest/list-view.d.ts +17 -0
  68. package/lib/manifest/list-view.d.ts.map +1 -1
  69. package/lib/manifest/list-view.js +38 -1
  70. package/lib/manifest/list-view.js.map +1 -1
  71. package/lib/manifest/list-view.mjs +38 -1
  72. package/lib/manifest/list-view.mjs.map +1 -1
  73. package/lib/manifest/manifest-builder.d.mts +59 -6
  74. package/lib/manifest/manifest-builder.d.mts.map +1 -1
  75. package/lib/manifest/manifest-builder.d.ts +59 -6
  76. package/lib/manifest/manifest-builder.d.ts.map +1 -1
  77. package/lib/manifest/manifest-builder.js +61 -3
  78. package/lib/manifest/manifest-builder.js.map +1 -1
  79. package/lib/manifest/manifest-builder.mjs +61 -3
  80. package/lib/manifest/manifest-builder.mjs.map +1 -1
  81. package/lib/manifest/manifest-function.d.mts +10 -1
  82. package/lib/manifest/manifest-function.d.mts.map +1 -1
  83. package/lib/manifest/manifest-function.d.ts +10 -1
  84. package/lib/manifest/manifest-function.d.ts.map +1 -1
  85. package/lib/manifest/manifest-function.js +36 -1
  86. package/lib/manifest/manifest-function.js.map +1 -1
  87. package/lib/manifest/manifest-function.mjs +36 -1
  88. package/lib/manifest/manifest-function.mjs.map +1 -1
  89. package/lib/manifest/rule.d.mts +17 -0
  90. package/lib/manifest/rule.d.mts.map +1 -1
  91. package/lib/manifest/rule.d.ts +17 -0
  92. package/lib/manifest/rule.d.ts.map +1 -1
  93. package/lib/manifest/rule.js +45 -1
  94. package/lib/manifest/rule.js.map +1 -1
  95. package/lib/manifest/rule.mjs +45 -1
  96. package/lib/manifest/rule.mjs.map +1 -1
  97. package/lib/manifest/sdui-page.d.mts +10 -1
  98. package/lib/manifest/sdui-page.d.mts.map +1 -1
  99. package/lib/manifest/sdui-page.d.ts +10 -1
  100. package/lib/manifest/sdui-page.d.ts.map +1 -1
  101. package/lib/manifest/sdui-page.js +28 -1
  102. package/lib/manifest/sdui-page.js.map +1 -1
  103. package/lib/manifest/sdui-page.mjs +28 -1
  104. package/lib/manifest/sdui-page.mjs.map +1 -1
  105. package/lib/manifest/validation.d.mts +17 -0
  106. package/lib/manifest/validation.d.mts.map +1 -1
  107. package/lib/manifest/validation.d.ts +17 -0
  108. package/lib/manifest/validation.d.ts.map +1 -1
  109. package/lib/manifest/validation.js +31 -1
  110. package/lib/manifest/validation.js.map +1 -1
  111. package/lib/manifest/validation.mjs +31 -1
  112. package/lib/manifest/validation.mjs.map +1 -1
  113. package/package.json +1 -1
  114. package/src/lib/manifest/app.ts +46 -2
  115. package/src/lib/manifest/category.ts +28 -2
  116. package/src/lib/manifest/custom-object-field-section.ts +37 -1
  117. package/src/lib/manifest/custom-object-page-layout.ts +61 -1
  118. package/src/lib/manifest/custom-object.ts +58 -3
  119. package/src/lib/manifest/existing-manifest-context.ts +673 -0
  120. package/src/lib/manifest/field.ts +140 -1
  121. package/src/lib/manifest/index.ts +39 -4
  122. package/src/lib/manifest/list-view.ts +50 -1
  123. package/src/lib/manifest/manifest-builder.ts +114 -4
  124. package/src/lib/manifest/manifest-function.ts +36 -2
  125. package/src/lib/manifest/rule.ts +60 -1
  126. package/src/lib/manifest/sdui-page.ts +37 -2
  127. package/src/lib/manifest/validation.ts +48 -1
  128. package/src/version.ts +1 -1
  129. package/version.d.mts +1 -1
  130. package/version.d.ts +1 -1
  131. package/version.js +1 -1
  132. package/version.mjs +1 -1
@@ -1,4 +1,5 @@
1
1
  import { generateId } from "./_helpers.mjs";
2
+ import { getExistingManifestContext } from "./existing-manifest-context.mjs";
2
3
  /**
3
4
  * Defines a validation rule and registers it with the manifest.
4
5
  *
@@ -25,6 +26,13 @@ import { generateId } from "./_helpers.mjs";
25
26
  * ```
26
27
  */
27
28
  export class Validation {
29
+ static toDeletionIdentifier(identifier) {
30
+ return {
31
+ type: Validation.componentType,
32
+ rule_id: identifier.ruleId,
33
+ model_rql_name: identifier.modelRqlName,
34
+ };
35
+ }
28
36
  /**
29
37
  * @param customObject - The custom object this rule applies to.
30
38
  * @param props - Initialization properties.
@@ -41,6 +49,27 @@ export class Validation {
41
49
  this._fieldRef = props.fieldRef;
42
50
  customObject._register(this);
43
51
  }
52
+ /**
53
+ * Loads this validation rule from the active existing-manifest JSON context.
54
+ */
55
+ static loadFromExisting(ruleId, options = {}) {
56
+ return getExistingManifestContext(options).loadValidation(ruleId, options);
57
+ }
58
+ /** @internal Hydrates a validation rule from existing manifest wire JSON. */
59
+ static _fromExistingComponent(customObject, component, resolveField) {
60
+ const props = {
61
+ ruleId: component['rule_id'],
62
+ displayName: component['display_name'] ?? component['rule_id'],
63
+ rqlFormula: component['rql_formula'] ?? '',
64
+ errorMessage: component['custom_error_message'] ?? '',
65
+ state: component['state'],
66
+ level: component['level'],
67
+ ruleDescription: component['rule_description'] ?? '',
68
+ };
69
+ if (component['field_rql_name'] != null)
70
+ props.fieldRef = resolveField(component['field_rql_name']);
71
+ return new Validation(customObject, props);
72
+ }
44
73
  /**
45
74
  * Returns the stable identifier for this validation rule (e.g. `'gym_session_end_after_start'`).
46
75
  */
@@ -61,7 +90,7 @@ export class Validation {
61
90
  */
62
91
  toDict() {
63
92
  const component = {
64
- type: 'CUSTOM_OBJECT_VALIDATION_RULE',
93
+ type: Validation.componentType,
65
94
  model_rql_name: this._customObjectApiName,
66
95
  rule_id: this._ruleId,
67
96
  state: this._state,
@@ -76,4 +105,5 @@ export class Validation {
76
105
  return component;
77
106
  }
78
107
  }
108
+ Validation.componentType = 'CUSTOM_OBJECT_VALIDATION_RULE';
79
109
  //# sourceMappingURL=validation.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.mjs","sourceRoot":"","sources":["../../src/lib/manifest/validation.ts"],"names":[],"mappings":"OAAO,EAAE,UAAU,EAA8C;AA6DjE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,UAAU;IAWrB;;;OAGG;IACH,YAAY,YAA0B,EAAE,KAAsB;QAC5D,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,MAAM,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;QAClD,IAAI,CAAC,oBAAoB,GAAG,YAAY,CAAC,UAAU,EAAE,CAAC;QACtD,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC;QACtC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,UAAU,CAAC;QACpC,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,YAAY,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC,eAAe,CAAC;QAC9C,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC;QAChC,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,SAAS;QACP,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,sBAAsB;QACpB,OAAO,IAAI,CAAC,oBAAoB,CAAC;IACnC,CAAC;IAED;;;;;OAKG;IACH,MAAM;QACJ,MAAM,SAAS,GAAwB;YACrC,IAAI,EAAE,+BAA+B;YACrC,cAAc,EAAE,IAAI,CAAC,oBAAoB;YACzC,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,KAAK,EAAE,IAAI,CAAC,MAAM;YAClB,KAAK,EAAE,IAAI,CAAC,MAAM;YAClB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,oBAAoB,EAAE,IAAI,CAAC,aAAa;YACxC,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC;QACF,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI;YAAE,SAAS,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;QACtF,OAAO,SAAS,CAAC;IACnB,CAAC;CACF"}
1
+ {"version":3,"file":"validation.mjs","sourceRoot":"","sources":["../../src/lib/manifest/validation.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rippling/rippling-sdk",
3
- "version": "0.2.0-alpha.38",
3
+ "version": "0.2.0-alpha.39",
4
4
  "description": "The official TypeScript library for the Rippling SDK API",
5
5
  "author": "Rippling SDK <no-reply@rippling.com>",
6
6
  "types": "./index.d.ts",
@@ -1,5 +1,6 @@
1
1
  import { CustomObject } from './custom-object';
2
- import type { ManifestScope } from './manifest-builder';
2
+ import { getExistingManifestContext, type ExistingManifestContextOptions } from './existing-manifest-context';
3
+ import type { ManifestComponentDeletion, ManifestScope } from './manifest-builder';
3
4
  import { SduiPage } from './sdui-page';
4
5
 
5
6
  /**
@@ -82,6 +83,11 @@ function resolvePage(entry: AppPage): Record<string, any> {
82
83
  }
83
84
  }
84
85
 
86
+ function cloneJson<T>(value: T): T {
87
+ if (value == null) return value;
88
+ return JSON.parse(JSON.stringify(value)) as T;
89
+ }
90
+
85
91
  /**
86
92
  * Defines a Rippling app and registers it with the manifest.
87
93
  *
@@ -105,6 +111,15 @@ function resolvePage(entry: AppPage): Record<string, any> {
105
111
  * @see {@link SduiPage} — SDUI surface tab source.
106
112
  */
107
113
  export class App {
114
+ static readonly componentType = 'CUSTOM_APP' as const;
115
+
116
+ static toDeletionIdentifier(apiName: string): ManifestComponentDeletion {
117
+ return {
118
+ type: App.componentType,
119
+ api_name: apiName,
120
+ };
121
+ }
122
+
108
123
  private readonly _apiName: string;
109
124
  private readonly _name: string;
110
125
  private readonly _description: string | undefined;
@@ -127,6 +142,35 @@ export class App {
127
142
  scope._register(this);
128
143
  }
129
144
 
145
+ /**
146
+ * Loads this app from the active existing-manifest JSON context.
147
+ */
148
+ static loadFromExisting(apiName: string, options: ExistingManifestContextOptions = {}): App {
149
+ return getExistingManifestContext(options).loadApp(apiName);
150
+ }
151
+
152
+ /** @internal Hydrates an app from existing manifest wire JSON. */
153
+ static _fromExistingComponent(scope: ManifestScope, component: Record<string, any>): App {
154
+ const app = Object.create(App.prototype) as App;
155
+ Object.assign(app as Record<string, any>, {
156
+ _apiName: component['api_name'],
157
+ _name: component['name'] ?? component['api_name'],
158
+ _description: component['description'],
159
+ _appType: component['app_type'],
160
+ _icon: App.appIconFromExistingComponent(component['icon']),
161
+ _pages: cloneJson(component['pages'] ?? []),
162
+ });
163
+ scope._register(app);
164
+ return app;
165
+ }
166
+
167
+ private static appIconFromExistingComponent(value: unknown): AppIcon | undefined {
168
+ if (value == null || typeof value !== 'object') return undefined;
169
+ const wire = value as Record<string, any>;
170
+ if (typeof wire['s3_bucket'] !== 'string' || typeof wire['s3_key'] !== 'string') return undefined;
171
+ return { s3Bucket: wire['s3_bucket'], s3Key: wire['s3_key'] };
172
+ }
173
+
130
174
  /**
131
175
  * Returns the api_name of this app (e.g. `'gym_membership_management_app'`).
132
176
  */
@@ -148,7 +192,7 @@ export class App {
148
192
  */
149
193
  toDict(): Record<string, any> {
150
194
  const component: Record<string, any> = {
151
- type: 'CUSTOM_APP',
195
+ type: App.componentType,
152
196
  api_name: this._apiName,
153
197
  name: this._name,
154
198
  };
@@ -1,4 +1,5 @@
1
- import type { ManifestScope } from './manifest-builder';
1
+ import { getExistingManifestContext, type ExistingManifestContextOptions } from './existing-manifest-context';
2
+ import type { ManifestComponentDeletion, ManifestScope } from './manifest-builder';
2
3
 
3
4
  /**
4
5
  * Initialization properties for {@link Category}.
@@ -57,6 +58,15 @@ export interface CategoryProps {
57
58
  * @see {@link CustomObject} — references this category via instance or api_name string.
58
59
  */
59
60
  export class Category {
61
+ static readonly componentType = 'CUSTOM_CATEGORY' as const;
62
+
63
+ static toDeletionIdentifier(apiName: string): ManifestComponentDeletion {
64
+ return {
65
+ type: Category.componentType,
66
+ api_name: apiName,
67
+ };
68
+ }
69
+
60
70
  private readonly _apiName: string;
61
71
  private readonly _name: string;
62
72
  private readonly _description: string;
@@ -72,6 +82,22 @@ export class Category {
72
82
  scope._register(this);
73
83
  }
74
84
 
85
+ /**
86
+ * Loads this category from the active existing-manifest JSON context.
87
+ */
88
+ static loadFromExisting(apiName: string, options: ExistingManifestContextOptions = {}): Category {
89
+ return getExistingManifestContext(options).loadCategory(apiName);
90
+ }
91
+
92
+ /** @internal Hydrates a category from existing manifest wire JSON. */
93
+ static _fromExistingComponent(scope: ManifestScope, component: Record<string, any>): Category {
94
+ return new Category(scope, {
95
+ apiName: component['api_name'],
96
+ name: component['name'] ?? component['api_name'],
97
+ description: component['description'] ?? '',
98
+ });
99
+ }
100
+
75
101
  /**
76
102
  * Returns the api_name of this category (e.g. `'gym__c'`).
77
103
  */
@@ -93,7 +119,7 @@ export class Category {
93
119
  */
94
120
  toDict(): Record<string, any> {
95
121
  return {
96
- type: 'CUSTOM_CATEGORY',
122
+ type: Category.componentType,
97
123
  api_name: this._apiName,
98
124
  name: this._name,
99
125
  description: this._description,
@@ -1,5 +1,11 @@
1
1
  import { generateId } from './_helpers';
2
2
  import type { CustomObject } from './custom-object';
3
+ import { getExistingManifestContext, type ExistingManifestContextOptions } from './existing-manifest-context';
4
+ import type { ManifestComponentDeletion } from './manifest-builder';
5
+
6
+ export interface CustomObjectFieldSectionLoadFromExistingOptions extends ExistingManifestContextOptions {
7
+ customObjectApiName?: string;
8
+ }
3
9
 
4
10
  /**
5
11
  * Initialization properties for {@link CustomObjectFieldSection}.
@@ -45,6 +51,15 @@ export interface CustomObjectFieldSectionProps {
45
51
  * @see {@link CustomObjectPageLayout} — controls which sections appear on which tab.
46
52
  */
47
53
  export class CustomObjectFieldSection {
54
+ static readonly componentType = 'CUSTOM_OBJECT_FIELD_SECTION' as const;
55
+
56
+ static toDeletionIdentifier(sectionId: string): ManifestComponentDeletion {
57
+ return {
58
+ type: CustomObjectFieldSection.componentType,
59
+ section_id: sectionId,
60
+ };
61
+ }
62
+
48
63
  private readonly _sectionId: string;
49
64
  private readonly _name: string;
50
65
  private readonly _model: CustomObject;
@@ -60,6 +75,27 @@ export class CustomObjectFieldSection {
60
75
  customObject._register(this);
61
76
  }
62
77
 
78
+ /**
79
+ * Loads this field section from the active existing-manifest JSON context.
80
+ */
81
+ static loadFromExisting(
82
+ sectionId: string,
83
+ options: CustomObjectFieldSectionLoadFromExistingOptions = {},
84
+ ): CustomObjectFieldSection {
85
+ return getExistingManifestContext(options).loadFieldSection(sectionId, options);
86
+ }
87
+
88
+ /** @internal Hydrates a field section from existing manifest wire JSON. */
89
+ static _fromExistingComponent(
90
+ customObject: CustomObject,
91
+ component: Record<string, any>,
92
+ ): CustomObjectFieldSection {
93
+ return new CustomObjectFieldSection(customObject, {
94
+ sectionId: component['section_id'],
95
+ name: component['name'] ?? component['section_id'],
96
+ });
97
+ }
98
+
63
99
  /**
64
100
  * Returns the stable identifier for this section (e.g. `'sec_gm_profile'`).
65
101
  *
@@ -91,7 +127,7 @@ export class CustomObjectFieldSection {
91
127
  */
92
128
  toDict(): Record<string, any> {
93
129
  return {
94
- type: 'CUSTOM_OBJECT_FIELD_SECTION',
130
+ type: CustomObjectFieldSection.componentType,
95
131
  section_id: this._sectionId,
96
132
  model_name: this._model.getApiName(),
97
133
  name: this._name,
@@ -1,7 +1,14 @@
1
1
  import { generateId } from './_helpers';
2
2
  import { CustomObjectFieldSection } from './custom-object-field-section';
3
3
  import type { CustomObject } from './custom-object';
4
+ import { getExistingManifestContext, type ExistingManifestContextOptions } from './existing-manifest-context';
4
5
  import type { Field } from './field';
6
+ import type { ManifestComponentDeletion } from './manifest-builder';
7
+
8
+ export interface PageLayoutDeletionIdentifier {
9
+ layoutId: string;
10
+ objectRqlName: string;
11
+ }
5
12
 
6
13
  // ---------------------------------------------------------------------------
7
14
  // header_edits — HeaderEditsDTO
@@ -267,6 +274,10 @@ export interface CustomObjectPageLayoutProps {
267
274
  blueprintKey?: string;
268
275
  }
269
276
 
277
+ export interface CustomObjectPageLayoutLoadFromExistingOptions extends ExistingManifestContextOptions {
278
+ customObjectApiName?: string;
279
+ }
280
+
270
281
  interface SerializedSectionField extends SectionFieldOptions {
271
282
  fieldRqlName: string;
272
283
  }
@@ -458,6 +469,11 @@ function normalizeTabEdits(customObject: CustomObject, tabEdits: TabEdits): Seri
458
469
  return normalized;
459
470
  }
460
471
 
472
+ function cloneJson<T>(value: T): T {
473
+ if (value == null) return value;
474
+ return JSON.parse(JSON.stringify(value)) as T;
475
+ }
476
+
461
477
  // ---------------------------------------------------------------------------
462
478
  // Class
463
479
  // ---------------------------------------------------------------------------
@@ -503,6 +519,16 @@ function normalizeTabEdits(customObject: CustomObject, tabEdits: TabEdits): Seri
503
519
  * @see {@link CustomObjectPageLayout.basic} — factory for simple single-tab layouts.
504
520
  */
505
521
  export class CustomObjectPageLayout {
522
+ static readonly componentType = 'CUSTOM_OBJECT_PAGE_LAYOUT' as const;
523
+
524
+ static toDeletionIdentifier(identifier: PageLayoutDeletionIdentifier): ManifestComponentDeletion {
525
+ return {
526
+ type: CustomObjectPageLayout.componentType,
527
+ layout_id: identifier.layoutId,
528
+ object_rql_name: identifier.objectRqlName,
529
+ };
530
+ }
531
+
506
532
  private readonly _layoutId: string;
507
533
  private readonly _apiName: string;
508
534
  private readonly _customObjectApiName: string;
@@ -531,6 +557,40 @@ export class CustomObjectPageLayout {
531
557
  customObject._register(this);
532
558
  }
533
559
 
560
+ /**
561
+ * Loads this page layout from the active existing-manifest JSON context.
562
+ *
563
+ * `id` resolves against `layout_id` first, then `api_name`. When loading by
564
+ * api_name such as `'default'`, pass `customObjectApiName` if multiple objects
565
+ * have a layout with the same api_name.
566
+ */
567
+ static loadFromExisting(
568
+ layoutIdOrApiName: string,
569
+ options: CustomObjectPageLayoutLoadFromExistingOptions = {},
570
+ ): CustomObjectPageLayout {
571
+ return getExistingManifestContext(options).loadPageLayout(layoutIdOrApiName, options);
572
+ }
573
+
574
+ /** @internal Hydrates a page layout from existing manifest wire JSON. */
575
+ static _fromExistingComponent(
576
+ customObject: CustomObject,
577
+ component: Record<string, any>,
578
+ ): CustomObjectPageLayout {
579
+ const layout = Object.create(CustomObjectPageLayout.prototype) as CustomObjectPageLayout;
580
+ Object.assign(layout as Record<string, any>, {
581
+ _layoutId: component['layout_id'],
582
+ _apiName: component['api_name'],
583
+ _customObjectApiName: component['object_rql_name'],
584
+ _name: component['name'] ?? component['api_name'],
585
+ _tabEdits: cloneJson(component['tab_edits'] ?? {}),
586
+ _headerEdits: cloneJson(component['header_edits'] ?? {}),
587
+ _visibilityConditions: cloneJson(component['visibility_conditions'] ?? {}),
588
+ _blueprintKey: component['blueprint_key'],
589
+ });
590
+ customObject._register(layout);
591
+ return layout;
592
+ }
593
+
534
594
  /**
535
595
  * Returns the stable identifier for this layout record (e.g. `'layout_gym_member'`).
536
596
  */
@@ -559,7 +619,7 @@ export class CustomObjectPageLayout {
559
619
  */
560
620
  toDict(): Record<string, any> {
561
621
  const d: Record<string, any> = {
562
- type: 'CUSTOM_OBJECT_PAGE_LAYOUT',
622
+ type: CustomObjectPageLayout.componentType,
563
623
  layout_id: this._layoutId,
564
624
  api_name: this._apiName,
565
625
  object_rql_name: this._customObjectApiName,
@@ -1,5 +1,6 @@
1
1
  import { Category } from './category';
2
- import type { ManifestComponent, ManifestScope } from './manifest-builder';
2
+ import { getExistingManifestContext, type ExistingManifestContextOptions } from './existing-manifest-context';
3
+ import type { ManifestComponent, ManifestComponentDeletion, ManifestScope } from './manifest-builder';
3
4
 
4
5
  // ────────────────────────────────────────────────────────────────────────
5
6
  // Platform-managed standard fields — mirrors
@@ -113,6 +114,8 @@ const STANDARD_FIELD_SPECS: ReadonlyArray<{
113
114
  * (`id`, `created_at`, …) don't carry the custom-field suffix.
114
115
  */
115
116
  class _StandardField implements ManifestComponent {
117
+ static readonly componentType = 'CUSTOM_OBJECT_FIELD' as const;
118
+
116
119
  constructor(
117
120
  private readonly customObjectApiName: string,
118
121
  private readonly spec: (typeof STANDARD_FIELD_SPECS)[number],
@@ -120,7 +123,7 @@ class _StandardField implements ManifestComponent {
120
123
 
121
124
  toDict(): Record<string, any> {
122
125
  return {
123
- type: 'CUSTOM_OBJECT_FIELD',
126
+ type: _StandardField.componentType,
124
127
  custom_object_api_name: this.customObjectApiName,
125
128
  field_api_name: this.spec.apiName,
126
129
  field_display_name: this.spec.displayName,
@@ -297,6 +300,15 @@ export interface CustomObjectProps {
297
300
  * can fragment ownership and complicate the permission story.
298
301
  */
299
302
  export class CustomObject implements ManifestScope {
303
+ static readonly componentType = 'CUSTOM_OBJECT' as const;
304
+
305
+ static toDeletionIdentifier(apiName: string): ManifestComponentDeletion {
306
+ return {
307
+ type: CustomObject.componentType,
308
+ api_name: apiName,
309
+ };
310
+ }
311
+
300
312
  private readonly _scope: ManifestScope;
301
313
  private readonly _apiName: string;
302
314
  private readonly _name: string;
@@ -333,6 +345,49 @@ export class CustomObject implements ManifestScope {
333
345
  }
334
346
  }
335
347
 
348
+ /**
349
+ * Loads this custom object from the active existing-manifest JSON context.
350
+ */
351
+ static loadFromExisting(apiName: string, options: ExistingManifestContextOptions = {}): CustomObject {
352
+ return getExistingManifestContext(options).loadCustomObject(apiName);
353
+ }
354
+
355
+ /** @internal Hydrates a custom object from existing manifest wire JSON. */
356
+ static _fromExistingComponent(
357
+ scope: ManifestScope,
358
+ component: Record<string, any>,
359
+ resolveCategory: (apiName: string) => Category | string,
360
+ ): CustomObject {
361
+ const categoryApiName = component['category_api_name'];
362
+ const props: CustomObjectProps = {
363
+ apiName: component['api_name'],
364
+ name: component['name'] ?? component['api_name'],
365
+ category: typeof categoryApiName === 'string' ? resolveCategory(categoryApiName) : categoryApiName,
366
+ };
367
+ if (component['description'] !== undefined) props.description = component['description'];
368
+ if (component['plural_label'] !== undefined) props.pluralLabel = component['plural_label'];
369
+ if (component['name_field_details'] !== undefined) {
370
+ props.nameFieldDetails = component['name_field_details'];
371
+ }
372
+ if (component['default_field_section_name'] !== undefined) {
373
+ props.defaultFieldSectionName = component['default_field_section_name'];
374
+ }
375
+ const icon = CustomObject.iconFromExistingComponent(component['icon']);
376
+ if (icon != null) props.icon = icon;
377
+ return new CustomObject(scope, props);
378
+ }
379
+
380
+ private static iconFromExistingComponent(value: unknown): IconConfig | undefined {
381
+ if (value == null || typeof value !== 'object') return undefined;
382
+ const wire = value as Record<string, any>;
383
+ const icon: IconConfig = {};
384
+ if (wire['emoji'] !== undefined) icon.emoji = wire['emoji'];
385
+ if (wire['rippling_icon'] !== undefined) icon.ripplingIcon = wire['rippling_icon'];
386
+ if (wire['s3_bucket'] !== undefined) icon.s3Bucket = wire['s3_bucket'];
387
+ if (wire['s3_key'] !== undefined) icon.s3Key = wire['s3_key'];
388
+ return Object.keys(icon).length > 0 ? icon : undefined;
389
+ }
390
+
336
391
  /** @internal Scope hook — forwards child registration up to the root builder. */
337
392
  _register(component: ManifestComponent): void {
338
393
  this._scope._register(component);
@@ -362,7 +417,7 @@ export class CustomObject implements ManifestScope {
362
417
  */
363
418
  toDict(): Record<string, any> {
364
419
  const component: Record<string, any> = {
365
- type: 'CUSTOM_OBJECT',
420
+ type: CustomObject.componentType,
366
421
  api_name: this._apiName,
367
422
  name: this._name,
368
423
  description: this._description ?? null,