@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.
- package/lib/manifest/app.d.mts +11 -1
- package/lib/manifest/app.d.mts.map +1 -1
- package/lib/manifest/app.d.ts +11 -1
- package/lib/manifest/app.d.ts.map +1 -1
- package/lib/manifest/app.js +42 -1
- package/lib/manifest/app.js.map +1 -1
- package/lib/manifest/app.mjs +42 -1
- package/lib/manifest/app.mjs.map +1 -1
- package/lib/manifest/category.d.mts +10 -1
- package/lib/manifest/category.d.mts.map +1 -1
- package/lib/manifest/category.d.ts +10 -1
- package/lib/manifest/category.d.ts.map +1 -1
- package/lib/manifest/category.js +23 -1
- package/lib/manifest/category.js.map +1 -1
- package/lib/manifest/category.mjs +23 -1
- package/lib/manifest/category.mjs.map +1 -1
- package/lib/manifest/custom-object-field-section.d.mts +13 -0
- package/lib/manifest/custom-object-field-section.d.mts.map +1 -1
- package/lib/manifest/custom-object-field-section.d.ts +13 -0
- package/lib/manifest/custom-object-field-section.d.ts.map +1 -1
- package/lib/manifest/custom-object-field-section.js +22 -1
- package/lib/manifest/custom-object-field-section.js.map +1 -1
- package/lib/manifest/custom-object-field-section.mjs +22 -1
- package/lib/manifest/custom-object-field-section.mjs.map +1 -1
- package/lib/manifest/custom-object-page-layout.d.mts +21 -0
- package/lib/manifest/custom-object-page-layout.d.mts.map +1 -1
- package/lib/manifest/custom-object-page-layout.d.ts +21 -0
- package/lib/manifest/custom-object-page-layout.d.ts.map +1 -1
- package/lib/manifest/custom-object-page-layout.js +41 -1
- package/lib/manifest/custom-object-page-layout.js.map +1 -1
- package/lib/manifest/custom-object-page-layout.mjs +41 -1
- package/lib/manifest/custom-object-page-layout.mjs.map +1 -1
- package/lib/manifest/custom-object.d.mts +11 -1
- package/lib/manifest/custom-object.d.mts.map +1 -1
- package/lib/manifest/custom-object.d.ts +11 -1
- package/lib/manifest/custom-object.d.ts.map +1 -1
- package/lib/manifest/custom-object.js +55 -2
- package/lib/manifest/custom-object.js.map +1 -1
- package/lib/manifest/custom-object.mjs +55 -2
- package/lib/manifest/custom-object.mjs.map +1 -1
- package/lib/manifest/existing-manifest-context.d.mts +87 -0
- package/lib/manifest/existing-manifest-context.d.mts.map +1 -0
- package/lib/manifest/existing-manifest-context.d.ts +87 -0
- package/lib/manifest/existing-manifest-context.d.ts.map +1 -0
- package/lib/manifest/existing-manifest-context.js +511 -0
- package/lib/manifest/existing-manifest-context.js.map +1 -0
- package/lib/manifest/existing-manifest-context.mjs +506 -0
- package/lib/manifest/existing-manifest-context.mjs.map +1 -0
- package/lib/manifest/field.d.mts +24 -0
- package/lib/manifest/field.d.mts.map +1 -1
- package/lib/manifest/field.d.ts +24 -0
- package/lib/manifest/field.d.ts.map +1 -1
- package/lib/manifest/field.js +114 -1
- package/lib/manifest/field.js.map +1 -1
- package/lib/manifest/field.mjs +114 -1
- package/lib/manifest/field.mjs.map +1 -1
- package/lib/manifest/index.d.mts +8 -7
- package/lib/manifest/index.d.mts.map +1 -1
- package/lib/manifest/index.d.ts +8 -7
- package/lib/manifest/index.d.ts.map +1 -1
- package/lib/manifest/index.js +3 -1
- package/lib/manifest/index.js.map +1 -1
- package/lib/manifest/index.mjs +5 -4
- package/lib/manifest/index.mjs.map +1 -1
- package/lib/manifest/list-view.d.mts +17 -0
- package/lib/manifest/list-view.d.mts.map +1 -1
- package/lib/manifest/list-view.d.ts +17 -0
- package/lib/manifest/list-view.d.ts.map +1 -1
- package/lib/manifest/list-view.js +38 -1
- package/lib/manifest/list-view.js.map +1 -1
- package/lib/manifest/list-view.mjs +38 -1
- package/lib/manifest/list-view.mjs.map +1 -1
- package/lib/manifest/manifest-builder.d.mts +59 -6
- package/lib/manifest/manifest-builder.d.mts.map +1 -1
- package/lib/manifest/manifest-builder.d.ts +59 -6
- package/lib/manifest/manifest-builder.d.ts.map +1 -1
- package/lib/manifest/manifest-builder.js +61 -3
- package/lib/manifest/manifest-builder.js.map +1 -1
- package/lib/manifest/manifest-builder.mjs +61 -3
- package/lib/manifest/manifest-builder.mjs.map +1 -1
- package/lib/manifest/manifest-function.d.mts +10 -1
- package/lib/manifest/manifest-function.d.mts.map +1 -1
- package/lib/manifest/manifest-function.d.ts +10 -1
- package/lib/manifest/manifest-function.d.ts.map +1 -1
- package/lib/manifest/manifest-function.js +36 -1
- package/lib/manifest/manifest-function.js.map +1 -1
- package/lib/manifest/manifest-function.mjs +36 -1
- package/lib/manifest/manifest-function.mjs.map +1 -1
- package/lib/manifest/rule.d.mts +17 -0
- package/lib/manifest/rule.d.mts.map +1 -1
- package/lib/manifest/rule.d.ts +17 -0
- package/lib/manifest/rule.d.ts.map +1 -1
- package/lib/manifest/rule.js +45 -1
- package/lib/manifest/rule.js.map +1 -1
- package/lib/manifest/rule.mjs +45 -1
- package/lib/manifest/rule.mjs.map +1 -1
- package/lib/manifest/sdui-page.d.mts +10 -1
- package/lib/manifest/sdui-page.d.mts.map +1 -1
- package/lib/manifest/sdui-page.d.ts +10 -1
- package/lib/manifest/sdui-page.d.ts.map +1 -1
- package/lib/manifest/sdui-page.js +28 -1
- package/lib/manifest/sdui-page.js.map +1 -1
- package/lib/manifest/sdui-page.mjs +28 -1
- package/lib/manifest/sdui-page.mjs.map +1 -1
- package/lib/manifest/validation.d.mts +17 -0
- package/lib/manifest/validation.d.mts.map +1 -1
- package/lib/manifest/validation.d.ts +17 -0
- package/lib/manifest/validation.d.ts.map +1 -1
- package/lib/manifest/validation.js +31 -1
- package/lib/manifest/validation.js.map +1 -1
- package/lib/manifest/validation.mjs +31 -1
- package/lib/manifest/validation.mjs.map +1 -1
- package/package.json +1 -1
- package/src/lib/manifest/app.ts +46 -2
- package/src/lib/manifest/category.ts +28 -2
- package/src/lib/manifest/custom-object-field-section.ts +37 -1
- package/src/lib/manifest/custom-object-page-layout.ts +61 -1
- package/src/lib/manifest/custom-object.ts +58 -3
- package/src/lib/manifest/existing-manifest-context.ts +673 -0
- package/src/lib/manifest/field.ts +140 -1
- package/src/lib/manifest/index.ts +39 -4
- package/src/lib/manifest/list-view.ts +50 -1
- package/src/lib/manifest/manifest-builder.ts +114 -4
- package/src/lib/manifest/manifest-function.ts +36 -2
- package/src/lib/manifest/rule.ts +60 -1
- package/src/lib/manifest/sdui-page.ts +37 -2
- package/src/lib/manifest/validation.ts +48 -1
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
|
@@ -7,6 +7,13 @@ import {
|
|
|
7
7
|
} from './_helpers';
|
|
8
8
|
import type { CustomObject } from './custom-object';
|
|
9
9
|
import type { CustomObjectFieldSection } from './custom-object-field-section';
|
|
10
|
+
import { getExistingManifestContext, type ExistingManifestContextOptions } from './existing-manifest-context';
|
|
11
|
+
import type { ManifestComponentDeletion } from './manifest-builder';
|
|
12
|
+
|
|
13
|
+
export interface CustomObjectFieldDeletionIdentifier {
|
|
14
|
+
customObjectApiName: string;
|
|
15
|
+
fieldApiName: string;
|
|
16
|
+
}
|
|
10
17
|
|
|
11
18
|
/**
|
|
12
19
|
* A filter condition applied to a {@link SummaryField}.
|
|
@@ -53,6 +60,11 @@ function validateApiName(apiName: string): void {
|
|
|
53
60
|
}
|
|
54
61
|
}
|
|
55
62
|
|
|
63
|
+
function cloneJson<T>(value: T): T {
|
|
64
|
+
if (value == null) return value;
|
|
65
|
+
return JSON.parse(JSON.stringify(value)) as T;
|
|
66
|
+
}
|
|
67
|
+
|
|
56
68
|
/** Props shared by every field variant. */
|
|
57
69
|
interface CommonFieldProps {
|
|
58
70
|
apiName: string;
|
|
@@ -90,6 +102,61 @@ export interface FieldProps {
|
|
|
90
102
|
summarySpec?: SummarySpec | undefined;
|
|
91
103
|
}
|
|
92
104
|
|
|
105
|
+
export interface FieldLoadFromExistingOptions extends ExistingManifestContextOptions {
|
|
106
|
+
customObjectApiName?: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
class ExistingFieldReference {
|
|
110
|
+
private _section: string | null;
|
|
111
|
+
|
|
112
|
+
constructor(
|
|
113
|
+
private readonly customObjectApiName: string,
|
|
114
|
+
private readonly component: Record<string, any>,
|
|
115
|
+
) {
|
|
116
|
+
this._section = (component['section'] as string | null | undefined) ?? null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getApiName(): string {
|
|
120
|
+
return this.component['field_api_name'];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
getDisplayName(): string {
|
|
124
|
+
return this.component['field_display_name'] ?? this.getApiName();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
getCustomObjectApiName(): string {
|
|
128
|
+
return this.customObjectApiName;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getFieldType(): FieldType | null {
|
|
132
|
+
return (this.getDataType()['field_type'] as FieldType | undefined) ?? null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
getDataType(): Readonly<Record<string, any>> {
|
|
136
|
+
return this.component['data_type'] ?? {};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
isFormula(): boolean {
|
|
140
|
+
return this.component['derived_field_formula'] != null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
isSummary(): boolean {
|
|
144
|
+
return this.component['derived_aggregated_field'] != null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
getSectionId(): string | null {
|
|
148
|
+
return this._section;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
_assignSection(section: CustomObjectFieldSection | string): void {
|
|
152
|
+
this._section = typeof section === 'string' ? section : section.getSectionId();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
toDict(): Record<string, any> {
|
|
156
|
+
return cloneJson(this.component);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
93
160
|
// ────────────────────────────────────────────────────────────────────────
|
|
94
161
|
// Base class
|
|
95
162
|
// ────────────────────────────────────────────────────────────────────────
|
|
@@ -119,6 +186,16 @@ export interface FieldProps {
|
|
|
119
186
|
* @see {@link TextField}, {@link NumberField}, {@link LookupField}, {@link SummaryField}
|
|
120
187
|
*/
|
|
121
188
|
export class Field {
|
|
189
|
+
static readonly componentType = 'CUSTOM_OBJECT_FIELD' as const;
|
|
190
|
+
|
|
191
|
+
static toDeletionIdentifier(identifier: CustomObjectFieldDeletionIdentifier): ManifestComponentDeletion {
|
|
192
|
+
return {
|
|
193
|
+
type: Field.componentType,
|
|
194
|
+
custom_object_api_name: identifier.customObjectApiName,
|
|
195
|
+
field_api_name: identifier.fieldApiName,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
122
199
|
private readonly _fieldApiName: string;
|
|
123
200
|
private readonly _fieldDisplayName: string;
|
|
124
201
|
private readonly _customObjectApiName: string;
|
|
@@ -172,6 +249,68 @@ export class Field {
|
|
|
172
249
|
customObject._register(this);
|
|
173
250
|
}
|
|
174
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Loads this field from the active existing-manifest JSON context.
|
|
254
|
+
*
|
|
255
|
+
* For fields whose api_name is not globally unique, pass `customObjectApiName`
|
|
256
|
+
* or use a qualified id like `'object__c.field__c'`.
|
|
257
|
+
*/
|
|
258
|
+
static loadFromExisting(apiName: string, options: FieldLoadFromExistingOptions = {}): Field {
|
|
259
|
+
return getExistingManifestContext(options).loadField(apiName, options);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** @internal Hydrates a custom object field from existing manifest wire JSON. */
|
|
263
|
+
static _fromExistingComponent(customObject: CustomObject, component: Record<string, any>): Field {
|
|
264
|
+
const props: FieldProps = {
|
|
265
|
+
apiName: component['field_api_name'],
|
|
266
|
+
displayName: component['field_display_name'] ?? component['field_api_name'],
|
|
267
|
+
};
|
|
268
|
+
if (component['data_type'] !== undefined) props.dataType = cloneJson(component['data_type'] ?? {});
|
|
269
|
+
if (component['description'] !== undefined) props.description = component['description'];
|
|
270
|
+
if (component['is_required'] !== undefined) props.required = component['is_required'];
|
|
271
|
+
if (component['is_unique'] !== undefined) props.unique = component['is_unique'];
|
|
272
|
+
if (component['is_indexed'] !== undefined) props.indexed = component['is_indexed'];
|
|
273
|
+
if (component['rql_formula'] !== undefined) props.rqlFormula = component['rql_formula'];
|
|
274
|
+
if (component['formula_attr_metas'] !== undefined)
|
|
275
|
+
props.formulaAttrMetas = component['formula_attr_metas'];
|
|
276
|
+
if (component['derived_aggregated_field'] !== undefined) {
|
|
277
|
+
props.derivedAggregatedField = component['derived_aggregated_field'];
|
|
278
|
+
}
|
|
279
|
+
if (component['derived_field_formula'] !== undefined)
|
|
280
|
+
props.derivedFieldFormula = component['derived_field_formula'];
|
|
281
|
+
if (component['section'] !== undefined) props.section = component['section'];
|
|
282
|
+
return new Field(customObject, props);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** @internal Creates a non-registered reference for standard or external fields. */
|
|
286
|
+
static _referenceFromExistingComponent(component: Record<string, any>): Field {
|
|
287
|
+
return new ExistingFieldReference(
|
|
288
|
+
component['custom_object_api_name'],
|
|
289
|
+
cloneJson(component),
|
|
290
|
+
) as unknown as Field;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** @internal Creates a non-registered reference for a known wire field name. */
|
|
294
|
+
static _syntheticReference(customObjectApiName: string, fieldApiName: string): Field {
|
|
295
|
+
return new ExistingFieldReference(customObjectApiName, {
|
|
296
|
+
type: 'CUSTOM_OBJECT_FIELD',
|
|
297
|
+
custom_object_api_name: customObjectApiName,
|
|
298
|
+
field_api_name: fieldApiName,
|
|
299
|
+
field_display_name: fieldApiName,
|
|
300
|
+
data_type: {},
|
|
301
|
+
description: null,
|
|
302
|
+
is_required: null,
|
|
303
|
+
is_unique: null,
|
|
304
|
+
is_indexed: null,
|
|
305
|
+
rql_formula: null,
|
|
306
|
+
formula_attr_metas: null,
|
|
307
|
+
is_standard: true,
|
|
308
|
+
derived_aggregated_field: null,
|
|
309
|
+
derived_field_formula: null,
|
|
310
|
+
section: null,
|
|
311
|
+
}) as unknown as Field;
|
|
312
|
+
}
|
|
313
|
+
|
|
175
314
|
// ── Getters ────────────────────────────────────────────────────────────
|
|
176
315
|
|
|
177
316
|
/** Returns the api_name of this field (e.g. `'first_name__c'`). */
|
|
@@ -220,7 +359,7 @@ export class Field {
|
|
|
220
359
|
*/
|
|
221
360
|
toDict(): Record<string, any> {
|
|
222
361
|
return {
|
|
223
|
-
type:
|
|
362
|
+
type: Field.componentType,
|
|
224
363
|
custom_object_api_name: this._customObjectApiName,
|
|
225
364
|
field_api_name: this._fieldApiName,
|
|
226
365
|
field_display_name: this._fieldDisplayName,
|
|
@@ -23,15 +23,21 @@ export { Category, type CategoryProps } from './category';
|
|
|
23
23
|
|
|
24
24
|
export { CustomObject, type CustomObjectProps, type IconConfig } from './custom-object';
|
|
25
25
|
|
|
26
|
-
export {
|
|
26
|
+
export {
|
|
27
|
+
CustomObjectFieldSection,
|
|
28
|
+
type CustomObjectFieldSectionLoadFromExistingOptions,
|
|
29
|
+
type CustomObjectFieldSectionProps,
|
|
30
|
+
} from './custom-object-field-section';
|
|
27
31
|
|
|
28
32
|
export {
|
|
29
33
|
CustomObjectPageLayout,
|
|
34
|
+
type CustomObjectPageLayoutLoadFromExistingOptions,
|
|
30
35
|
type CustomObjectPageLayoutProps,
|
|
31
36
|
type CustomTab,
|
|
32
37
|
type FieldsSection,
|
|
33
38
|
type HeaderEdits,
|
|
34
39
|
type NewHeaderField,
|
|
40
|
+
type PageLayoutDeletionIdentifier,
|
|
35
41
|
type PageLayoutSection,
|
|
36
42
|
type PageLayoutTab,
|
|
37
43
|
type SectionField,
|
|
@@ -49,6 +55,8 @@ export {
|
|
|
49
55
|
|
|
50
56
|
export {
|
|
51
57
|
Field,
|
|
58
|
+
type CustomObjectFieldDeletionIdentifier,
|
|
59
|
+
type FieldLoadFromExistingOptions,
|
|
52
60
|
type FieldProps,
|
|
53
61
|
type SummaryFilter,
|
|
54
62
|
|
|
@@ -94,12 +102,28 @@ export {
|
|
|
94
102
|
type SummaryFieldProps,
|
|
95
103
|
} from './field';
|
|
96
104
|
|
|
97
|
-
export {
|
|
105
|
+
export {
|
|
106
|
+
ExistingManifestContext,
|
|
107
|
+
type ExistingManifest,
|
|
108
|
+
type ExistingManifestContextOptions,
|
|
109
|
+
type ExistingManifestSource,
|
|
110
|
+
} from './existing-manifest-context';
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
ListViewDef,
|
|
114
|
+
type ListViewDefLoadFromExistingOptions,
|
|
115
|
+
type ListViewDefProps,
|
|
116
|
+
type ListViewDeletionIdentifier,
|
|
117
|
+
} from './list-view';
|
|
98
118
|
|
|
99
119
|
export {
|
|
100
120
|
ManifestBuilder,
|
|
121
|
+
type BuiltManifest,
|
|
101
122
|
type ManifestBuilderProps,
|
|
102
123
|
type ManifestComponent,
|
|
124
|
+
type ManifestComponentClass,
|
|
125
|
+
type ManifestComponentDeletion,
|
|
126
|
+
type ManifestComponentType,
|
|
103
127
|
type ManifestScope,
|
|
104
128
|
} from './manifest-builder';
|
|
105
129
|
|
|
@@ -109,8 +133,19 @@ export {
|
|
|
109
133
|
type ManifestFunctionProps,
|
|
110
134
|
} from './manifest-function';
|
|
111
135
|
|
|
112
|
-
export {
|
|
136
|
+
export {
|
|
137
|
+
Rule,
|
|
138
|
+
type RuleAction,
|
|
139
|
+
type RuleDeletionIdentifier,
|
|
140
|
+
type RuleLoadFromExistingOptions,
|
|
141
|
+
type RuleProps,
|
|
142
|
+
} from './rule';
|
|
113
143
|
|
|
114
144
|
export { SduiPage, type SduiPageProps } from './sdui-page';
|
|
115
145
|
|
|
116
|
-
export {
|
|
146
|
+
export {
|
|
147
|
+
Validation,
|
|
148
|
+
type ValidationDeletionIdentifier,
|
|
149
|
+
type ValidationLoadFromExistingOptions,
|
|
150
|
+
type ValidationProps,
|
|
151
|
+
} from './validation';
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { generateId, type ObjectScope, type SortOrder } from './_helpers';
|
|
2
2
|
import type { CustomObject } from './custom-object';
|
|
3
|
+
import { getExistingManifestContext, type ExistingManifestContextOptions } from './existing-manifest-context';
|
|
3
4
|
import type { Field } from './field';
|
|
5
|
+
import type { ManifestComponentDeletion } from './manifest-builder';
|
|
6
|
+
|
|
7
|
+
export interface ListViewDeletionIdentifier {
|
|
8
|
+
viewId: string;
|
|
9
|
+
objectRqlName: string;
|
|
10
|
+
}
|
|
4
11
|
|
|
5
12
|
/**
|
|
6
13
|
* Initialization properties for {@link ListViewDef}.
|
|
@@ -70,6 +77,10 @@ export interface ListViewDefProps {
|
|
|
70
77
|
rqlFilter?: string;
|
|
71
78
|
}
|
|
72
79
|
|
|
80
|
+
export interface ListViewDefLoadFromExistingOptions extends ExistingManifestContextOptions {
|
|
81
|
+
customObjectApiName?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
73
84
|
/**
|
|
74
85
|
* Defines a list view and registers it with the manifest.
|
|
75
86
|
*
|
|
@@ -104,6 +115,16 @@ export interface ListViewDefProps {
|
|
|
104
115
|
* ```
|
|
105
116
|
*/
|
|
106
117
|
export class ListViewDef {
|
|
118
|
+
static readonly componentType = 'CUSTOM_OBJECT_LIST_VIEW' as const;
|
|
119
|
+
|
|
120
|
+
static toDeletionIdentifier(identifier: ListViewDeletionIdentifier): ManifestComponentDeletion {
|
|
121
|
+
return {
|
|
122
|
+
type: ListViewDef.componentType,
|
|
123
|
+
view_id: identifier.viewId,
|
|
124
|
+
object_rql_name: identifier.objectRqlName,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
107
128
|
private readonly _viewId: string;
|
|
108
129
|
private readonly _customObjectApiName: string;
|
|
109
130
|
private readonly _name: string;
|
|
@@ -143,6 +164,34 @@ export class ListViewDef {
|
|
|
143
164
|
customObject._register(this);
|
|
144
165
|
}
|
|
145
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Loads this list view from the active existing-manifest JSON context.
|
|
169
|
+
*/
|
|
170
|
+
static loadFromExisting(viewId: string, options: ListViewDefLoadFromExistingOptions = {}): ListViewDef {
|
|
171
|
+
return getExistingManifestContext(options).loadListView(viewId, options);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** @internal Hydrates a list view from existing manifest wire JSON. */
|
|
175
|
+
static _fromExistingComponent(
|
|
176
|
+
customObject: CustomObject,
|
|
177
|
+
component: Record<string, any>,
|
|
178
|
+
resolveField: (apiName: string) => Field,
|
|
179
|
+
): ListViewDef {
|
|
180
|
+
const props: ListViewDefProps = {
|
|
181
|
+
viewId: component['view_id'],
|
|
182
|
+
name: component['name'] ?? component['view_id'],
|
|
183
|
+
fields: (component['fields'] ?? []).map((fieldApiName: string) => resolveField(fieldApiName)),
|
|
184
|
+
isPublic: Boolean(component['public']),
|
|
185
|
+
};
|
|
186
|
+
if (component['description'] !== undefined) props.description = component['description'];
|
|
187
|
+
if (component['object_scope'] !== undefined) props.objectScope = component['object_scope'];
|
|
188
|
+
if (component['sort_order'] !== undefined) props.sortOrder = component['sort_order'];
|
|
189
|
+
if (component['column_config'] !== undefined) props.columnConfig = component['column_config'];
|
|
190
|
+
if (component['rql_filter'] !== undefined) props.rqlFilter = component['rql_filter'];
|
|
191
|
+
if (component['order_by'] != null) props.orderBy = resolveField(component['order_by']);
|
|
192
|
+
return new ListViewDef(customObject, props);
|
|
193
|
+
}
|
|
194
|
+
|
|
146
195
|
/**
|
|
147
196
|
* Returns the stable identifier for this view (e.g. `'view_gym_all_members'`).
|
|
148
197
|
*/
|
|
@@ -165,7 +214,7 @@ export class ListViewDef {
|
|
|
165
214
|
*/
|
|
166
215
|
toDict(): Record<string, any> {
|
|
167
216
|
const d: Record<string, any> = {
|
|
168
|
-
type:
|
|
217
|
+
type: ListViewDef.componentType,
|
|
169
218
|
object_rql_name: this._customObjectApiName,
|
|
170
219
|
view_id: this._viewId,
|
|
171
220
|
name: this._name,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ExistingManifestContext, type ExistingManifestSource } from './existing-manifest-context';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Any object that can be included in a manifest.
|
|
3
5
|
*
|
|
@@ -8,6 +10,39 @@ export interface ManifestComponent {
|
|
|
8
10
|
toDict(): Record<string, any>;
|
|
9
11
|
}
|
|
10
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Wire-level component types supported by the manifest builder.
|
|
15
|
+
*/
|
|
16
|
+
export type ManifestComponentType =
|
|
17
|
+
| 'CUSTOM_APP'
|
|
18
|
+
| 'CUSTOM_CATEGORY'
|
|
19
|
+
| 'CUSTOM_OBJECT'
|
|
20
|
+
| 'CUSTOM_OBJECT_FIELD'
|
|
21
|
+
| 'CUSTOM_OBJECT_FIELD_SECTION'
|
|
22
|
+
| 'CUSTOM_OBJECT_LIST_VIEW'
|
|
23
|
+
| 'CUSTOM_OBJECT_PAGE_LAYOUT'
|
|
24
|
+
| 'CUSTOM_OBJECT_RECORD_TRIGGERED_FLOW'
|
|
25
|
+
| 'CUSTOM_OBJECT_VALIDATION_RULE'
|
|
26
|
+
| 'FUNCTION'
|
|
27
|
+
| 'SDUI_PAGE';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Static side of a manifest component class.
|
|
31
|
+
*/
|
|
32
|
+
export interface ManifestComponentClass<TIdentifierInput = unknown> {
|
|
33
|
+
readonly componentType: ManifestComponentType;
|
|
34
|
+
toDeletionIdentifier(identifier: TIdentifierInput): ManifestComponentDeletion;
|
|
35
|
+
new (...args: any[]): ManifestComponent;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A manifest component reference that should be deleted during install.
|
|
40
|
+
*/
|
|
41
|
+
export interface ManifestComponentDeletion extends Record<string, any> {
|
|
42
|
+
/** Wire-level component type, e.g. `'CUSTOM_OBJECT'` or `'CUSTOM_OBJECT_FIELD'`. */
|
|
43
|
+
type: ManifestComponentType;
|
|
44
|
+
}
|
|
45
|
+
|
|
11
46
|
/**
|
|
12
47
|
* A scope — anything that can accept child component registrations.
|
|
13
48
|
*
|
|
@@ -44,6 +79,15 @@ export interface ManifestBuilderProps {
|
|
|
44
79
|
description?: string;
|
|
45
80
|
}
|
|
46
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Plain object returned by {@link ManifestBuilder.buildManifest}.
|
|
84
|
+
*/
|
|
85
|
+
export interface BuiltManifest {
|
|
86
|
+
metadata: Record<string, any>;
|
|
87
|
+
components: Record<string, any>[];
|
|
88
|
+
components_to_delete?: ManifestComponentDeletion[];
|
|
89
|
+
}
|
|
90
|
+
|
|
47
91
|
/**
|
|
48
92
|
* Root container for a manifest — collects all components and serializes them to JSON.
|
|
49
93
|
*
|
|
@@ -75,6 +119,7 @@ export class ManifestBuilder implements ManifestScope {
|
|
|
75
119
|
readonly name: string;
|
|
76
120
|
readonly description: string;
|
|
77
121
|
private readonly _components: ManifestComponent[] = [];
|
|
122
|
+
private readonly _componentsToDelete: ManifestComponentDeletion[] = [];
|
|
78
123
|
|
|
79
124
|
/**
|
|
80
125
|
* @param props - Initialization properties.
|
|
@@ -89,6 +134,24 @@ export class ManifestBuilder implements ManifestScope {
|
|
|
89
134
|
this.description = props.description ?? '';
|
|
90
135
|
}
|
|
91
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Parses an existing manifest JSON blob, installs it as the active load context,
|
|
139
|
+
* hydrates every known component, and returns a normal `ManifestBuilder`.
|
|
140
|
+
*/
|
|
141
|
+
static loadFromExistingJson(source: ExistingManifestSource): ManifestBuilder {
|
|
142
|
+
return ExistingManifestContext.fromJson(source).loadManifestBuilder();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Parses an existing manifest JSON blob and installs it as the active lazy-load context.
|
|
147
|
+
*
|
|
148
|
+
* After calling this, individual components can be loaded with
|
|
149
|
+
* `CustomObjectPageLayout.loadFromExisting(...)`, `Field.loadFromExisting(...)`, etc.
|
|
150
|
+
*/
|
|
151
|
+
static useExistingJson(source: ExistingManifestSource): ExistingManifestContext {
|
|
152
|
+
return ExistingManifestContext.fromJson(source);
|
|
153
|
+
}
|
|
154
|
+
|
|
92
155
|
/**
|
|
93
156
|
* @internal Scope hook invoked by child constructors. Components enter the
|
|
94
157
|
* manifest here — there is no other path.
|
|
@@ -101,11 +164,45 @@ export class ManifestBuilder implements ManifestScope {
|
|
|
101
164
|
}
|
|
102
165
|
|
|
103
166
|
/**
|
|
104
|
-
*
|
|
167
|
+
* Marks a component to be deleted when this manifest is installed.
|
|
105
168
|
*
|
|
106
|
-
* @
|
|
169
|
+
* @param componentClass - Component class, e.g. `CustomObject`.
|
|
170
|
+
* @param identifier - Component identifier/key, such as an api_name or the component's identifier object.
|
|
171
|
+
* @returns This builder for chaining.
|
|
107
172
|
*/
|
|
108
|
-
|
|
173
|
+
markComponentForDeletion<TIdentifierInput>(
|
|
174
|
+
componentClass: ManifestComponentClass<TIdentifierInput>,
|
|
175
|
+
identifier: TIdentifierInput,
|
|
176
|
+
): this {
|
|
177
|
+
if (identifier == null || identifier === '') {
|
|
178
|
+
throw new Error('identifier is required');
|
|
179
|
+
}
|
|
180
|
+
if (!componentClass.componentType) {
|
|
181
|
+
throw new Error('type is required');
|
|
182
|
+
}
|
|
183
|
+
if (typeof componentClass.toDeletionIdentifier !== 'function') {
|
|
184
|
+
throw new TypeError('Component class must have a toDeletionIdentifier() method');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const deletion = componentClass.toDeletionIdentifier(identifier);
|
|
188
|
+
if (!deletion.type) {
|
|
189
|
+
throw new Error('type is required');
|
|
190
|
+
}
|
|
191
|
+
if (Object.keys(deletion).length <= 1) {
|
|
192
|
+
throw new Error('identifier is required');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
this._componentsToDelete.push({ ...deletion });
|
|
196
|
+
return this;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Returns the manifest as a plain object with `metadata`, a flat `components` array,
|
|
201
|
+
* and a `components_to_delete` array when deletions are registered.
|
|
202
|
+
*
|
|
203
|
+
* @returns `{ metadata: { key, name, description? }, components, components_to_delete? }`
|
|
204
|
+
*/
|
|
205
|
+
buildManifest(): BuiltManifest {
|
|
109
206
|
const metadata: Record<string, any> = {
|
|
110
207
|
key: this.key,
|
|
111
208
|
name: this.name,
|
|
@@ -114,7 +211,11 @@ export class ManifestBuilder implements ManifestScope {
|
|
|
114
211
|
metadata['description'] = this.description;
|
|
115
212
|
}
|
|
116
213
|
const components = this._components.map((c) => c.toDict());
|
|
117
|
-
|
|
214
|
+
const manifest: BuiltManifest = { metadata, components };
|
|
215
|
+
if (this._componentsToDelete.length > 0) {
|
|
216
|
+
manifest.components_to_delete = this.componentsToDeleteList();
|
|
217
|
+
}
|
|
218
|
+
return manifest;
|
|
118
219
|
}
|
|
119
220
|
|
|
120
221
|
/**
|
|
@@ -126,6 +227,15 @@ export class ManifestBuilder implements ManifestScope {
|
|
|
126
227
|
return this._components.map((c) => c.toDict());
|
|
127
228
|
}
|
|
128
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Returns the flat list of component deletion references.
|
|
232
|
+
*
|
|
233
|
+
* @returns An array of component identifier objects, in registration order.
|
|
234
|
+
*/
|
|
235
|
+
componentsToDeleteList(): ManifestComponentDeletion[] {
|
|
236
|
+
return this._componentsToDelete.map((component) => ({ ...component }));
|
|
237
|
+
}
|
|
238
|
+
|
|
129
239
|
/**
|
|
130
240
|
* Returns the full manifest as a pretty-printed JSON string.
|
|
131
241
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FunctionRuntime } from './_helpers';
|
|
2
|
-
import type
|
|
2
|
+
import { getExistingManifestContext, type ExistingManifestContextOptions } from './existing-manifest-context';
|
|
3
|
+
import type { ManifestComponentDeletion, ManifestScope } from './manifest-builder';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Initialization properties for {@link ManifestFunction}.
|
|
@@ -100,6 +101,15 @@ export interface ManifestFunctionProps {
|
|
|
100
101
|
* @see {@link SduiPage} — wraps a `ManifestFunction` as a navigable UI surface.
|
|
101
102
|
*/
|
|
102
103
|
export class ManifestFunction {
|
|
104
|
+
static readonly componentType = 'FUNCTION' as const;
|
|
105
|
+
|
|
106
|
+
static toDeletionIdentifier(apiName: string): ManifestComponentDeletion {
|
|
107
|
+
return {
|
|
108
|
+
type: ManifestFunction.componentType,
|
|
109
|
+
api_name: apiName,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
103
113
|
private readonly _apiName: string;
|
|
104
114
|
private readonly _name: string;
|
|
105
115
|
private readonly _code: string;
|
|
@@ -128,6 +138,30 @@ export class ManifestFunction {
|
|
|
128
138
|
scope._register(this);
|
|
129
139
|
}
|
|
130
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Loads this function from the active existing-manifest JSON context.
|
|
143
|
+
*/
|
|
144
|
+
static loadFromExisting(apiName: string, options: ExistingManifestContextOptions = {}): ManifestFunction {
|
|
145
|
+
return getExistingManifestContext(options).loadFunction(apiName);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** @internal Hydrates a function from existing manifest wire JSON. */
|
|
149
|
+
static _fromExistingComponent(scope: ManifestScope, component: Record<string, any>): ManifestFunction {
|
|
150
|
+
const props: ManifestFunctionProps = {
|
|
151
|
+
apiName: component['api_name'],
|
|
152
|
+
name: component['name'] ?? component['api_name'],
|
|
153
|
+
};
|
|
154
|
+
if (component['code_draft'] !== undefined) props.code = component['code_draft'];
|
|
155
|
+
if (component['description'] !== undefined) props.description = component['description'];
|
|
156
|
+
if (component['dependencies'] !== undefined) props.dependencies = component['dependencies'];
|
|
157
|
+
if (component['deployment_options'] !== undefined) {
|
|
158
|
+
props.deploymentOptions = component['deployment_options'];
|
|
159
|
+
}
|
|
160
|
+
if (component['allowed_domains'] !== undefined) props.allowedDomains = component['allowed_domains'];
|
|
161
|
+
if (component['is_async'] !== undefined) props.isAsync = component['is_async'];
|
|
162
|
+
return new ManifestFunction(scope, props);
|
|
163
|
+
}
|
|
164
|
+
|
|
131
165
|
/**
|
|
132
166
|
* Returns the api_name of this function (e.g. `'gym_today_schedule_fn'`).
|
|
133
167
|
*/
|
|
@@ -142,7 +176,7 @@ export class ManifestFunction {
|
|
|
142
176
|
*/
|
|
143
177
|
toDict(): Record<string, any> {
|
|
144
178
|
const component: Record<string, any> = {
|
|
145
|
-
type:
|
|
179
|
+
type: ManifestFunction.componentType,
|
|
146
180
|
name: this._name,
|
|
147
181
|
api_name: this._apiName,
|
|
148
182
|
};
|
package/src/lib/manifest/rule.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { generateId, type TriggerType, type UpdateTriggerType } from './_helpers';
|
|
2
2
|
import type { CustomObject } from './custom-object';
|
|
3
|
+
import { getExistingManifestContext, type ExistingManifestContextOptions } from './existing-manifest-context';
|
|
3
4
|
import type { Field } from './field';
|
|
5
|
+
import type { ManifestComponentDeletion } from './manifest-builder';
|
|
6
|
+
|
|
7
|
+
export interface RuleDeletionIdentifier {
|
|
8
|
+
flowId: string;
|
|
9
|
+
modelRqlName: string;
|
|
10
|
+
}
|
|
4
11
|
|
|
5
12
|
/**
|
|
6
13
|
* A single action performed when a {@link Rule} fires.
|
|
@@ -79,6 +86,10 @@ export interface RuleProps {
|
|
|
79
86
|
actions?: RuleAction[];
|
|
80
87
|
}
|
|
81
88
|
|
|
89
|
+
export interface RuleLoadFromExistingOptions extends ExistingManifestContextOptions {
|
|
90
|
+
customObjectApiName?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
82
93
|
/** SELECT / RADIO rule actions use list `fixed_value` (Rippling rule builder). */
|
|
83
94
|
function fixedValueToWire(targetField: Field, value: any): any {
|
|
84
95
|
const ft = targetField.getFieldType();
|
|
@@ -103,6 +114,10 @@ function actionToDict(action: RuleAction): Record<string, any> {
|
|
|
103
114
|
return out;
|
|
104
115
|
}
|
|
105
116
|
|
|
117
|
+
function hasOwn(value: Record<string, any>, key: string): boolean {
|
|
118
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
119
|
+
}
|
|
120
|
+
|
|
106
121
|
/**
|
|
107
122
|
* Defines a record-triggered automation rule and registers it with the manifest.
|
|
108
123
|
*
|
|
@@ -122,6 +137,16 @@ function actionToDict(action: RuleAction): Record<string, any> {
|
|
|
122
137
|
* ```
|
|
123
138
|
*/
|
|
124
139
|
export class Rule {
|
|
140
|
+
static readonly componentType = 'CUSTOM_OBJECT_RECORD_TRIGGERED_FLOW' as const;
|
|
141
|
+
|
|
142
|
+
static toDeletionIdentifier(identifier: RuleDeletionIdentifier): ManifestComponentDeletion {
|
|
143
|
+
return {
|
|
144
|
+
type: Rule.componentType,
|
|
145
|
+
flow_id: identifier.flowId,
|
|
146
|
+
model_rql_name: identifier.modelRqlName,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
125
150
|
private readonly _flowId: string;
|
|
126
151
|
private readonly _customObjectApiName: string;
|
|
127
152
|
private readonly _flowName: string;
|
|
@@ -154,6 +179,40 @@ export class Rule {
|
|
|
154
179
|
customObject._register(this);
|
|
155
180
|
}
|
|
156
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Loads this record-triggered rule from the active existing-manifest JSON context.
|
|
184
|
+
*/
|
|
185
|
+
static loadFromExisting(flowId: string, options: RuleLoadFromExistingOptions = {}): Rule {
|
|
186
|
+
return getExistingManifestContext(options).loadRule(flowId, options);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** @internal Hydrates a record-triggered rule from existing manifest wire JSON. */
|
|
190
|
+
static _fromExistingComponent(
|
|
191
|
+
customObject: CustomObject,
|
|
192
|
+
component: Record<string, any>,
|
|
193
|
+
resolveField: (apiName: string) => Field,
|
|
194
|
+
): Rule {
|
|
195
|
+
const props: RuleProps = {
|
|
196
|
+
flowId: component['flow_id'],
|
|
197
|
+
flowName: component['flow_name'] ?? component['flow_id'],
|
|
198
|
+
triggerType: component['trigger_type'],
|
|
199
|
+
actions: (component['actions'] ?? []).map((action: Record<string, any>): RuleAction => {
|
|
200
|
+
const out: RuleAction = {
|
|
201
|
+
targetField: resolveField(action['target_field']),
|
|
202
|
+
};
|
|
203
|
+
if (hasOwn(action, 'fixed_value')) out.fixedValue = action['fixed_value'];
|
|
204
|
+
if (hasOwn(action, 'rql_formula')) out.rqlFormula = action['rql_formula'];
|
|
205
|
+
return out;
|
|
206
|
+
}),
|
|
207
|
+
};
|
|
208
|
+
if (component['description'] !== undefined) props.description = component['description'];
|
|
209
|
+
if (component['rql_condition'] !== undefined) props.rqlCondition = component['rql_condition'];
|
|
210
|
+
if (component['update_trigger_type'] !== undefined) {
|
|
211
|
+
props.updateTriggerType = component['update_trigger_type'];
|
|
212
|
+
}
|
|
213
|
+
return new Rule(customObject, props);
|
|
214
|
+
}
|
|
215
|
+
|
|
157
216
|
/**
|
|
158
217
|
* Returns the stable identifier for this rule (e.g. `'gym_default_enrollment_status'`).
|
|
159
218
|
*/
|
|
@@ -176,7 +235,7 @@ export class Rule {
|
|
|
176
235
|
*/
|
|
177
236
|
toDict(): Record<string, any> {
|
|
178
237
|
const component: Record<string, any> = {
|
|
179
|
-
type:
|
|
238
|
+
type: Rule.componentType,
|
|
180
239
|
model_rql_name: this._customObjectApiName,
|
|
181
240
|
flow_id: this._flowId,
|
|
182
241
|
flow_name: this._flowName,
|