@stackbit/cms-core 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/content-store-utils.d.ts +6 -0
- package/dist/content-store-utils.d.ts.map +1 -1
- package/dist/content-store-utils.js +20 -1
- package/dist/content-store-utils.js.map +1 -1
- package/dist/content-store.d.ts +25 -19
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +249 -167
- package/dist/content-store.js.map +1 -1
- package/dist/stackbit/index.d.ts.map +1 -1
- package/dist/stackbit/index.js +1 -1
- package/dist/stackbit/index.js.map +1 -1
- package/dist/utils/csi-to-store-docs-converter.d.ts.map +1 -1
- package/dist/utils/csi-to-store-docs-converter.js.map +1 -1
- package/dist/utils/model-utils.d.ts +11 -0
- package/dist/utils/model-utils.d.ts.map +1 -0
- package/dist/utils/model-utils.js +64 -0
- package/dist/utils/model-utils.js.map +1 -0
- package/dist/utils/search-utils.d.ts +2 -2
- package/dist/utils/search-utils.d.ts.map +1 -1
- package/dist/utils/search-utils.js +1 -1
- package/dist/utils/search-utils.js.map +1 -1
- package/package.json +4 -4
- package/src/content-store-utils.ts +19 -0
- package/src/content-store.ts +327 -182
- package/src/stackbit/index.ts +2 -2
- package/src/utils/csi-to-store-docs-converter.ts +2 -2
- package/src/utils/model-utils.ts +72 -0
- package/src/utils/search-utils.ts +3 -3
package/dist/content-store.js
CHANGED
|
@@ -16,11 +16,16 @@ const csi_to_store_docs_converter_1 = require("./utils/csi-to-store-docs-convert
|
|
|
16
16
|
const content_store_utils_1 = require("./content-store-utils");
|
|
17
17
|
const store_to_api_docs_converter_1 = require("./utils/store-to-api-docs-converter");
|
|
18
18
|
const create_update_csi_docs_1 = require("./utils/create-update-csi-docs");
|
|
19
|
+
const model_utils_1 = require("./utils/model-utils");
|
|
20
|
+
const common_schema_1 = require("./common/common-schema");
|
|
19
21
|
class ContentStore {
|
|
20
22
|
constructor(options) {
|
|
21
23
|
this.contentSources = [];
|
|
22
24
|
this.contentSourceDataById = {};
|
|
23
|
-
this.
|
|
25
|
+
this.stackbitConfig = null;
|
|
26
|
+
this.yamlModels = [];
|
|
27
|
+
this.configModels = [];
|
|
28
|
+
this.presets = {};
|
|
24
29
|
this.logger = options.logger.createLogger({ label: 'content-store' });
|
|
25
30
|
this.userLogger = options.userLogger.createLogger({ label: 'content-store' });
|
|
26
31
|
this.localDev = options.localDev;
|
|
@@ -63,6 +68,10 @@ class ContentStore {
|
|
|
63
68
|
}
|
|
64
69
|
async init({ stackbitConfig }) {
|
|
65
70
|
this.logger.debug('init');
|
|
71
|
+
if (stackbitConfig) {
|
|
72
|
+
this.yamlModels = await this.loadYamlModels({ stackbitConfig });
|
|
73
|
+
this.presets = await this.loadPresets({ stackbitConfig });
|
|
74
|
+
}
|
|
66
75
|
await this.setStackbitConfig({ stackbitConfig, init: true });
|
|
67
76
|
}
|
|
68
77
|
async onStackbitConfigChange({ stackbitConfig }) {
|
|
@@ -70,38 +79,17 @@ class ContentStore {
|
|
|
70
79
|
await this.setStackbitConfig({ stackbitConfig, init: true });
|
|
71
80
|
}
|
|
72
81
|
async setStackbitConfig({ stackbitConfig, init }) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// 2. load content-source models
|
|
77
|
-
// 3. merge content-source models with config.models or via config.mapModels, sanitize and validate
|
|
78
|
-
// 4. load presets, adjust presets to have srcType and srcProjectId
|
|
79
|
-
if (!stackbitConfig) {
|
|
80
|
-
this.rawStackbitConfig = null;
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
const rawConfigResult = await (0, sdk_1.loadConfigFromDir)({
|
|
84
|
-
dirPath: stackbitConfig.dirPath,
|
|
85
|
-
logger: this.logger
|
|
86
|
-
});
|
|
87
|
-
for (const error of rawConfigResult.errors) {
|
|
88
|
-
this.userLogger.warn(error.message);
|
|
89
|
-
}
|
|
90
|
-
if (rawConfigResult.config) {
|
|
91
|
-
this.rawStackbitConfig = {
|
|
92
|
-
...rawConfigResult.config,
|
|
93
|
-
contentSources: stackbitConfig.contentSources
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
this.rawStackbitConfig = null;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
await this.loadContentSources({ init });
|
|
82
|
+
this.stackbitConfig = stackbitConfig;
|
|
83
|
+
this.configModels = this.mergeConfigModels(this.stackbitConfig, this.yamlModels);
|
|
84
|
+
await this.loadAllContentSourcesAndProcessData({ init });
|
|
101
85
|
}
|
|
102
86
|
/**
|
|
103
87
|
* This method is called when contentUpdatesWatchTimer receives timeout.
|
|
104
|
-
*
|
|
88
|
+
* This happens when the user is not using the Stackbit app for some time
|
|
89
|
+
* but container is not hibernated.
|
|
90
|
+
* It then notifies all content sources to stop watching for content
|
|
91
|
+
* changes, which in turn stops polling CMS for content changes and helps
|
|
92
|
+
* reducing the CMS API usage.
|
|
105
93
|
*/
|
|
106
94
|
handleTimerTimeout() {
|
|
107
95
|
for (const contentSourceInstance of this.contentSources) {
|
|
@@ -119,78 +107,157 @@ class ContentStore {
|
|
|
119
107
|
return;
|
|
120
108
|
}
|
|
121
109
|
this.logger.debug('keepAlive => contentUpdatesWatchTimer is not running => load content source data');
|
|
122
|
-
await this.
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* This method is called when a content source notifies Stackbit of models
|
|
126
|
-
* changes via webhook. When this happens, all content source data
|
|
127
|
-
*
|
|
128
|
-
* For example, Contentful notifies Stackbit of any content-type changes via
|
|
129
|
-
* special webhook.
|
|
130
|
-
*
|
|
131
|
-
* @param contentSourceId
|
|
132
|
-
*/
|
|
133
|
-
async onContentSourceSchemaChange({ contentSourceId }) {
|
|
134
|
-
this.logger.debug('onContentSourceSchemaChange', { contentSourceId });
|
|
135
|
-
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
136
|
-
this.contentSourceDataById[contentSourceId] = await this.loadContentSourceData({
|
|
137
|
-
contentSourceInstance: contentSourceData.instance,
|
|
138
|
-
init: false
|
|
139
|
-
});
|
|
140
|
-
this.onSchemaChangeCallback();
|
|
110
|
+
await this.loadAllContentSourcesAndProcessData({ init: false });
|
|
141
111
|
}
|
|
142
112
|
async onFilesChange(updatedFiles) {
|
|
143
113
|
var _a, _b;
|
|
144
114
|
this.logger.debug('onFilesChange');
|
|
145
|
-
let
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
115
|
+
let schemaChanged = false;
|
|
116
|
+
if (this.stackbitConfig) {
|
|
117
|
+
// Check if any of the yaml models files were changed. If yaml model files were changed,
|
|
118
|
+
// reload them and merge them with models defined in stackbit config.
|
|
119
|
+
const modelDirs = (0, sdk_1.getYamlModelDirs)(this.stackbitConfig);
|
|
120
|
+
const yamlModelsChanged = updatedFiles.find((updatedFile) => lodash_1.default.some(modelDirs, (modelDir) => updatedFile.startsWith(modelDir)));
|
|
121
|
+
if (yamlModelsChanged) {
|
|
122
|
+
this.logger.debug('identified change in stackbit model files');
|
|
123
|
+
schemaChanged = true;
|
|
124
|
+
this.yamlModels = await this.loadYamlModels({ stackbitConfig: this.stackbitConfig });
|
|
125
|
+
this.configModels = this.mergeConfigModels(this.stackbitConfig, this.yamlModels);
|
|
126
|
+
}
|
|
127
|
+
// Check if any of the preset files were changed. If presets were changed, reload them.
|
|
128
|
+
const presetDirs = (0, sdk_1.getPresetDirs)(this.stackbitConfig);
|
|
129
|
+
const presetsChanged = updatedFiles.find((updatedFile) => lodash_1.default.some(presetDirs, (presetDir) => updatedFile.startsWith(presetDir)));
|
|
130
|
+
if (presetsChanged) {
|
|
131
|
+
this.logger.debug('identified change in stackbit preset files');
|
|
132
|
+
schemaChanged = true;
|
|
133
|
+
this.presets = await this.loadPresets({ stackbitConfig: this.stackbitConfig });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const contentSourceIdsWithChangedSchema = [];
|
|
137
|
+
const contentChangeEvents = [];
|
|
152
138
|
for (const contentSourceInstance of this.contentSources) {
|
|
153
139
|
const contentSourceId = (0, content_store_utils_1.getContentSourceIdForContentSource)(contentSourceInstance);
|
|
154
140
|
this.logger.debug(`call onFilesChange for contentSource: ${contentSourceId}`);
|
|
155
|
-
const
|
|
156
|
-
this.logger.debug(`schemaChanged: ${schemaChanged}, has contentChangeEvent: ${!!contentChangeEvent}`);
|
|
141
|
+
const onFilesChangeResult = (_b = (await ((_a = contentSourceInstance.onFilesChange) === null || _a === void 0 ? void 0 : _a.call(contentSourceInstance, { updatedFiles: updatedFiles })))) !== null && _b !== void 0 ? _b : {};
|
|
142
|
+
this.logger.debug(`schemaChanged: ${onFilesChangeResult.schemaChanged}, has contentChangeEvent: ${!!onFilesChangeResult.contentChangeEvent}`);
|
|
157
143
|
// if schema is changed, there is no need to return contentChanges
|
|
158
144
|
// because schema changes reloads everything and implies content changes
|
|
159
|
-
if (schemaChanged) {
|
|
160
|
-
|
|
161
|
-
|
|
145
|
+
if (onFilesChangeResult.schemaChanged) {
|
|
146
|
+
schemaChanged = true;
|
|
147
|
+
contentSourceIdsWithChangedSchema.push(contentSourceId);
|
|
148
|
+
}
|
|
149
|
+
else if (onFilesChangeResult.contentChangeEvent) {
|
|
150
|
+
contentChangeEvents.push({ contentSourceId, contentChangeEvent: onFilesChangeResult.contentChangeEvent });
|
|
162
151
|
}
|
|
163
|
-
|
|
152
|
+
}
|
|
153
|
+
const contentChanges = {
|
|
154
|
+
updatedDocuments: [],
|
|
155
|
+
updatedAssets: [],
|
|
156
|
+
deletedDocuments: [],
|
|
157
|
+
deletedAssets: []
|
|
158
|
+
};
|
|
159
|
+
// If the schema was changed, there is no need to accumulate or notify about content changes.
|
|
160
|
+
// The processData will update the store with the latest data. And once the Studio receives
|
|
161
|
+
// the schemaChanged notification it will reload all the models and the documents with their latest state.
|
|
162
|
+
if (schemaChanged) {
|
|
163
|
+
await this.reloadContentSourcesByIdAndProcessData({ contentSourceIds: contentSourceIdsWithChangedSchema });
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
contentChangeEvents.reduce((contentChanges, { contentSourceId, contentChangeEvent }) => {
|
|
164
167
|
const contentChangeResult = this.onContentChange(contentSourceId, contentChangeEvent);
|
|
165
168
|
contentChanges.updatedDocuments = contentChanges.updatedDocuments.concat(contentChangeResult.updatedDocuments);
|
|
166
169
|
contentChanges.updatedAssets = contentChanges.updatedAssets.concat(contentChangeResult.updatedAssets);
|
|
167
170
|
contentChanges.deletedDocuments = contentChanges.deletedDocuments.concat(contentChangeResult.deletedDocuments);
|
|
168
171
|
contentChanges.deletedAssets = contentChanges.deletedAssets.concat(contentChangeResult.deletedAssets);
|
|
169
|
-
|
|
172
|
+
return contentChanges;
|
|
173
|
+
}, contentChanges);
|
|
170
174
|
}
|
|
175
|
+
// TODO: maybe instead of returning object with results
|
|
176
|
+
// replace with this.onSchemaChangeCallback() and this.onContentChangeCallback(contentChanges) for consistency of data flow?
|
|
171
177
|
return {
|
|
172
|
-
schemaChanged:
|
|
178
|
+
schemaChanged: schemaChanged,
|
|
173
179
|
contentChanges: contentChanges
|
|
174
180
|
};
|
|
175
181
|
}
|
|
176
|
-
async
|
|
182
|
+
async loadYamlModels({ stackbitConfig }) {
|
|
183
|
+
const yamlModelsResult = await (0, sdk_1.loadYamlModelsFromFiles)(stackbitConfig);
|
|
184
|
+
for (const error of yamlModelsResult.errors) {
|
|
185
|
+
this.userLogger.warn(error.message);
|
|
186
|
+
}
|
|
187
|
+
return yamlModelsResult.models;
|
|
188
|
+
}
|
|
189
|
+
mergeConfigModels(stackbitConfig, modelsFromFiles) {
|
|
190
|
+
var _a;
|
|
191
|
+
const configModelsResult = (0, sdk_1.mergeConfigModelsWithModelsFromFiles)((_a = stackbitConfig === null || stackbitConfig === void 0 ? void 0 : stackbitConfig.models) !== null && _a !== void 0 ? _a : [], modelsFromFiles);
|
|
192
|
+
for (const error of configModelsResult.errors) {
|
|
193
|
+
this.userLogger.warn(error.message);
|
|
194
|
+
}
|
|
195
|
+
return configModelsResult.models;
|
|
196
|
+
}
|
|
197
|
+
async loadPresets({ stackbitConfig }) {
|
|
198
|
+
var _a;
|
|
199
|
+
const contentSources = (_a = stackbitConfig === null || stackbitConfig === void 0 ? void 0 : stackbitConfig.contentSources) !== null && _a !== void 0 ? _a : [];
|
|
200
|
+
const singleContentSource = contentSources.length === 1 ? contentSources[0] : null;
|
|
201
|
+
const presetResult = await (0, sdk_1.loadPresets)({
|
|
202
|
+
config: stackbitConfig,
|
|
203
|
+
...(singleContentSource
|
|
204
|
+
? {
|
|
205
|
+
fallbackSrcType: singleContentSource.getContentSourceType(),
|
|
206
|
+
fallbackSrcProjectId: singleContentSource.getProjectId()
|
|
207
|
+
}
|
|
208
|
+
: null)
|
|
209
|
+
});
|
|
210
|
+
for (const error of presetResult.errors) {
|
|
211
|
+
this.userLogger.warn(error.message);
|
|
212
|
+
}
|
|
213
|
+
const { presets } = await this.handleConfigAssets({ presets: presetResult.presets });
|
|
214
|
+
return presets;
|
|
215
|
+
}
|
|
216
|
+
async loadAllContentSourcesAndProcessData({ init }) {
|
|
177
217
|
var _a, _b;
|
|
178
|
-
this.logger.debug('
|
|
218
|
+
this.logger.debug('loadAllContentSourcesAndProcessData', { init });
|
|
179
219
|
this.contentUpdatesWatchTimer.stopTimer();
|
|
180
|
-
|
|
181
|
-
const contentSources = ((_b = (_a = this.rawStackbitConfig) === null || _a === void 0 ? void 0 : _a.contentSources) !== null && _b !== void 0 ? _b : []);
|
|
220
|
+
const contentSources = (_b = (_a = this.stackbitConfig) === null || _a === void 0 ? void 0 : _a.contentSources) !== null && _b !== void 0 ? _b : [];
|
|
182
221
|
const promises = contentSources.map((contentSourceInstance) => {
|
|
183
222
|
return this.loadContentSourceData({ contentSourceInstance, init });
|
|
184
223
|
});
|
|
185
|
-
const
|
|
186
|
-
const contentSourceDataById = lodash_1.default.keyBy(contentSourceDataArr, 'id');
|
|
224
|
+
const contentSourceRawDataArr = await Promise.all(promises);
|
|
187
225
|
// update all content sources at once to prevent race conditions
|
|
226
|
+
this.contentSourceDataById = await this.processData({
|
|
227
|
+
stackbitConfig: this.stackbitConfig,
|
|
228
|
+
configModels: this.configModels,
|
|
229
|
+
presets: this.presets,
|
|
230
|
+
contentSourceRawDataArr: contentSourceRawDataArr
|
|
231
|
+
});
|
|
188
232
|
this.contentSources = contentSources;
|
|
189
|
-
this.
|
|
233
|
+
this.contentUpdatesWatchTimer.startTimer();
|
|
234
|
+
}
|
|
235
|
+
async reloadContentSourcesByIdAndProcessData({ contentSourceIds }) {
|
|
236
|
+
this.logger.debug('reloadContentSourcesByIdAndProcessData', { contentSourceIds });
|
|
237
|
+
this.contentUpdatesWatchTimer.stopTimer();
|
|
238
|
+
const promises = this.contentSources.map((contentSourceInstance) => {
|
|
239
|
+
const contentSourceId = (0, content_store_utils_1.getContentSourceIdForContentSource)(contentSourceInstance);
|
|
240
|
+
if (contentSourceIds.includes(contentSourceId)) {
|
|
241
|
+
return this.loadContentSourceData({
|
|
242
|
+
contentSourceInstance: contentSourceInstance,
|
|
243
|
+
init: false
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
return Promise.resolve(lodash_1.default.omit(this.contentSourceDataById[contentSourceId], ['models', 'modelMap', 'documents', 'documentMap']));
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
const contentSourceRawDataArr = await Promise.all(promises);
|
|
251
|
+
this.contentSourceDataById = await this.processData({
|
|
252
|
+
stackbitConfig: this.stackbitConfig,
|
|
253
|
+
configModels: this.configModels,
|
|
254
|
+
presets: this.presets,
|
|
255
|
+
contentSourceRawDataArr: contentSourceRawDataArr
|
|
256
|
+
});
|
|
190
257
|
this.contentUpdatesWatchTimer.startTimer();
|
|
191
258
|
}
|
|
192
259
|
async loadContentSourceData({ contentSourceInstance, init }) {
|
|
193
|
-
var _a
|
|
260
|
+
var _a;
|
|
194
261
|
const contentSourceId = (0, content_store_utils_1.getContentSourceIdForContentSource)(contentSourceInstance);
|
|
195
262
|
this.logger.debug('loadContentSourceData', { contentSourceId, init });
|
|
196
263
|
if (init) {
|
|
@@ -207,87 +274,27 @@ class ContentStore {
|
|
|
207
274
|
contentSourceInstance.stopWatchingContentUpdates();
|
|
208
275
|
await contentSourceInstance.reset();
|
|
209
276
|
}
|
|
210
|
-
// TODO: introduce optimization: don't fetch content source models,
|
|
211
|
-
// documents, assets if only stackbitConfig was changed
|
|
212
277
|
const csiModels = await contentSourceInstance.getModels();
|
|
213
|
-
const locales = await (contentSourceInstance === null || contentSourceInstance === void 0 ? void 0 : contentSourceInstance.getLocales());
|
|
214
|
-
const defaultLocaleCode = (_a = locales === null || locales === void 0 ? void 0 : locales.find((locale) => locale.default)) === null || _a === void 0 ? void 0 : _a.code;
|
|
215
|
-
// for older versions of stackbit, it uses models to extend content source models
|
|
216
|
-
let modelsNoImage = [];
|
|
217
|
-
let imageModel;
|
|
218
|
-
if (this.rawStackbitConfig) {
|
|
219
|
-
const result = await (0, sdk_1.extendConfig)({
|
|
220
|
-
config: this.rawStackbitConfig,
|
|
221
|
-
externalModels: csiModels
|
|
222
|
-
});
|
|
223
|
-
for (const error of (_b = result === null || result === void 0 ? void 0 : result.errors) !== null && _b !== void 0 ? _b : []) {
|
|
224
|
-
this.userLogger.warn(error.message);
|
|
225
|
-
}
|
|
226
|
-
const config = await this.handleConfigAssets(result.config);
|
|
227
|
-
const modelsWithImageModel = (_c = config === null || config === void 0 ? void 0 : config.models) !== null && _c !== void 0 ? _c : [];
|
|
228
|
-
const imageModelIndex = modelsWithImageModel.findIndex((model) => (0, sdk_1.isImageModel)(model));
|
|
229
|
-
if (imageModelIndex > -1) {
|
|
230
|
-
imageModel = modelsWithImageModel[imageModelIndex];
|
|
231
|
-
modelsWithImageModel.splice(imageModelIndex, 1);
|
|
232
|
-
}
|
|
233
|
-
modelsNoImage = modelsWithImageModel;
|
|
234
|
-
// TODO: load presets externally from config, and create additional map
|
|
235
|
-
// that maps presetIds by model name instead of storing that map inside every model
|
|
236
|
-
// Augment presets with srcType and srcProjectId if they don't exist
|
|
237
|
-
this.presets = lodash_1.default.reduce(Object.keys((_d = config === null || config === void 0 ? void 0 : config.presets) !== null && _d !== void 0 ? _d : {}), (accum, presetId) => {
|
|
238
|
-
var _a, _b, _c;
|
|
239
|
-
const preset = (_a = config === null || config === void 0 ? void 0 : config.presets) === null || _a === void 0 ? void 0 : _a[presetId];
|
|
240
|
-
lodash_1.default.set(accum, [presetId], {
|
|
241
|
-
...preset,
|
|
242
|
-
srcType: (_b = preset === null || preset === void 0 ? void 0 : preset.srcType) !== null && _b !== void 0 ? _b : contentSourceInstance.getContentSourceType(),
|
|
243
|
-
srcProjectId: (_c = preset === null || preset === void 0 ? void 0 : preset.srcProjectId) !== null && _c !== void 0 ? _c : contentSourceInstance.getProjectId()
|
|
244
|
-
});
|
|
245
|
-
return accum;
|
|
246
|
-
}, {});
|
|
247
|
-
}
|
|
248
|
-
if ((_e = this.rawStackbitConfig) === null || _e === void 0 ? void 0 : _e.mapModels) {
|
|
249
|
-
const srcType = contentSourceInstance.getContentSourceType();
|
|
250
|
-
const srcProjectId = contentSourceInstance.getProjectId();
|
|
251
|
-
const modelsWithSource = modelsNoImage.map((model) => ({
|
|
252
|
-
srcType,
|
|
253
|
-
srcProjectId,
|
|
254
|
-
...model
|
|
255
|
-
}));
|
|
256
|
-
const mappedModels = this.rawStackbitConfig.mapModels({
|
|
257
|
-
models: modelsWithSource
|
|
258
|
-
});
|
|
259
|
-
modelsNoImage = mappedModels.map((model) => {
|
|
260
|
-
const { srcType, srcProjectId, ...rest } = model;
|
|
261
|
-
return rest;
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
const models = imageModel ? [...modelsNoImage, imageModel] : modelsNoImage;
|
|
265
|
-
const modelMap = lodash_1.default.keyBy(models, 'name');
|
|
266
278
|
const csiModelMap = lodash_1.default.keyBy(csiModels, 'name');
|
|
279
|
+
const locales = await contentSourceInstance.getLocales();
|
|
280
|
+
const defaultLocaleCode = (_a = locales === null || locales === void 0 ? void 0 : locales.find((locale) => locale.default)) === null || _a === void 0 ? void 0 : _a.code;
|
|
267
281
|
const csiDocuments = await contentSourceInstance.getDocuments({ modelMap: csiModelMap });
|
|
268
282
|
const csiAssets = await contentSourceInstance.getAssets();
|
|
269
283
|
const csiDocumentMap = lodash_1.default.keyBy(csiDocuments, 'id');
|
|
270
284
|
const csiAssetMap = lodash_1.default.keyBy(csiAssets, 'id');
|
|
271
|
-
const contentStoreDocuments = (0, csi_to_store_docs_converter_1.mapCSIDocumentsToStoreDocuments)({
|
|
272
|
-
csiDocuments,
|
|
273
|
-
contentSourceInstance,
|
|
274
|
-
modelMap,
|
|
275
|
-
defaultLocaleCode
|
|
276
|
-
});
|
|
277
285
|
const contentStoreAssets = (0, csi_to_store_docs_converter_1.mapCSIAssetsToStoreAssets)({
|
|
278
286
|
csiAssets,
|
|
279
287
|
contentSourceInstance,
|
|
280
288
|
defaultLocaleCode
|
|
281
289
|
});
|
|
282
|
-
const documentMap = lodash_1.default.keyBy(contentStoreDocuments, 'srcObjectId');
|
|
283
290
|
const assetMap = lodash_1.default.keyBy(contentStoreAssets, 'srcObjectId');
|
|
284
291
|
this.logger.debug('loaded content source data', {
|
|
285
292
|
contentSourceId,
|
|
286
|
-
locales,
|
|
287
293
|
defaultLocaleCode,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
294
|
+
localesCount: locales.length,
|
|
295
|
+
modelCount: csiModels.length,
|
|
296
|
+
documentCount: csiDocuments.length,
|
|
297
|
+
assetCount: csiAssets.length
|
|
291
298
|
});
|
|
292
299
|
contentSourceInstance.startWatchingContentUpdates({
|
|
293
300
|
getModelMap: () => {
|
|
@@ -306,27 +313,21 @@ class ContentStore {
|
|
|
306
313
|
},
|
|
307
314
|
onSchemaChange: async () => {
|
|
308
315
|
this.logger.debug('content source called onSchemaChange', { contentSourceId });
|
|
309
|
-
this.
|
|
310
|
-
contentSourceInstance: contentSourceInstance,
|
|
311
|
-
init: false
|
|
312
|
-
});
|
|
316
|
+
await this.reloadContentSourcesByIdAndProcessData({ contentSourceIds: [contentSourceId] });
|
|
313
317
|
this.onSchemaChangeCallback();
|
|
314
318
|
}
|
|
315
319
|
});
|
|
316
320
|
return {
|
|
317
321
|
id: contentSourceId,
|
|
318
|
-
|
|
319
|
-
|
|
322
|
+
srcType: contentSourceInstance.getContentSourceType(),
|
|
323
|
+
srcProjectId: contentSourceInstance.getProjectId(),
|
|
320
324
|
instance: contentSourceInstance,
|
|
321
325
|
locales: locales,
|
|
322
326
|
defaultLocaleCode: defaultLocaleCode,
|
|
323
|
-
|
|
324
|
-
modelMap: modelMap,
|
|
327
|
+
csiModels: csiModels,
|
|
325
328
|
csiModelMap: csiModelMap,
|
|
326
329
|
csiDocuments: csiDocuments,
|
|
327
330
|
csiDocumentMap: csiDocumentMap,
|
|
328
|
-
documents: contentStoreDocuments,
|
|
329
|
-
documentMap: documentMap,
|
|
330
331
|
csiAssets: csiAssets,
|
|
331
332
|
csiAssetMap: csiAssetMap,
|
|
332
333
|
assets: contentStoreAssets,
|
|
@@ -335,6 +336,7 @@ class ContentStore {
|
|
|
335
336
|
}
|
|
336
337
|
onContentChange(contentSourceId, contentChangeEvent) {
|
|
337
338
|
// TODO: prevent content change process for contentSourceId if loading content is in progress
|
|
339
|
+
var _a, _b, _c, _d;
|
|
338
340
|
this.logger.debug('onContentChange', {
|
|
339
341
|
contentSourceId,
|
|
340
342
|
documentCount: contentChangeEvent.documents.length,
|
|
@@ -362,8 +364,8 @@ class ContentStore {
|
|
|
362
364
|
contentSourceData.csiDocuments.splice(index, 1);
|
|
363
365
|
}
|
|
364
366
|
result.deletedDocuments.push({
|
|
365
|
-
srcType: contentSourceData.
|
|
366
|
-
srcProjectId: contentSourceData.
|
|
367
|
+
srcType: contentSourceData.srcType,
|
|
368
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
367
369
|
srcObjectId: docId
|
|
368
370
|
});
|
|
369
371
|
});
|
|
@@ -380,14 +382,35 @@ class ContentStore {
|
|
|
380
382
|
contentSourceData.csiAssets.splice(index, 1);
|
|
381
383
|
}
|
|
382
384
|
result.deletedAssets.push({
|
|
383
|
-
srcType: contentSourceData.
|
|
384
|
-
srcProjectId: contentSourceData.
|
|
385
|
+
srcType: contentSourceData.srcType,
|
|
386
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
385
387
|
srcObjectId: assetId
|
|
386
388
|
});
|
|
387
389
|
});
|
|
390
|
+
// map csi documents through stackbitConfig.mapDocuments
|
|
391
|
+
let mappedDocs = contentChangeEvent.documents;
|
|
392
|
+
if ((_a = this.stackbitConfig) === null || _a === void 0 ? void 0 : _a.mapDocuments) {
|
|
393
|
+
const csiDocumentsWithSource = contentChangeEvent.documents.map((csiDocument) => ({
|
|
394
|
+
srcType: contentSourceData.srcType,
|
|
395
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
396
|
+
...csiDocument
|
|
397
|
+
}));
|
|
398
|
+
const modelsWithSource = contentSourceData.models.map((model) => {
|
|
399
|
+
return {
|
|
400
|
+
srcType: contentSourceData.srcType,
|
|
401
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
402
|
+
...model
|
|
403
|
+
};
|
|
404
|
+
});
|
|
405
|
+
mappedDocs =
|
|
406
|
+
(_d = (_c = (_b = this.stackbitConfig) === null || _b === void 0 ? void 0 : _b.mapDocuments) === null || _c === void 0 ? void 0 : _c.call(_b, {
|
|
407
|
+
documents: lodash_1.default.cloneDeep(csiDocumentsWithSource),
|
|
408
|
+
models: lodash_1.default.cloneDeep(modelsWithSource)
|
|
409
|
+
})) !== null && _d !== void 0 ? _d : csiDocumentsWithSource;
|
|
410
|
+
}
|
|
388
411
|
// map csi documents and assets to content store documents and assets
|
|
389
412
|
const documents = (0, csi_to_store_docs_converter_1.mapCSIDocumentsToStoreDocuments)({
|
|
390
|
-
csiDocuments:
|
|
413
|
+
csiDocuments: mappedDocs,
|
|
391
414
|
contentSourceInstance: contentSourceData.instance,
|
|
392
415
|
modelMap: contentSourceData.modelMap,
|
|
393
416
|
defaultLocaleCode: contentSourceData.defaultLocaleCode
|
|
@@ -399,7 +422,7 @@ class ContentStore {
|
|
|
399
422
|
});
|
|
400
423
|
// update contentSourceData with new or updated documents and assets
|
|
401
424
|
Object.assign(contentSourceData.csiDocumentMap, lodash_1.default.keyBy(contentChangeEvent.documents, 'id'));
|
|
402
|
-
Object.assign(contentSourceData.
|
|
425
|
+
Object.assign(contentSourceData.csiAssetMap, lodash_1.default.keyBy(contentChangeEvent.assets, 'id'));
|
|
403
426
|
Object.assign(contentSourceData.documentMap, lodash_1.default.keyBy(documents, 'srcObjectId'));
|
|
404
427
|
Object.assign(contentSourceData.assetMap, lodash_1.default.keyBy(assets, 'srcObjectId'));
|
|
405
428
|
for (let idx = 0; idx < documents.length; idx++) {
|
|
@@ -417,8 +440,8 @@ class ContentStore {
|
|
|
417
440
|
contentSourceData.csiDocuments.splice(dataIndex, 1, csiDocument);
|
|
418
441
|
}
|
|
419
442
|
result.updatedDocuments.push({
|
|
420
|
-
srcType: contentSourceData.
|
|
421
|
-
srcProjectId: contentSourceData.
|
|
443
|
+
srcType: contentSourceData.srcType,
|
|
444
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
422
445
|
srcObjectId: document.srcObjectId
|
|
423
446
|
});
|
|
424
447
|
}
|
|
@@ -437,18 +460,77 @@ class ContentStore {
|
|
|
437
460
|
contentSourceData.csiAssets.splice(index, 1, csiAsset);
|
|
438
461
|
}
|
|
439
462
|
result.updatedAssets.push({
|
|
440
|
-
srcType: contentSourceData.
|
|
441
|
-
srcProjectId: contentSourceData.
|
|
463
|
+
srcType: contentSourceData.srcType,
|
|
464
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
442
465
|
srcObjectId: asset.srcObjectId
|
|
443
466
|
});
|
|
444
467
|
}
|
|
445
468
|
return result;
|
|
446
469
|
}
|
|
470
|
+
async processData({ stackbitConfig, configModels, presets, contentSourceRawDataArr }) {
|
|
471
|
+
var _a, _b, _c, _d;
|
|
472
|
+
const modelsWithSource = contentSourceRawDataArr.reduce((accum, csData) => {
|
|
473
|
+
const mergedModels = (0, sdk_1.mergeConfigModelsWithExternalModels)({
|
|
474
|
+
configModels: configModels,
|
|
475
|
+
externalModels: csData.csiModels
|
|
476
|
+
});
|
|
477
|
+
const modelsWithSource = mergedModels.map((model) => {
|
|
478
|
+
return {
|
|
479
|
+
srcType: csData.srcType,
|
|
480
|
+
srcProjectId: csData.id,
|
|
481
|
+
...model
|
|
482
|
+
};
|
|
483
|
+
});
|
|
484
|
+
return accum.concat(modelsWithSource);
|
|
485
|
+
}, []);
|
|
486
|
+
// TODO: Is there a better way than deep cloning objects before passing them to user methods?
|
|
487
|
+
// Not cloning mutable objects will break the internal state if user mutates the objects.
|
|
488
|
+
const mappedModels = (_b = (_a = stackbitConfig === null || stackbitConfig === void 0 ? void 0 : stackbitConfig.mapModels) === null || _a === void 0 ? void 0 : _a.call(stackbitConfig, { models: lodash_1.default.cloneDeep(modelsWithSource) })) !== null && _b !== void 0 ? _b : modelsWithSource;
|
|
489
|
+
const normalizedModels = (0, model_utils_1.normalizeModels)({ models: mappedModels, logger: this.userLogger });
|
|
490
|
+
const validatedModels = (0, model_utils_1.validateModels)({ models: normalizedModels, logger: this.userLogger });
|
|
491
|
+
const modelsWithPresetsIds = (0, sdk_1.extendModelsWithPresetsIds)({ models: validatedModels, presets });
|
|
492
|
+
const { models } = await this.handleConfigAssets({ models: modelsWithPresetsIds });
|
|
493
|
+
const csiDocumentsWithSource = contentSourceRawDataArr.reduce((accum, csData) => {
|
|
494
|
+
const csiDocumentsWithSource = csData.csiDocuments.map((csiDocument) => ({
|
|
495
|
+
srcType: csData.srcType,
|
|
496
|
+
srcProjectId: csData.id,
|
|
497
|
+
...csiDocument
|
|
498
|
+
}));
|
|
499
|
+
return accum.concat(csiDocumentsWithSource);
|
|
500
|
+
}, []);
|
|
501
|
+
// TODO: Is there a better way than deep cloning objects before passing them to user methods?
|
|
502
|
+
// Not cloning mutable objects will break the internal state if user mutates the objects.
|
|
503
|
+
const mappedDocs = (_d = (_c = stackbitConfig === null || stackbitConfig === void 0 ? void 0 : stackbitConfig.mapDocuments) === null || _c === void 0 ? void 0 : _c.call(stackbitConfig, {
|
|
504
|
+
documents: lodash_1.default.cloneDeep(csiDocumentsWithSource),
|
|
505
|
+
models: lodash_1.default.cloneDeep(models)
|
|
506
|
+
})) !== null && _d !== void 0 ? _d : csiDocumentsWithSource;
|
|
507
|
+
const modelMapByContentSource = (0, content_store_utils_1.groupModelsByContentSource)({ models: models });
|
|
508
|
+
const documentMapByContentSource = (0, content_store_utils_1.groupDocumentsByContentSource)({ documents: mappedDocs });
|
|
509
|
+
const contentSourceDataArr = contentSourceRawDataArr.map((csData) => {
|
|
510
|
+
const modelMap = lodash_1.default.get(modelMapByContentSource, [csData.srcType, csData.id], {});
|
|
511
|
+
const mappedCSIDocuments = lodash_1.default.get(documentMapByContentSource, [csData.srcType, csData.id]);
|
|
512
|
+
const documents = (0, csi_to_store_docs_converter_1.mapCSIDocumentsToStoreDocuments)({
|
|
513
|
+
csiDocuments: mappedCSIDocuments,
|
|
514
|
+
contentSourceInstance: csData.instance,
|
|
515
|
+
defaultLocaleCode: csData.defaultLocaleCode,
|
|
516
|
+
modelMap: modelMap
|
|
517
|
+
});
|
|
518
|
+
return {
|
|
519
|
+
...csData,
|
|
520
|
+
models: Object.values(modelMap),
|
|
521
|
+
modelMap,
|
|
522
|
+
documents,
|
|
523
|
+
documentMap: lodash_1.default.keyBy(documents, 'srcObjectId')
|
|
524
|
+
};
|
|
525
|
+
});
|
|
526
|
+
return lodash_1.default.keyBy(contentSourceDataArr, 'id');
|
|
527
|
+
}
|
|
447
528
|
getModels() {
|
|
448
529
|
return lodash_1.default.reduce(this.contentSourceDataById, (result, contentSourceData) => {
|
|
449
530
|
const contentSourceType = contentSourceData.instance.getContentSourceType();
|
|
450
531
|
const srcProjectId = contentSourceData.instance.getProjectId();
|
|
451
532
|
lodash_1.default.set(result, [contentSourceType, srcProjectId], contentSourceData.modelMap);
|
|
533
|
+
lodash_1.default.set(result, [contentSourceType, srcProjectId, '__image_model'], common_schema_1.IMAGE_MODEL);
|
|
452
534
|
return result;
|
|
453
535
|
}, {});
|
|
454
536
|
}
|
|
@@ -479,8 +561,8 @@ class ContentStore {
|
|
|
479
561
|
contentSourceDataArr = Object.values(this.contentSourceDataById);
|
|
480
562
|
}
|
|
481
563
|
return (0, utils_1.reducePromise)(contentSourceDataArr, async (accum, contentSourceData) => {
|
|
482
|
-
const srcType = contentSourceData.
|
|
483
|
-
const srcProjectId = contentSourceData.
|
|
564
|
+
const srcType = contentSourceData.srcType;
|
|
565
|
+
const srcProjectId = contentSourceData.srcProjectId;
|
|
484
566
|
const userContext = (0, content_store_utils_1.getUserContextForSrcType)(srcType, user);
|
|
485
567
|
let result = await contentSourceData.instance.hasAccess({ userContext });
|
|
486
568
|
// backwards compatibility with older CSI version
|
|
@@ -949,13 +1031,13 @@ class ContentStore {
|
|
|
949
1031
|
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
950
1032
|
locale = locale !== null && locale !== void 0 ? locale : contentSourceData.defaultLocaleCode;
|
|
951
1033
|
const { documents, assets } = getCSIDocumentsAndAssetsFromContentSourceDataByIds(contentSourceData, contentSourceObjects);
|
|
952
|
-
const userContext = (0, content_store_utils_1.getUserContextForSrcType)(contentSourceData.
|
|
1034
|
+
const userContext = (0, content_store_utils_1.getUserContextForSrcType)(contentSourceData.srcType, user);
|
|
953
1035
|
const internalValidationErrors = internalValidateContent(documents, assets, contentSourceData);
|
|
954
1036
|
const validationResult = await contentSourceData.instance.validateDocuments({ documents, assets, locale, userContext });
|
|
955
1037
|
errors = errors.concat(internalValidationErrors, validationResult.errors.map((validationError) => ({
|
|
956
1038
|
message: validationError.message,
|
|
957
|
-
srcType: contentSourceData.
|
|
958
|
-
srcProjectId: contentSourceData.
|
|
1039
|
+
srcType: contentSourceData.srcType,
|
|
1040
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
959
1041
|
srcObjectType: validationError.objectType,
|
|
960
1042
|
srcObjectId: validationError.objectId,
|
|
961
1043
|
fieldPath: validationError.fieldPath,
|
|
@@ -986,7 +1068,7 @@ class ContentStore {
|
|
|
986
1068
|
Object.keys(objectsBySourceId).forEach((contentSourceId) => {
|
|
987
1069
|
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
988
1070
|
documents.push(...contentSourceData.documents);
|
|
989
|
-
lodash_1.default.set(schema, [contentSourceData.
|
|
1071
|
+
lodash_1.default.set(schema, [contentSourceData.srcType, contentSourceData.srcProjectId], contentSourceData.modelMap);
|
|
990
1072
|
});
|
|
991
1073
|
return (0, search_utils_1.searchDocuments)({
|
|
992
1074
|
...data,
|
|
@@ -999,7 +1081,7 @@ class ContentStore {
|
|
|
999
1081
|
const objectsBySourceId = lodash_1.default.groupBy(objects, (object) => (0, content_store_utils_1.getContentSourceId)(object.srcType, object.srcProjectId));
|
|
1000
1082
|
for (const [contentSourceId, contentSourceObjects] of Object.entries(objectsBySourceId)) {
|
|
1001
1083
|
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
1002
|
-
const userContext = (0, content_store_utils_1.getUserContextForSrcType)(contentSourceData.
|
|
1084
|
+
const userContext = (0, content_store_utils_1.getUserContextForSrcType)(contentSourceData.srcType, user);
|
|
1003
1085
|
const { documents, assets } = getCSIDocumentsAndAssetsFromContentSourceDataByIds(contentSourceData, contentSourceObjects);
|
|
1004
1086
|
await contentSourceData.instance.publishDocuments({ documents, assets, userContext });
|
|
1005
1087
|
}
|
|
@@ -1074,8 +1156,8 @@ function validateDocumentFields(document, documentField, fieldPath, contentSourc
|
|
|
1074
1156
|
if (!objRef) {
|
|
1075
1157
|
errors.push({
|
|
1076
1158
|
fieldPath,
|
|
1077
|
-
srcType: contentSourceData.
|
|
1078
|
-
srcProjectId: contentSourceData.
|
|
1159
|
+
srcType: contentSourceData.srcType,
|
|
1160
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
1079
1161
|
srcObjectType: documentField.refType,
|
|
1080
1162
|
srcObjectId: document.id,
|
|
1081
1163
|
message: `Can't find referenced ${documentField.refType}: ${documentField.refId}`
|