@stackbit/cms-core 1.0.13-develop.1 → 1.0.13-develop.2

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 (101) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/annotator/html.js +1 -0
  3. package/dist/annotator/html.js.map +1 -1
  4. package/dist/annotator/index.js +2 -1
  5. package/dist/annotator/index.js.map +1 -1
  6. package/dist/annotator/react.js +1 -0
  7. package/dist/annotator/react.js.map +1 -1
  8. package/dist/connector/ConnectorWrapper.d.mts +62 -0
  9. package/dist/connector/ConnectorWrapper.d.mts.map +1 -0
  10. package/dist/connector/ConnectorWrapper.mjs +163 -0
  11. package/dist/connector/ConnectorWrapper.mjs.map +1 -0
  12. package/dist/connector/ModelConverter.d.mts +3 -0
  13. package/dist/connector/ModelConverter.d.mts.map +1 -0
  14. package/dist/connector/ModelConverter.mjs +125 -0
  15. package/dist/connector/ModelConverter.mjs.map +1 -0
  16. package/dist/connector/connector-wrapper.d.mts +102 -0
  17. package/dist/connector/connector-wrapper.d.mts.map +1 -0
  18. package/dist/connector/connector-wrapper.mjs +152 -0
  19. package/dist/connector/connector-wrapper.mjs.map +1 -0
  20. package/dist/connector/model-converter.d.mts +3 -0
  21. package/dist/connector/model-converter.d.mts.map +1 -0
  22. package/dist/connector/model-converter.mjs +138 -0
  23. package/dist/connector/model-converter.mjs.map +1 -0
  24. package/dist/consts.d.ts +8 -5
  25. package/dist/consts.d.ts.map +1 -1
  26. package/dist/consts.js +14 -9
  27. package/dist/consts.js.map +1 -1
  28. package/dist/content-store-utils.js +10 -13
  29. package/dist/content-store-utils.js.map +1 -1
  30. package/dist/content-store.d.ts +1 -1
  31. package/dist/content-store.d.ts.map +1 -1
  32. package/dist/content-store.js +163 -197
  33. package/dist/content-store.js.map +1 -1
  34. package/dist/integration/IntegrationContentSourceWrapper.d.mts +62 -0
  35. package/dist/integration/IntegrationContentSourceWrapper.d.mts.map +1 -0
  36. package/dist/integration/IntegrationContentSourceWrapper.d.ts +2 -0
  37. package/dist/integration/IntegrationContentSourceWrapper.d.ts.map +1 -0
  38. package/dist/integration/IntegrationContentSourceWrapper.js +3 -0
  39. package/dist/integration/IntegrationContentSourceWrapper.js.map +1 -0
  40. package/dist/integration/IntegrationContentSourceWrapper.mjs +161 -0
  41. package/dist/integration/IntegrationContentSourceWrapper.mjs.map +1 -0
  42. package/dist/integration/IntegrationWrapper.d.mts +62 -0
  43. package/dist/integration/IntegrationWrapper.d.mts.map +1 -0
  44. package/dist/integration/IntegrationWrapper.mjs +164 -0
  45. package/dist/integration/IntegrationWrapper.mjs.map +1 -0
  46. package/dist/integration/ModelConverter.d.mts +3 -0
  47. package/dist/integration/ModelConverter.d.mts.map +1 -0
  48. package/dist/integration/ModelConverter.mjs +125 -0
  49. package/dist/integration/ModelConverter.mjs.map +1 -0
  50. package/dist/services/git.js +2 -2
  51. package/dist/services/git.js.map +1 -1
  52. package/dist/services/run.d.ts +1 -0
  53. package/dist/services/run.d.ts.map +1 -1
  54. package/dist/services/run.js +6 -6
  55. package/dist/services/run.js.map +1 -1
  56. package/dist/utils/asset-sources-utils.js +14 -17
  57. package/dist/utils/asset-sources-utils.js.map +1 -1
  58. package/dist/utils/backward-compatibility.js +2 -4
  59. package/dist/utils/backward-compatibility.js.map +1 -1
  60. package/dist/utils/config-delegate.js +5 -7
  61. package/dist/utils/config-delegate.js.map +1 -1
  62. package/dist/utils/create-update-csi-docs.js +13 -13
  63. package/dist/utils/create-update-csi-docs.js.map +1 -1
  64. package/dist/utils/csi-to-store-docs-converter.js +49 -61
  65. package/dist/utils/csi-to-store-docs-converter.js.map +1 -1
  66. package/dist/utils/custom-actions.js +26 -31
  67. package/dist/utils/custom-actions.js.map +1 -1
  68. package/dist/utils/document-hooks.js +12 -24
  69. package/dist/utils/document-hooks.js.map +1 -1
  70. package/dist/utils/duplicate-document.js +6 -7
  71. package/dist/utils/duplicate-document.js.map +1 -1
  72. package/dist/utils/file-cache.js +1 -1
  73. package/dist/utils/file-cache.js.map +1 -1
  74. package/dist/utils/filtered-entities.js +1 -1
  75. package/dist/utils/filtered-entities.js.map +1 -1
  76. package/dist/utils/index.js +1 -0
  77. package/dist/utils/index.js.map +1 -1
  78. package/dist/utils/meta-fields-filters.js +5 -9
  79. package/dist/utils/meta-fields-filters.js.map +1 -1
  80. package/dist/utils/model-utils.js +4 -7
  81. package/dist/utils/model-utils.js.map +1 -1
  82. package/dist/utils/preset-utils.js +18 -19
  83. package/dist/utils/preset-utils.js.map +1 -1
  84. package/dist/utils/search-utils.js +40 -51
  85. package/dist/utils/search-utils.js.map +1 -1
  86. package/dist/utils/site-map.js +9 -14
  87. package/dist/utils/site-map.js.map +1 -1
  88. package/dist/utils/store-to-api-docs-converter.js +14 -17
  89. package/dist/utils/store-to-api-docs-converter.js.map +1 -1
  90. package/dist/utils/store-to-api-v2-docs-converter.js +15 -19
  91. package/dist/utils/store-to-api-v2-docs-converter.js.map +1 -1
  92. package/dist/utils/timer.js +2 -3
  93. package/dist/utils/timer.js.map +1 -1
  94. package/dist/utils/tree-views.js +5 -7
  95. package/dist/utils/tree-views.js.map +1 -1
  96. package/package.json +8 -7
  97. package/src/connector/connector-wrapper.mts +219 -0
  98. package/src/connector/model-converter.mts +162 -0
  99. package/src/consts.js +31 -0
  100. package/src/content-store.ts +51 -33
  101. package/src/consts.ts +0 -21
@@ -0,0 +1,162 @@
1
+ import * as CSITypes from '@stackbit/types';
2
+
3
+ export function convertModels(models: Map<string, any>) {
4
+ const createModels: (CSITypes.DataModel | CSITypes.ObjectModel)[] = [];
5
+ models.forEach((model: any) => {
6
+ if (model.getTypeName() === `Image`) {
7
+ return;
8
+ }
9
+ const createModel = convertModel(model, models);
10
+ if (createModel) {
11
+ createModels.push(createModel);
12
+ }
13
+ });
14
+ return createModels;
15
+ }
16
+
17
+ function convertModel(model: any, models: any) {
18
+ let fieldGroups = model.fieldGroups;
19
+
20
+ if (model.devOnlyFieldGroup) {
21
+ fieldGroups ||= [];
22
+ fieldGroups.push(model.devOnlyFieldGroup);
23
+ }
24
+
25
+ // Create doesn't have named union types
26
+ if (model.isUnionType) {
27
+ return false;
28
+ }
29
+ const name = model.originalName || model.typeName;
30
+ const csiModel = {
31
+ name: name,
32
+ label: model.label || model.name,
33
+ type: model.isObjectType ? (`object` as const) : (`data` as const),
34
+ fieldGroups,
35
+ fields: sdkFieldsToStackbitFields(model.fields, models)
36
+ };
37
+
38
+ return csiModel;
39
+ }
40
+
41
+ function sdkFieldsToStackbitFields(fields: any[], models: any) {
42
+ return fields.map((field): CSITypes.Field | CSITypes.FieldList => {
43
+ const definedTypename = field.type;
44
+ const fieldModel = models.get(definedTypename);
45
+ const typename = fieldModel?.originalName || definedTypename;
46
+
47
+ const isObject = fieldModel?.isObjectType;
48
+
49
+ const scalarTypename = isObject
50
+ ? `object`
51
+ : ({
52
+ // create only has a number field
53
+ float: 'number',
54
+ int: 'number'
55
+ } as Record<string, CSITypes.Field['type'] | undefined>)[typename.toLowerCase()] ||
56
+ // all other scalar types are supported as lowercase typenames
57
+ (typename.toLowerCase() as CSITypes.Field['type']);
58
+
59
+ const isImage = typename === `Image`;
60
+ const isReference = !isImage && fieldModel?.isNodeType;
61
+ const isUnion = fieldModel?.isUnionType;
62
+ const firstUnionType = isUnion && fieldModel?.compositeTypes && models.get(fieldModel.compositeTypes[0]);
63
+
64
+ const isObjectUnion = firstUnionType && firstUnionType.isObjectType;
65
+ const isReferenceUnion = firstUnionType && firstUnionType.isNodeType;
66
+
67
+ const type: CSITypes.FieldType = (() => {
68
+ if (/*isObject || */isObjectUnion) {
69
+ return `model`;
70
+ }
71
+ if (isReference || isReferenceUnion) {
72
+ return `reference`;
73
+ }
74
+ if (isUnion) {
75
+ // throw new ModelError(`Unhandled union type for model ${typename}. This is a bug in the Netlify SDK.`);
76
+ }
77
+
78
+ if (isImage) {
79
+ return `image`;
80
+ }
81
+ return scalarTypename;
82
+ })();
83
+
84
+ const stackbitField: Partial<CSITypes.Field> = {
85
+ type,
86
+ name: field.name
87
+ };
88
+
89
+ if (typename.toLowerCase() === `float`) {
90
+ (stackbitField as CSITypes.FieldNumberProps).subtype = `float`;
91
+ }
92
+
93
+ //TODO
94
+ if (isObject) {
95
+ (stackbitField as CSITypes.FieldObject).fields = fieldModel.sdkFieldsToStackbitFields();
96
+ }
97
+
98
+ // if (isObject) {
99
+ // (stackbitField as CSITypes.FieldModelProps).models = [typename];
100
+ // }
101
+
102
+ // if (isObjectUnion) {
103
+ // (stackbitField as CSITypes.FieldModelProps).models = fieldModel.compositeTypes.map((ct: any) => {
104
+ // const fullType = models.get(ct);
105
+ // return fullType!.getOriginalName();
106
+ // });
107
+ // }
108
+
109
+ // any field names starting with _ are internal mandatory fields.
110
+ // they shouldn't be set as required in the Create UI as content admins
111
+ // don't need to set these fields, connector authors do.
112
+ if (`required` in field && !field.name.startsWith(`_`)) {
113
+ stackbitField.required = field.required;
114
+ }
115
+
116
+ if (`label` in field) {
117
+ stackbitField.label = field.label;
118
+ }
119
+
120
+ if (`hidden` in field) {
121
+ stackbitField.hidden = field.hidden;
122
+ }
123
+
124
+ if (`readOnly` in field) {
125
+ stackbitField.readOnly = field.readOnly;
126
+ }
127
+
128
+ if (`localized` in field) {
129
+ stackbitField.localized = field.localized;
130
+ }
131
+
132
+ if (`defaultValue` in field) {
133
+ stackbitField.default = field.defaultValue;
134
+ }
135
+
136
+ if (`group` in field) {
137
+ stackbitField.group = field.group;
138
+ }
139
+
140
+ if (isUnion) {
141
+ (stackbitField as CSITypes.FieldReferenceProps).models = (fieldModel.compositeTypes || []).map((ct: any) => {
142
+ const fullType = models.get(ct);
143
+ return fullType!.getOriginalName();
144
+ });
145
+ } else if (isReference) {
146
+ (stackbitField as CSITypes.FieldReferenceProps).models = [fieldModel.getOriginalName()];
147
+ }
148
+
149
+ if (field.list) {
150
+ const { name, required, hidden, ...items } = stackbitField;
151
+ return {
152
+ name,
153
+ required,
154
+ hidden,
155
+ type: `list`,
156
+ items
157
+ } as CSITypes.FieldList;
158
+ } else {
159
+ return stackbitField as CSITypes.Field;
160
+ }
161
+ });
162
+ }
package/src/consts.js ADDED
@@ -0,0 +1,31 @@
1
+ const { RICH_TEXT_NODE_TYPES, RICH_TEXT_MARK_TYPES, RICH_TEXT_HINT_MAX_LENGTH } = require('@stackbit/types');
2
+
3
+ const SIMPLE_VALUE_FIELDS = ['string', 'text', 'markdown', 'number', 'url', 'slug', 'boolean', 'date', 'datetime', 'color'];
4
+
5
+ const LOADER_EXCLUDED_FILES = [
6
+ 'LICENSE.md',
7
+ 'README.md',
8
+ 'README.theme.md',
9
+ 'CONTRIBUTING.md',
10
+ 'CHANGELOG.md',
11
+ 'stackbit.yaml',
12
+ 'netlify.toml',
13
+ 'theme.toml',
14
+ 'package.json',
15
+ 'package-lock.json',
16
+ 'yarn-lock.json'
17
+ ];
18
+
19
+ const LOADER_EXCLUDED_DIRS = ['src/pages'];
20
+
21
+ const STACKBIT_PRESET_MODEL_NAME = 'stackbitPreset';
22
+
23
+ module.exports = {
24
+ RICH_TEXT_NODE_TYPES,
25
+ RICH_TEXT_MARK_TYPES,
26
+ RICH_TEXT_HINT_MAX_LENGTH,
27
+ SIMPLE_VALUE_FIELDS,
28
+ LOADER_EXCLUDED_FILES,
29
+ LOADER_EXCLUDED_DIRS,
30
+ STACKBIT_PRESET_MODEL_NAME
31
+ };
@@ -278,9 +278,6 @@ export class ContentStore {
278
278
 
279
279
  async destroy() {
280
280
  this.contentUpdatesWatchTimer.stopTimer();
281
- this.contentEngine?.clearListeners();
282
- await this.contentEngine?.stop();
283
- this.contentEngine = null;
284
281
  for (const contentSourceInstance of this.contentSources) {
285
282
  contentSourceInstance.stopWatchingContentUpdates?.();
286
283
  await contentSourceInstance.destroy?.();
@@ -414,34 +411,31 @@ export class ContentStore {
414
411
  */
415
412
  private async createContentEngineIfNeeded() {
416
413
  const logger = this.userLogger.createLogger({ label: 'content-engine' });
414
+
417
415
  if (this.contentEngine) {
418
- logger.info('stopping content engine');
419
- this.contentEngine.clearListeners();
420
- await this.contentEngine.stop();
421
- this.contentEngine = null;
416
+ logger.debug(
417
+ `Content Engine already exists, cannot recreate it as it's running in this process. To recreate it with updated config options the current process must be restarted.`
418
+ );
419
+ return;
422
420
  }
421
+
422
+ const contentSources = this.contentSources || [];
423
423
  const plugins = Array.from(
424
- new Set(this.contentSources?.flatMap((contentSource) => contentSource.getContentEngineConfig?.()?.plugins).filter(Boolean))
424
+ new Set(contentSources.flatMap((contentSource) => contentSource.getContentEngineConfig?.()?.plugins).filter(Boolean))
425
425
  ) as PluginRef[];
426
426
  if (plugins.length < 1) {
427
427
  return;
428
428
  }
429
- logger.info('creating content engine');
429
+ logger.info('Creating content engine');
430
430
  this.contentEngine = contentEngine({
431
431
  directory: this.stackbitConfig?.dirPath,
432
432
  port: this.stackbitConfig?.contentEngine?.port,
433
433
  host: this.stackbitConfig?.contentEngine?.host,
434
434
  engineConfig: {
435
435
  plugins
436
- }
437
- });
438
-
439
- this.contentEngine?.onStdOut((data) => {
440
- logger.info(data.toString());
441
- });
442
-
443
- this.contentEngine?.onStdErr((data) => {
444
- logger.error(data.toString());
436
+ },
437
+ // content engine must run in the same process so that unified connectors can share state in memory
438
+ runInSubProcess: false
445
439
  });
446
440
  }
447
441
 
@@ -491,12 +485,29 @@ export class ContentStore {
491
485
 
492
486
  if (init) {
493
487
  this.logger.debug('init content sources');
488
+ if (this.stackbitConfig?.connectors) {
489
+ const contentSources = this.contentSources || [];
490
+ if (this.stackbitConfig?.connectors?.length) {
491
+ const ConnectorWrapper = (await import('./connector/connector-wrapper.mjs')).default;
492
+ for (const connector of this.stackbitConfig.connectors) {
493
+ contentSources.push(new ConnectorWrapper(connector));
494
+ }
495
+ }
496
+ this.contentSources = contentSources;
497
+ }
494
498
  await this.createContentEngineIfNeeded();
495
- await this.contentEngine?.sync({
496
- runServer: true
497
- });
498
499
  }
499
500
 
501
+ const contentEngineSyncPromise = this.contentEngine
502
+ ? // store the content engine sync promise instead of awaiting here
503
+ // unified connectors need the internal CSI + source plugin to run simultaneously.
504
+ // waiting for this promise here means internal unified connector APIs will be stuck forever
505
+ // it's awaited further below
506
+ this.contentEngine?.sync({
507
+ runServer: true
508
+ })
509
+ : Promise.resolve();
510
+
500
511
  const promises = contentSources.map((contentSourceInstance): Promise<ContentSourceRawData> => {
501
512
  const contentSourceId = getContentSourceIdForContentSource(contentSourceInstance);
502
513
  if (init || !contentSourceIds || contentSourceIds.includes(contentSourceId)) {
@@ -561,6 +572,8 @@ export class ContentStore {
561
572
  const processingPromise = this.processingContentSourcesPromise;
562
573
  this.processingContentSourcesPromise = null;
563
574
 
575
+ await contentEngineSyncPromise;
576
+
564
577
  // Do not "await" on processContentStoreEvents as it may introduce a deadlock with
565
578
  // the nested loadContentSourcesAndProcessData call which is wrapped by deferWhileRunning.
566
579
  this.processContentStoreEvents()
@@ -819,14 +832,6 @@ export class ContentStore {
819
832
  }
820
833
  this.logger.debug('content source called updateContent', { contentSourceId });
821
834
 
822
- if (contentEngineConfig && this.contentEngine) {
823
- this.logger.debug(`syncing ${contentEngineConfig.connector}`);
824
- await this.contentEngine.sync({
825
- buildSchema: false,
826
- connector: contentEngineConfig.connector
827
- });
828
- }
829
-
830
835
  this.pushContentSourceEvent({
831
836
  eventName: ContentStoreEventType.ContentSourceContentChange,
832
837
  contentSourceId: contentSourceId,
@@ -3179,10 +3184,23 @@ export class ContentStore {
3179
3184
  return getContentSourceDataByIdOrThrow(contentSourceId, this.contentSourceDataById);
3180
3185
  }
3181
3186
 
3182
- onWebhook({ srcType, srcProjectId, data, headers }: { srcType: string; srcProjectId: string; data: unknown; headers: Record<string, string> }) {
3183
- const contentSourceId = getContentSourceId(srcType, srcProjectId);
3184
- const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
3185
- return contentSourceData.instance.onWebhook?.({ data, headers });
3187
+ async onWebhook({ srcType, srcProjectId, data, headers }: { srcType: string; srcProjectId: string; data: unknown; headers: Record<string, string> }) {
3188
+ if (this.contentEngine) {
3189
+ // if there's a content-engine always pass the webhook body to it
3190
+ await this.contentEngine.sync({
3191
+ webhookBody: data as any,
3192
+ // selective connector syncing can happen via a header
3193
+ connector: headers?.connector
3194
+ });
3195
+ }
3196
+
3197
+ // unified connectors handle their own webhook syncing via the .sync call above.
3198
+ // passing "connector" as the source type in the webhook url opts out of traditional CSI onWebhook calls
3199
+ if (srcType !== `connector`) {
3200
+ const contentSourceId = getContentSourceId(srcType, srcProjectId);
3201
+ const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
3202
+ return contentSourceData.instance.onWebhook?.({ data, headers });
3203
+ }
3186
3204
  }
3187
3205
 
3188
3206
  getWebhookUrl(contentSourceType: string, projectId: string) {
package/src/consts.ts DELETED
@@ -1,21 +0,0 @@
1
- export { RICH_TEXT_NODE_TYPES, RICH_TEXT_MARK_TYPES, RICH_TEXT_HINT_MAX_LENGTH } from '@stackbit/types';
2
-
3
- export const SIMPLE_VALUE_FIELDS = ['string', 'text', 'markdown', 'number', 'url', 'slug', 'boolean', 'date', 'datetime', 'color'];
4
-
5
- export const LOADER_EXCLUDED_FILES = [
6
- 'LICENSE.md',
7
- 'README.md',
8
- 'README.theme.md',
9
- 'CONTRIBUTING.md',
10
- 'CHANGELOG.md',
11
- 'stackbit.yaml',
12
- 'netlify.toml',
13
- 'theme.toml',
14
- 'package.json',
15
- 'package-lock.json',
16
- 'yarn-lock.json'
17
- ];
18
-
19
- export const LOADER_EXCLUDED_DIRS = ['src/pages'];
20
-
21
- export const STACKBIT_PRESET_MODEL_NAME = 'stackbitPreset';