@okf/ootils 1.42.0 → 1.44.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.
- package/dist/node.d.mts +6556 -1
- package/dist/node.d.ts +6556 -1
- package/dist/node.js +810 -19
- package/dist/node.mjs +796 -19
- package/package.json +2 -1
package/dist/node.js
CHANGED
|
@@ -935,7 +935,7 @@ var init_Tpl = __esm({
|
|
|
935
935
|
var require_MongoConnector = __commonJS({
|
|
936
936
|
"src/db/MongoConnector.js"(exports2, module2) {
|
|
937
937
|
"use strict";
|
|
938
|
-
var
|
|
938
|
+
var mongoose10 = require("mongoose");
|
|
939
939
|
var { ServerApiVersion } = require("mongodb");
|
|
940
940
|
var AnnotationsSchema = (init_Annotations(), __toCommonJS(Annotations_exports));
|
|
941
941
|
var PlatformConfigsSchema2 = (init_PlatformConfigs(), __toCommonJS(PlatformConfigs_exports));
|
|
@@ -999,7 +999,7 @@ var require_MongoConnector = __commonJS({
|
|
|
999
999
|
`Missing CLUSTER_NAME or CLUSTER_URI in cluster conf: ${JSON.stringify(clusterConf)}`
|
|
1000
1000
|
);
|
|
1001
1001
|
}
|
|
1002
|
-
this.clusterConnections[CLUSTER_NAME] =
|
|
1002
|
+
this.clusterConnections[CLUSTER_NAME] = mongoose10.createConnection(
|
|
1003
1003
|
CLUSTER_URI,
|
|
1004
1004
|
this.mongoOptions
|
|
1005
1005
|
);
|
|
@@ -1026,7 +1026,7 @@ var require_MongoConnector = __commonJS({
|
|
|
1026
1026
|
this.skippedClusters.add(CLUSTER_NAME);
|
|
1027
1027
|
return;
|
|
1028
1028
|
}
|
|
1029
|
-
const connection =
|
|
1029
|
+
const connection = mongoose10.createConnection(CLUSTER_URI, this.mongoOptions);
|
|
1030
1030
|
this.clusterConnections[CLUSTER_NAME] = connection;
|
|
1031
1031
|
this.initiateConnectionEventListeners(CLUSTER_NAME);
|
|
1032
1032
|
await new Promise((resolve, reject) => {
|
|
@@ -1491,7 +1491,7 @@ var require_getModelByTenant = __commonJS({
|
|
|
1491
1491
|
"use strict";
|
|
1492
1492
|
var { getDbByTenant: getDbByTenant2 } = (init_getDbByTenant(), __toCommonJS(getDbByTenant_exports));
|
|
1493
1493
|
var { AIChatSchema: AIChatSchema2, AnnotationSchema: AnnotationSchema2, PlatformConfigsSchema: PlatformConfigsSchema2, TplSchema: TplSchema2, GeneratedTopicsSchema: GeneratedTopicsSchema2, GeneratedEntitiesSchema: GeneratedEntitiesSchema2 } = (init_models(), __toCommonJS(models_exports));
|
|
1494
|
-
var
|
|
1494
|
+
var getModelByTenant4 = ({ tenant, modelName, schema, env }) => {
|
|
1495
1495
|
if (!tenant) {
|
|
1496
1496
|
throw new Error("tenant id has not been provided");
|
|
1497
1497
|
}
|
|
@@ -1501,48 +1501,48 @@ var require_getModelByTenant = __commonJS({
|
|
|
1501
1501
|
}
|
|
1502
1502
|
return db.model(modelName);
|
|
1503
1503
|
};
|
|
1504
|
-
var getAnnotationsModelByTenant2 = ({ tenant, env, mongodb, modelName }) =>
|
|
1504
|
+
var getAnnotationsModelByTenant2 = ({ tenant, env, mongodb, modelName }) => getModelByTenant4({
|
|
1505
1505
|
tenant,
|
|
1506
1506
|
modelName: modelName || "annotations",
|
|
1507
1507
|
schema: AnnotationSchema2,
|
|
1508
1508
|
env
|
|
1509
1509
|
});
|
|
1510
|
-
var getPlatformConfigsModelByTenant2 = ({ tenant, env, mongodb }) =>
|
|
1510
|
+
var getPlatformConfigsModelByTenant2 = ({ tenant, env, mongodb }) => getModelByTenant4({
|
|
1511
1511
|
tenant,
|
|
1512
1512
|
modelName: "platformConfigs",
|
|
1513
1513
|
schema: PlatformConfigsSchema2,
|
|
1514
1514
|
env
|
|
1515
1515
|
});
|
|
1516
|
-
var
|
|
1516
|
+
var getTplModelByTenant3 = ({ tenant, env, mongodb }) => getModelByTenant4({
|
|
1517
1517
|
tenant,
|
|
1518
1518
|
modelName: "tpl",
|
|
1519
1519
|
schema: TplSchema2,
|
|
1520
1520
|
env
|
|
1521
1521
|
});
|
|
1522
|
-
var getAIChatModelByTenant2 = ({ tenant, env, mongodb }) =>
|
|
1522
|
+
var getAIChatModelByTenant2 = ({ tenant, env, mongodb }) => getModelByTenant4({
|
|
1523
1523
|
tenant,
|
|
1524
1524
|
modelName: "aiChat",
|
|
1525
1525
|
schema: AIChatSchema2,
|
|
1526
1526
|
env
|
|
1527
1527
|
});
|
|
1528
|
-
var getGeneratedTopicsModelByTenant2 = ({ tenant, env }) =>
|
|
1528
|
+
var getGeneratedTopicsModelByTenant2 = ({ tenant, env }) => getModelByTenant4({
|
|
1529
1529
|
tenant,
|
|
1530
1530
|
modelName: "generatedTopics",
|
|
1531
1531
|
schema: GeneratedTopicsSchema2,
|
|
1532
1532
|
env
|
|
1533
1533
|
});
|
|
1534
|
-
var getGeneratedEntitiesModelByTenant2 = ({ tenant, env }) =>
|
|
1534
|
+
var getGeneratedEntitiesModelByTenant2 = ({ tenant, env }) => getModelByTenant4({
|
|
1535
1535
|
tenant,
|
|
1536
1536
|
modelName: "generatedEntities",
|
|
1537
1537
|
schema: GeneratedEntitiesSchema2,
|
|
1538
1538
|
env
|
|
1539
1539
|
});
|
|
1540
1540
|
module2.exports = {
|
|
1541
|
-
getModelByTenant:
|
|
1541
|
+
getModelByTenant: getModelByTenant4,
|
|
1542
1542
|
getAIChatModelByTenant: getAIChatModelByTenant2,
|
|
1543
1543
|
getAnnotationsModelByTenant: getAnnotationsModelByTenant2,
|
|
1544
1544
|
getPlatformConfigsModelByTenant: getPlatformConfigsModelByTenant2,
|
|
1545
|
-
getTplModelByTenant:
|
|
1545
|
+
getTplModelByTenant: getTplModelByTenant3,
|
|
1546
1546
|
getGeneratedTopicsModelByTenant: getGeneratedTopicsModelByTenant2,
|
|
1547
1547
|
getGeneratedEntitiesModelByTenant: getGeneratedEntitiesModelByTenant2
|
|
1548
1548
|
};
|
|
@@ -2385,6 +2385,7 @@ __export(node_exports, {
|
|
|
2385
2385
|
GET_GLOBAL_BULLMQ_CONFIG: () => import_GET_GLOBAL_BULLMQ_CONFIG.GET_GLOBAL_BULLMQ_CONFIG,
|
|
2386
2386
|
GeneratedEntitiesSchema: () => GeneratedEntities_default,
|
|
2387
2387
|
GeneratedTopicsSchema: () => GeneratedTopics_default,
|
|
2388
|
+
LIVE_COLLAB_DOC_REFRESH_MS: () => LIVE_COLLAB_DOC_REFRESH_MS,
|
|
2388
2389
|
MONGO_SCHEMA_PRESETS: () => MONGO_SCHEMA_PRESETS,
|
|
2389
2390
|
MongoConnector: () => import_MongoConnector3.MongoConnector,
|
|
2390
2391
|
PlatformConfigsSchema: () => PlatformConfigs_default,
|
|
@@ -2392,9 +2393,13 @@ __export(node_exports, {
|
|
|
2392
2393
|
RedisCacheConnector: () => RedisCacheConnector,
|
|
2393
2394
|
SecretManagerConnector: () => SecretManagerConnector,
|
|
2394
2395
|
TEMP_removeDuplicateFilters: () => TEMP_removeDuplicateFilters,
|
|
2396
|
+
TenantRegistry: () => TenantRegistry,
|
|
2395
2397
|
TplSchema: () => Tpl_default,
|
|
2396
2398
|
UI_CONTENT: () => UI_CONTENT,
|
|
2397
2399
|
WorkerManager: () => import_WorkerManager.WorkerManager,
|
|
2400
|
+
_backOfficeFields__contr: () => _backOfficeFields__contr,
|
|
2401
|
+
_constantMeta__contr: () => _constantMeta__contr,
|
|
2402
|
+
_fixedMainFields__contr: () => _fixedMainFields__contr,
|
|
2398
2403
|
_self_managed_buildAnnoHierarchyConfig: () => _self_managed_buildAnnoHierarchyConfig,
|
|
2399
2404
|
_self_managed_buildDocHierarchyConfig: () => _self_managed_buildDocHierarchyConfig,
|
|
2400
2405
|
_self_managed_getFixedAnnoRollupBlocks: () => _self_managed_getFixedAnnoRollupBlocks,
|
|
@@ -2402,23 +2407,28 @@ __export(node_exports, {
|
|
|
2402
2407
|
autoGenFilterConfigsFromTpl: () => autoGenFilterConfigsFromTpl,
|
|
2403
2408
|
blockRegistry: () => blockRegistry,
|
|
2404
2409
|
buildFilterConfigurations: () => buildFilterConfigurations,
|
|
2410
|
+
clearLiveCollabDoc: () => clearLiveCollabDoc,
|
|
2405
2411
|
collectMarkIdsInOrder: () => collectMarkIdsInOrder,
|
|
2406
2412
|
compareAndGroupBlocks: () => compareAndGroupBlocks,
|
|
2407
2413
|
deleteVal: () => deleteVal,
|
|
2408
2414
|
extractAllBlocksFromTpl: () => extractAllBlocksFromTpl,
|
|
2415
|
+
extractAllUniqueAnnoTagTypesFromTpl: () => extractAllUniqueAnnoTagTypesFromTpl,
|
|
2409
2416
|
extractAndOrganizeBlocks: () => extractAndOrganizeBlocks,
|
|
2417
|
+
extractNumberFromValue: () => extractNumberFromValue,
|
|
2410
2418
|
genCleanCamelCaseId: () => genCleanCamelCaseId,
|
|
2419
|
+
genContentTypeSchema: () => genContentTypeSchema,
|
|
2411
2420
|
genTagId: () => genTagId,
|
|
2412
2421
|
generateFilterKey: () => generateFilterKey,
|
|
2413
|
-
getAIChatModelByTenant: () =>
|
|
2422
|
+
getAIChatModelByTenant: () => import_getModelByTenant4.getAIChatModelByTenant,
|
|
2414
2423
|
getAnnoFilterBucketKey: () => getAnnoFilterBucketKey,
|
|
2415
|
-
getAnnotationsModelByTenant: () =>
|
|
2424
|
+
getAnnotationsModelByTenant: () => import_getModelByTenant4.getAnnotationsModelByTenant,
|
|
2425
|
+
getContrModelByTenant: () => getContrModelByTenant,
|
|
2416
2426
|
getDbByTenant: () => getDbByTenant,
|
|
2417
2427
|
getFilterKeyForBlock: () => getFilterKeyForBlock,
|
|
2418
|
-
getGeneratedEntitiesModelByTenant: () =>
|
|
2419
|
-
getGeneratedTopicsModelByTenant: () =>
|
|
2420
|
-
getModelByTenant: () =>
|
|
2421
|
-
getPlatformConfigsModelByTenant: () =>
|
|
2428
|
+
getGeneratedEntitiesModelByTenant: () => import_getModelByTenant4.getGeneratedEntitiesModelByTenant,
|
|
2429
|
+
getGeneratedTopicsModelByTenant: () => import_getModelByTenant4.getGeneratedTopicsModelByTenant,
|
|
2430
|
+
getModelByTenant: () => import_getModelByTenant4.getModelByTenant,
|
|
2431
|
+
getPlatformConfigsModelByTenant: () => import_getModelByTenant4.getPlatformConfigsModelByTenant,
|
|
2422
2432
|
getPlatformContextContent: () => getPlatformContextContent,
|
|
2423
2433
|
getRollupPossibilities: () => getRollupPossibilities,
|
|
2424
2434
|
getRoutePathToContentTypeLanding: () => getRoutePathToContentTypeLanding,
|
|
@@ -2429,9 +2439,13 @@ __export(node_exports, {
|
|
|
2429
2439
|
getRoutePathToReviewDashboard: () => getRoutePathToReviewDashboard,
|
|
2430
2440
|
getRoutePathToTCI: () => getRoutePathToTCI,
|
|
2431
2441
|
getRoutePathToTagCategoryLanding: () => getRoutePathToTagCategoryLanding,
|
|
2432
|
-
|
|
2442
|
+
getTpl: () => getTpl,
|
|
2443
|
+
getTplModelByTenant: () => import_getModelByTenant4.getTplModelByTenant,
|
|
2444
|
+
getTypesForSchemaFromTpl: () => getTypesForSchemaFromTpl,
|
|
2433
2445
|
getVal: () => getVal,
|
|
2446
|
+
isLiveCollabDoc: () => isLiveCollabDoc,
|
|
2434
2447
|
isTplAnnotationEnabled: () => isTplAnnotationEnabled,
|
|
2448
|
+
markLiveCollabDoc: () => markLiveCollabDoc,
|
|
2435
2449
|
mergeAnnoDataIntoAnnotationsTags: () => mergeAnnoDataIntoAnnotationsTags,
|
|
2436
2450
|
parseSpecialConfigSyntax: () => parseSpecialConfigSyntax,
|
|
2437
2451
|
plainTextToLexical: () => plainTextToLexical,
|
|
@@ -5092,8 +5106,771 @@ RedisCacheConnector.instance = null;
|
|
|
5092
5106
|
// src/node.ts
|
|
5093
5107
|
init_SecretManagerConnector();
|
|
5094
5108
|
init_getDbByTenant();
|
|
5109
|
+
var import_getModelByTenant4 = __toESM(require_getModelByTenant());
|
|
5110
|
+
|
|
5111
|
+
// src/contentSchema/genContentTypeSchema.js
|
|
5112
|
+
var import_mongoose12 = __toESM(require("mongoose"));
|
|
5113
|
+
|
|
5114
|
+
// src/contentSchema/components/_backOfficeFields__contr.js
|
|
5115
|
+
var import_mongoose8 = __toESM(require("mongoose"));
|
|
5116
|
+
var _backOfficeFields__contr = (thisContentModelRef) => ({
|
|
5117
|
+
//----- (USUSED AT THE MOMENT) version 2 properties for better tracking & interlinking of document clones ---- //
|
|
5118
|
+
isPrimaryDoc: Boolean,
|
|
5119
|
+
cloneStack: [import_mongoose8.default.Schema.Types.ObjectId],
|
|
5120
|
+
// the last item in the array being the last clone
|
|
5121
|
+
ogStack: [import_mongoose8.default.Schema.Types.ObjectId],
|
|
5122
|
+
// the last item in the array being the og-est og ( the primaryDoc )
|
|
5123
|
+
//----- END version 2 properties ---- //
|
|
5124
|
+
tagId: String,
|
|
5125
|
+
kp_published_status: {
|
|
5126
|
+
type: String,
|
|
5127
|
+
// options : draft | published | editPublished | awaitingModeration | underModeration
|
|
5128
|
+
default: "draft",
|
|
5129
|
+
required: true
|
|
5130
|
+
},
|
|
5131
|
+
warnings: [Object],
|
|
5132
|
+
kp_og_published_doc: {
|
|
5133
|
+
type: import_mongoose8.default.Schema.Types.ObjectId,
|
|
5134
|
+
ref: thisContentModelRef
|
|
5135
|
+
},
|
|
5136
|
+
// ----- mod rel --- //
|
|
5137
|
+
kp_pre_mod_doc: {
|
|
5138
|
+
type: import_mongoose8.default.Schema.Types.ObjectId
|
|
5139
|
+
},
|
|
5140
|
+
kp_is_pbl_taken_into_mod: Boolean,
|
|
5141
|
+
kp_is_mod_clone: Boolean,
|
|
5142
|
+
kp_moderator: {
|
|
5143
|
+
type: import_mongoose8.default.Schema.Types.ObjectId,
|
|
5144
|
+
ref: "user"
|
|
5145
|
+
},
|
|
5146
|
+
kp_date_mod_begin: Date,
|
|
5147
|
+
kp_date_mod_end: Date,
|
|
5148
|
+
kp_date_submitted: Date,
|
|
5149
|
+
// ----- END mod rel --- //
|
|
5150
|
+
kp_date_created: Date,
|
|
5151
|
+
kp_date_last_saved: Date,
|
|
5152
|
+
kp_date_published: {
|
|
5153
|
+
type: Date
|
|
5154
|
+
},
|
|
5155
|
+
editPublishedDocId: {
|
|
5156
|
+
//only comes on og docs that have an editPbl clone
|
|
5157
|
+
type: import_mongoose8.default.Schema.Types.ObjectId,
|
|
5158
|
+
ref: thisContentModelRef
|
|
5159
|
+
},
|
|
5160
|
+
currentEditor: {
|
|
5161
|
+
type: import_mongoose8.default.Schema.Types.ObjectId,
|
|
5162
|
+
//relevant for segments : staticPages & collection, where author dont matter.
|
|
5163
|
+
ref: "user"
|
|
5164
|
+
},
|
|
5165
|
+
lastActivity: {
|
|
5166
|
+
date: {
|
|
5167
|
+
type: Date
|
|
5168
|
+
}
|
|
5169
|
+
}
|
|
5170
|
+
});
|
|
5171
|
+
|
|
5172
|
+
// src/contentSchema/components/_comments__contr.js
|
|
5173
|
+
var import_mongoose9 = __toESM(require("mongoose"));
|
|
5174
|
+
var baseCommentsObj = {
|
|
5175
|
+
user: {
|
|
5176
|
+
userId: { type: import_mongoose9.default.Schema.Types.ObjectId, ref: "user" },
|
|
5177
|
+
profileType: String,
|
|
5178
|
+
name: String,
|
|
5179
|
+
avatar: Object
|
|
5180
|
+
},
|
|
5181
|
+
text: Object,
|
|
5182
|
+
dateCreated: Date,
|
|
5183
|
+
dateModified: [Date]
|
|
5184
|
+
//the start of tracking mod dates on things..experiment...
|
|
5185
|
+
};
|
|
5186
|
+
var _comments__contr = {
|
|
5187
|
+
comments: [
|
|
5188
|
+
{
|
|
5189
|
+
...baseCommentsObj,
|
|
5190
|
+
responses: [baseCommentsObj]
|
|
5191
|
+
}
|
|
5192
|
+
]
|
|
5193
|
+
};
|
|
5194
|
+
|
|
5195
|
+
// src/contentSchema/components/_reaction_contr.js
|
|
5196
|
+
var import_mongoose10 = __toESM(require("mongoose"));
|
|
5197
|
+
var baseSchema = {
|
|
5198
|
+
type: {
|
|
5199
|
+
type: String,
|
|
5200
|
+
enum: ["like", "celebrate", "inspiring", "learnings"]
|
|
5201
|
+
},
|
|
5202
|
+
dateCreated: Date,
|
|
5203
|
+
userId: { type: import_mongoose10.default.Schema.Types.ObjectId, ref: "user" }
|
|
5204
|
+
};
|
|
5205
|
+
var _reaction_contr = {
|
|
5206
|
+
reactions: [
|
|
5207
|
+
{
|
|
5208
|
+
...baseSchema
|
|
5209
|
+
}
|
|
5210
|
+
]
|
|
5211
|
+
};
|
|
5212
|
+
|
|
5213
|
+
// src/contentSchema/components/_constantMeta__contr.js
|
|
5214
|
+
var import_mongoose11 = __toESM(require("mongoose"));
|
|
5215
|
+
var _constantMeta__contr = {
|
|
5216
|
+
kp_contributed_by: {
|
|
5217
|
+
type: import_mongoose11.default.Schema.Types.ObjectId,
|
|
5218
|
+
ref: "user",
|
|
5219
|
+
required: true
|
|
5220
|
+
},
|
|
5221
|
+
kp_content_type: {
|
|
5222
|
+
type: String,
|
|
5223
|
+
required: true
|
|
5224
|
+
},
|
|
5225
|
+
// "thematic" or "attribute" — distinguishes thematic sub-themes/tags from
|
|
5226
|
+
// attribute classification categories (speech act, emotion, etc.).
|
|
5227
|
+
taxonomyRole: {
|
|
5228
|
+
type: String
|
|
5229
|
+
},
|
|
5230
|
+
// Only meaningful on sub-theme docs. When false, new tags cannot be created under
|
|
5231
|
+
// this sub-theme (by humans via API or by the inductive AI annotator).
|
|
5232
|
+
// Absent/true = allowed (default).
|
|
5233
|
+
allowNewTags: {
|
|
5234
|
+
type: Boolean
|
|
5235
|
+
},
|
|
5236
|
+
// Only meaningful on attribute-role sub-themes. "single" = pick one tag,
|
|
5237
|
+
// "multi" = pick one or more. Default "multi".
|
|
5238
|
+
selectMode: {
|
|
5239
|
+
type: String
|
|
5240
|
+
},
|
|
5241
|
+
// "autoAI" | "humanInTheLoop" — absent means human editor (default).
|
|
5242
|
+
createdVia: {
|
|
5243
|
+
type: String
|
|
5244
|
+
}
|
|
5245
|
+
};
|
|
5246
|
+
|
|
5247
|
+
// src/contentSchema/components/_fixedMainFields__contr.js
|
|
5248
|
+
var _fixedMainFields__contr = {
|
|
5249
|
+
/**
|
|
5250
|
+
* hard coding title & subtitle is required because,
|
|
5251
|
+
* in tpl categories like 'researchNotes' the title comp is never defined in the template.
|
|
5252
|
+
* instead, it is hardcoded in the code, and it expects a 'main.title' value path.
|
|
5253
|
+
* Hence the hardcode below. Besides its not a problem, since, if in the template a
|
|
5254
|
+
* main.title component is indeed defined, then these hardcodes would be overwritten by
|
|
5255
|
+
* mainExtract. Which is what we want.
|
|
5256
|
+
*/
|
|
5257
|
+
title: {
|
|
5258
|
+
type: String,
|
|
5259
|
+
cast: (v) => typeof v === "string" ? String(v) : Object(v)
|
|
5260
|
+
},
|
|
5261
|
+
subtitle: {
|
|
5262
|
+
type: String,
|
|
5263
|
+
cast: (v) => typeof v === "string" ? String(v) : Object(v)
|
|
5264
|
+
},
|
|
5265
|
+
titleInfo: String
|
|
5266
|
+
// this is used to show the meta text ("This story is supported by AI-assisted bot") on the published page
|
|
5267
|
+
};
|
|
5268
|
+
|
|
5269
|
+
// src/contentSchema/extractAllUniqueAnnoTagTypesFromTpl.js
|
|
5270
|
+
var extractAllUniqueAnnoTagTypesFromTpl = (extractedBlocks, { smTagTypesConfig } = {}) => {
|
|
5271
|
+
return Array.from(
|
|
5272
|
+
new Set(
|
|
5273
|
+
blockRegistry.getAnnotationEnabledBlocks(extractedBlocks).flatMap((bl) => [
|
|
5274
|
+
...blockRegistry.getTagTypesConfig(bl, smTagTypesConfig).map((tt) => tt.tagType),
|
|
5275
|
+
...bl.props?.annotation?.toggleTagTypesConfig?.map((tt) => tt.tagType) || []
|
|
5276
|
+
])
|
|
5277
|
+
)
|
|
5278
|
+
);
|
|
5279
|
+
};
|
|
5280
|
+
|
|
5281
|
+
// src/contentSchema/getTypesForSchemaFromTpl/compToTypeMap/getConditionalDataType.js
|
|
5282
|
+
var getConditionalDataType = ({ block }) => {
|
|
5283
|
+
if (block.props?.saveValueAsString)
|
|
5284
|
+
return {
|
|
5285
|
+
type: Number,
|
|
5286
|
+
cast: (v) => {
|
|
5287
|
+
if (typeof v === "number" && !isNaN(v)) {
|
|
5288
|
+
return Number(v);
|
|
5289
|
+
} else if (typeof v === "string") {
|
|
5290
|
+
return String(v);
|
|
5291
|
+
} else {
|
|
5292
|
+
return Boolean(v);
|
|
5293
|
+
}
|
|
5294
|
+
}
|
|
5295
|
+
};
|
|
5296
|
+
else {
|
|
5297
|
+
const returnValue = ["DropdownMulti", "CheckboxList"].includes(block.comp) ? { type: Array } : { type: Object };
|
|
5298
|
+
return returnValue;
|
|
5299
|
+
}
|
|
5300
|
+
};
|
|
5301
|
+
|
|
5302
|
+
// src/contentSchema/utils/extractNumberFromValue.js
|
|
5303
|
+
var extractNumberFromValue = (value) => {
|
|
5304
|
+
if (value === null || value === void 0) return void 0;
|
|
5305
|
+
if (typeof value === "number") return isNaN(value) ? void 0 : value;
|
|
5306
|
+
const str = String(value).trim();
|
|
5307
|
+
if (str === "") return void 0;
|
|
5308
|
+
const directParse = Number(str);
|
|
5309
|
+
if (!isNaN(directParse)) return directParse;
|
|
5310
|
+
const commaNumberMatch = str.match(/-?\d[\d,]*\d(?:\.\d+)?/);
|
|
5311
|
+
if (commaNumberMatch && commaNumberMatch[0].includes(",")) {
|
|
5312
|
+
const num = Number(commaNumberMatch[0].replace(/,/g, ""));
|
|
5313
|
+
if (!isNaN(num)) return num;
|
|
5314
|
+
}
|
|
5315
|
+
const numberMatch = str.match(/-?\d+(?:\.\d+)?/);
|
|
5316
|
+
if (numberMatch) {
|
|
5317
|
+
const num = Number(numberMatch[0]);
|
|
5318
|
+
return isNaN(num) ? void 0 : num;
|
|
5319
|
+
}
|
|
5320
|
+
return void 0;
|
|
5321
|
+
};
|
|
5322
|
+
|
|
5323
|
+
// src/contentSchema/getTypesForSchemaFromTpl/compToTypeMap/index.js
|
|
5324
|
+
var compToTypeMap = ({ block }) => {
|
|
5325
|
+
const comp = block.comp;
|
|
5326
|
+
const blockDef = blockRegistry.getBlock(comp);
|
|
5327
|
+
if (blockDef) return blockDef.mongoSchemaType;
|
|
5328
|
+
switch (comp) {
|
|
5329
|
+
case "SummaryInput":
|
|
5330
|
+
case "KPRichInput":
|
|
5331
|
+
//deprecated
|
|
5332
|
+
case "RichTextEditor":
|
|
5333
|
+
//deprecated
|
|
5334
|
+
case "StaticRichText":
|
|
5335
|
+
case "CardRadioInput":
|
|
5336
|
+
case "NumberRangeInput":
|
|
5337
|
+
case "KPLikeContactChannelsBlock":
|
|
5338
|
+
case "TitleInput":
|
|
5339
|
+
case "SubtitleInput":
|
|
5340
|
+
case "PluginSentinel":
|
|
5341
|
+
case "DynamicRefetchDropdown":
|
|
5342
|
+
case "VideoInput":
|
|
5343
|
+
// data type for videoinput should actually be array, but since the old version of the comp used a string data type, we are currently setting it to object to avoid error
|
|
5344
|
+
case "SimpleTable":
|
|
5345
|
+
case "GroupQuestionsInputSingle":
|
|
5346
|
+
return { type: Object };
|
|
5347
|
+
case "ProfileImageInput":
|
|
5348
|
+
case "ResourceInput":
|
|
5349
|
+
case "ImageInput":
|
|
5350
|
+
case "VideoInput":
|
|
5351
|
+
case "Repeater":
|
|
5352
|
+
case "RepeaterTabs":
|
|
5353
|
+
case "PDFInput":
|
|
5354
|
+
case "TextInputToArray":
|
|
5355
|
+
return { type: Array };
|
|
5356
|
+
case "RichTextAsPlainText":
|
|
5357
|
+
case "PasswordInput":
|
|
5358
|
+
case "EmailInput":
|
|
5359
|
+
case "TextInput":
|
|
5360
|
+
case "URLInput":
|
|
5361
|
+
case "PhoneInput":
|
|
5362
|
+
case "KPMetaPrimary":
|
|
5363
|
+
case "IFrameInput":
|
|
5364
|
+
case "JSONInput":
|
|
5365
|
+
return { type: String };
|
|
5366
|
+
case "NumberInput":
|
|
5367
|
+
return {
|
|
5368
|
+
type: Number,
|
|
5369
|
+
// Smart extraction of numbers from messy values (e.g. "18+", "18 years old", "$100")
|
|
5370
|
+
// Uses extractNumberFromValue utility for consistent behavior with direct import
|
|
5371
|
+
set: extractNumberFromValue
|
|
5372
|
+
};
|
|
5373
|
+
case "DropdownSingleLanguage":
|
|
5374
|
+
case "ComboboxSingleLanguage":
|
|
5375
|
+
return {
|
|
5376
|
+
type: [
|
|
5377
|
+
{
|
|
5378
|
+
display: String,
|
|
5379
|
+
value: String
|
|
5380
|
+
}
|
|
5381
|
+
]
|
|
5382
|
+
};
|
|
5383
|
+
case "DatePicker":
|
|
5384
|
+
return { type: Date };
|
|
5385
|
+
case "DateTimePicker":
|
|
5386
|
+
return { type: Date };
|
|
5387
|
+
case "TimePicker":
|
|
5388
|
+
return {
|
|
5389
|
+
type: String,
|
|
5390
|
+
// Validates time is in 24-hour format (00:00-23:59) with hours 00-23, a colon, and minutes 00-59
|
|
5391
|
+
validate: {
|
|
5392
|
+
validator: function(v) {
|
|
5393
|
+
return /^([01]\d|2[0-3]):([0-5]\d)$/.test(v);
|
|
5394
|
+
},
|
|
5395
|
+
message: (props) => `${props.value} is not a valid time format!`
|
|
5396
|
+
}
|
|
5397
|
+
};
|
|
5398
|
+
case "CheckboxButton":
|
|
5399
|
+
return { type: Boolean };
|
|
5400
|
+
case "TagsInputMulti":
|
|
5401
|
+
case "FragmentsSet":
|
|
5402
|
+
case "FragmentsDict":
|
|
5403
|
+
case "TagsInputSingle":
|
|
5404
|
+
return {
|
|
5405
|
+
type: {
|
|
5406
|
+
collectionId: String,
|
|
5407
|
+
data: [
|
|
5408
|
+
{
|
|
5409
|
+
_id: {
|
|
5410
|
+
type: String,
|
|
5411
|
+
index: block.index === true
|
|
5412
|
+
},
|
|
5413
|
+
display: String,
|
|
5414
|
+
tagId: String
|
|
5415
|
+
}
|
|
5416
|
+
]
|
|
5417
|
+
}
|
|
5418
|
+
};
|
|
5419
|
+
case "SPECIAL_AnnotationTagsField":
|
|
5420
|
+
return {
|
|
5421
|
+
/**
|
|
5422
|
+
* very important that we wrap the whole thing in type,
|
|
5423
|
+
* because, if the tag name itself is type (like in the case of noora),
|
|
5424
|
+
* then it completely confuses mongoose, since type is a reserved word,
|
|
5425
|
+
* and causes all sorts of bugs.
|
|
5426
|
+
*/
|
|
5427
|
+
type: {
|
|
5428
|
+
collectionId: String,
|
|
5429
|
+
data: Array
|
|
5430
|
+
//leaving this weakly typed for now cuz annotations have several other properties in here which i am not aware of..
|
|
5431
|
+
}
|
|
5432
|
+
};
|
|
5433
|
+
case "LinkEmbedWithInput":
|
|
5434
|
+
return {
|
|
5435
|
+
type: {
|
|
5436
|
+
url: String,
|
|
5437
|
+
metadata: Object
|
|
5438
|
+
}
|
|
5439
|
+
};
|
|
5440
|
+
case "CheckboxList":
|
|
5441
|
+
//new
|
|
5442
|
+
case "RadioList":
|
|
5443
|
+
//new
|
|
5444
|
+
case "DropdownSingle":
|
|
5445
|
+
//new
|
|
5446
|
+
case "DropdownMulti":
|
|
5447
|
+
//new
|
|
5448
|
+
case "SwitchDouble":
|
|
5449
|
+
case "SwitchSingle":
|
|
5450
|
+
return getConditionalDataType({ block });
|
|
5451
|
+
case "DateTimeRangePicker":
|
|
5452
|
+
return {
|
|
5453
|
+
type: {
|
|
5454
|
+
date: Date,
|
|
5455
|
+
timeRange: Array
|
|
5456
|
+
}
|
|
5457
|
+
};
|
|
5458
|
+
case "DateRangePicker":
|
|
5459
|
+
return { type: Array };
|
|
5460
|
+
}
|
|
5461
|
+
};
|
|
5462
|
+
|
|
5463
|
+
// src/contentSchema/getTypesForSchemaFromTpl/index.js
|
|
5464
|
+
var getTypesForSchemaFromTpl = (options = {}) => {
|
|
5465
|
+
const { tpl, smTagTypesConfig } = options;
|
|
5466
|
+
const defaultSchemaFieldsByTplLayout = {
|
|
5467
|
+
researchNotes2: {
|
|
5468
|
+
tags: {
|
|
5469
|
+
quotes: {
|
|
5470
|
+
collectionId: String,
|
|
5471
|
+
data: Array
|
|
5472
|
+
},
|
|
5473
|
+
vocabs: {
|
|
5474
|
+
collectionId: String,
|
|
5475
|
+
data: Array
|
|
5476
|
+
}
|
|
5477
|
+
}
|
|
5478
|
+
}
|
|
5479
|
+
};
|
|
5480
|
+
let mainExtract = {};
|
|
5481
|
+
let metaExtract = {};
|
|
5482
|
+
let tagsExtract = {};
|
|
5483
|
+
let rootExtract = {};
|
|
5484
|
+
let annoExtract = {
|
|
5485
|
+
...defaultSchemaFieldsByTplLayout[tpl?.category] || {}
|
|
5486
|
+
};
|
|
5487
|
+
const extractedBlocks = extractAllBlocksFromTpl({ tpl });
|
|
5488
|
+
const annotationTagTypes = extractAllUniqueAnnoTagTypesFromTpl(extractedBlocks, {
|
|
5489
|
+
smTagTypesConfig
|
|
5490
|
+
});
|
|
5491
|
+
annotationTagTypes.forEach((annoTagType) => {
|
|
5492
|
+
const annotationFieldDef = compToTypeMap({
|
|
5493
|
+
block: { comp: "SPECIAL_AnnotationTagsField" }
|
|
5494
|
+
});
|
|
5495
|
+
annoExtract = setVal(annoExtract, `tags.${annoTagType}`, annotationFieldDef);
|
|
5496
|
+
});
|
|
5497
|
+
extractedBlocks.forEach((block) => {
|
|
5498
|
+
const { valuePath } = block;
|
|
5499
|
+
if (!valuePath) return;
|
|
5500
|
+
const pathSegments = valuePath.split(".");
|
|
5501
|
+
const [prefix, ...remainingPath] = pathSegments;
|
|
5502
|
+
const nestedPath = remainingPath.join(".");
|
|
5503
|
+
const fieldDefinition = compToTypeMap({ block });
|
|
5504
|
+
switch (prefix) {
|
|
5505
|
+
case "meta":
|
|
5506
|
+
metaExtract = setVal(
|
|
5507
|
+
metaExtract,
|
|
5508
|
+
nestedPath,
|
|
5509
|
+
// "author.name" (without "meta" prefix)
|
|
5510
|
+
fieldDefinition
|
|
5511
|
+
);
|
|
5512
|
+
break;
|
|
5513
|
+
case "main":
|
|
5514
|
+
mainExtract = setVal(
|
|
5515
|
+
mainExtract,
|
|
5516
|
+
nestedPath,
|
|
5517
|
+
// "content.title" (without "main" prefix)
|
|
5518
|
+
fieldDefinition
|
|
5519
|
+
);
|
|
5520
|
+
break;
|
|
5521
|
+
case "tags":
|
|
5522
|
+
tagsExtract = setVal(
|
|
5523
|
+
tagsExtract,
|
|
5524
|
+
nestedPath,
|
|
5525
|
+
// "categories.primary" (without "tags" prefix)
|
|
5526
|
+
fieldDefinition
|
|
5527
|
+
);
|
|
5528
|
+
break;
|
|
5529
|
+
default:
|
|
5530
|
+
if (block.comp && block.comp !== "TplMetaBlock" && valuePath !== ".") {
|
|
5531
|
+
rootExtract = setVal(
|
|
5532
|
+
rootExtract,
|
|
5533
|
+
valuePath,
|
|
5534
|
+
// Use full path since it's not prefixed
|
|
5535
|
+
fieldDefinition
|
|
5536
|
+
);
|
|
5537
|
+
}
|
|
5538
|
+
break;
|
|
5539
|
+
}
|
|
5540
|
+
});
|
|
5541
|
+
return {
|
|
5542
|
+
metaExtract,
|
|
5543
|
+
mainExtract,
|
|
5544
|
+
tagsExtract,
|
|
5545
|
+
rootExtract,
|
|
5546
|
+
annoExtract
|
|
5547
|
+
};
|
|
5548
|
+
};
|
|
5549
|
+
|
|
5550
|
+
// src/contentSchema/genContentTypeSchema.js
|
|
5551
|
+
var genContentTypeSchema = ({ contentType, segment, contentTypeConfig, smTagTypesConfig }) => {
|
|
5552
|
+
let SchemaToReturn;
|
|
5553
|
+
let {
|
|
5554
|
+
mainExtract = {},
|
|
5555
|
+
metaExtract = {},
|
|
5556
|
+
tagsExtract = {},
|
|
5557
|
+
rootExtract = {},
|
|
5558
|
+
annoExtract = {}
|
|
5559
|
+
} = getTypesForSchemaFromTpl({ tpl: contentTypeConfig, smTagTypesConfig });
|
|
5560
|
+
const schemaDef = {
|
|
5561
|
+
/**
|
|
5562
|
+
* this metadata is v.important from an optimization point of view.
|
|
5563
|
+
* it helps with getAllContrModelsByTenant, without having to
|
|
5564
|
+
* query the database for content type configurations.
|
|
5565
|
+
*/
|
|
5566
|
+
metadata: {
|
|
5567
|
+
isContentType: { type: String, default: true },
|
|
5568
|
+
contentType: { type: String, default: contentType },
|
|
5569
|
+
segment: { type: String, default: segment }
|
|
5570
|
+
},
|
|
5571
|
+
..._backOfficeFields__contr(contentType),
|
|
5572
|
+
...rootExtract,
|
|
5573
|
+
tags: {
|
|
5574
|
+
...tagsExtract
|
|
5575
|
+
},
|
|
5576
|
+
annotations: {
|
|
5577
|
+
...annoExtract
|
|
5578
|
+
},
|
|
5579
|
+
meta: {
|
|
5580
|
+
..._constantMeta__contr,
|
|
5581
|
+
...metaExtract
|
|
5582
|
+
},
|
|
5583
|
+
main: {
|
|
5584
|
+
..._fixedMainFields__contr,
|
|
5585
|
+
...mainExtract
|
|
5586
|
+
},
|
|
5587
|
+
archive: Object,
|
|
5588
|
+
//burial ground for past fields that have eventually been removed from the tpl (and therefore the schema)
|
|
5589
|
+
translations: Object,
|
|
5590
|
+
reviewerNotes: Object,
|
|
5591
|
+
..._reaction_contr,
|
|
5592
|
+
..._comments__contr,
|
|
5593
|
+
...segment === "publishing" && {
|
|
5594
|
+
shares: {
|
|
5595
|
+
count: {
|
|
5596
|
+
type: Number,
|
|
5597
|
+
default: 0
|
|
5598
|
+
}
|
|
5599
|
+
},
|
|
5600
|
+
views: {
|
|
5601
|
+
count: {
|
|
5602
|
+
type: Number,
|
|
5603
|
+
default: 0
|
|
5604
|
+
}
|
|
5605
|
+
}
|
|
5606
|
+
},
|
|
5607
|
+
updates: Object
|
|
5608
|
+
// DEPRECATED. DONT USE IT. DONT DELETE EITHER. ELSE WE WILL HAVE TO MAKE DATA UPDATES
|
|
5609
|
+
};
|
|
5610
|
+
SchemaToReturn = new import_mongoose12.default.Schema(schemaDef, {
|
|
5611
|
+
collection: contentType,
|
|
5612
|
+
toJSON: { virtuals: true },
|
|
5613
|
+
// So `res.json()` and other `JSON.stringify()` functions include virtuals
|
|
5614
|
+
toObject: { virtuals: true },
|
|
5615
|
+
// So `toObject()` output includes virtuals
|
|
5616
|
+
strict: "throw"
|
|
5617
|
+
//will throw an error if we try to add properties that arent on this schema
|
|
5618
|
+
});
|
|
5619
|
+
SchemaToReturn.index(
|
|
5620
|
+
{ tagId: 1 },
|
|
5621
|
+
...segment === "collections" ? [
|
|
5622
|
+
{
|
|
5623
|
+
unique: true,
|
|
5624
|
+
partialFilterExpression: {
|
|
5625
|
+
kp_published_status: { $eq: "published" }
|
|
5626
|
+
}
|
|
5627
|
+
}
|
|
5628
|
+
] : []
|
|
5629
|
+
);
|
|
5630
|
+
SchemaToReturn.index({
|
|
5631
|
+
kp_published_status: 1,
|
|
5632
|
+
kp_date_published: -1,
|
|
5633
|
+
_id: -1
|
|
5634
|
+
});
|
|
5635
|
+
SchemaToReturn.index(
|
|
5636
|
+
{ kp_published_status: 1, "main.title": 1, _id: 1 },
|
|
5637
|
+
{ collation: { locale: "en", numericOrdering: true } }
|
|
5638
|
+
);
|
|
5639
|
+
SchemaToReturn.index({ kp_published_status: 1, kp_is_mod_clone: 1 });
|
|
5640
|
+
return SchemaToReturn;
|
|
5641
|
+
};
|
|
5642
|
+
|
|
5643
|
+
// src/TenantRegistry.js
|
|
5644
|
+
var import_storage = require("@google-cloud/storage");
|
|
5645
|
+
var BUCKET_NAME = "dots-system-configs";
|
|
5646
|
+
var FILE_NAME = "tenantRegistry.json";
|
|
5647
|
+
var TenantRegistry = class {
|
|
5648
|
+
// ─── Initialization ─────────────────────────────────
|
|
5649
|
+
/**
|
|
5650
|
+
* Must be awaited at startup before any sync getter is called.
|
|
5651
|
+
* Uses Application Default Credentials (ADC) for GCS auth.
|
|
5652
|
+
*/
|
|
5653
|
+
static async init() {
|
|
5654
|
+
this._data = await this._load();
|
|
5655
|
+
this._rebuildCaches();
|
|
5656
|
+
this._lastRefreshAt = Date.now();
|
|
5657
|
+
}
|
|
5658
|
+
/**
|
|
5659
|
+
* Reload from GCS when this instance sees an unknown tenant/origin — likely one
|
|
5660
|
+
* provisioned on another instance after startup. Rate-limited; on failure keeps
|
|
5661
|
+
* the existing cache (never throws). Returns true if a reload actually ran.
|
|
5662
|
+
*/
|
|
5663
|
+
static async refreshIfStale() {
|
|
5664
|
+
const now = Date.now();
|
|
5665
|
+
if (now - this._lastRefreshAt < this.REFRESH_COOLDOWN_MS) return false;
|
|
5666
|
+
this._lastRefreshAt = now;
|
|
5667
|
+
try {
|
|
5668
|
+
this._data = await this._load();
|
|
5669
|
+
this._rebuildCaches();
|
|
5670
|
+
return true;
|
|
5671
|
+
} catch (err) {
|
|
5672
|
+
console.error(`[TenantRegistry] refresh failed (keeping cache): ${err.message}`);
|
|
5673
|
+
return false;
|
|
5674
|
+
}
|
|
5675
|
+
}
|
|
5676
|
+
// ─── Storage layer (swap only these to change backend) ───
|
|
5677
|
+
static async _load() {
|
|
5678
|
+
const storage = new import_storage.Storage();
|
|
5679
|
+
const file = storage.bucket(BUCKET_NAME).file(FILE_NAME);
|
|
5680
|
+
const [buffer] = await file.download();
|
|
5681
|
+
const { data } = JSON.parse(buffer.toString("utf-8"));
|
|
5682
|
+
console.log(`[TenantRegistry] Loaded ${data.length} tenants from GCS`);
|
|
5683
|
+
return data;
|
|
5684
|
+
}
|
|
5685
|
+
static async _save() {
|
|
5686
|
+
const storage = new import_storage.Storage();
|
|
5687
|
+
const file = storage.bucket(BUCKET_NAME).file(FILE_NAME);
|
|
5688
|
+
await file.save(JSON.stringify({ data: this._data }, null, 2), {
|
|
5689
|
+
contentType: "application/json"
|
|
5690
|
+
});
|
|
5691
|
+
}
|
|
5692
|
+
// ─── End storage layer ──────────────────────────────
|
|
5693
|
+
static _rebuildCaches() {
|
|
5694
|
+
this._byEnv = {};
|
|
5695
|
+
this._selfManagedSet = /* @__PURE__ */ new Set();
|
|
5696
|
+
for (const entry of this._data) {
|
|
5697
|
+
const { tenantId, urls, isSelfManaged } = entry;
|
|
5698
|
+
if (isSelfManaged) this._selfManagedSet.add(tenantId);
|
|
5699
|
+
for (const env of ["dev", "staging", "prod"]) {
|
|
5700
|
+
if (!urls[env]) continue;
|
|
5701
|
+
if (!this._byEnv[env]) this._byEnv[env] = {};
|
|
5702
|
+
this._byEnv[env][tenantId] = urls[env];
|
|
5703
|
+
}
|
|
5704
|
+
}
|
|
5705
|
+
}
|
|
5706
|
+
// ─── Sync getters ───────────────────────────────────
|
|
5707
|
+
/** { tenantId: url } for the given env */
|
|
5708
|
+
static getMappingsForEnv(env) {
|
|
5709
|
+
return this._byEnv[env] || {};
|
|
5710
|
+
}
|
|
5711
|
+
/** [url, url, ...] for the given env */
|
|
5712
|
+
static getUrlsForEnv(env) {
|
|
5713
|
+
return Object.values(this._byEnv[env] || {});
|
|
5714
|
+
}
|
|
5715
|
+
/** [tenantId, tenantId, ...] for the given env */
|
|
5716
|
+
static getTenantIdsForEnv(env) {
|
|
5717
|
+
return Object.keys(this._byEnv[env] || {});
|
|
5718
|
+
}
|
|
5719
|
+
/** Single URL for a tenant+env, or undefined */
|
|
5720
|
+
static getUrl(tenant, env) {
|
|
5721
|
+
return (this._byEnv[env] || {})[tenant];
|
|
5722
|
+
}
|
|
5723
|
+
static isSelfManaged(tenant) {
|
|
5724
|
+
return this._selfManagedSet.has(tenant);
|
|
5725
|
+
}
|
|
5726
|
+
static getSelfManagedTenantIds() {
|
|
5727
|
+
return [...this._selfManagedSet];
|
|
5728
|
+
}
|
|
5729
|
+
/** Full registry data array (read-only snapshot) */
|
|
5730
|
+
static getData() {
|
|
5731
|
+
return this._data;
|
|
5732
|
+
}
|
|
5733
|
+
// ─── Mutator ────────────────────────────────────────
|
|
5734
|
+
/**
|
|
5735
|
+
* Idempotent: removes existing entry for tenantId (if any), then adds.
|
|
5736
|
+
* Persists to GCS and updates in-memory caches.
|
|
5737
|
+
*/
|
|
5738
|
+
static async addTenant({ tenantId, urls, isSelfManaged = false }) {
|
|
5739
|
+
this._data = this._data.filter((e) => e.tenantId !== tenantId);
|
|
5740
|
+
this._data.push({ tenantId, urls, isSelfManaged });
|
|
5741
|
+
await this._save();
|
|
5742
|
+
this._rebuildCaches();
|
|
5743
|
+
}
|
|
5744
|
+
/**
|
|
5745
|
+
* Idempotent inverse of addTenant: drops the tenant's entry from the registry,
|
|
5746
|
+
* persists to GCS, and rebuilds in-memory caches (so the tenant stops resolving
|
|
5747
|
+
* on THIS instance immediately). Returns true if an entry was actually removed.
|
|
5748
|
+
* Other running instances pick up the removal via refreshIfStale() on their next
|
|
5749
|
+
* unknown-origin miss.
|
|
5750
|
+
*/
|
|
5751
|
+
static async removeTenant(tenantId) {
|
|
5752
|
+
const before = this._data.length;
|
|
5753
|
+
this._data = this._data.filter((e) => e.tenantId !== tenantId);
|
|
5754
|
+
const removed = this._data.length < before;
|
|
5755
|
+
if (removed) {
|
|
5756
|
+
await this._save();
|
|
5757
|
+
this._rebuildCaches();
|
|
5758
|
+
}
|
|
5759
|
+
return removed;
|
|
5760
|
+
}
|
|
5761
|
+
};
|
|
5762
|
+
// ─── In-memory state ────────────────────────────────
|
|
5763
|
+
__publicField(TenantRegistry, "_data", null);
|
|
5764
|
+
// [{ tenantId, urls: { dev, staging, prod }, isSelfManaged }]
|
|
5765
|
+
__publicField(TenantRegistry, "_byEnv", {});
|
|
5766
|
+
// { dev: { tenantId: url }, staging: {...}, prod: {...} }
|
|
5767
|
+
__publicField(TenantRegistry, "_selfManagedSet", /* @__PURE__ */ new Set());
|
|
5768
|
+
// On-miss refresh rate-limit (see refreshIfStale).
|
|
5769
|
+
__publicField(TenantRegistry, "_lastRefreshAt", 0);
|
|
5770
|
+
__publicField(TenantRegistry, "REFRESH_COOLDOWN_MS", 3e4);
|
|
5771
|
+
|
|
5772
|
+
// src/contentSchema/selfManagedAnnotationTagTypesConfig.js
|
|
5773
|
+
var SM_ANNOTATION_TAG_TYPES_CONFIG = [{ tagType: "tags" }];
|
|
5774
|
+
var getSmTagTypesConfigForTenant = async (tenant) => {
|
|
5775
|
+
return TenantRegistry.isSelfManaged(tenant) ? SM_ANNOTATION_TAG_TYPES_CONFIG : null;
|
|
5776
|
+
};
|
|
5777
|
+
|
|
5778
|
+
// src/db/getContrModelByTenant.js
|
|
5779
|
+
var import_getModelByTenant3 = __toESM(require_getModelByTenant());
|
|
5780
|
+
init_getDbByTenant();
|
|
5781
|
+
|
|
5782
|
+
// src/db/getTpl.js
|
|
5095
5783
|
var import_getModelByTenant2 = __toESM(require_getModelByTenant());
|
|
5784
|
+
var getTpl = async ({ tenant, contentType, env, source = "db" }) => {
|
|
5785
|
+
if (source === "cache") {
|
|
5786
|
+
return RedisCacheConnector.findOneConfigInCache({
|
|
5787
|
+
tenant,
|
|
5788
|
+
modelName: "tpl",
|
|
5789
|
+
type: contentType,
|
|
5790
|
+
env
|
|
5791
|
+
});
|
|
5792
|
+
}
|
|
5793
|
+
return (0, import_getModelByTenant2.getTplModelByTenant)({ tenant, env }).findOne({ kp_content_type: contentType }).lean();
|
|
5794
|
+
};
|
|
5795
|
+
|
|
5796
|
+
// src/db/getContrModelByTenant.js
|
|
5797
|
+
var getContrModelByTenant = async ({
|
|
5798
|
+
tenant,
|
|
5799
|
+
contentType,
|
|
5800
|
+
env = process.env.ENV,
|
|
5801
|
+
// v.imp, else the reuse check below always fails when env isn't passed (env is meant to be optional)
|
|
5802
|
+
tpl,
|
|
5803
|
+
//optional : if passed uses it instead of fetching the tpl for this contenttype from db. important in the case of _createTpl, where, within a transaction we create a tpl and then use this tpl to create some content against it. since this is within a transaction, this newly created tpl wont be available in the db just yet.
|
|
5804
|
+
tplSource = "db",
|
|
5805
|
+
// "db" (okf-be default, original semantics) | "cache" (okf-sub — tpl cache is kept in sync on every tpl write)
|
|
5806
|
+
/**
|
|
5807
|
+
* When true, the registered-model reuse is SKIPPED — the schema is rebuilt
|
|
5808
|
+
* from the current tpl on every call (~5-10ms CPU + the tpl read).
|
|
5809
|
+
*
|
|
5810
|
+
* okf-sub passes this on every collab call: unlike okf-be — which
|
|
5811
|
+
* invalidates registered models on tpl change via
|
|
5812
|
+
* reRegisterModelInLocalCache + the '${env}-tpl-model-recache' Pub/Sub —
|
|
5813
|
+
* okf-sub subscribes to nothing, so a reused model could carry a stale
|
|
5814
|
+
* schema after a tpl edit, and strict:"throw" would reject collab flushes
|
|
5815
|
+
* for newly added fields. Rebuilding every time is correct by
|
|
5816
|
+
* construction: no freshness bookkeeping for tpl write paths (or raw
|
|
5817
|
+
* mongodbscripts that mutate tpls + recache) to ever get wrong.
|
|
5818
|
+
*/
|
|
5819
|
+
rebuild = false
|
|
5820
|
+
}) => {
|
|
5821
|
+
try {
|
|
5822
|
+
const db = getDbByTenant({ tenant });
|
|
5823
|
+
if (!rebuild && db.models[contentType] && env === process.env.ENV && !tpl) {
|
|
5824
|
+
const modelToReturn = db.model(contentType);
|
|
5825
|
+
if (modelToReturn.schema.obj.metadata && modelToReturn.schema.obj.metadata.isContentType && modelToReturn.schema.obj.metadata.isContentType.default === true) {
|
|
5826
|
+
return modelToReturn;
|
|
5827
|
+
}
|
|
5828
|
+
return void 0;
|
|
5829
|
+
}
|
|
5830
|
+
const contentTypeConfig = tpl || await getTpl({ tenant, contentType, env, source: tplSource });
|
|
5831
|
+
if (!contentTypeConfig) return void 0;
|
|
5832
|
+
const smTagTypesConfig = await getSmTagTypesConfigForTenant(tenant);
|
|
5833
|
+
const thisSchema = genContentTypeSchema({
|
|
5834
|
+
contentType,
|
|
5835
|
+
segment: contentTypeConfig.general?.segment,
|
|
5836
|
+
contentTypeConfig,
|
|
5837
|
+
smTagTypesConfig
|
|
5838
|
+
});
|
|
5839
|
+
if (rebuild && db.models[contentType]) {
|
|
5840
|
+
db.deleteModel(contentType);
|
|
5841
|
+
}
|
|
5842
|
+
return (0, import_getModelByTenant3.getModelByTenant)({
|
|
5843
|
+
tenant,
|
|
5844
|
+
modelName: contentType,
|
|
5845
|
+
schema: thisSchema,
|
|
5846
|
+
env
|
|
5847
|
+
});
|
|
5848
|
+
} catch (err) {
|
|
5849
|
+
throw err;
|
|
5850
|
+
}
|
|
5851
|
+
};
|
|
5852
|
+
|
|
5853
|
+
// src/node.ts
|
|
5096
5854
|
init_models();
|
|
5855
|
+
|
|
5856
|
+
// src/collab/liveCollabDocMarker.js
|
|
5857
|
+
var TTL_SECONDS = 300;
|
|
5858
|
+
var LIVE_COLLAB_DOC_REFRESH_MS = 12e4;
|
|
5859
|
+
var buildKey = ({ env, tenant, contentType, docId }) => `${env || RedisCacheConnector.getEnv()}:liveCollabDoc:${tenant}:${contentType}:${docId}`;
|
|
5860
|
+
var markLiveCollabDoc = async ({ tenant, contentType, docId, env }) => {
|
|
5861
|
+
const client = RedisCacheConnector.getClient(env || RedisCacheConnector.getEnv());
|
|
5862
|
+
await client.set(buildKey({ env, tenant, contentType, docId }), Date.now(), "EX", TTL_SECONDS);
|
|
5863
|
+
};
|
|
5864
|
+
var clearLiveCollabDoc = async ({ tenant, contentType, docId, env }) => {
|
|
5865
|
+
const client = RedisCacheConnector.getClient(env || RedisCacheConnector.getEnv());
|
|
5866
|
+
await client.del(buildKey({ env, tenant, contentType, docId }));
|
|
5867
|
+
};
|
|
5868
|
+
var isLiveCollabDoc = async ({ tenant, contentType, docId, env }) => {
|
|
5869
|
+
const client = RedisCacheConnector.getClient(env || RedisCacheConnector.getEnv());
|
|
5870
|
+
return await client.exists(buildKey({ env, tenant, contentType, docId })) === 1;
|
|
5871
|
+
};
|
|
5872
|
+
|
|
5873
|
+
// src/node.ts
|
|
5097
5874
|
var import_WorkerManager = __toESM(require_WorkerManager());
|
|
5098
5875
|
var import_ProducerManager = __toESM(require_ProducerManager());
|
|
5099
5876
|
var import_BaseProducer = __toESM(require_BaseProducer());
|
|
@@ -5122,6 +5899,7 @@ var import_GET_GLOBAL_BULLMQ_CONFIG = __toESM(require_GET_GLOBAL_BULLMQ_CONFIG()
|
|
|
5122
5899
|
GET_GLOBAL_BULLMQ_CONFIG,
|
|
5123
5900
|
GeneratedEntitiesSchema,
|
|
5124
5901
|
GeneratedTopicsSchema,
|
|
5902
|
+
LIVE_COLLAB_DOC_REFRESH_MS,
|
|
5125
5903
|
MONGO_SCHEMA_PRESETS,
|
|
5126
5904
|
MongoConnector,
|
|
5127
5905
|
PlatformConfigsSchema,
|
|
@@ -5129,9 +5907,13 @@ var import_GET_GLOBAL_BULLMQ_CONFIG = __toESM(require_GET_GLOBAL_BULLMQ_CONFIG()
|
|
|
5129
5907
|
RedisCacheConnector,
|
|
5130
5908
|
SecretManagerConnector,
|
|
5131
5909
|
TEMP_removeDuplicateFilters,
|
|
5910
|
+
TenantRegistry,
|
|
5132
5911
|
TplSchema,
|
|
5133
5912
|
UI_CONTENT,
|
|
5134
5913
|
WorkerManager,
|
|
5914
|
+
_backOfficeFields__contr,
|
|
5915
|
+
_constantMeta__contr,
|
|
5916
|
+
_fixedMainFields__contr,
|
|
5135
5917
|
_self_managed_buildAnnoHierarchyConfig,
|
|
5136
5918
|
_self_managed_buildDocHierarchyConfig,
|
|
5137
5919
|
_self_managed_getFixedAnnoRollupBlocks,
|
|
@@ -5139,17 +5921,22 @@ var import_GET_GLOBAL_BULLMQ_CONFIG = __toESM(require_GET_GLOBAL_BULLMQ_CONFIG()
|
|
|
5139
5921
|
autoGenFilterConfigsFromTpl,
|
|
5140
5922
|
blockRegistry,
|
|
5141
5923
|
buildFilterConfigurations,
|
|
5924
|
+
clearLiveCollabDoc,
|
|
5142
5925
|
collectMarkIdsInOrder,
|
|
5143
5926
|
compareAndGroupBlocks,
|
|
5144
5927
|
deleteVal,
|
|
5145
5928
|
extractAllBlocksFromTpl,
|
|
5929
|
+
extractAllUniqueAnnoTagTypesFromTpl,
|
|
5146
5930
|
extractAndOrganizeBlocks,
|
|
5931
|
+
extractNumberFromValue,
|
|
5147
5932
|
genCleanCamelCaseId,
|
|
5933
|
+
genContentTypeSchema,
|
|
5148
5934
|
genTagId,
|
|
5149
5935
|
generateFilterKey,
|
|
5150
5936
|
getAIChatModelByTenant,
|
|
5151
5937
|
getAnnoFilterBucketKey,
|
|
5152
5938
|
getAnnotationsModelByTenant,
|
|
5939
|
+
getContrModelByTenant,
|
|
5153
5940
|
getDbByTenant,
|
|
5154
5941
|
getFilterKeyForBlock,
|
|
5155
5942
|
getGeneratedEntitiesModelByTenant,
|
|
@@ -5166,9 +5953,13 @@ var import_GET_GLOBAL_BULLMQ_CONFIG = __toESM(require_GET_GLOBAL_BULLMQ_CONFIG()
|
|
|
5166
5953
|
getRoutePathToReviewDashboard,
|
|
5167
5954
|
getRoutePathToTCI,
|
|
5168
5955
|
getRoutePathToTagCategoryLanding,
|
|
5956
|
+
getTpl,
|
|
5169
5957
|
getTplModelByTenant,
|
|
5958
|
+
getTypesForSchemaFromTpl,
|
|
5170
5959
|
getVal,
|
|
5960
|
+
isLiveCollabDoc,
|
|
5171
5961
|
isTplAnnotationEnabled,
|
|
5962
|
+
markLiveCollabDoc,
|
|
5172
5963
|
mergeAnnoDataIntoAnnotationsTags,
|
|
5173
5964
|
parseSpecialConfigSyntax,
|
|
5174
5965
|
plainTextToLexical,
|