@stackbit/cms-core 0.7.6-develop.2 → 0.8.0-develop.1

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 (63) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/content-store-utils.d.ts +44 -0
  3. package/dist/content-store-utils.d.ts.map +1 -1
  4. package/dist/content-store-utils.js +213 -1
  5. package/dist/content-store-utils.js.map +1 -1
  6. package/dist/content-store.d.ts +12 -1
  7. package/dist/content-store.d.ts.map +1 -1
  8. package/dist/content-store.js +45 -6
  9. package/dist/content-store.js.map +1 -1
  10. package/dist/index.d.ts +0 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +0 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/types/content-store-document-fields.d.ts +105 -0
  15. package/dist/types/content-store-document-fields.d.ts.map +1 -1
  16. package/dist/types/content-store-documents.d.ts +4 -1
  17. package/dist/types/content-store-documents.d.ts.map +1 -1
  18. package/dist/types/content-store-types.d.ts +10 -10
  19. package/dist/types/content-store-types.d.ts.map +1 -1
  20. package/dist/types/custom-actions.d.ts +108 -0
  21. package/dist/types/custom-actions.d.ts.map +1 -0
  22. package/dist/types/custom-actions.js +3 -0
  23. package/dist/types/custom-actions.js.map +1 -0
  24. package/dist/types/index.d.ts +1 -0
  25. package/dist/types/index.d.ts.map +1 -1
  26. package/dist/types/index.js +1 -0
  27. package/dist/types/index.js.map +1 -1
  28. package/dist/utils/csi-to-store-docs-converter.d.ts +2 -1
  29. package/dist/utils/csi-to-store-docs-converter.d.ts.map +1 -1
  30. package/dist/utils/csi-to-store-docs-converter.js +144 -11
  31. package/dist/utils/csi-to-store-docs-converter.js.map +1 -1
  32. package/dist/utils/custom-actions.d.ts +53 -0
  33. package/dist/utils/custom-actions.d.ts.map +1 -0
  34. package/dist/utils/custom-actions.js +586 -0
  35. package/dist/utils/custom-actions.js.map +1 -0
  36. package/dist/utils/document-hooks.d.ts +9 -0
  37. package/dist/utils/document-hooks.d.ts.map +1 -1
  38. package/dist/utils/document-hooks.js +3 -2
  39. package/dist/utils/document-hooks.js.map +1 -1
  40. package/dist/utils/search-utils.d.ts +1 -1
  41. package/dist/utils/search-utils.d.ts.map +1 -1
  42. package/dist/utils/store-to-api-docs-converter.d.ts.map +1 -1
  43. package/dist/utils/store-to-api-docs-converter.js +77 -29
  44. package/dist/utils/store-to-api-docs-converter.js.map +1 -1
  45. package/dist/utils/store-to-csi-docs-converter.d.ts +3 -0
  46. package/dist/utils/store-to-csi-docs-converter.d.ts.map +1 -1
  47. package/dist/utils/store-to-csi-docs-converter.js +2 -1
  48. package/dist/utils/store-to-csi-docs-converter.js.map +1 -1
  49. package/package.json +5 -5
  50. package/src/content-store-utils.ts +247 -0
  51. package/src/content-store.ts +62 -7
  52. package/src/index.ts +0 -1
  53. package/src/types/content-store-document-fields.ts +55 -2
  54. package/src/types/content-store-documents.ts +4 -1
  55. package/src/types/content-store-types.ts +10 -10
  56. package/src/types/custom-actions.ts +154 -0
  57. package/src/types/index.ts +1 -0
  58. package/src/utils/csi-to-store-docs-converter.ts +220 -12
  59. package/src/utils/custom-actions.ts +730 -0
  60. package/src/utils/document-hooks.ts +3 -3
  61. package/src/utils/search-utils.ts +1 -1
  62. package/src/utils/store-to-api-docs-converter.ts +88 -33
  63. package/src/utils/store-to-csi-docs-converter.ts +4 -4
@@ -0,0 +1,730 @@
1
+ import _ from 'lodash';
2
+ import { Config, mapModelFieldsRecursively } from '@stackbit/sdk';
3
+ import * as StackbitTypes from '@stackbit/types';
4
+ import { mapPromise, omitByNil } from '@stackbit/utils';
5
+ import * as ContentStoreTypes from '../types';
6
+ import {
7
+ CustomActionStateChange,
8
+ APICustomAction,
9
+ APICustomActionDocumentSpecifier,
10
+ APICustomActionDocument,
11
+ APICustomActionObject,
12
+ APICustomActionField,
13
+ APIGetCustomActionRequest,
14
+ APIRunCustomActionRequest,
15
+ APIRunCustomActionRequestBulk,
16
+ ContentStoreCustomActionMap,
17
+ ContentStoreCustomActionGlobal,
18
+ ContentStoreCustomActionBulk,
19
+ ContentStoreCustomActionDocument,
20
+ ContentStoreCustomActionObjectModel,
21
+ ContentStoreCustomActionField,
22
+ APICustomActionGlobal,
23
+ APICustomActionBulk,
24
+ ContentStoreCustomAction,
25
+ ContentStoreCustomActionObjectField
26
+ } from '../types';
27
+ import { createConfigDelegate } from './config-delegate';
28
+ import { getContentSourceActionsForSourceThunk } from './document-hooks';
29
+ import { mapStoreDocumentToCSIDocumentWithSource, mapStoreFieldToCSIField } from './store-to-csi-docs-converter';
30
+ import {
31
+ getContentSourceDataByTypeAndProjectIdOrThrow,
32
+ getModelAndDocumentFieldForLocalizedFieldPath,
33
+ getUserContextForSrcTypeThunk
34
+ } from '../content-store-utils';
35
+
36
+ /**
37
+ * Removes "run" and "state" functions from actions of models, nested objects and fields.
38
+ * Sets "needsResolving" flag to let client know if the action needs additional resolving.
39
+ * @param {StackbitTypes.ModelMap} modelMap
40
+ */
41
+ export function stripModelActions({ modelMap }: { modelMap: StackbitTypes.ModelMap }): StackbitTypes.ModelMap {
42
+ return _.mapValues(modelMap, (model) => {
43
+ if ('actions' in model) {
44
+ const { actions, ...restModel } = model;
45
+ model = restModel;
46
+ }
47
+
48
+ model = mapModelFieldsRecursively(model, (field) => {
49
+ if ('actions' in field && Array.isArray(field.actions)) {
50
+ const { actions, ...restField } = field;
51
+ field = restField;
52
+ }
53
+ return field;
54
+ });
55
+
56
+ return model;
57
+ });
58
+ }
59
+
60
+ export async function getGlobalAndBulkAPIActions({
61
+ stackbitConfig,
62
+ customActionMap,
63
+ contentSourceDataById,
64
+ userLogger,
65
+ pageUrl,
66
+ user,
67
+ locale,
68
+ currentPageDocument
69
+ }: {
70
+ stackbitConfig: Config | null;
71
+ customActionMap: ContentStoreCustomActionMap;
72
+ contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
73
+ userLogger: StackbitTypes.Logger;
74
+ pageUrl?: string;
75
+ user?: ContentStoreTypes.User;
76
+ locale?: string;
77
+ currentPageDocument?: APICustomActionDocumentSpecifier;
78
+ }): Promise<(APICustomActionGlobal | APICustomActionBulk)[]> {
79
+ if (!stackbitConfig || !Array.isArray(stackbitConfig.actions)) {
80
+ return [];
81
+ }
82
+
83
+ const configDelegate = createConfigDelegate({
84
+ contentSourceDataById: contentSourceDataById,
85
+ logger: userLogger
86
+ });
87
+
88
+ return mapPromise(stackbitConfig.actions, async (action) => {
89
+ const actionId = `config.actions.${action.name}`;
90
+ const prevStoreAction = customActionMap[actionId] as ContentStoreCustomActionGlobal | ContentStoreCustomActionBulk | undefined;
91
+ const storeAction: ContentStoreCustomActionGlobal | ContentStoreCustomActionBulk = {
92
+ // if configuration is updated, the new action properties will override the stored action properties
93
+ ...action,
94
+ actionId,
95
+ label: action.label ?? _.startCase(action.name),
96
+ runningHandler: prevStoreAction?.runningHandler,
97
+ lastResultState: prevStoreAction?.lastResultState
98
+ };
99
+ customActionMap[actionId] = storeAction;
100
+
101
+ let state: StackbitTypes.CustomActionState | 'unknown';
102
+
103
+ if (storeAction.runningHandler) {
104
+ state = 'running';
105
+ } else if (storeAction.state) {
106
+ const pageDocument = getCSIDocumentWithSourceFromDocumentSpec(currentPageDocument, configDelegate);
107
+ state = await storeAction.state({
108
+ actionId: actionId,
109
+ currentLocale: locale,
110
+ currentUser: user ? { name: user.name, email: user.email } : undefined,
111
+ currentPageUrl: pageUrl,
112
+ currentPageDocument: pageDocument,
113
+ ...configDelegate
114
+ });
115
+ } else {
116
+ state = storeAction.lastResultState ?? 'enabled';
117
+ }
118
+
119
+ return omitByNil({
120
+ actionId: actionId,
121
+ type: storeAction.type,
122
+ name: storeAction.name,
123
+ label: storeAction.label,
124
+ icon: storeAction.icon,
125
+ state: state,
126
+ inputFields: storeAction.inputFields
127
+ });
128
+ });
129
+ }
130
+
131
+ export function convertDocumentActionToAPICustomDocumentAction({
132
+ action,
133
+ customActionMap,
134
+ csiDocument
135
+ }: {
136
+ action: StackbitTypes.CustomActionDocument;
137
+ customActionMap: ContentStoreCustomActionMap;
138
+ csiDocument: StackbitTypes.DocumentWithSource;
139
+ }): APICustomActionDocument {
140
+ const actionId = `${csiDocument.srcType}:${csiDocument.srcProjectId}:${csiDocument.id}:${action.name}`;
141
+
142
+ const prevStoreAction = customActionMap[actionId] as ContentStoreCustomActionDocument | undefined;
143
+ const storeAction: ContentStoreCustomActionDocument = {
144
+ // if configuration is updated, the new action properties will override the stored action properties
145
+ ...action,
146
+ type: 'document',
147
+ actionId,
148
+ label: action.label ?? _.startCase(action.name),
149
+ runningHandler: prevStoreAction?.runningHandler,
150
+ lastResultState: prevStoreAction?.lastResultState,
151
+ documentSpec: {
152
+ srcType: csiDocument.srcType,
153
+ srcProjectId: csiDocument.srcProjectId,
154
+ srcDocumentId: csiDocument.id
155
+ }
156
+ };
157
+ customActionMap[actionId] = storeAction;
158
+
159
+ let state: StackbitTypes.CustomActionState | 'unknown';
160
+ let needsResolving = false;
161
+ if (storeAction?.runningHandler) {
162
+ state = 'running';
163
+ } else if (action.state) {
164
+ state = 'unknown';
165
+ needsResolving = true;
166
+ } else {
167
+ state = storeAction?.lastResultState ?? 'enabled';
168
+ }
169
+
170
+ return omitByNil({
171
+ actionId: actionId,
172
+ type: 'document',
173
+ name: storeAction.name,
174
+ label: storeAction.label,
175
+ icon: storeAction.icon,
176
+ state: state,
177
+ inputFields: storeAction.inputFields,
178
+ needsResolving: needsResolving
179
+ });
180
+ }
181
+
182
+ export function convertObjectModelActionToAPICustomObjectAction({
183
+ action,
184
+ customActionMap,
185
+ csiParentDocument,
186
+ fieldPath
187
+ }: {
188
+ action: StackbitTypes.CustomActionObjectModel;
189
+ customActionMap: ContentStoreCustomActionMap;
190
+ csiParentDocument: StackbitTypes.DocumentWithSource;
191
+ fieldPath: (string | number)[];
192
+ }): APICustomActionObject {
193
+ const actionId = `${csiParentDocument.srcType}:${csiParentDocument.srcProjectId}:${csiParentDocument.id}:${fieldPath.join('.')}:${action.name}`;
194
+
195
+ const prevStoreAction = customActionMap[actionId] as ContentStoreCustomActionObjectModel | undefined;
196
+ const storeAction: ContentStoreCustomActionObjectModel = {
197
+ // if configuration is updated, the new action properties will override the stored action properties
198
+ ...action,
199
+ type: 'objectModel',
200
+ actionId,
201
+ label: action.label ?? _.startCase(action.name),
202
+ runningHandler: prevStoreAction?.runningHandler,
203
+ lastResultState: prevStoreAction?.lastResultState,
204
+ documentSpec: {
205
+ srcType: csiParentDocument.srcType,
206
+ srcProjectId: csiParentDocument.srcProjectId,
207
+ srcDocumentId: csiParentDocument.id
208
+ },
209
+ fieldPath
210
+ };
211
+ customActionMap[actionId] = storeAction;
212
+
213
+ let state: StackbitTypes.CustomActionState | 'unknown';
214
+ let needsResolving = false;
215
+ if (storeAction?.runningHandler) {
216
+ state = 'running';
217
+ } else if (action.state) {
218
+ state = 'unknown';
219
+ needsResolving = true;
220
+ } else {
221
+ state = storeAction?.lastResultState ?? 'enabled';
222
+ }
223
+
224
+ return omitByNil({
225
+ actionId: actionId,
226
+ type: 'object',
227
+ name: storeAction.name,
228
+ label: storeAction.label,
229
+ icon: storeAction.icon,
230
+ state: state,
231
+ inputFields: storeAction.inputFields,
232
+ needsResolving: needsResolving
233
+ });
234
+ }
235
+
236
+ export function convertFieldActionToAPICustomAction<T extends StackbitTypes.CustomActionField | StackbitTypes.CustomActionObjectField>({
237
+ action,
238
+ customActionMap,
239
+ csiParentDocument,
240
+ fieldPath
241
+ }: {
242
+ action: T;
243
+ customActionMap: ContentStoreCustomActionMap;
244
+ csiParentDocument: StackbitTypes.DocumentWithSource;
245
+ fieldPath: (string | number)[];
246
+ }): T extends StackbitTypes.CustomActionField ? APICustomActionField : APICustomActionObject {
247
+ const actionId = `${csiParentDocument.srcType}:${csiParentDocument.srcProjectId}:${csiParentDocument.id}:${fieldPath.join('.')}:${action.name}`;
248
+
249
+ const prevStoreAction = customActionMap[actionId] as ContentStoreCustomActionField | ContentStoreCustomActionObjectField | undefined;
250
+ const storeAction = {
251
+ // if configuration is updated, the new action properties will override the stored action properties
252
+ ...action,
253
+ type: action.type === 'object' ? 'objectField' : 'field',
254
+ actionId,
255
+ label: action.label ?? _.startCase(action.name),
256
+ runningHandler: prevStoreAction?.runningHandler,
257
+ lastResultState: prevStoreAction?.lastResultState,
258
+ documentSpec: {
259
+ srcType: csiParentDocument.srcType,
260
+ srcProjectId: csiParentDocument.srcProjectId,
261
+ srcDocumentId: csiParentDocument.id
262
+ },
263
+ fieldPath
264
+ } as ContentStoreCustomActionField | ContentStoreCustomActionObjectField;
265
+ customActionMap[actionId] = storeAction;
266
+
267
+ let state: StackbitTypes.CustomActionState | 'unknown';
268
+ let needsResolving = false;
269
+ if (storeAction?.runningHandler) {
270
+ state = 'running';
271
+ } else if (action.state) {
272
+ state = 'unknown';
273
+ needsResolving = true;
274
+ } else {
275
+ state = storeAction?.lastResultState ?? 'enabled';
276
+ }
277
+
278
+ return omitByNil({
279
+ actionId: actionId,
280
+ type: action.type ?? 'field',
281
+ name: storeAction.name,
282
+ label: storeAction.label,
283
+ icon: storeAction.icon,
284
+ state: state,
285
+ inputFields: storeAction.inputFields,
286
+ needsResolving: needsResolving
287
+ }) as T extends StackbitTypes.CustomActionField ? APICustomActionField : APICustomActionObject;
288
+ }
289
+
290
+ export async function resolveCustomActionsById({
291
+ getActionRequest,
292
+ customActionMap,
293
+ contentSourceDataById,
294
+ userLogger
295
+ }: {
296
+ getActionRequest: APIGetCustomActionRequest;
297
+ customActionMap: ContentStoreCustomActionMap;
298
+ contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
299
+ userLogger: StackbitTypes.Logger;
300
+ }): Promise<APICustomAction[]> {
301
+ const result: APICustomAction[] = [];
302
+ const { customActionIds, locale, user, pageUrl, currentPageDocument } = getActionRequest;
303
+
304
+ const configDelegate = createConfigDelegate({
305
+ contentSourceDataById,
306
+ logger: userLogger
307
+ });
308
+
309
+ for (const actionId of customActionIds) {
310
+ const storeAction = customActionMap[actionId];
311
+ if (!storeAction) {
312
+ userLogger.debug(`getCustomActionsById: custom action not found, id: '${actionId}'`);
313
+ continue;
314
+ }
315
+
316
+ try {
317
+ let state: StackbitTypes.CustomActionState;
318
+ if (storeAction.runningHandler) {
319
+ state = 'running';
320
+ } else if (storeAction.state) {
321
+ const pageDocument = getCSIDocumentWithSourceFromDocumentSpec(currentPageDocument, configDelegate);
322
+ const commonStateOptions: StackbitTypes.CustomActionStateCommonOptions = {
323
+ actionId: actionId,
324
+ currentLocale: locale,
325
+ currentUser: user
326
+ ? {
327
+ name: user.name,
328
+ email: user.email
329
+ }
330
+ : undefined,
331
+ currentPageUrl: pageUrl,
332
+ currentPageDocument: pageDocument,
333
+ ...configDelegate
334
+ };
335
+ if (storeAction.type === 'global' || storeAction.type === 'bulk') {
336
+ state = await storeAction.state(commonStateOptions);
337
+ } else if (storeAction.type === 'document') {
338
+ const { document, model } = getCSIDocumentAndModelWithSourceFromDocumentSpec({
339
+ documentSpec: storeAction.documentSpec,
340
+ contentSourceDataById
341
+ });
342
+ state = await storeAction.state({
343
+ ...commonStateOptions,
344
+ document: document,
345
+ model: model
346
+ });
347
+ } else if (storeAction.type === 'objectModel') {
348
+ const stateObjectParams = getHandlerParamsForObjectModelAction({
349
+ storeAction,
350
+ contentSourceDataById
351
+ });
352
+ state = await storeAction.state({
353
+ ...commonStateOptions,
354
+ ...stateObjectParams
355
+ });
356
+ } else if (storeAction.type === 'objectField') {
357
+ const stateObjectParams = getHandlerParamsForObjectFieldAction({
358
+ storeAction,
359
+ contentSourceDataById
360
+ });
361
+ state = await storeAction.state({
362
+ ...commonStateOptions,
363
+ ...stateObjectParams
364
+ });
365
+ } else if (storeAction.type === 'field') {
366
+ const stateFieldParams = getHandlerParamsForFieldAction({
367
+ storeAction,
368
+ contentSourceDataById
369
+ });
370
+ state = await storeAction.state({
371
+ ...commonStateOptions,
372
+ ...stateFieldParams
373
+ });
374
+ } else {
375
+ const _exhaustiveCheck: never = storeAction;
376
+ continue;
377
+ }
378
+ } else {
379
+ state = storeAction?.lastResultState ?? 'enabled';
380
+ }
381
+ result.push(
382
+ omitByNil({
383
+ actionId: actionId,
384
+ type: storeActionTypeToAPIActionType(storeAction.type),
385
+ name: storeAction.name,
386
+ label: storeAction.label,
387
+ icon: storeAction.icon,
388
+ state: state,
389
+ inputFields: storeAction.inputFields
390
+ })
391
+ );
392
+ } catch (error: any) {
393
+ userLogger.warn(`getCustomActionsById: error resolving custom action, id: '${actionId}', error: ${error.message}`);
394
+ }
395
+ }
396
+
397
+ return result;
398
+ }
399
+
400
+ export function runCustomAction({
401
+ runActionRequest,
402
+ customActionMap,
403
+ contentSourceDataById,
404
+ userLogger,
405
+ stackbitConfig
406
+ }: {
407
+ runActionRequest: APIRunCustomActionRequest;
408
+ customActionMap: ContentStoreCustomActionMap;
409
+ contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
410
+ userLogger: StackbitTypes.Logger;
411
+ stackbitConfig: Config | null;
412
+ }): Promise<CustomActionStateChange> {
413
+ const storeAction = customActionMap[runActionRequest.actionId];
414
+ if (!storeAction) {
415
+ throw new Error(`Error running action: action not found, action name: '${runActionRequest.actionName}' action ID: '${runActionRequest.actionId}'.`);
416
+ }
417
+ const prevResultState = storeAction.lastResultState;
418
+ if (storeAction.lastResultState && storeAction.lastResultState !== 'enabled') {
419
+ throw new Error(
420
+ `Error running action: action is not enabled, action name: '${runActionRequest.actionName}' action ID: '${runActionRequest.actionId}'.`
421
+ );
422
+ }
423
+
424
+ try {
425
+ const actionLogger = userLogger.createLogger({ label: `action:${storeAction.name}` });
426
+ const configDelegate = createConfigDelegate({ contentSourceDataById: contentSourceDataById, logger: actionLogger });
427
+ const currentPageDocument = getCSIDocumentWithSourceFromDocumentSpec(runActionRequest.currentPageDocument, configDelegate);
428
+
429
+ storeAction.runningHandler = true;
430
+ storeAction.lastResultState = 'running';
431
+
432
+ const commonRunOptions: StackbitTypes.CustomActionRunCommonOptions = {
433
+ actionId: storeAction.actionId,
434
+ inputData: runActionRequest.inputData,
435
+ currentLocale: runActionRequest.locale,
436
+ currentUser: runActionRequest.user,
437
+ currentPageUrl: runActionRequest.pageUrl,
438
+ currentPageDocument: currentPageDocument,
439
+ getContentSourceActionsForSource: getContentSourceActionsForSourceThunk({
440
+ getContentSourceDataById: () => contentSourceDataById,
441
+ logger: userLogger,
442
+ user: runActionRequest.user,
443
+ stackbitConfig: stackbitConfig
444
+ }),
445
+ getUserContextForContentSourceType: getUserContextForSrcTypeThunk(runActionRequest.user),
446
+ ...configDelegate
447
+ };
448
+
449
+ let promise;
450
+
451
+ if (storeAction.type === 'global') {
452
+ promise = storeAction.run(commonRunOptions);
453
+ } else if (storeAction.type === 'bulk') {
454
+ const documents = (runActionRequest as APIRunCustomActionRequestBulk).documents.map((documentSpec) => {
455
+ const { document } = getCSIDocumentAndModelWithSourceFromDocumentSpec({
456
+ documentSpec,
457
+ contentSourceDataById
458
+ });
459
+ return document;
460
+ });
461
+ promise = storeAction.run({
462
+ ...commonRunOptions,
463
+ documents
464
+ });
465
+ } else if (storeAction.type === 'document') {
466
+ const { document, model } = getCSIDocumentAndModelWithSourceFromDocumentSpec({
467
+ documentSpec: storeAction.documentSpec,
468
+ contentSourceDataById
469
+ });
470
+ promise = storeAction.run({
471
+ ...commonRunOptions,
472
+ document,
473
+ model,
474
+ contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
475
+ srcType: storeAction.documentSpec.srcType,
476
+ srcProjectId: storeAction.documentSpec.srcProjectId
477
+ })!
478
+ });
479
+ } else if (storeAction.type === 'objectModel') {
480
+ const handlerObjectParams = getHandlerParamsForObjectModelAction({
481
+ storeAction,
482
+ contentSourceDataById
483
+ });
484
+ promise = storeAction.run({
485
+ ...commonRunOptions,
486
+ ...handlerObjectParams,
487
+ contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
488
+ srcType: storeAction.documentSpec.srcType,
489
+ srcProjectId: storeAction.documentSpec.srcProjectId
490
+ })!
491
+ });
492
+ } else if (storeAction.type === 'objectField') {
493
+ const handlerObjectParams = getHandlerParamsForObjectFieldAction({
494
+ storeAction,
495
+ contentSourceDataById
496
+ });
497
+ promise = storeAction.run({
498
+ ...commonRunOptions,
499
+ ...handlerObjectParams,
500
+ contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
501
+ srcType: storeAction.documentSpec.srcType,
502
+ srcProjectId: storeAction.documentSpec.srcProjectId
503
+ })!
504
+ });
505
+ } else if (storeAction.type === 'field') {
506
+ const handlerFieldParams = getHandlerParamsForFieldAction({ storeAction, contentSourceDataById });
507
+ promise = storeAction.run({
508
+ ...commonRunOptions,
509
+ ...handlerFieldParams,
510
+ contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
511
+ srcType: storeAction.documentSpec.srcType,
512
+ srcProjectId: storeAction.documentSpec.srcProjectId
513
+ })!
514
+ });
515
+ } else {
516
+ throw new Error(`action type ${(storeAction as any).type} not supported`);
517
+ }
518
+
519
+ return promise
520
+ .then((actionResult) => {
521
+ storeAction.runningHandler = false;
522
+ storeAction.lastResultState = actionResult?.state;
523
+ userLogger.debug(`Action completed: ${storeAction.actionId}`);
524
+ return Promise.resolve(
525
+ omitByNil({
526
+ actionId: storeAction.actionId,
527
+ actionName: storeAction.name,
528
+ actionType: storeActionTypeToAPIActionType(storeAction.type),
529
+ // TODO: resolve the state if state function is defined
530
+ state: actionResult?.state ?? 'enabled',
531
+ success: actionResult?.success,
532
+ error: actionResult?.error
533
+ })
534
+ );
535
+ })
536
+ .catch((error: any) => {
537
+ storeAction.runningHandler = false;
538
+ storeAction.lastResultState = prevResultState;
539
+ userLogger.debug(`Error running action: ${error.message}`);
540
+ return Promise.resolve({
541
+ actionId: storeAction.actionId,
542
+ actionName: storeAction.name,
543
+ actionType: storeActionTypeToAPIActionType(storeAction.type),
544
+ // TODO: resolve the state if state function is defined
545
+ state: prevResultState ?? 'enabled',
546
+ error: `Error running action: ${error.message}`
547
+ });
548
+ });
549
+ } catch (error: any) {
550
+ if (storeAction) {
551
+ storeAction.runningHandler = false;
552
+ storeAction.lastResultState = prevResultState;
553
+ }
554
+ userLogger.debug(`Error running action: ${error.message}`);
555
+ return Promise.resolve({
556
+ actionId: runActionRequest.actionId,
557
+ actionName: storeAction?.name ?? runActionRequest.actionName,
558
+ actionType: storeActionTypeToAPIActionType(storeAction?.type) ?? runActionRequest.actionType,
559
+ // TODO: resolve the state if state function is defined
560
+ state: prevResultState ?? 'enabled',
561
+ error: `Error running action: ${error.message}`
562
+ });
563
+ }
564
+ }
565
+
566
+ function getCSIDocumentWithSourceFromDocumentSpec(
567
+ documentSpec: APICustomActionDocumentSpecifier | undefined,
568
+ configDelegate: StackbitTypes.ConfigDelegate
569
+ ): StackbitTypes.DocumentWithSource | undefined {
570
+ return documentSpec
571
+ ? configDelegate.getDocumentById({
572
+ id: documentSpec.srcDocumentId,
573
+ srcType: documentSpec.srcType,
574
+ srcProjectId: documentSpec.srcProjectId
575
+ })
576
+ : undefined;
577
+ }
578
+
579
+ function getHandlerParamsForObjectModelAction({
580
+ storeAction,
581
+ contentSourceDataById
582
+ }: {
583
+ storeAction: Pick<ContentStoreCustomActionObjectModel, 'documentSpec' | 'fieldPath'>;
584
+ contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
585
+ }): StackbitTypes.CustomActionObjectModelStateParams {
586
+ const fieldActionCommonParams = getHandlerParamsForFieldAction({
587
+ storeAction,
588
+ contentSourceDataById
589
+ });
590
+
591
+ if (!fieldActionCommonParams.documentField) {
592
+ throw new Error(`object document field not found at field path: ${storeAction.fieldPath.join('.')}`);
593
+ }
594
+
595
+ const documentField = fieldActionCommonParams.documentField as StackbitTypes.DocumentModelFieldNonLocalized;
596
+ const documentSpec = storeAction.documentSpec;
597
+ const contentSourceData = getContentSourceDataByTypeAndProjectIdOrThrow(documentSpec.srcType, documentSpec.srcProjectId, contentSourceDataById);
598
+ const objectModel = contentSourceData.modelMap[documentField.modelName];
599
+ if (!objectModel || objectModel.type !== 'object') {
600
+ throw new Error(`object model '${documentField.modelName}' not found`);
601
+ }
602
+
603
+ return {
604
+ ...fieldActionCommonParams,
605
+ documentField,
606
+ modelField: fieldActionCommonParams.modelField as StackbitTypes.FieldModel,
607
+ objectModel: {
608
+ ...objectModel,
609
+ srcType: documentSpec.srcType,
610
+ srcProjectId: documentSpec.srcProjectId
611
+ }
612
+ };
613
+ }
614
+
615
+ function getHandlerParamsForObjectFieldAction({
616
+ storeAction,
617
+ contentSourceDataById
618
+ }: {
619
+ storeAction: Pick<ContentStoreCustomActionObjectField, 'documentSpec' | 'fieldPath'>;
620
+ contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
621
+ }): StackbitTypes.CustomActionObjectFieldStateParams {
622
+ const fieldActionCommonParams = getHandlerParamsForFieldAction({
623
+ storeAction,
624
+ contentSourceDataById
625
+ });
626
+
627
+ if (!fieldActionCommonParams.documentField) {
628
+ throw new Error(`object document field not found at field path: ${storeAction.fieldPath.join('.')}`);
629
+ }
630
+
631
+ return {
632
+ ...fieldActionCommonParams,
633
+ documentField: fieldActionCommonParams.documentField as StackbitTypes.DocumentObjectFieldNonLocalized,
634
+ modelField: fieldActionCommonParams.modelField as StackbitTypes.FieldObject
635
+ };
636
+ }
637
+
638
+ function getHandlerParamsForFieldAction({
639
+ storeAction,
640
+ contentSourceDataById
641
+ }: {
642
+ storeAction: Pick<ContentStoreCustomActionField, 'documentSpec' | 'fieldPath'>;
643
+ contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
644
+ }): StackbitTypes.CustomActionFieldStateParams {
645
+ const documentSpec = storeAction.documentSpec;
646
+ const contentSourceData = getContentSourceDataByTypeAndProjectIdOrThrow(documentSpec.srcType, documentSpec.srcProjectId, contentSourceDataById);
647
+ const document = contentSourceData.documentMap[documentSpec.srcDocumentId];
648
+ const csiDocument = contentSourceData.csiDocumentMap[documentSpec.srcDocumentId];
649
+ if (!document || !csiDocument) {
650
+ throw new Error(
651
+ `document not found for srcType: ${documentSpec.srcType}, srcProjectId: ${documentSpec.srcProjectId}, srcDocumentId: ${documentSpec.srcDocumentId}`
652
+ );
653
+ }
654
+
655
+ const model = contentSourceData.modelMap[document.srcModelName];
656
+ if (!model) {
657
+ throw new Error(`model '${document.srcModelName}' not found`);
658
+ }
659
+
660
+ const mappedCSIDocument = mapStoreDocumentToCSIDocumentWithSource({ document, csiDocument });
661
+
662
+ // the documentField should be localized because fieldPath includes locales
663
+ const { modelField, documentField } = getModelAndDocumentFieldForLocalizedFieldPath({
664
+ document,
665
+ model,
666
+ fieldPath: storeAction.fieldPath,
667
+ modelMap: contentSourceData.modelMap
668
+ }) as {
669
+ // list items cannot have actions, therefore we can safely reduce the type to non list item fields
670
+ modelField: StackbitTypes.Field;
671
+ documentField: ContentStoreTypes.DocumentField;
672
+ };
673
+
674
+ const csiDocumentField = mapStoreFieldToCSIField(documentField);
675
+
676
+ return {
677
+ parentDocument: mappedCSIDocument,
678
+ parentModel: {
679
+ ...model,
680
+ srcType: documentSpec.srcType,
681
+ srcProjectId: documentSpec.srcProjectId
682
+ },
683
+ documentField: csiDocumentField,
684
+ modelField: modelField,
685
+ fieldPath: storeAction.fieldPath
686
+ };
687
+ }
688
+
689
+ function getCSIDocumentAndModelWithSourceFromDocumentSpec({
690
+ documentSpec,
691
+ contentSourceDataById
692
+ }: {
693
+ documentSpec: APICustomActionDocumentSpecifier;
694
+ contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
695
+ }): {
696
+ document: StackbitTypes.DocumentWithSource;
697
+ model: StackbitTypes.ModelWithSource;
698
+ } {
699
+ const contentSourceData = getContentSourceDataByTypeAndProjectIdOrThrow(documentSpec.srcType, documentSpec.srcProjectId, contentSourceDataById);
700
+ const document = contentSourceData.documentMap[documentSpec.srcDocumentId];
701
+ const csiDocument = contentSourceData.csiDocumentMap[documentSpec.srcDocumentId];
702
+ if (!document || !csiDocument) {
703
+ throw new Error(
704
+ `document not found, srcType: ${documentSpec.srcType}, srcProjectId: ${documentSpec.srcProjectId}, srcDocumentId: ${documentSpec.srcDocumentId}`
705
+ );
706
+ }
707
+ const mappedDocument = mapStoreDocumentToCSIDocumentWithSource({
708
+ document,
709
+ csiDocument
710
+ });
711
+ const model = contentSourceData.modelMap[mappedDocument.modelName];
712
+ if (!model) {
713
+ throw new Error(`model '${mappedDocument.modelName}' not found`);
714
+ }
715
+ return {
716
+ document: mappedDocument,
717
+ model: {
718
+ ...model,
719
+ srcType: documentSpec.srcType,
720
+ srcProjectId: documentSpec.srcProjectId
721
+ }
722
+ };
723
+ }
724
+
725
+ function storeActionTypeToAPIActionType(storeActionType: ContentStoreCustomAction['type']): APICustomAction['type'] {
726
+ if (storeActionType === 'objectModel' || storeActionType === 'objectField') {
727
+ return 'object';
728
+ }
729
+ return storeActionType;
730
+ }