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

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 +564 -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 +707 -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,707 @@
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
+ let state: StackbitTypes.CustomActionState;
317
+ if (storeAction.runningHandler) {
318
+ state = 'running';
319
+ } else if (storeAction.state) {
320
+ const pageDocument = getCSIDocumentWithSourceFromDocumentSpec(currentPageDocument, configDelegate);
321
+ const commonStateOptions: StackbitTypes.CustomActionStateCommonOptions = {
322
+ actionId: actionId,
323
+ currentLocale: locale,
324
+ currentUser: user ? { name: user.name, email: user.email } : undefined,
325
+ currentPageUrl: pageUrl,
326
+ currentPageDocument: pageDocument,
327
+ ...configDelegate
328
+ };
329
+ if (storeAction.type === 'global' || storeAction.type === 'bulk') {
330
+ state = await storeAction.state(commonStateOptions);
331
+ } else if (storeAction.type === 'document') {
332
+ const { document, model } = getCSIDocumentAndModelWithSourceFromDocumentSpec({
333
+ documentSpec: storeAction.documentSpec,
334
+ contentSourceDataById
335
+ });
336
+ state = await storeAction.state({
337
+ ...commonStateOptions,
338
+ document: document,
339
+ model: model
340
+ });
341
+ } else if (storeAction.type === 'objectModel') {
342
+ const stateObjectParams = getHandlerParamsForObjectModelAction({ storeAction, contentSourceDataById });
343
+ state = await storeAction.state({
344
+ ...commonStateOptions,
345
+ ...stateObjectParams
346
+ });
347
+ } else if (storeAction.type === 'objectField') {
348
+ const stateObjectParams = getHandlerParamsForObjectFieldAction({ storeAction, contentSourceDataById });
349
+ state = await storeAction.state({
350
+ ...commonStateOptions,
351
+ ...stateObjectParams
352
+ });
353
+ } else if (storeAction.type === 'field') {
354
+ const stateFieldParams = getHandlerParamsForFieldAction({ storeAction, contentSourceDataById });
355
+ state = await storeAction.state({
356
+ ...commonStateOptions,
357
+ ...stateFieldParams
358
+ });
359
+ } else {
360
+ const _exhaustiveCheck: never = storeAction;
361
+ continue;
362
+ }
363
+ } else {
364
+ state = storeAction?.lastResultState ?? 'enabled';
365
+ }
366
+ result.push(
367
+ omitByNil({
368
+ actionId: actionId,
369
+ type: storeActionTypeToAPIActionType(storeAction.type),
370
+ name: storeAction.name,
371
+ label: storeAction.label,
372
+ icon: storeAction.icon,
373
+ state: state,
374
+ inputFields: storeAction.inputFields
375
+ })
376
+ );
377
+ }
378
+
379
+ return result;
380
+ }
381
+
382
+ export function runCustomAction({
383
+ runActionRequest,
384
+ customActionMap,
385
+ contentSourceDataById,
386
+ userLogger,
387
+ stackbitConfig
388
+ }: {
389
+ runActionRequest: APIRunCustomActionRequest;
390
+ customActionMap: ContentStoreCustomActionMap;
391
+ contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
392
+ userLogger: StackbitTypes.Logger;
393
+ stackbitConfig: Config | null;
394
+ }): Promise<CustomActionStateChange> {
395
+ const storeAction = customActionMap[runActionRequest.actionId];
396
+ if (!storeAction) {
397
+ throw new Error(`Error running action: action not found, action name: '${runActionRequest.actionName}' action ID: '${runActionRequest.actionId}'.`);
398
+ }
399
+ const prevResultState = storeAction.lastResultState;
400
+ if (storeAction.lastResultState && storeAction.lastResultState !== 'enabled') {
401
+ throw new Error(
402
+ `Error running action: action is not enabled, action name: '${runActionRequest.actionName}' action ID: '${runActionRequest.actionId}'.`
403
+ );
404
+ }
405
+
406
+ try {
407
+ const actionLogger = userLogger.createLogger({ label: `action:${storeAction.name}` });
408
+ const configDelegate = createConfigDelegate({ contentSourceDataById: contentSourceDataById, logger: actionLogger });
409
+ const currentPageDocument = getCSIDocumentWithSourceFromDocumentSpec(runActionRequest.currentPageDocument, configDelegate);
410
+
411
+ storeAction.runningHandler = true;
412
+ storeAction.lastResultState = 'running';
413
+
414
+ const commonRunOptions: StackbitTypes.CustomActionRunCommonOptions = {
415
+ actionId: storeAction.actionId,
416
+ inputData: runActionRequest.inputData,
417
+ currentLocale: runActionRequest.locale,
418
+ currentUser: runActionRequest.user,
419
+ currentPageUrl: runActionRequest.pageUrl,
420
+ currentPageDocument: currentPageDocument,
421
+ getContentSourceActionsForSource: getContentSourceActionsForSourceThunk({
422
+ getContentSourceDataById: () => contentSourceDataById,
423
+ logger: userLogger,
424
+ user: runActionRequest.user,
425
+ stackbitConfig: stackbitConfig
426
+ }),
427
+ getUserContextForContentSourceType: getUserContextForSrcTypeThunk(runActionRequest.user),
428
+ ...configDelegate
429
+ };
430
+
431
+ let promise;
432
+
433
+ if (storeAction.type === 'global') {
434
+ promise = storeAction.run(commonRunOptions);
435
+ } else if (storeAction.type === 'bulk') {
436
+ const documents = (runActionRequest as APIRunCustomActionRequestBulk).documents.map((documentSpec) => {
437
+ const { document } = getCSIDocumentAndModelWithSourceFromDocumentSpec({
438
+ documentSpec,
439
+ contentSourceDataById
440
+ });
441
+ return document;
442
+ });
443
+ promise = storeAction.run({
444
+ ...commonRunOptions,
445
+ documents
446
+ });
447
+ } else if (storeAction.type === 'document') {
448
+ const { document, model } = getCSIDocumentAndModelWithSourceFromDocumentSpec({
449
+ documentSpec: storeAction.documentSpec,
450
+ contentSourceDataById
451
+ });
452
+ promise = storeAction.run({
453
+ ...commonRunOptions,
454
+ document,
455
+ model,
456
+ contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
457
+ srcType: storeAction.documentSpec.srcType,
458
+ srcProjectId: storeAction.documentSpec.srcProjectId
459
+ })!
460
+ });
461
+ } else if (storeAction.type === 'objectModel') {
462
+ const handlerObjectParams = getHandlerParamsForObjectModelAction({
463
+ storeAction,
464
+ contentSourceDataById
465
+ });
466
+ promise = storeAction.run({
467
+ ...commonRunOptions,
468
+ ...handlerObjectParams,
469
+ contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
470
+ srcType: storeAction.documentSpec.srcType,
471
+ srcProjectId: storeAction.documentSpec.srcProjectId
472
+ })!
473
+ });
474
+ } else if (storeAction.type === 'objectField') {
475
+ const handlerObjectParams = getHandlerParamsForObjectFieldAction({
476
+ storeAction,
477
+ contentSourceDataById
478
+ });
479
+ promise = storeAction.run({
480
+ ...commonRunOptions,
481
+ ...handlerObjectParams,
482
+ contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
483
+ srcType: storeAction.documentSpec.srcType,
484
+ srcProjectId: storeAction.documentSpec.srcProjectId
485
+ })!
486
+ });
487
+ } else if (storeAction.type === 'field') {
488
+ const handlerFieldParams = getHandlerParamsForFieldAction({ storeAction, contentSourceDataById });
489
+ promise = storeAction.run({
490
+ ...commonRunOptions,
491
+ ...handlerFieldParams,
492
+ contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
493
+ srcType: storeAction.documentSpec.srcType,
494
+ srcProjectId: storeAction.documentSpec.srcProjectId
495
+ })!
496
+ });
497
+ } else {
498
+ throw new Error(`action type ${(storeAction as any).type} not supported`);
499
+ }
500
+
501
+ return promise
502
+ .then((actionResult) => {
503
+ storeAction.runningHandler = false;
504
+ storeAction.lastResultState = actionResult?.state;
505
+ userLogger.debug(`Action completed: ${storeAction.actionId}`);
506
+ return Promise.resolve(
507
+ omitByNil({
508
+ actionId: storeAction.actionId,
509
+ actionName: storeAction.name,
510
+ actionType: storeActionTypeToAPIActionType(storeAction.type),
511
+ // TODO: resolve the state if state function is defined
512
+ state: actionResult?.state ?? 'enabled',
513
+ success: actionResult?.success,
514
+ error: actionResult?.error
515
+ })
516
+ );
517
+ })
518
+ .catch((error: any) => {
519
+ storeAction.runningHandler = false;
520
+ storeAction.lastResultState = prevResultState;
521
+ userLogger.debug(`Error running action: ${error.message}`);
522
+ return Promise.resolve({
523
+ actionId: storeAction.actionId,
524
+ actionName: storeAction.name,
525
+ actionType: storeActionTypeToAPIActionType(storeAction.type),
526
+ // TODO: resolve the state if state function is defined
527
+ state: prevResultState ?? 'enabled',
528
+ error: `Error running action: ${error.message}`
529
+ });
530
+ });
531
+ } catch (error: any) {
532
+ if (storeAction) {
533
+ storeAction.runningHandler = false;
534
+ storeAction.lastResultState = prevResultState;
535
+ }
536
+ userLogger.debug(`Error running action: ${error.message}`);
537
+ return Promise.resolve({
538
+ actionId: runActionRequest.actionId,
539
+ actionName: storeAction?.name ?? runActionRequest.actionName,
540
+ actionType: storeActionTypeToAPIActionType(storeAction?.type) ?? runActionRequest.actionType,
541
+ // TODO: resolve the state if state function is defined
542
+ state: prevResultState ?? 'enabled',
543
+ error: `Error running action: ${error.message}`
544
+ });
545
+ }
546
+ }
547
+
548
+ function getCSIDocumentWithSourceFromDocumentSpec(
549
+ documentSpec: APICustomActionDocumentSpecifier | undefined,
550
+ configDelegate: StackbitTypes.ConfigDelegate
551
+ ): StackbitTypes.DocumentWithSource | undefined {
552
+ return documentSpec
553
+ ? configDelegate.getDocumentById({
554
+ id: documentSpec.srcDocumentId,
555
+ srcType: documentSpec.srcType,
556
+ srcProjectId: documentSpec.srcProjectId
557
+ })
558
+ : undefined;
559
+ }
560
+
561
+ function getHandlerParamsForObjectModelAction({
562
+ storeAction,
563
+ contentSourceDataById
564
+ }: {
565
+ storeAction: Pick<ContentStoreCustomActionObjectModel, 'documentSpec' | 'fieldPath'>;
566
+ contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
567
+ }): StackbitTypes.CustomActionObjectModelStateParams {
568
+ const fieldActionCommonParams = getHandlerParamsForFieldAction({
569
+ storeAction,
570
+ contentSourceDataById
571
+ });
572
+
573
+ const documentField = fieldActionCommonParams.documentField as StackbitTypes.DocumentModelFieldNonLocalized;
574
+ const documentSpec = storeAction.documentSpec;
575
+ const contentSourceData = getContentSourceDataByTypeAndProjectIdOrThrow(documentSpec.srcType, documentSpec.srcProjectId, contentSourceDataById);
576
+ const objectModel = contentSourceData.modelMap[documentField.modelName];
577
+ if (!objectModel || objectModel.type !== 'object') {
578
+ throw new Error(`object model '${documentField.modelName}' not found`);
579
+ }
580
+
581
+ return {
582
+ ...fieldActionCommonParams,
583
+ documentField,
584
+ modelField: fieldActionCommonParams.modelField as StackbitTypes.FieldModel,
585
+ objectModel: {
586
+ ...objectModel,
587
+ srcType: documentSpec.srcType,
588
+ srcProjectId: documentSpec.srcProjectId
589
+ }
590
+ };
591
+ }
592
+
593
+ function getHandlerParamsForObjectFieldAction({
594
+ storeAction,
595
+ contentSourceDataById
596
+ }: {
597
+ storeAction: Pick<ContentStoreCustomActionObjectField, 'documentSpec' | 'fieldPath'>;
598
+ contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
599
+ }): StackbitTypes.CustomActionObjectFieldStateParams {
600
+ const fieldActionCommonParams = getHandlerParamsForFieldAction({
601
+ storeAction,
602
+ contentSourceDataById
603
+ });
604
+
605
+ return {
606
+ ...fieldActionCommonParams,
607
+ documentField: fieldActionCommonParams.documentField as StackbitTypes.DocumentObjectFieldNonLocalized,
608
+ modelField: fieldActionCommonParams.modelField as StackbitTypes.FieldObject
609
+ };
610
+ }
611
+
612
+ function getHandlerParamsForFieldAction({
613
+ storeAction,
614
+ contentSourceDataById
615
+ }: {
616
+ storeAction: Pick<ContentStoreCustomActionField, 'documentSpec' | 'fieldPath'>;
617
+ contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
618
+ }): StackbitTypes.CustomActionFieldStateParams {
619
+ const documentSpec = storeAction.documentSpec;
620
+ const contentSourceData = getContentSourceDataByTypeAndProjectIdOrThrow(documentSpec.srcType, documentSpec.srcProjectId, contentSourceDataById);
621
+ const document = contentSourceData.documentMap[documentSpec.srcDocumentId];
622
+ const csiDocument = contentSourceData.csiDocumentMap[documentSpec.srcDocumentId];
623
+ if (!document || !csiDocument) {
624
+ throw new Error(
625
+ `document not found for srcType: ${documentSpec.srcType}, srcProjectId: ${documentSpec.srcProjectId}, srcDocumentId: ${documentSpec.srcDocumentId}`
626
+ );
627
+ }
628
+
629
+ const model = contentSourceData.modelMap[document.srcModelName];
630
+ if (!model) {
631
+ throw new Error(`model '${document.srcModelName}' not found`);
632
+ }
633
+
634
+ const mappedCSIDocument = mapStoreDocumentToCSIDocumentWithSource({ document, csiDocument });
635
+
636
+ // the documentField should be localized because fieldPath includes locales
637
+ const { modelField, documentField } = getModelAndDocumentFieldForLocalizedFieldPath({
638
+ document,
639
+ model,
640
+ fieldPath: storeAction.fieldPath,
641
+ modelMap: contentSourceData.modelMap
642
+ }) as {
643
+ // list items cannot have actions, therefore we can safely reduce the type to non list item fields
644
+ modelField: StackbitTypes.Field;
645
+ documentField: ContentStoreTypes.DocumentField;
646
+ };
647
+
648
+ const csiDocumentField = mapStoreFieldToCSIField(documentField);
649
+ if (!csiDocumentField) {
650
+ throw new Error(`object not found at field path: ${storeAction.fieldPath}`);
651
+ }
652
+
653
+ return {
654
+ parentDocument: mappedCSIDocument,
655
+ parentModel: {
656
+ ...model,
657
+ srcType: documentSpec.srcType,
658
+ srcProjectId: documentSpec.srcProjectId
659
+ },
660
+ documentField: csiDocumentField,
661
+ modelField: modelField,
662
+ fieldPath: storeAction.fieldPath
663
+ };
664
+ }
665
+
666
+ function getCSIDocumentAndModelWithSourceFromDocumentSpec({
667
+ documentSpec,
668
+ contentSourceDataById
669
+ }: {
670
+ documentSpec: APICustomActionDocumentSpecifier;
671
+ contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
672
+ }): {
673
+ document: StackbitTypes.DocumentWithSource;
674
+ model: StackbitTypes.ModelWithSource;
675
+ } {
676
+ const contentSourceData = getContentSourceDataByTypeAndProjectIdOrThrow(documentSpec.srcType, documentSpec.srcProjectId, contentSourceDataById);
677
+ const document = contentSourceData.documentMap[documentSpec.srcDocumentId];
678
+ const csiDocument = contentSourceData.csiDocumentMap[documentSpec.srcDocumentId];
679
+ if (!document || !csiDocument) {
680
+ throw new Error(
681
+ `document not found, srcType: ${documentSpec.srcType}, srcProjectId: ${documentSpec.srcProjectId}, srcDocumentId: ${documentSpec.srcDocumentId}`
682
+ );
683
+ }
684
+ const mappedDocument = mapStoreDocumentToCSIDocumentWithSource({
685
+ document,
686
+ csiDocument
687
+ });
688
+ const model = contentSourceData.modelMap[mappedDocument.modelName];
689
+ if (!model) {
690
+ throw new Error(`model '${mappedDocument.modelName}' not found`);
691
+ }
692
+ return {
693
+ document: mappedDocument,
694
+ model: {
695
+ ...model,
696
+ srcType: documentSpec.srcType,
697
+ srcProjectId: documentSpec.srcProjectId
698
+ }
699
+ };
700
+ }
701
+
702
+ function storeActionTypeToAPIActionType(storeActionType: ContentStoreCustomAction['type']): APICustomAction['type'] {
703
+ if (storeActionType === 'objectModel' || storeActionType === 'objectField') {
704
+ return 'object';
705
+ }
706
+ return storeActionType;
707
+ }