@rippling/rippling-sdk 0.2.0-alpha.84 → 0.2.0-alpha.86

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 (195) hide show
  1. package/examples/manifest/custom-object-creation-workflow/custom-object-creation-workflow.ts +44 -26
  2. package/examples/manifest/dental-practice-management/dental-practice-management.ts +470 -142
  3. package/examples/manifest/dental-practice-management/functions/chair-schedule.ts +23 -13
  4. package/examples/manifest/dental-practice-management/functions/complete-visit.ts +55 -32
  5. package/examples/manifest/dental-practice-management/functions/operatory-list.ts +23 -13
  6. package/examples/manifest/dental-practice-management/functions/patient-list.ts +23 -13
  7. package/examples/manifest/dental-practice-management/functions/treatment-plan-list.ts +23 -13
  8. package/examples/manifest/existing-manifest-mutations/existing-manifest-mutations.ts +23 -68
  9. package/examples/manifest/field-service-dispatch/field-service-dispatch.ts +375 -102
  10. package/examples/manifest/grant-program-management/grant-program-management.ts +419 -125
  11. package/examples/manifest/gym-membership-management/functions/check-in-desk.ts +23 -13
  12. package/examples/manifest/gym-membership-management/functions/member-list.ts +23 -13
  13. package/examples/manifest/gym-membership-management/functions/today-schedule.ts +23 -13
  14. package/examples/manifest/gym-membership-management/functions/trainer-list.ts +23 -13
  15. package/examples/manifest/gym-membership-management/gym-membership-management.ts +442 -92
  16. package/examples/manifest/procurement-approval-hub/procurement-approval-hub.ts +408 -122
  17. package/examples/manifest/simple-todo-app/functions/mark-done.ts +12 -16
  18. package/examples/manifest/simple-todo-app/functions/todo-list.ts +23 -13
  19. package/examples/manifest/simple-todo-app/simple-todo-app.ts +109 -40
  20. package/examples/manifest/todo-app-without-object-list-view/functions/todo-page.ts +193 -166
  21. package/examples/manifest/todo-app-without-object-list-view/todo-app-without-object-list-view.ts +47 -24
  22. package/package.json +13 -7
  23. package/resources/custom-objects/fields.d.mts +1 -40
  24. package/resources/custom-objects/fields.d.mts.map +1 -1
  25. package/resources/custom-objects/fields.d.ts +1 -40
  26. package/resources/custom-objects/fields.d.ts.map +1 -1
  27. package/resources/levels.d.mts +25 -4
  28. package/resources/levels.d.mts.map +1 -1
  29. package/resources/levels.d.ts +25 -4
  30. package/resources/levels.d.ts.map +1 -1
  31. package/src/resources/custom-objects/fields.ts +1 -46
  32. package/src/resources/levels.ts +28 -4
  33. package/src/tsconfig.json +1 -1
  34. package/src/version.ts +1 -1
  35. package/version.d.mts +1 -1
  36. package/version.d.ts +1 -1
  37. package/version.js +1 -1
  38. package/version.mjs +1 -1
  39. package/core/platform/manifest/custom-objects/action.d.mts +0 -58
  40. package/core/platform/manifest/custom-objects/action.d.mts.map +0 -1
  41. package/core/platform/manifest/custom-objects/action.d.ts +0 -58
  42. package/core/platform/manifest/custom-objects/action.d.ts.map +0 -1
  43. package/core/platform/manifest/custom-objects/action.js +0 -44
  44. package/core/platform/manifest/custom-objects/action.js.map +0 -1
  45. package/core/platform/manifest/custom-objects/action.mjs +0 -40
  46. package/core/platform/manifest/custom-objects/action.mjs.map +0 -1
  47. package/core/platform/manifest/custom-objects/conditions.d.mts +0 -240
  48. package/core/platform/manifest/custom-objects/conditions.d.mts.map +0 -1
  49. package/core/platform/manifest/custom-objects/conditions.d.ts +0 -240
  50. package/core/platform/manifest/custom-objects/conditions.d.ts.map +0 -1
  51. package/core/platform/manifest/custom-objects/conditions.js +0 -326
  52. package/core/platform/manifest/custom-objects/conditions.js.map +0 -1
  53. package/core/platform/manifest/custom-objects/conditions.mjs +0 -321
  54. package/core/platform/manifest/custom-objects/conditions.mjs.map +0 -1
  55. package/core/platform/manifest/custom-objects/existing.d.mts +0 -87
  56. package/core/platform/manifest/custom-objects/existing.d.mts.map +0 -1
  57. package/core/platform/manifest/custom-objects/existing.d.ts +0 -87
  58. package/core/platform/manifest/custom-objects/existing.d.ts.map +0 -1
  59. package/core/platform/manifest/custom-objects/existing.js +0 -113
  60. package/core/platform/manifest/custom-objects/existing.js.map +0 -1
  61. package/core/platform/manifest/custom-objects/existing.mjs +0 -109
  62. package/core/platform/manifest/custom-objects/existing.mjs.map +0 -1
  63. package/core/platform/manifest/custom-objects/field-resolution.d.mts +0 -48
  64. package/core/platform/manifest/custom-objects/field-resolution.d.mts.map +0 -1
  65. package/core/platform/manifest/custom-objects/field-resolution.d.ts +0 -48
  66. package/core/platform/manifest/custom-objects/field-resolution.d.ts.map +0 -1
  67. package/core/platform/manifest/custom-objects/field-resolution.js +0 -61
  68. package/core/platform/manifest/custom-objects/field-resolution.js.map +0 -1
  69. package/core/platform/manifest/custom-objects/field-resolution.mjs +0 -58
  70. package/core/platform/manifest/custom-objects/field-resolution.mjs.map +0 -1
  71. package/core/platform/manifest/custom-objects/function-file.d.mts +0 -87
  72. package/core/platform/manifest/custom-objects/function-file.d.mts.map +0 -1
  73. package/core/platform/manifest/custom-objects/function-file.d.ts +0 -87
  74. package/core/platform/manifest/custom-objects/function-file.d.ts.map +0 -1
  75. package/core/platform/manifest/custom-objects/function-file.js +0 -76
  76. package/core/platform/manifest/custom-objects/function-file.js.map +0 -1
  77. package/core/platform/manifest/custom-objects/function-file.mjs +0 -69
  78. package/core/platform/manifest/custom-objects/function-file.mjs.map +0 -1
  79. package/core/platform/manifest/custom-objects/list-view.d.mts +0 -65
  80. package/core/platform/manifest/custom-objects/list-view.d.mts.map +0 -1
  81. package/core/platform/manifest/custom-objects/list-view.d.ts +0 -65
  82. package/core/platform/manifest/custom-objects/list-view.d.ts.map +0 -1
  83. package/core/platform/manifest/custom-objects/list-view.js +0 -33
  84. package/core/platform/manifest/custom-objects/list-view.js.map +0 -1
  85. package/core/platform/manifest/custom-objects/list-view.mjs +0 -30
  86. package/core/platform/manifest/custom-objects/list-view.mjs.map +0 -1
  87. package/core/platform/manifest/custom-objects/object-list-page.d.mts +0 -43
  88. package/core/platform/manifest/custom-objects/object-list-page.d.mts.map +0 -1
  89. package/core/platform/manifest/custom-objects/object-list-page.d.ts +0 -43
  90. package/core/platform/manifest/custom-objects/object-list-page.d.ts.map +0 -1
  91. package/core/platform/manifest/custom-objects/object-list-page.js +0 -54
  92. package/core/platform/manifest/custom-objects/object-list-page.js.map +0 -1
  93. package/core/platform/manifest/custom-objects/object-list-page.mjs +0 -50
  94. package/core/platform/manifest/custom-objects/object-list-page.mjs.map +0 -1
  95. package/core/platform/manifest/custom-objects/page-layout.d.mts +0 -142
  96. package/core/platform/manifest/custom-objects/page-layout.d.mts.map +0 -1
  97. package/core/platform/manifest/custom-objects/page-layout.d.ts +0 -142
  98. package/core/platform/manifest/custom-objects/page-layout.d.ts.map +0 -1
  99. package/core/platform/manifest/custom-objects/page-layout.js +0 -184
  100. package/core/platform/manifest/custom-objects/page-layout.js.map +0 -1
  101. package/core/platform/manifest/custom-objects/page-layout.mjs +0 -180
  102. package/core/platform/manifest/custom-objects/page-layout.mjs.map +0 -1
  103. package/core/platform/manifest/custom-objects/register.d.mts +0 -87
  104. package/core/platform/manifest/custom-objects/register.d.mts.map +0 -1
  105. package/core/platform/manifest/custom-objects/register.d.ts +0 -87
  106. package/core/platform/manifest/custom-objects/register.d.ts.map +0 -1
  107. package/core/platform/manifest/custom-objects/register.js +0 -395
  108. package/core/platform/manifest/custom-objects/register.js.map +0 -1
  109. package/core/platform/manifest/custom-objects/register.mjs +0 -391
  110. package/core/platform/manifest/custom-objects/register.mjs.map +0 -1
  111. package/core/platform/manifest/custom-objects/rule.d.mts +0 -113
  112. package/core/platform/manifest/custom-objects/rule.d.mts.map +0 -1
  113. package/core/platform/manifest/custom-objects/rule.d.ts +0 -113
  114. package/core/platform/manifest/custom-objects/rule.d.ts.map +0 -1
  115. package/core/platform/manifest/custom-objects/rule.js +0 -49
  116. package/core/platform/manifest/custom-objects/rule.js.map +0 -1
  117. package/core/platform/manifest/custom-objects/rule.mjs +0 -45
  118. package/core/platform/manifest/custom-objects/rule.mjs.map +0 -1
  119. package/core/platform/manifest/custom-objects/sdui-page.d.mts +0 -214
  120. package/core/platform/manifest/custom-objects/sdui-page.d.mts.map +0 -1
  121. package/core/platform/manifest/custom-objects/sdui-page.d.ts +0 -214
  122. package/core/platform/manifest/custom-objects/sdui-page.d.ts.map +0 -1
  123. package/core/platform/manifest/custom-objects/sdui-page.js +0 -194
  124. package/core/platform/manifest/custom-objects/sdui-page.js.map +0 -1
  125. package/core/platform/manifest/custom-objects/sdui-page.mjs +0 -182
  126. package/core/platform/manifest/custom-objects/sdui-page.mjs.map +0 -1
  127. package/core/platform/manifest/custom-objects/typed-condition.d.mts +0 -28
  128. package/core/platform/manifest/custom-objects/typed-condition.d.mts.map +0 -1
  129. package/core/platform/manifest/custom-objects/typed-condition.d.ts +0 -28
  130. package/core/platform/manifest/custom-objects/typed-condition.d.ts.map +0 -1
  131. package/core/platform/manifest/custom-objects/typed-condition.js +0 -434
  132. package/core/platform/manifest/custom-objects/typed-condition.js.map +0 -1
  133. package/core/platform/manifest/custom-objects/typed-condition.mjs +0 -431
  134. package/core/platform/manifest/custom-objects/typed-condition.mjs.map +0 -1
  135. package/core/platform/manifest/custom-objects/types.d.mts +0 -29
  136. package/core/platform/manifest/custom-objects/types.d.mts.map +0 -1
  137. package/core/platform/manifest/custom-objects/types.d.ts +0 -29
  138. package/core/platform/manifest/custom-objects/types.d.ts.map +0 -1
  139. package/core/platform/manifest/custom-objects/types.js +0 -3
  140. package/core/platform/manifest/custom-objects/types.js.map +0 -1
  141. package/core/platform/manifest/custom-objects/types.mjs +0 -2
  142. package/core/platform/manifest/custom-objects/types.mjs.map +0 -1
  143. package/core/platform/manifest/custom-objects/validation.d.mts +0 -44
  144. package/core/platform/manifest/custom-objects/validation.d.mts.map +0 -1
  145. package/core/platform/manifest/custom-objects/validation.d.ts +0 -44
  146. package/core/platform/manifest/custom-objects/validation.d.ts.map +0 -1
  147. package/core/platform/manifest/custom-objects/validation.js +0 -30
  148. package/core/platform/manifest/custom-objects/validation.js.map +0 -1
  149. package/core/platform/manifest/custom-objects/validation.mjs +0 -27
  150. package/core/platform/manifest/custom-objects/validation.mjs.map +0 -1
  151. package/core/platform/manifest/custom-objects/workflow.d.mts +0 -47
  152. package/core/platform/manifest/custom-objects/workflow.d.mts.map +0 -1
  153. package/core/platform/manifest/custom-objects/workflow.d.ts +0 -47
  154. package/core/platform/manifest/custom-objects/workflow.d.ts.map +0 -1
  155. package/core/platform/manifest/custom-objects/workflow.js +0 -38
  156. package/core/platform/manifest/custom-objects/workflow.js.map +0 -1
  157. package/core/platform/manifest/custom-objects/workflow.mjs +0 -34
  158. package/core/platform/manifest/custom-objects/workflow.mjs.map +0 -1
  159. package/core/platform/manifest/custom-objects.d.mts +0 -85
  160. package/core/platform/manifest/custom-objects.d.mts.map +0 -1
  161. package/core/platform/manifest/custom-objects.d.ts +0 -85
  162. package/core/platform/manifest/custom-objects.d.ts.map +0 -1
  163. package/core/platform/manifest/custom-objects.js +0 -84
  164. package/core/platform/manifest/custom-objects.js.map +0 -1
  165. package/core/platform/manifest/custom-objects.mjs +0 -55
  166. package/core/platform/manifest/custom-objects.mjs.map +0 -1
  167. package/examples/manifest/custom-object-creation-workflow/functions/intake-request-created.ts +0 -20
  168. package/examples/manifest/custom-object-creation-workflow/schema.ts +0 -29
  169. package/examples/manifest/dental-practice-management/schema.ts +0 -298
  170. package/examples/manifest/field-service-dispatch/schema.ts +0 -248
  171. package/examples/manifest/grant-program-management/schema.ts +0 -269
  172. package/examples/manifest/gym-membership-management/schema.ts +0 -266
  173. package/examples/manifest/procurement-approval-hub/functions/route-purchase-request.ts +0 -20
  174. package/examples/manifest/procurement-approval-hub/schema.ts +0 -256
  175. package/examples/manifest/simple-todo-app/schema.ts +0 -60
  176. package/examples/manifest/todo-app-without-object-list-view/schema.ts +0 -28
  177. package/src/core/platform/README.md +0 -20
  178. package/src/core/platform/manifest/README.md +0 -26
  179. package/src/core/platform/manifest/custom-objects/action.ts +0 -87
  180. package/src/core/platform/manifest/custom-objects/conditions.ts +0 -762
  181. package/src/core/platform/manifest/custom-objects/existing.ts +0 -221
  182. package/src/core/platform/manifest/custom-objects/field-resolution.ts +0 -116
  183. package/src/core/platform/manifest/custom-objects/function-file.ts +0 -149
  184. package/src/core/platform/manifest/custom-objects/list-view.ts +0 -95
  185. package/src/core/platform/manifest/custom-objects/object-list-page.ts +0 -87
  186. package/src/core/platform/manifest/custom-objects/page-layout.ts +0 -380
  187. package/src/core/platform/manifest/custom-objects/register.ts +0 -555
  188. package/src/core/platform/manifest/custom-objects/rule.ts +0 -192
  189. package/src/core/platform/manifest/custom-objects/sdui-page.ts +0 -515
  190. package/src/core/platform/manifest/custom-objects/typed-condition.ts +0 -617
  191. package/src/core/platform/manifest/custom-objects/types.ts +0 -92
  192. package/src/core/platform/manifest/custom-objects/validation.ts +0 -70
  193. package/src/core/platform/manifest/custom-objects/workflow.ts +0 -71
  194. package/src/core/platform/manifest/custom-objects.ts +0 -203
  195. package/src/pebble-sdui-web-custom-apps-shim.d.ts +0 -3
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * In `triggerFunction`, server-bound action params live under `inputs` so
12
12
  * repeated item values like `todo.$('id')` resolve before reaching
13
- * `event['parameters']`. Client-side controls such as `functionId`,
13
+ * `event['parameters']`. Client-side controls such as `functionApiName`,
14
14
  * `responsePath`, `onSuccess`, and `onError` stay top-level.
15
15
  *
16
16
  * `/isMutating` is a shared loading flag: it disables every action while an API
@@ -29,31 +29,23 @@ import {
29
29
  TextInput,
30
30
  VStack,
31
31
  render,
32
+ setState,
33
+ triggerFunction,
32
34
  } from '@rippling/pebble-sdui-web/catalogs/custom-apps';
33
- import { FunctionContext, FunctionResponse, SduiPageFunctionEvent } from '@rippling/rippling-sdk';
34
- import {
35
- createCustomObjectFunctionRuntime,
36
- createRqlHelpers,
37
- type RecordShape,
38
- type TypedCustomObjectRecordManager,
39
- } from '@rippling/rippling-sdk/core/platform/custom-objects';
40
- import {
41
- createTypedSduiMutationTrigger,
42
- defineTypedSduiPageHandlerFor,
43
- defineTypedSduiStateAssignments,
44
- resolveTypedSduiStatePath,
45
- type TypedSduiPageActionParams,
46
- type TypedSduiPageOutput,
47
- } from '@rippling/rippling-sdk/core/platform/manifest/custom-objects';
48
- import { todoDefinition } from '../schema';
49
-
50
- const todoRql = createRqlHelpers(todoDefinition);
35
+ import RipplingSDK, {
36
+ FunctionContext,
37
+ FunctionResponse,
38
+ SduiPageFunctionEvent,
39
+ } from '@rippling/rippling-sdk';
40
+ import type { CustomObjectDataRow } from '@rippling/rippling-sdk/resources/custom-objects/records';
41
+
42
+ const OBJECT_API_NAME = 'todo__c';
51
43
  type TodoPageAction = 'init' | 'addTodo' | 'toggleTodo' | 'clearCompleted';
52
- type TodoPageFunctionParameters =
53
- | { action: 'init' }
54
- | { action: 'addTodo'; text?: string }
55
- | { action: 'toggleTodo'; todoId?: string }
56
- | { action: 'clearCompleted' };
44
+ type TodoPageFunctionParameters = {
45
+ action: TodoPageAction;
46
+ text?: string;
47
+ todoId?: string;
48
+ };
57
49
 
58
50
  type Todo = {
59
51
  id: string;
@@ -61,8 +53,6 @@ type Todo = {
61
53
  completed: boolean;
62
54
  };
63
55
 
64
- type TodoRecord = RecordShape<typeof todoDefinition>;
65
-
66
56
  type TodoListState = {
67
57
  todos: Todo[];
68
58
  isEmpty: boolean;
@@ -77,43 +67,27 @@ type TodoAppState = {
77
67
 
78
68
  type TodoSpec = SpecContext<TodoAppState>;
79
69
 
80
- const todoStatePath = resolveTypedSduiStatePath<TodoAppState>();
81
- const todoStatePaths = {
82
- newTodoText: todoStatePath('/newTodoText'),
83
- todoList: todoStatePath('/todoList'),
84
- todoListTodos: todoStatePath('/todoList/todos'),
85
- todoListIsEmpty: todoStatePath('/todoList/isEmpty'),
86
- isMutating: todoStatePath('/isMutating'),
87
- errorMessage: todoStatePath('/errorMessage'),
88
- } as const;
89
-
90
- export type Input = SduiPageFunctionEvent<TodoPageFunctionParameters>;
91
-
92
- export type Output = TypedSduiPageOutput<TodoListState>;
93
-
94
- function createTodoManager(context: FunctionContext): TypedCustomObjectRecordManager<typeof todoDefinition> {
95
- const runtime = createCustomObjectFunctionRuntime(context);
96
- return runtime.for(todoDefinition);
70
+ function createClient(context: FunctionContext): RipplingSDK {
71
+ return new RipplingSDK({
72
+ bearerToken: context.env.rippling_user_bearer_token,
73
+ });
97
74
  }
98
75
 
99
- function toTodo(record: TodoRecord): Todo {
76
+ function toTodo(record: CustomObjectDataRow): Todo {
100
77
  return {
101
78
  id: record.id,
102
79
  text: record.name,
103
- completed: record.completed === true,
80
+ completed: record['completed__c'] === true,
104
81
  };
105
82
  }
106
83
 
107
- async function listTodos(
108
- manager: TypedCustomObjectRecordManager<typeof todoDefinition>,
109
- roleId: string,
110
- ): Promise<Todo[]> {
111
- const response = await manager.query({
84
+ async function listTodos(client: RipplingSDK, roleId: string): Promise<Todo[]> {
85
+ const response = await client.customObjects.records.query(OBJECT_API_NAME, {
112
86
  limit: 100,
113
- query: todoRql.render(todoRql.eq(todoRql.fields.created_by, { id: roleId, apiName: 'Employee' })),
87
+ query: `created_by = "${roleId}"`, // RQL query to filter todos by the current role
114
88
  });
115
89
 
116
- return response.results.map(toTodo);
90
+ return (response.results ?? []).map(toTodo);
117
91
  }
118
92
 
119
93
  function todoListState(todos: Todo[]): TodoListState {
@@ -132,32 +106,49 @@ function todoState(todos: Todo[]): TodoAppState {
132
106
  };
133
107
  }
134
108
 
135
- function todoTextParam(params: TypedSduiPageActionParams<TodoPageFunctionParameters, 'addTodo'>): string {
136
- return typeof params.text === 'string' ? params.text.trim() : '';
109
+ function ok(body: Record<string, unknown> | unknown[] = {}): FunctionResponse {
110
+ return new FunctionResponse({
111
+ body: body as Record<string, unknown>,
112
+ statusCode: 200,
113
+ headers: {},
114
+ message: '',
115
+ });
116
+ }
117
+
118
+ function badRequest(message: string): FunctionResponse {
119
+ return new FunctionResponse({
120
+ body: { error: message },
121
+ statusCode: 400,
122
+ headers: {},
123
+ message,
124
+ });
125
+ }
126
+
127
+ function serverError(error: unknown): FunctionResponse {
128
+ const message = error instanceof Error ? error.message : 'Unknown error';
129
+
130
+ return new FunctionResponse({
131
+ body: { error: message },
132
+ statusCode: 500,
133
+ headers: {},
134
+ message,
135
+ });
137
136
  }
138
137
 
139
- function todoIdParam(params: TypedSduiPageActionParams<TodoPageFunctionParameters, 'toggleTodo'>): string {
140
- return typeof params.todoId === 'string' ? params.todoId.trim() : '';
138
+ function stringParam(params: TodoPageFunctionParameters, key: 'text' | 'todoId'): string {
139
+ const value = params[key];
140
+ return typeof value === 'string' ? value.trim() : '';
141
141
  }
142
142
 
143
- function mutationActions<TInputs extends { action: Exclude<TodoPageAction, 'init'> }>(
144
- functionId: string,
145
- inputs: TInputs,
146
- errorMessage: string,
147
- onSuccessState?: ReturnType<typeof defineTypedSduiStateAssignments<TodoAppState>>,
148
- ) {
149
- return createTypedSduiMutationTrigger<TodoAppState, TodoListState, typeof inputs>({
150
- functionId,
151
- inputs,
152
- responsePath: todoStatePaths.todoList,
153
- errorMessage,
154
- mutationStatePath: todoStatePaths.isMutating,
155
- errorStatePath: todoStatePaths.errorMessage,
156
- ...(onSuccessState != null ? { onSuccessState } : {}),
143
+ // A single mutation flag disables all actions and drives the visible loading text.
144
+ function setMutationState(isMutating: boolean) {
145
+ return setState({
146
+ statePath: '/isMutating',
147
+ value: isMutating,
157
148
  });
158
149
  }
159
150
 
160
- function buildAddTodoSection(spec: TodoSpec, functionId: string) {
151
+ function buildAddTodoSection(spec: TodoSpec, functionApiName: string) {
161
152
  return VStack({
162
153
  id: 'addTodoSection',
163
154
  props: {},
@@ -173,8 +164,8 @@ function buildAddTodoSection(spec: TodoSpec, functionId: string) {
173
164
  props: {
174
165
  name: 'newTodo',
175
166
  placeholder: 'Enter a new todo item',
176
- value: spec.$bindState(todoStatePaths.newTodoText),
177
- isDisabled: spec.$state(todoStatePaths.isMutating),
167
+ value: spec.$bindState('/newTodoText'),
168
+ isDisabled: spec.$state('/isMutating'),
178
169
  },
179
170
  }),
180
171
  ],
@@ -186,34 +177,37 @@ function buildAddTodoSection(spec: TodoSpec, functionId: string) {
186
177
  appearance: 'PRIMARY',
187
178
  isFluid: true,
188
179
  isDisabled: spec.$cond(
189
- or_(
190
- equals(spec.$state(todoStatePaths.newTodoText), ''),
191
- equals(spec.$state(todoStatePaths.isMutating), true),
192
- ),
180
+ or_(equals(spec.$state('/newTodoText'), ''), equals(spec.$state('/isMutating'), true)),
193
181
  true,
194
182
  false,
195
183
  ),
196
184
  },
197
185
  on: {
198
- press: mutationActions(
199
- functionId,
200
- {
201
- action: 'addTodo',
202
- text: spec.$state(todoStatePaths.newTodoText),
203
- },
204
- 'Could not add todo. Try again.',
205
- defineTypedSduiStateAssignments<TodoAppState>({
206
- [todoStatePaths.newTodoText]: '',
207
- }),
208
- ),
186
+ press: [
187
+ setMutationState(true),
188
+ triggerFunction(
189
+ {
190
+ functionApiName,
191
+ inputs: {
192
+ action: 'addTodo',
193
+ text: spec.$state('/newTodoText'),
194
+ },
195
+ responsePath: '/todoList',
196
+ },
197
+ {
198
+ onSuccess: { set: { '/newTodoText': '', '/errorMessage': '', '/isMutating': false } },
199
+ onError: { set: { '/errorMessage': 'Could not add todo. Try again.', '/isMutating': false } },
200
+ },
201
+ ),
202
+ ],
209
203
  },
210
204
  }),
211
205
  ],
212
206
  });
213
207
  }
214
208
 
215
- function buildTodoItems(spec: TodoSpec, functionId: string) {
216
- const [todoRepeat, todo] = spec.repeat(todoStatePaths.todoListTodos, 'id');
209
+ function buildTodoItems(spec: TodoSpec, functionApiName: string) {
210
+ const [todoRepeat, todo] = spec.repeat('/todoList/todos', 'id');
217
211
 
218
212
  return VStack({
219
213
  id: 'todoItems',
@@ -237,17 +231,31 @@ function buildTodoItems(spec: TodoSpec, functionId: string) {
237
231
  props: {
238
232
  name: 'completed',
239
233
  value: todo.$bind('completed'),
240
- isDisabled: spec.$state(todoStatePaths.isMutating),
234
+ isDisabled: spec.$state('/isMutating'),
241
235
  },
242
236
  on: {
243
- change: mutationActions(
244
- functionId,
245
- {
246
- action: 'toggleTodo',
247
- todoId: todo.$('id'),
248
- },
249
- 'Could not update todo. Refresh and try again.',
250
- ),
237
+ change: [
238
+ setMutationState(true),
239
+ triggerFunction(
240
+ {
241
+ functionApiName,
242
+ inputs: {
243
+ action: 'toggleTodo',
244
+ todoId: todo.$('id'),
245
+ },
246
+ responsePath: '/todoList',
247
+ },
248
+ {
249
+ onSuccess: { set: { '/errorMessage': '', '/isMutating': false } },
250
+ onError: {
251
+ set: {
252
+ '/errorMessage': 'Could not update todo. Refresh and try again.',
253
+ '/isMutating': false,
254
+ },
255
+ },
256
+ },
257
+ ),
258
+ ],
251
259
  },
252
260
  }),
253
261
  Text({
@@ -264,7 +272,7 @@ function buildTodoItems(spec: TodoSpec, functionId: string) {
264
272
  });
265
273
  }
266
274
 
267
- function buildTodoList(spec: TodoSpec, functionId: string) {
275
+ function buildTodoList(spec: TodoSpec, functionApiName: string) {
268
276
  return VStack({
269
277
  id: 'todoList',
270
278
  props: {
@@ -293,35 +301,46 @@ function buildTodoList(spec: TodoSpec, functionId: string) {
293
301
  appearance: 'OUTLINE',
294
302
  size: 'S',
295
303
  isDisabled: spec.$cond(
296
- or_(
297
- equals(spec.$state(todoStatePaths.isMutating), true),
298
- equals(spec.$state(todoStatePaths.todoListIsEmpty), true),
299
- ),
304
+ or_(equals(spec.$state('/isMutating'), true), equals(spec.$state('/todoList/isEmpty'), true)),
300
305
  true,
301
306
  false,
302
307
  ),
303
308
  },
304
309
  on: {
305
- press: mutationActions(
306
- functionId,
307
- {
308
- action: 'clearCompleted',
309
- },
310
- 'Could not clear completed todos. Try again.',
311
- ),
310
+ press: [
311
+ setMutationState(true),
312
+ triggerFunction(
313
+ {
314
+ functionApiName,
315
+ inputs: {
316
+ action: 'clearCompleted',
317
+ },
318
+ responsePath: '/todoList',
319
+ },
320
+ {
321
+ onSuccess: { set: { '/errorMessage': '', '/isMutating': false } },
322
+ onError: {
323
+ set: {
324
+ '/errorMessage': 'Could not clear completed todos. Try again.',
325
+ '/isMutating': false,
326
+ },
327
+ },
328
+ },
329
+ ),
330
+ ],
312
331
  },
313
332
  }),
314
333
  ],
315
334
  }),
316
335
  Text({
317
336
  id: 'emptyTodos',
318
- visible: equals(spec.$state(todoStatePaths.todoListIsEmpty), true),
337
+ visible: equals(spec.$state('/todoList/isEmpty'), true),
319
338
  props: {
320
339
  text: 'No todos yet.',
321
340
  color: 'colorOnSurfaceVariant',
322
341
  },
323
342
  }),
324
- buildTodoItems(spec, functionId),
343
+ buildTodoItems(spec, functionApiName),
325
344
  ],
326
345
  });
327
346
  }
@@ -329,9 +348,9 @@ function buildTodoList(spec: TodoSpec, functionId: string) {
329
348
  function buildError(spec: TodoSpec) {
330
349
  return Text({
331
350
  id: 'todoError',
332
- visible: neq(spec.$state(todoStatePaths.errorMessage), ''),
351
+ visible: neq(spec.$state('/errorMessage'), ''),
333
352
  props: {
334
- text: spec.$state(todoStatePaths.errorMessage),
353
+ text: spec.$state('/errorMessage'),
335
354
  color: 'colorError',
336
355
  },
337
356
  });
@@ -341,7 +360,7 @@ function buildLoading(spec: TodoSpec) {
341
360
  return Text({
342
361
  id: 'todoLoading',
343
362
  // Keep disabled controls explainable while a triggerFunction call is in flight.
344
- visible: equals(spec.$state(todoStatePaths.isMutating), true),
363
+ visible: equals(spec.$state('/isMutating'), true),
345
364
  props: {
346
365
  text: 'Updating todos...',
347
366
  color: 'colorOnSurfaceVariant',
@@ -349,7 +368,7 @@ function buildLoading(spec: TodoSpec) {
349
368
  });
350
369
  }
351
370
 
352
- function buildTodoCard(spec: TodoSpec, functionId: string) {
371
+ function buildTodoCard(spec: TodoSpec, functionApiName: string) {
353
372
  return Card({
354
373
  id: 'todoCard',
355
374
  props: {},
@@ -361,17 +380,17 @@ function buildTodoCard(spec: TodoSpec, functionId: string) {
361
380
  padding: 'space400',
362
381
  },
363
382
  children: [
364
- buildAddTodoSection(spec, functionId),
383
+ buildAddTodoSection(spec, functionApiName),
365
384
  buildError(spec),
366
385
  buildLoading(spec),
367
- buildTodoList(spec, functionId),
386
+ buildTodoList(spec, functionApiName),
368
387
  ],
369
388
  }),
370
389
  ],
371
390
  });
372
391
  }
373
392
 
374
- function renderSpec(state: TodoAppState, functionId: string) {
393
+ function renderSpec(state: TodoAppState, functionApiName: string) {
375
394
  const spec = createSpec<TodoAppState>({ state });
376
395
  const root = PageContent({
377
396
  id: 'todoApp',
@@ -379,92 +398,100 @@ function renderSpec(state: TodoAppState, functionId: string) {
379
398
  size: '776',
380
399
  alignment: 'CENTER',
381
400
  },
382
- children: [buildTodoCard(spec, functionId)],
401
+ children: [buildTodoCard(spec, functionApiName)],
383
402
  });
384
403
 
385
404
  return render({ root, state });
386
405
  }
387
406
 
388
407
  async function addTodo(
389
- manager: TypedCustomObjectRecordManager<typeof todoDefinition>,
390
- params: TypedSduiPageActionParams<TodoPageFunctionParameters, 'addTodo'>,
408
+ client: RipplingSDK,
409
+ params: TodoPageFunctionParameters,
391
410
  roleId: string,
392
411
  ): Promise<TodoListState> {
393
- const text = todoTextParam(params);
412
+ const text = stringParam(params, 'text');
394
413
 
395
414
  if (!text) {
396
415
  throw new Error('Todo text is required');
397
416
  }
398
417
 
399
- await manager.create({
418
+ await client.customObjects.records.create(OBJECT_API_NAME, {
400
419
  name: text,
401
- completed: false,
420
+ completed__c: false,
402
421
  });
403
422
 
404
- return todoListState(await listTodos(manager, roleId));
423
+ return todoListState(await listTodos(client, roleId));
405
424
  }
406
425
 
407
426
  async function toggleTodo(
408
- manager: TypedCustomObjectRecordManager<typeof todoDefinition>,
409
- params: TypedSduiPageActionParams<TodoPageFunctionParameters, 'toggleTodo'>,
427
+ client: RipplingSDK,
428
+ params: TodoPageFunctionParameters,
410
429
  roleId: string,
411
430
  ): Promise<TodoListState> {
412
- const todoId = todoIdParam(params);
431
+ const todoId = stringParam(params, 'todoId');
413
432
 
414
433
  if (!todoId) {
415
434
  throw new Error('todoId must be a todo record ID');
416
435
  }
417
436
 
418
- const todo = toTodo(await manager.get(todoId));
419
- await manager.update(todoId, {
420
- completed: !todo.completed,
437
+ const todo = toTodo(await client.customObjects.records.retrieve(OBJECT_API_NAME, todoId));
438
+ await client.customObjects.records.update(OBJECT_API_NAME, todoId, {
439
+ completed__c: !todo.completed,
421
440
  });
422
441
 
423
- return todoListState(await listTodos(manager, roleId));
442
+ return todoListState(await listTodos(client, roleId));
424
443
  }
425
444
 
426
- async function clearCompleted(
427
- manager: TypedCustomObjectRecordManager<typeof todoDefinition>,
428
- roleId: string,
429
- ): Promise<TodoListState> {
430
- const todos = await listTodos(manager, roleId);
445
+ async function clearCompleted(client: RipplingSDK, roleId: string): Promise<TodoListState> {
446
+ const todos = await listTodos(client, roleId);
431
447
  const completedTodoIds = todos.filter((todo) => todo.completed).map((todo) => todo.id);
432
448
 
433
449
  if (completedTodoIds.length === 0) {
434
450
  return todoListState(todos);
435
451
  }
436
452
 
437
- await manager.deleteMany(completedTodoIds, {
438
- allOrNothing: false,
453
+ await client.customObjects.records.bulkDelete(OBJECT_API_NAME, {
454
+ all_or_nothing: false,
455
+ rows_to_delete: completedTodoIds,
439
456
  });
440
457
 
441
- return todoListState(await listTodos(manager, roleId));
458
+ return todoListState(await listTodos(client, roleId));
442
459
  }
443
460
 
444
- const defineTodoPageHandler = defineTypedSduiPageHandlerFor<TodoPageFunctionParameters, TodoListState>();
445
-
446
- const todoPageHandler = defineTodoPageHandler({
447
- requireRoleId: true,
448
- createRuntime(context) {
449
- return {
450
- manager: createTodoManager(context),
451
- functionId: context.function.function_id,
452
- };
453
- },
454
- async init({ runtime, roleId }) {
455
- const todos = await listTodos(runtime.manager, roleId);
456
- return renderSpec(todoState(todos), runtime.functionId);
457
- },
458
- actions: {
459
- addTodo: async ({ runtime, params, roleId }) => addTodo(runtime.manager, params, roleId),
460
- toggleTodo: async ({ runtime, params, roleId }) => toggleTodo(runtime.manager, params, roleId),
461
- clearCompleted: async ({ runtime, roleId }) => clearCompleted(runtime.manager, roleId),
462
- },
463
- });
464
-
465
461
  export async function onRipplingEvent(
466
- event: Input,
462
+ event: SduiPageFunctionEvent<TodoPageFunctionParameters>,
467
463
  context: FunctionContext,
468
- ): Promise<FunctionResponse<Output>> {
469
- return todoPageHandler(event, context);
464
+ ): Promise<FunctionResponse> {
465
+ const params = event.parameters;
466
+ const action = params.action;
467
+ const roleId = context.function.role_id;
468
+ if (!roleId) {
469
+ return badRequest('Role ID is required');
470
+ }
471
+
472
+ try {
473
+ const client = createClient(context);
474
+
475
+ if (action === 'init') {
476
+ const functionApiName = context.function.function_api_name;
477
+ const todos = await listTodos(client, roleId);
478
+ return ok({ spec: JSON.stringify(renderSpec(todoState(todos), functionApiName)) });
479
+ }
480
+
481
+ if (action === 'addTodo') {
482
+ return ok(await addTodo(client, params, roleId));
483
+ }
484
+
485
+ if (action === 'toggleTodo') {
486
+ return ok(await toggleTodo(client, params, roleId));
487
+ }
488
+
489
+ if (action === 'clearCompleted') {
490
+ return ok(await clearCompleted(client, roleId));
491
+ }
492
+
493
+ return badRequest(`Unknown action: ${action}`);
494
+ } catch (error) {
495
+ return serverError(error);
496
+ }
470
497
  }
@@ -1,9 +1,9 @@
1
1
  /*
2
- * Todo App Without ObjectListView — custom SDUI backed by typed custom-object records.
2
+ * Todo App Without ObjectListView — custom SDUI backed by Custom Object records.
3
3
  *
4
4
  * This example intentionally does not render ObjectListView. The SDUI page reads
5
- * and writes `todo__c` records from the function, then renders its own todo
6
- * list with TextInput, Button, Checkbox, and layout components.
5
+ * and writes `todo__c` records from the function, then renders its own
6
+ * todo list with TextInput, Button, Checkbox, and layout components.
7
7
  *
8
8
  * todo__c <- name, completed__c
9
9
  *
@@ -12,34 +12,54 @@
12
12
  * > examples/manifest/todo-app-without-object-list-view/manifest.json
13
13
  */
14
14
 
15
+ import * as fs from 'node:fs';
15
16
  import * as path from 'node:path';
16
- import { toCustomCategoryApiName } from '@rippling/rippling-sdk/core/platform/custom-objects';
17
17
  import {
18
- defineSduiPageFromFunctionFile,
19
- defineTypedPageLayout,
20
- registerManifestCustomObject,
21
- } from '@rippling/rippling-sdk/core/platform/manifest/custom-objects';
22
- import { App, Category, ManifestBuilder } from '@rippling/rippling-sdk/lib/manifest';
23
- import { CUSTOM_TODO_CATEGORY, todoDefinition } from './schema';
18
+ App,
19
+ BooleanField,
20
+ Category,
21
+ CustomObject,
22
+ CustomObjectFieldSection,
23
+ CustomObjectPageLayout,
24
+ ManifestBuilder,
25
+ ManifestFunction,
26
+ SduiPage,
27
+ } from '@rippling/rippling-sdk/lib/manifest';
24
28
 
25
29
  const manifest = new ManifestBuilder({
26
30
  key: 'todo_app_without_object_list_view',
27
31
  name: 'Todo App Without ObjectListView',
28
- description: 'A todo app that renders custom SDUI and persists changes to typed custom-object records',
32
+ description: 'A todo app that renders custom SDUI and persists changes to Custom Object records',
29
33
  });
30
34
 
31
35
  const todoCategory = new Category(manifest, {
32
- apiName: toCustomCategoryApiName(CUSTOM_TODO_CATEGORY),
36
+ apiName: 'custom_todo_app__c',
33
37
  name: 'Todo App',
34
38
  description: 'Custom todo app records',
35
39
  });
36
40
 
37
- const todo = registerManifestCustomObject(manifest, {
38
- definition: todoDefinition,
41
+ const todoObj = new CustomObject(manifest, {
42
+ apiName: 'todo__c',
43
+ name: 'Todo',
44
+ pluralLabel: 'Todos',
39
45
  category: todoCategory,
46
+ description: 'A todo item rendered by a custom SDUI page',
47
+ icon: { emoji: '✅' },
48
+ });
49
+
50
+ const detailsSection = new CustomObjectFieldSection(todoObj, {
51
+ name: 'Details',
52
+ sectionId: 'sec_custom_todo_details',
40
53
  });
41
54
 
42
- defineTypedPageLayout(todo, {
55
+ const completedField = new BooleanField(todoObj, {
56
+ apiName: 'completed__c',
57
+ displayName: 'Completed',
58
+ description: 'Whether the todo item is complete',
59
+ section: detailsSection,
60
+ });
61
+
62
+ new CustomObjectPageLayout(todoObj, {
43
63
  apiName: 'default',
44
64
  name: 'Todo',
45
65
  layoutId: 'layout_custom_todo',
@@ -53,8 +73,8 @@ defineTypedPageLayout(todo, {
53
73
  {
54
74
  name: 'Details',
55
75
  type: 'fields_section',
56
- section: todo.sections.details,
57
- fields: [{ field: 'name', editable: true }, 'completed'],
76
+ section: detailsSection,
77
+ fields: [{ field: 'name', editable: true }, completedField],
58
78
  },
59
79
  ],
60
80
  },
@@ -65,22 +85,25 @@ defineTypedPageLayout(todo, {
65
85
  visibilityConditions: {},
66
86
  });
67
87
 
68
- const { page: todoPage } = defineSduiPageFromFunctionFile(manifest, {
88
+ const todoPageFn = new ManifestFunction(manifest, {
69
89
  apiName: 'custom_todo_page_fn',
70
90
  name: 'renderTodoPage',
71
- description: 'Custom SDUI todo page backed by typed custom-object record APIs',
72
- codeDirectory: path.join(__dirname, 'functions'),
73
- codeFilename: 'todo-page.ts',
91
+ description: 'Custom SDUI todo page backed by Custom Object record APIs',
92
+ code: fs.readFileSync(path.join(__dirname, 'functions', 'todo-page.ts'), 'utf-8'),
74
93
  dependencies: { '@rippling/rippling-sdk': '^0.2.0-alpha.65', '@rippling/pebble-sdui-web': '^0.9.2' },
75
94
  isAsync: false,
76
- pageId: 'custom_todo_page',
77
- pageName: 'Todos',
95
+ });
96
+
97
+ const todoPage = new SduiPage(manifest, {
98
+ sduiPageId: 'custom_todo_page',
99
+ name: 'Todos',
100
+ function: todoPageFn,
78
101
  });
79
102
 
80
103
  new App(manifest, {
81
104
  apiName: 'todo_app_without_object_list_view',
82
105
  name: 'Todo App Without ObjectListView',
83
- description: 'Todo app with a fully custom SDUI page and typed custom-object persistence',
106
+ description: 'Todo app with a fully custom SDUI page and Custom Object persistence',
84
107
  appType: 'custom',
85
108
  pages: [todoPage],
86
109
  });