@stackbit/cms-core 0.1.6 → 0.1.7
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 -7
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +248 -156
- 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 +326 -170
- 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,7 +107,7 @@ class ContentStore {
|
|
|
119
107
|
return;
|
|
120
108
|
}
|
|
121
109
|
this.logger.debug('keepAlive => contentUpdatesWatchTimer is not running => load content source data');
|
|
122
|
-
await this.
|
|
110
|
+
await this.loadAllContentSourcesAndProcessData({ init: false });
|
|
123
111
|
}
|
|
124
112
|
/**
|
|
125
113
|
* This method is called when a content source notifies Stackbit of models
|
|
@@ -132,65 +120,155 @@ class ContentStore {
|
|
|
132
120
|
*/
|
|
133
121
|
async onContentSourceSchemaChange({ contentSourceId }) {
|
|
134
122
|
this.logger.debug('onContentSourceSchemaChange', { contentSourceId });
|
|
135
|
-
|
|
136
|
-
this.contentSourceDataById[contentSourceId] = await this.loadContentSourceData({
|
|
137
|
-
contentSourceInstance: contentSourceData.instance,
|
|
138
|
-
init: false
|
|
139
|
-
});
|
|
140
|
-
this.onSchemaChangeCallback();
|
|
123
|
+
await this.reloadContentSourcesByIdAndProcessData({ contentSourceIds: [contentSourceId] });
|
|
141
124
|
}
|
|
142
125
|
async onFilesChange(updatedFiles) {
|
|
143
126
|
var _a, _b;
|
|
144
127
|
this.logger.debug('onFilesChange');
|
|
145
|
-
let
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
128
|
+
let schemaChanged = false;
|
|
129
|
+
if (this.stackbitConfig) {
|
|
130
|
+
// Check if any of the yaml models files were changed. If yaml model files were changed,
|
|
131
|
+
// reload them and merge them with models defined in stackbit config.
|
|
132
|
+
const modelDirs = (0, sdk_1.getYamlModelDirs)(this.stackbitConfig);
|
|
133
|
+
const yamlModelsChanged = updatedFiles.find((updatedFile) => lodash_1.default.some(modelDirs, (modelDir) => updatedFile.startsWith(modelDir)));
|
|
134
|
+
if (yamlModelsChanged) {
|
|
135
|
+
this.logger.debug('identified change in stackbit model files');
|
|
136
|
+
schemaChanged = true;
|
|
137
|
+
this.yamlModels = await this.loadYamlModels({ stackbitConfig: this.stackbitConfig });
|
|
138
|
+
this.configModels = this.mergeConfigModels(this.stackbitConfig, this.yamlModels);
|
|
139
|
+
}
|
|
140
|
+
// Check if any of the preset files were changed. If presets were changed, reload them.
|
|
141
|
+
const presetDirs = (0, sdk_1.getPresetDirs)(this.stackbitConfig);
|
|
142
|
+
const presetsChanged = updatedFiles.find((updatedFile) => lodash_1.default.some(presetDirs, (presetDir) => updatedFile.startsWith(presetDir)));
|
|
143
|
+
if (presetsChanged) {
|
|
144
|
+
this.logger.debug('identified change in stackbit preset files');
|
|
145
|
+
schemaChanged = true;
|
|
146
|
+
this.presets = await this.loadPresets({ stackbitConfig: this.stackbitConfig });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const contentSourceIdsWithChangedSchema = [];
|
|
150
|
+
const contentChangeEvents = [];
|
|
152
151
|
for (const contentSourceInstance of this.contentSources) {
|
|
153
152
|
const contentSourceId = (0, content_store_utils_1.getContentSourceIdForContentSource)(contentSourceInstance);
|
|
154
153
|
this.logger.debug(`call onFilesChange for contentSource: ${contentSourceId}`);
|
|
155
|
-
const
|
|
156
|
-
this.logger.debug(`schemaChanged: ${schemaChanged}, has contentChangeEvent: ${!!contentChangeEvent}`);
|
|
154
|
+
const onFilesChangeResult = (_b = (await ((_a = contentSourceInstance.onFilesChange) === null || _a === void 0 ? void 0 : _a.call(contentSourceInstance, { updatedFiles: updatedFiles })))) !== null && _b !== void 0 ? _b : {};
|
|
155
|
+
this.logger.debug(`schemaChanged: ${onFilesChangeResult.schemaChanged}, has contentChangeEvent: ${!!onFilesChangeResult.contentChangeEvent}`);
|
|
157
156
|
// if schema is changed, there is no need to return contentChanges
|
|
158
157
|
// because schema changes reloads everything and implies content changes
|
|
159
|
-
if (schemaChanged) {
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
if (onFilesChangeResult.schemaChanged) {
|
|
159
|
+
schemaChanged = true;
|
|
160
|
+
contentSourceIdsWithChangedSchema.push(contentSourceId);
|
|
161
|
+
}
|
|
162
|
+
else if (onFilesChangeResult.contentChangeEvent) {
|
|
163
|
+
contentChangeEvents.push({ contentSourceId, contentChangeEvent: onFilesChangeResult.contentChangeEvent });
|
|
162
164
|
}
|
|
163
|
-
|
|
165
|
+
}
|
|
166
|
+
const contentChanges = {
|
|
167
|
+
updatedDocuments: [],
|
|
168
|
+
updatedAssets: [],
|
|
169
|
+
deletedDocuments: [],
|
|
170
|
+
deletedAssets: []
|
|
171
|
+
};
|
|
172
|
+
// If the schema was changed, there is no need to accumulate or notify about content changes.
|
|
173
|
+
// The processData will update the store with the latest data. And once the Studio receives
|
|
174
|
+
// the schemaChanged notification it will reload all the models and the documents with their latest state.
|
|
175
|
+
if (schemaChanged) {
|
|
176
|
+
await this.reloadContentSourcesByIdAndProcessData({ contentSourceIds: contentSourceIdsWithChangedSchema });
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
contentChangeEvents.reduce((contentChanges, { contentSourceId, contentChangeEvent }) => {
|
|
164
180
|
const contentChangeResult = this.onContentChange(contentSourceId, contentChangeEvent);
|
|
165
181
|
contentChanges.updatedDocuments = contentChanges.updatedDocuments.concat(contentChangeResult.updatedDocuments);
|
|
166
182
|
contentChanges.updatedAssets = contentChanges.updatedAssets.concat(contentChangeResult.updatedAssets);
|
|
167
183
|
contentChanges.deletedDocuments = contentChanges.deletedDocuments.concat(contentChangeResult.deletedDocuments);
|
|
168
184
|
contentChanges.deletedAssets = contentChanges.deletedAssets.concat(contentChangeResult.deletedAssets);
|
|
169
|
-
|
|
185
|
+
return contentChanges;
|
|
186
|
+
}, contentChanges);
|
|
170
187
|
}
|
|
171
188
|
return {
|
|
172
|
-
schemaChanged:
|
|
189
|
+
schemaChanged: schemaChanged,
|
|
173
190
|
contentChanges: contentChanges
|
|
174
191
|
};
|
|
175
192
|
}
|
|
176
|
-
async
|
|
193
|
+
async loadYamlModels({ stackbitConfig }) {
|
|
194
|
+
const yamlModelsResult = await (0, sdk_1.loadYamlModelsFromFiles)(stackbitConfig);
|
|
195
|
+
for (const error of yamlModelsResult.errors) {
|
|
196
|
+
this.userLogger.warn(error.message);
|
|
197
|
+
}
|
|
198
|
+
return yamlModelsResult.models;
|
|
199
|
+
}
|
|
200
|
+
mergeConfigModels(stackbitConfig, modelsFromFiles) {
|
|
201
|
+
var _a;
|
|
202
|
+
const configModelsResult = (0, sdk_1.mergeConfigModelsWithModelsFromFiles)((_a = stackbitConfig === null || stackbitConfig === void 0 ? void 0 : stackbitConfig.models) !== null && _a !== void 0 ? _a : [], modelsFromFiles);
|
|
203
|
+
for (const error of configModelsResult.errors) {
|
|
204
|
+
this.userLogger.warn(error.message);
|
|
205
|
+
}
|
|
206
|
+
return configModelsResult.models;
|
|
207
|
+
}
|
|
208
|
+
async loadPresets({ stackbitConfig }) {
|
|
209
|
+
var _a;
|
|
210
|
+
const contentSources = (_a = stackbitConfig === null || stackbitConfig === void 0 ? void 0 : stackbitConfig.contentSources) !== null && _a !== void 0 ? _a : [];
|
|
211
|
+
const singleContentSource = contentSources.length === 1 ? contentSources[0] : null;
|
|
212
|
+
const presetResult = await (0, sdk_1.loadPresets)({
|
|
213
|
+
config: stackbitConfig,
|
|
214
|
+
...(singleContentSource
|
|
215
|
+
? {
|
|
216
|
+
fallbackSrcType: singleContentSource.getContentSourceType(),
|
|
217
|
+
fallbackSrcProjectId: singleContentSource.getProjectId()
|
|
218
|
+
}
|
|
219
|
+
: null)
|
|
220
|
+
});
|
|
221
|
+
for (const error of presetResult.errors) {
|
|
222
|
+
this.userLogger.warn(error.message);
|
|
223
|
+
}
|
|
224
|
+
const { presets } = await this.handleConfigAssets({ presets: presetResult.presets });
|
|
225
|
+
return presets;
|
|
226
|
+
}
|
|
227
|
+
async loadAllContentSourcesAndProcessData({ init }) {
|
|
177
228
|
var _a, _b;
|
|
178
|
-
this.logger.debug('
|
|
229
|
+
this.logger.debug('loadAllContentSourcesAndProcessData', { init });
|
|
179
230
|
this.contentUpdatesWatchTimer.stopTimer();
|
|
180
|
-
|
|
181
|
-
const contentSources = ((_b = (_a = this.rawStackbitConfig) === null || _a === void 0 ? void 0 : _a.contentSources) !== null && _b !== void 0 ? _b : []);
|
|
231
|
+
const contentSources = (_b = (_a = this.stackbitConfig) === null || _a === void 0 ? void 0 : _a.contentSources) !== null && _b !== void 0 ? _b : [];
|
|
182
232
|
const promises = contentSources.map((contentSourceInstance) => {
|
|
183
233
|
return this.loadContentSourceData({ contentSourceInstance, init });
|
|
184
234
|
});
|
|
185
|
-
const
|
|
186
|
-
const contentSourceDataById = lodash_1.default.keyBy(contentSourceDataArr, 'id');
|
|
235
|
+
const contentSourceRawDataArr = await Promise.all(promises);
|
|
187
236
|
// update all content sources at once to prevent race conditions
|
|
237
|
+
this.contentSourceDataById = await this.processData({
|
|
238
|
+
stackbitConfig: this.stackbitConfig,
|
|
239
|
+
configModels: this.configModels,
|
|
240
|
+
presets: this.presets,
|
|
241
|
+
contentSourceRawDataArr: contentSourceRawDataArr
|
|
242
|
+
});
|
|
188
243
|
this.contentSources = contentSources;
|
|
189
|
-
this.
|
|
244
|
+
this.contentUpdatesWatchTimer.startTimer();
|
|
245
|
+
}
|
|
246
|
+
async reloadContentSourcesByIdAndProcessData({ contentSourceIds }) {
|
|
247
|
+
this.logger.debug('reloadContentSourcesByIdAndProcessData', { contentSourceIds });
|
|
248
|
+
this.contentUpdatesWatchTimer.stopTimer();
|
|
249
|
+
const promises = this.contentSources.map((contentSourceInstance) => {
|
|
250
|
+
const contentSourceId = (0, content_store_utils_1.getContentSourceIdForContentSource)(contentSourceInstance);
|
|
251
|
+
if (contentSourceIds.includes(contentSourceId)) {
|
|
252
|
+
return this.loadContentSourceData({
|
|
253
|
+
contentSourceInstance: contentSourceInstance,
|
|
254
|
+
init: false
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
return Promise.resolve(lodash_1.default.omit(this.contentSourceDataById[contentSourceId], ['models', 'modelMap', 'documents', 'documentMap']));
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
const contentSourceRawDataArr = await Promise.all(promises);
|
|
262
|
+
this.contentSourceDataById = await this.processData({
|
|
263
|
+
stackbitConfig: this.stackbitConfig,
|
|
264
|
+
configModels: this.configModels,
|
|
265
|
+
presets: this.presets,
|
|
266
|
+
contentSourceRawDataArr: contentSourceRawDataArr
|
|
267
|
+
});
|
|
190
268
|
this.contentUpdatesWatchTimer.startTimer();
|
|
191
269
|
}
|
|
192
270
|
async loadContentSourceData({ contentSourceInstance, init }) {
|
|
193
|
-
var _a
|
|
271
|
+
var _a;
|
|
194
272
|
const contentSourceId = (0, content_store_utils_1.getContentSourceIdForContentSource)(contentSourceInstance);
|
|
195
273
|
this.logger.debug('loadContentSourceData', { contentSourceId, init });
|
|
196
274
|
if (init) {
|
|
@@ -207,87 +285,27 @@ class ContentStore {
|
|
|
207
285
|
contentSourceInstance.stopWatchingContentUpdates();
|
|
208
286
|
await contentSourceInstance.reset();
|
|
209
287
|
}
|
|
210
|
-
// TODO: introduce optimization: don't fetch content source models,
|
|
211
|
-
// documents, assets if only stackbitConfig was changed
|
|
212
288
|
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
289
|
const csiModelMap = lodash_1.default.keyBy(csiModels, 'name');
|
|
290
|
+
const locales = await contentSourceInstance.getLocales();
|
|
291
|
+
const defaultLocaleCode = (_a = locales === null || locales === void 0 ? void 0 : locales.find((locale) => locale.default)) === null || _a === void 0 ? void 0 : _a.code;
|
|
267
292
|
const csiDocuments = await contentSourceInstance.getDocuments({ modelMap: csiModelMap });
|
|
268
293
|
const csiAssets = await contentSourceInstance.getAssets();
|
|
269
294
|
const csiDocumentMap = lodash_1.default.keyBy(csiDocuments, 'id');
|
|
270
295
|
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
296
|
const contentStoreAssets = (0, csi_to_store_docs_converter_1.mapCSIAssetsToStoreAssets)({
|
|
278
297
|
csiAssets,
|
|
279
298
|
contentSourceInstance,
|
|
280
299
|
defaultLocaleCode
|
|
281
300
|
});
|
|
282
|
-
const documentMap = lodash_1.default.keyBy(contentStoreDocuments, 'srcObjectId');
|
|
283
301
|
const assetMap = lodash_1.default.keyBy(contentStoreAssets, 'srcObjectId');
|
|
284
302
|
this.logger.debug('loaded content source data', {
|
|
285
303
|
contentSourceId,
|
|
286
|
-
locales,
|
|
287
304
|
defaultLocaleCode,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
305
|
+
localesCount: locales.length,
|
|
306
|
+
modelCount: csiModels.length,
|
|
307
|
+
documentCount: csiDocuments.length,
|
|
308
|
+
assetCount: csiAssets.length
|
|
291
309
|
});
|
|
292
310
|
contentSourceInstance.startWatchingContentUpdates({
|
|
293
311
|
getModelMap: () => {
|
|
@@ -306,27 +324,20 @@ class ContentStore {
|
|
|
306
324
|
},
|
|
307
325
|
onSchemaChange: async () => {
|
|
308
326
|
this.logger.debug('content source called onSchemaChange', { contentSourceId });
|
|
309
|
-
this.
|
|
310
|
-
contentSourceInstance: contentSourceInstance,
|
|
311
|
-
init: false
|
|
312
|
-
});
|
|
313
|
-
this.onSchemaChangeCallback();
|
|
327
|
+
await this.reloadContentSourcesByIdAndProcessData({ contentSourceIds: [contentSourceId] });
|
|
314
328
|
}
|
|
315
329
|
});
|
|
316
330
|
return {
|
|
317
331
|
id: contentSourceId,
|
|
318
|
-
|
|
319
|
-
|
|
332
|
+
srcType: contentSourceInstance.getContentSourceType(),
|
|
333
|
+
srcProjectId: contentSourceInstance.getProjectId(),
|
|
320
334
|
instance: contentSourceInstance,
|
|
321
335
|
locales: locales,
|
|
322
336
|
defaultLocaleCode: defaultLocaleCode,
|
|
323
|
-
|
|
324
|
-
modelMap: modelMap,
|
|
337
|
+
csiModels: csiModels,
|
|
325
338
|
csiModelMap: csiModelMap,
|
|
326
339
|
csiDocuments: csiDocuments,
|
|
327
340
|
csiDocumentMap: csiDocumentMap,
|
|
328
|
-
documents: contentStoreDocuments,
|
|
329
|
-
documentMap: documentMap,
|
|
330
341
|
csiAssets: csiAssets,
|
|
331
342
|
csiAssetMap: csiAssetMap,
|
|
332
343
|
assets: contentStoreAssets,
|
|
@@ -335,6 +346,7 @@ class ContentStore {
|
|
|
335
346
|
}
|
|
336
347
|
onContentChange(contentSourceId, contentChangeEvent) {
|
|
337
348
|
// TODO: prevent content change process for contentSourceId if loading content is in progress
|
|
349
|
+
var _a, _b, _c, _d;
|
|
338
350
|
this.logger.debug('onContentChange', {
|
|
339
351
|
contentSourceId,
|
|
340
352
|
documentCount: contentChangeEvent.documents.length,
|
|
@@ -362,8 +374,8 @@ class ContentStore {
|
|
|
362
374
|
contentSourceData.csiDocuments.splice(index, 1);
|
|
363
375
|
}
|
|
364
376
|
result.deletedDocuments.push({
|
|
365
|
-
srcType: contentSourceData.
|
|
366
|
-
srcProjectId: contentSourceData.
|
|
377
|
+
srcType: contentSourceData.srcType,
|
|
378
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
367
379
|
srcObjectId: docId
|
|
368
380
|
});
|
|
369
381
|
});
|
|
@@ -380,14 +392,35 @@ class ContentStore {
|
|
|
380
392
|
contentSourceData.csiAssets.splice(index, 1);
|
|
381
393
|
}
|
|
382
394
|
result.deletedAssets.push({
|
|
383
|
-
srcType: contentSourceData.
|
|
384
|
-
srcProjectId: contentSourceData.
|
|
395
|
+
srcType: contentSourceData.srcType,
|
|
396
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
385
397
|
srcObjectId: assetId
|
|
386
398
|
});
|
|
387
399
|
});
|
|
400
|
+
// map csi documents through stackbitConfig.mapDocuments
|
|
401
|
+
let mappedDocs = contentChangeEvent.documents;
|
|
402
|
+
if ((_a = this.stackbitConfig) === null || _a === void 0 ? void 0 : _a.mapDocuments) {
|
|
403
|
+
const csiDocumentsWithSource = contentChangeEvent.documents.map((csiDocument) => ({
|
|
404
|
+
srcType: contentSourceData.srcType,
|
|
405
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
406
|
+
...csiDocument
|
|
407
|
+
}));
|
|
408
|
+
const modelsWithSource = contentSourceData.models.map((model) => {
|
|
409
|
+
return {
|
|
410
|
+
srcType: contentSourceData.srcType,
|
|
411
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
412
|
+
...model
|
|
413
|
+
};
|
|
414
|
+
});
|
|
415
|
+
mappedDocs =
|
|
416
|
+
(_d = (_c = (_b = this.stackbitConfig) === null || _b === void 0 ? void 0 : _b.mapDocuments) === null || _c === void 0 ? void 0 : _c.call(_b, {
|
|
417
|
+
documents: lodash_1.default.cloneDeep(csiDocumentsWithSource),
|
|
418
|
+
models: lodash_1.default.cloneDeep(modelsWithSource)
|
|
419
|
+
})) !== null && _d !== void 0 ? _d : csiDocumentsWithSource;
|
|
420
|
+
}
|
|
388
421
|
// map csi documents and assets to content store documents and assets
|
|
389
422
|
const documents = (0, csi_to_store_docs_converter_1.mapCSIDocumentsToStoreDocuments)({
|
|
390
|
-
csiDocuments:
|
|
423
|
+
csiDocuments: mappedDocs,
|
|
391
424
|
contentSourceInstance: contentSourceData.instance,
|
|
392
425
|
modelMap: contentSourceData.modelMap,
|
|
393
426
|
defaultLocaleCode: contentSourceData.defaultLocaleCode
|
|
@@ -399,7 +432,7 @@ class ContentStore {
|
|
|
399
432
|
});
|
|
400
433
|
// update contentSourceData with new or updated documents and assets
|
|
401
434
|
Object.assign(contentSourceData.csiDocumentMap, lodash_1.default.keyBy(contentChangeEvent.documents, 'id'));
|
|
402
|
-
Object.assign(contentSourceData.
|
|
435
|
+
Object.assign(contentSourceData.csiAssetMap, lodash_1.default.keyBy(contentChangeEvent.assets, 'id'));
|
|
403
436
|
Object.assign(contentSourceData.documentMap, lodash_1.default.keyBy(documents, 'srcObjectId'));
|
|
404
437
|
Object.assign(contentSourceData.assetMap, lodash_1.default.keyBy(assets, 'srcObjectId'));
|
|
405
438
|
for (let idx = 0; idx < documents.length; idx++) {
|
|
@@ -417,8 +450,8 @@ class ContentStore {
|
|
|
417
450
|
contentSourceData.csiDocuments.splice(dataIndex, 1, csiDocument);
|
|
418
451
|
}
|
|
419
452
|
result.updatedDocuments.push({
|
|
420
|
-
srcType: contentSourceData.
|
|
421
|
-
srcProjectId: contentSourceData.
|
|
453
|
+
srcType: contentSourceData.srcType,
|
|
454
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
422
455
|
srcObjectId: document.srcObjectId
|
|
423
456
|
});
|
|
424
457
|
}
|
|
@@ -437,18 +470,77 @@ class ContentStore {
|
|
|
437
470
|
contentSourceData.csiAssets.splice(index, 1, csiAsset);
|
|
438
471
|
}
|
|
439
472
|
result.updatedAssets.push({
|
|
440
|
-
srcType: contentSourceData.
|
|
441
|
-
srcProjectId: contentSourceData.
|
|
473
|
+
srcType: contentSourceData.srcType,
|
|
474
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
442
475
|
srcObjectId: asset.srcObjectId
|
|
443
476
|
});
|
|
444
477
|
}
|
|
445
478
|
return result;
|
|
446
479
|
}
|
|
480
|
+
async processData({ stackbitConfig, configModels, presets, contentSourceRawDataArr }) {
|
|
481
|
+
var _a, _b, _c, _d;
|
|
482
|
+
const modelsWithSource = contentSourceRawDataArr.reduce((accum, csData) => {
|
|
483
|
+
const mergedModels = (0, sdk_1.mergeConfigModelsWithExternalModels)({
|
|
484
|
+
configModels: configModels,
|
|
485
|
+
externalModels: csData.csiModels
|
|
486
|
+
});
|
|
487
|
+
const modelsWithSource = mergedModels.map((model) => {
|
|
488
|
+
return {
|
|
489
|
+
srcType: csData.srcType,
|
|
490
|
+
srcProjectId: csData.id,
|
|
491
|
+
...model
|
|
492
|
+
};
|
|
493
|
+
});
|
|
494
|
+
return accum.concat(modelsWithSource);
|
|
495
|
+
}, []);
|
|
496
|
+
// TODO: Is there a better way than deep cloning objects before passing them to user methods?
|
|
497
|
+
// Not cloning mutable objects will break the internal state if user mutates the objects.
|
|
498
|
+
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;
|
|
499
|
+
const normalizedModels = (0, model_utils_1.normalizeModels)({ models: mappedModels, logger: this.userLogger });
|
|
500
|
+
const validatedModels = (0, model_utils_1.validateModels)({ models: normalizedModels, logger: this.userLogger });
|
|
501
|
+
const modelsWithPresetsIds = (0, sdk_1.extendModelsWithPresetsIds)({ models: validatedModels, presets });
|
|
502
|
+
const { models } = await this.handleConfigAssets({ models: modelsWithPresetsIds });
|
|
503
|
+
const csiDocumentsWithSource = contentSourceRawDataArr.reduce((accum, csData) => {
|
|
504
|
+
const csiDocumentsWithSource = csData.csiDocuments.map((csiDocument) => ({
|
|
505
|
+
srcType: csData.srcType,
|
|
506
|
+
srcProjectId: csData.id,
|
|
507
|
+
...csiDocument
|
|
508
|
+
}));
|
|
509
|
+
return accum.concat(csiDocumentsWithSource);
|
|
510
|
+
}, []);
|
|
511
|
+
// TODO: Is there a better way than deep cloning objects before passing them to user methods?
|
|
512
|
+
// Not cloning mutable objects will break the internal state if user mutates the objects.
|
|
513
|
+
const mappedDocs = (_d = (_c = stackbitConfig === null || stackbitConfig === void 0 ? void 0 : stackbitConfig.mapDocuments) === null || _c === void 0 ? void 0 : _c.call(stackbitConfig, {
|
|
514
|
+
documents: lodash_1.default.cloneDeep(csiDocumentsWithSource),
|
|
515
|
+
models: lodash_1.default.cloneDeep(models)
|
|
516
|
+
})) !== null && _d !== void 0 ? _d : csiDocumentsWithSource;
|
|
517
|
+
const modelMapByContentSource = (0, content_store_utils_1.groupModelsByContentSource)({ models: models });
|
|
518
|
+
const documentMapByContentSource = (0, content_store_utils_1.groupDocumentsByContentSource)({ documents: mappedDocs });
|
|
519
|
+
const contentSourceDataArr = contentSourceRawDataArr.map((csData) => {
|
|
520
|
+
const modelMap = lodash_1.default.get(modelMapByContentSource, [csData.srcType, csData.id], {});
|
|
521
|
+
const mappedCSIDocuments = lodash_1.default.get(documentMapByContentSource, [csData.srcType, csData.id]);
|
|
522
|
+
const documents = (0, csi_to_store_docs_converter_1.mapCSIDocumentsToStoreDocuments)({
|
|
523
|
+
csiDocuments: mappedCSIDocuments,
|
|
524
|
+
contentSourceInstance: csData.instance,
|
|
525
|
+
defaultLocaleCode: csData.defaultLocaleCode,
|
|
526
|
+
modelMap: modelMap
|
|
527
|
+
});
|
|
528
|
+
return {
|
|
529
|
+
...csData,
|
|
530
|
+
models: Object.values(modelMap),
|
|
531
|
+
modelMap,
|
|
532
|
+
documents,
|
|
533
|
+
documentMap: lodash_1.default.keyBy(documents, 'srcObjectId')
|
|
534
|
+
};
|
|
535
|
+
});
|
|
536
|
+
return lodash_1.default.keyBy(contentSourceDataArr, 'id');
|
|
537
|
+
}
|
|
447
538
|
getModels() {
|
|
448
539
|
return lodash_1.default.reduce(this.contentSourceDataById, (result, contentSourceData) => {
|
|
449
540
|
const contentSourceType = contentSourceData.instance.getContentSourceType();
|
|
450
541
|
const srcProjectId = contentSourceData.instance.getProjectId();
|
|
451
542
|
lodash_1.default.set(result, [contentSourceType, srcProjectId], contentSourceData.modelMap);
|
|
543
|
+
lodash_1.default.set(result, [contentSourceType, srcProjectId, '__image_model'], common_schema_1.IMAGE_MODEL);
|
|
452
544
|
return result;
|
|
453
545
|
}, {});
|
|
454
546
|
}
|
|
@@ -479,8 +571,8 @@ class ContentStore {
|
|
|
479
571
|
contentSourceDataArr = Object.values(this.contentSourceDataById);
|
|
480
572
|
}
|
|
481
573
|
return (0, utils_1.reducePromise)(contentSourceDataArr, async (accum, contentSourceData) => {
|
|
482
|
-
const srcType = contentSourceData.
|
|
483
|
-
const srcProjectId = contentSourceData.
|
|
574
|
+
const srcType = contentSourceData.srcType;
|
|
575
|
+
const srcProjectId = contentSourceData.srcProjectId;
|
|
484
576
|
const userContext = (0, content_store_utils_1.getUserContextForSrcType)(srcType, user);
|
|
485
577
|
let result = await contentSourceData.instance.hasAccess({ userContext });
|
|
486
578
|
// backwards compatibility with older CSI version
|
|
@@ -949,13 +1041,13 @@ class ContentStore {
|
|
|
949
1041
|
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
950
1042
|
locale = locale !== null && locale !== void 0 ? locale : contentSourceData.defaultLocaleCode;
|
|
951
1043
|
const { documents, assets } = getCSIDocumentsAndAssetsFromContentSourceDataByIds(contentSourceData, contentSourceObjects);
|
|
952
|
-
const userContext = (0, content_store_utils_1.getUserContextForSrcType)(contentSourceData.
|
|
1044
|
+
const userContext = (0, content_store_utils_1.getUserContextForSrcType)(contentSourceData.srcType, user);
|
|
953
1045
|
const internalValidationErrors = internalValidateContent(documents, assets, contentSourceData);
|
|
954
1046
|
const validationResult = await contentSourceData.instance.validateDocuments({ documents, assets, locale, userContext });
|
|
955
1047
|
errors = errors.concat(internalValidationErrors, validationResult.errors.map((validationError) => ({
|
|
956
1048
|
message: validationError.message,
|
|
957
|
-
srcType: contentSourceData.
|
|
958
|
-
srcProjectId: contentSourceData.
|
|
1049
|
+
srcType: contentSourceData.srcType,
|
|
1050
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
959
1051
|
srcObjectType: validationError.objectType,
|
|
960
1052
|
srcObjectId: validationError.objectId,
|
|
961
1053
|
fieldPath: validationError.fieldPath,
|
|
@@ -986,7 +1078,7 @@ class ContentStore {
|
|
|
986
1078
|
Object.keys(objectsBySourceId).forEach((contentSourceId) => {
|
|
987
1079
|
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
988
1080
|
documents.push(...contentSourceData.documents);
|
|
989
|
-
lodash_1.default.set(schema, [contentSourceData.
|
|
1081
|
+
lodash_1.default.set(schema, [contentSourceData.srcType, contentSourceData.srcProjectId], contentSourceData.modelMap);
|
|
990
1082
|
});
|
|
991
1083
|
return (0, search_utils_1.searchDocuments)({
|
|
992
1084
|
...data,
|
|
@@ -999,7 +1091,7 @@ class ContentStore {
|
|
|
999
1091
|
const objectsBySourceId = lodash_1.default.groupBy(objects, (object) => (0, content_store_utils_1.getContentSourceId)(object.srcType, object.srcProjectId));
|
|
1000
1092
|
for (const [contentSourceId, contentSourceObjects] of Object.entries(objectsBySourceId)) {
|
|
1001
1093
|
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
1002
|
-
const userContext = (0, content_store_utils_1.getUserContextForSrcType)(contentSourceData.
|
|
1094
|
+
const userContext = (0, content_store_utils_1.getUserContextForSrcType)(contentSourceData.srcType, user);
|
|
1003
1095
|
const { documents, assets } = getCSIDocumentsAndAssetsFromContentSourceDataByIds(contentSourceData, contentSourceObjects);
|
|
1004
1096
|
await contentSourceData.instance.publishDocuments({ documents, assets, userContext });
|
|
1005
1097
|
}
|
|
@@ -1074,8 +1166,8 @@ function validateDocumentFields(document, documentField, fieldPath, contentSourc
|
|
|
1074
1166
|
if (!objRef) {
|
|
1075
1167
|
errors.push({
|
|
1076
1168
|
fieldPath,
|
|
1077
|
-
srcType: contentSourceData.
|
|
1078
|
-
srcProjectId: contentSourceData.
|
|
1169
|
+
srcType: contentSourceData.srcType,
|
|
1170
|
+
srcProjectId: contentSourceData.srcProjectId,
|
|
1079
1171
|
srcObjectType: documentField.refType,
|
|
1080
1172
|
srcObjectId: document.id,
|
|
1081
1173
|
message: `Can't find referenced ${documentField.refType}: ${documentField.refId}`
|