@mattisvensson/strapi-plugin-webatlas 0.11.4 → 0.11.6

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.
@@ -4662,6 +4662,205 @@ function calculateParentAndOrder({
4662
4662
  calculatedOrder
4663
4663
  };
4664
4664
  }
4665
+ async function enrichWebatlasData(data, contentTypeUid) {
4666
+ if (!data || typeof data !== "object") return data;
4667
+ const collectDocumentIds = (obj, uid) => {
4668
+ if (!obj || typeof obj !== "object") return [];
4669
+ const ids = [];
4670
+ if (obj.documentId) ids.push(obj.documentId);
4671
+ if (!uid) return ids;
4672
+ const ct = strapi.contentTypes[uid];
4673
+ if (!ct?.attributes) return ids;
4674
+ for (const [key, attr] of Object.entries(ct.attributes)) {
4675
+ const attribute = attr;
4676
+ if (attribute.type === "relation" && obj[key]) {
4677
+ const targetUid = attribute.target;
4678
+ if (Array.isArray(obj[key])) {
4679
+ ids.push(...obj[key].flatMap((item) => collectDocumentIds(item, targetUid)));
4680
+ } else {
4681
+ ids.push(...collectDocumentIds(obj[key], targetUid));
4682
+ }
4683
+ }
4684
+ if (attribute.type === "component" && obj[key]) {
4685
+ const componentUid = attribute.component;
4686
+ if (Array.isArray(obj[key])) {
4687
+ ids.push(...obj[key].flatMap((item) => collectDocumentIds(item, componentUid)));
4688
+ } else {
4689
+ ids.push(...collectDocumentIds(obj[key], componentUid));
4690
+ }
4691
+ }
4692
+ if (attribute.type === "dynamiczone" && Array.isArray(obj[key])) {
4693
+ ids.push(
4694
+ ...obj[key].flatMap((item) => {
4695
+ if (!item || !item.__component) return [];
4696
+ return collectDocumentIds(item, item.__component);
4697
+ })
4698
+ );
4699
+ }
4700
+ }
4701
+ return ids;
4702
+ };
4703
+ const documentIds = collectDocumentIds(data, contentTypeUid);
4704
+ if (documentIds.length === 0) return data;
4705
+ const uniqueDocumentIds = [...new Set(documentIds)];
4706
+ const routes2 = await strapi.db?.query(waRoute).findMany({
4707
+ where: {
4708
+ relatedDocumentId: { $in: uniqueDocumentIds }
4709
+ },
4710
+ select: ["relatedDocumentId", "relatedContentType", "path", "canonicalPath", "slug", "uidPath"]
4711
+ });
4712
+ const routeMap = new Map(routes2?.map((route2) => [route2.relatedDocumentId, route2]) || []);
4713
+ const enrichEntity2 = (entity, uid) => {
4714
+ if (!entity || typeof entity !== "object") return entity;
4715
+ const ct = uid ? strapi.contentTypes[uid] : null;
4716
+ const isWebatlasEnabled = ct?.pluginOptions?.webatlas?.enabled === true;
4717
+ if (isWebatlasEnabled && entity.documentId && routeMap.has(entity.documentId)) {
4718
+ const route2 = routeMap.get(entity.documentId);
4719
+ entity.webatlas = {
4720
+ path: route2.path || "",
4721
+ canonicalPath: route2.canonicalPath || "",
4722
+ slug: route2.slug || "",
4723
+ uidPath: route2.uidPath || ""
4724
+ };
4725
+ }
4726
+ if (ct?.attributes) {
4727
+ for (const [key, attr] of Object.entries(ct.attributes)) {
4728
+ const attribute = attr;
4729
+ if (attribute.type === "relation" && entity[key]) {
4730
+ const targetUid = attribute.target;
4731
+ if (Array.isArray(entity[key])) {
4732
+ entity[key] = entity[key].map(
4733
+ (item) => item ? enrichEntity2(item, targetUid) : item
4734
+ );
4735
+ } else {
4736
+ entity[key] = enrichEntity2(entity[key], targetUid);
4737
+ }
4738
+ }
4739
+ if (attribute.type === "component" && entity[key]) {
4740
+ const componentUid = attribute.component;
4741
+ if (Array.isArray(entity[key])) {
4742
+ entity[key] = entity[key].map(
4743
+ (item) => item ? enrichEntity2(item, componentUid) : item
4744
+ );
4745
+ } else {
4746
+ entity[key] = enrichEntity2(entity[key], componentUid);
4747
+ }
4748
+ }
4749
+ if (attribute.type === "dynamiczone" && Array.isArray(entity[key])) {
4750
+ entity[key] = entity[key].map((item) => {
4751
+ if (!item || !item.__component) return item;
4752
+ return enrichEntity2(item, item.__component);
4753
+ });
4754
+ }
4755
+ }
4756
+ }
4757
+ return entity;
4758
+ };
4759
+ return enrichEntity2(data, contentTypeUid);
4760
+ }
4761
+ function isExternalUrl(value) {
4762
+ return !/^[a-z0-9]+$/.test(value);
4763
+ }
4764
+ function collectRoutePickerIds(obj, uid) {
4765
+ if (!obj || typeof obj !== "object") return [];
4766
+ const ids = [];
4767
+ if (!uid) return ids;
4768
+ const ct = strapi.contentTypes[uid] || strapi.components?.[uid];
4769
+ if (!ct?.attributes) return ids;
4770
+ for (const [key, attr] of Object.entries(ct.attributes)) {
4771
+ const attribute = attr;
4772
+ if (attribute.customField === "plugin::webatlas.route-picker" && obj[key]) {
4773
+ const value = obj[key];
4774
+ if (typeof value === "string" && !isExternalUrl(value)) {
4775
+ ids.push(value);
4776
+ }
4777
+ }
4778
+ if (attribute.type === "relation" && obj[key]) {
4779
+ const targetUid = attribute.target;
4780
+ if (Array.isArray(obj[key])) {
4781
+ ids.push(...obj[key].flatMap((item) => collectRoutePickerIds(item, targetUid)));
4782
+ } else {
4783
+ ids.push(...collectRoutePickerIds(obj[key], targetUid));
4784
+ }
4785
+ }
4786
+ if (attribute.type === "component" && obj[key]) {
4787
+ const componentUid = attribute.component;
4788
+ if (Array.isArray(obj[key])) {
4789
+ ids.push(...obj[key].flatMap((item) => collectRoutePickerIds(item, componentUid)));
4790
+ } else {
4791
+ ids.push(...collectRoutePickerIds(obj[key], componentUid));
4792
+ }
4793
+ }
4794
+ if (attribute.type === "dynamiczone" && Array.isArray(obj[key])) {
4795
+ ids.push(
4796
+ ...obj[key].flatMap((item) => {
4797
+ if (!item || !item.__component) return [];
4798
+ return collectRoutePickerIds(item, item.__component);
4799
+ })
4800
+ );
4801
+ }
4802
+ }
4803
+ return ids;
4804
+ }
4805
+ function enrichEntity(entity, uid, routeMap) {
4806
+ if (!entity || typeof entity !== "object") return entity;
4807
+ const ct = strapi.contentTypes[uid] || strapi.components?.[uid];
4808
+ if (!ct?.attributes) return entity;
4809
+ for (const [key, attr] of Object.entries(ct.attributes)) {
4810
+ const attribute = attr;
4811
+ if (attribute.customField === "plugin::webatlas.route-picker" && entity[key]) {
4812
+ const value = entity[key];
4813
+ if (typeof value === "string" && !isExternalUrl(value)) {
4814
+ const route2 = routeMap.get(value);
4815
+ if (route2) {
4816
+ entity[key] = route2.canonicalPath || route2.path || value;
4817
+ }
4818
+ }
4819
+ }
4820
+ if (attribute.type === "relation" && entity[key]) {
4821
+ const targetUid = attribute.target;
4822
+ if (Array.isArray(entity[key])) {
4823
+ entity[key] = entity[key].map(
4824
+ (item) => item ? enrichEntity(item, targetUid, routeMap) : item
4825
+ );
4826
+ } else {
4827
+ entity[key] = enrichEntity(entity[key], targetUid, routeMap);
4828
+ }
4829
+ }
4830
+ if (attribute.type === "component" && entity[key]) {
4831
+ const componentUid = attribute.component;
4832
+ if (Array.isArray(entity[key])) {
4833
+ entity[key] = entity[key].map(
4834
+ (item) => item ? enrichEntity(item, componentUid, routeMap) : item
4835
+ );
4836
+ } else {
4837
+ entity[key] = enrichEntity(entity[key], componentUid, routeMap);
4838
+ }
4839
+ }
4840
+ if (attribute.type === "dynamiczone" && Array.isArray(entity[key])) {
4841
+ entity[key] = entity[key].map((item) => {
4842
+ if (!item || !item.__component) return item;
4843
+ return enrichEntity(item, item.__component, routeMap);
4844
+ });
4845
+ }
4846
+ }
4847
+ return entity;
4848
+ }
4849
+ async function enrichRoutePickerFields(data, contentTypeUid) {
4850
+ if (!data || typeof data !== "object") return data;
4851
+ const documentIds = collectRoutePickerIds(data, contentTypeUid);
4852
+ if (documentIds.length === 0) return data;
4853
+ const uniqueDocumentIds = [...new Set(documentIds)];
4854
+ const routes2 = await strapi.db?.query(waRoute).findMany({
4855
+ where: {
4856
+ documentId: { $in: uniqueDocumentIds }
4857
+ },
4858
+ select: ["documentId", "path", "canonicalPath"]
4859
+ });
4860
+ const routeMap = new Map(routes2?.map((route2) => [route2.documentId, route2]) || []);
4861
+ const enriched = enrichEntity(data, contentTypeUid, routeMap);
4862
+ return enriched;
4863
+ }
4665
4864
  const migration_001_canonical_path = {
4666
4865
  version: "001",
4667
4866
  description: "Migrate title field to canonicalPath using transformToUrl",
@@ -4900,7 +5099,7 @@ function documentMiddleware(strapi2, enabledContentTypes, config2) {
4900
5099
  const data = context.params.data;
4901
5100
  const { webatlas } = data;
4902
5101
  const { slug, parentDocumentId, isOverride } = webatlas || {};
4903
- const transformedSlug = slug ? transformToUrl(slug) : null;
5102
+ const transformedSlug = slug ? transformToUrl(slug, !isOverride) : null;
4904
5103
  const result2 = await next();
4905
5104
  if (!transformedSlug) return result2;
4906
5105
  const existing = await strapi2.db?.query(waRoute).findOne({
@@ -4973,7 +5172,7 @@ function documentMiddleware(strapi2, enabledContentTypes, config2) {
4973
5172
  });
4974
5173
  }
4975
5174
  }
4976
- const transformedSlug = transformToUrl(slug);
5175
+ const transformedSlug = transformToUrl(slug, !isOverride);
4977
5176
  let rawPath = transformedSlug;
4978
5177
  if (!isOverride) rawPath = parent ? `${parent.path}/${transformedSlug}` : transformedSlug;
4979
5178
  const validatedPath = await duplicateCheck(rawPath, relatedRoute?.documentId ?? null);
@@ -5084,130 +5283,59 @@ const addWebatlasField = ({ strapi: strapi2 }) => {
5084
5283
  ([uid, ct]) => ct.info?.pluralName === apiEndpoint
5085
5284
  );
5086
5285
  if (!contentTypeEntry) return;
5087
- const [contentTypeUid, contentType] = contentTypeEntry;
5088
- const collectDocumentIds = (data, uid = contentTypeUid) => {
5089
- if (!data || typeof data !== "object") return [];
5090
- const ids = [];
5091
- if (data.documentId) {
5092
- ids.push(data.documentId);
5093
- }
5094
- const ct = strapi2.contentTypes[uid];
5095
- if (ct?.attributes) {
5096
- for (const [key, attr] of Object.entries(ct.attributes)) {
5097
- const attribute = attr;
5098
- if (attribute.type === "relation" && data[key]) {
5099
- const targetUid = attribute.target;
5100
- if (Array.isArray(data[key])) {
5101
- ids.push(...data[key].flatMap((item) => collectDocumentIds(item, targetUid)));
5102
- } else {
5103
- ids.push(...collectDocumentIds(data[key], targetUid));
5104
- }
5105
- }
5106
- if (attribute.type === "component" && data[key]) {
5107
- const componentUid = attribute.component;
5108
- if (Array.isArray(data[key])) {
5109
- ids.push(...data[key].flatMap((item) => collectDocumentIds(item, componentUid)));
5110
- } else {
5111
- ids.push(...collectDocumentIds(data[key], componentUid));
5112
- }
5113
- }
5114
- if (attribute.type === "dynamiczone" && Array.isArray(data[key])) {
5115
- ids.push(
5116
- ...data[key].flatMap((item) => {
5117
- const componentUid = item.__component;
5118
- return collectDocumentIds(item, componentUid);
5119
- })
5120
- );
5121
- }
5122
- }
5123
- }
5124
- return ids;
5125
- };
5126
- let documentIds = [];
5286
+ const [contentTypeUid] = contentTypeEntry;
5127
5287
  if (ctx.body.data) {
5128
5288
  if (Array.isArray(ctx.body.data)) {
5129
- documentIds = ctx.body.data.flatMap((item) => collectDocumentIds(item, contentTypeUid));
5289
+ ctx.body.data = await Promise.all(
5290
+ ctx.body.data.map((item) => enrichWebatlasData(item, contentTypeUid))
5291
+ );
5130
5292
  } else {
5131
- documentIds = collectDocumentIds(ctx.body.data, contentTypeUid);
5293
+ ctx.body.data = await enrichWebatlasData(ctx.body.data, contentTypeUid);
5132
5294
  }
5133
5295
  } else if (Array.isArray(ctx.body)) {
5134
- documentIds = ctx.body.flatMap((item) => collectDocumentIds(item, contentTypeUid));
5296
+ ctx.body = await Promise.all(ctx.body.map((item) => enrichWebatlasData(item, contentTypeUid)));
5135
5297
  } else if (typeof ctx.body === "object") {
5136
- documentIds = collectDocumentIds(ctx.body, contentTypeUid);
5298
+ ctx.body = await enrichWebatlasData(ctx.body, contentTypeUid);
5137
5299
  }
5138
- if (documentIds.length === 0) return;
5139
- const uniqueDocumentIds = [...new Set(documentIds)];
5140
- const routes2 = await strapi2.db?.query(waRoute).findMany({
5141
- where: {
5142
- relatedDocumentId: { $in: uniqueDocumentIds }
5143
- },
5144
- select: [
5145
- "relatedDocumentId",
5146
- "relatedContentType",
5147
- "path",
5148
- "canonicalPath",
5149
- "slug",
5150
- "uidPath"
5151
- ]
5152
- });
5153
- const routeMap = new Map(routes2?.map((route2) => [route2.relatedDocumentId, route2]) || []);
5154
- const enrichWebatlasField = (data, uid = contentTypeUid) => {
5155
- if (!data || typeof data !== "object") return data;
5156
- const ct = strapi2.contentTypes[uid];
5157
- const isWebatlasEnabled = ct?.pluginOptions?.webatlas?.enabled === true;
5158
- if (isWebatlasEnabled && data.documentId && routeMap.has(data.documentId)) {
5159
- const route2 = routeMap.get(data.documentId);
5160
- data.webatlas = {
5161
- path: route2.path || "",
5162
- canonicalPath: route2.canonicalPath || "",
5163
- slug: route2.slug || "",
5164
- uidPath: route2.uidPath || ""
5165
- };
5166
- }
5167
- if (ct?.attributes) {
5168
- for (const [key, attr] of Object.entries(ct.attributes)) {
5169
- const attribute = attr;
5170
- if (attribute.type === "relation" && data[key]) {
5171
- const targetUid = attribute.target;
5172
- if (Array.isArray(data[key])) {
5173
- data[key] = data[key].map((item) => enrichWebatlasField(item, targetUid));
5174
- } else {
5175
- data[key] = enrichWebatlasField(data[key], targetUid);
5176
- }
5177
- }
5178
- if (attribute.type === "component" && data[key]) {
5179
- const componentUid = attribute.component;
5180
- if (Array.isArray(data[key])) {
5181
- data[key] = data[key].map((item) => enrichWebatlasField(item, componentUid));
5182
- } else {
5183
- data[key] = enrichWebatlasField(data[key], componentUid);
5184
- }
5185
- }
5186
- if (attribute.type === "dynamiczone" && Array.isArray(data[key])) {
5187
- data[key] = data[key].map((item) => {
5188
- const componentUid = item.__component;
5189
- return enrichWebatlasField(item, componentUid);
5190
- });
5191
- }
5192
- }
5193
- }
5194
- return data;
5195
- };
5300
+ };
5301
+ };
5302
+ const enrichRoutePicker = ({ strapi: strapi2 }) => {
5303
+ return async (ctx, next) => {
5304
+ await next();
5305
+ if (!ctx.request.url.startsWith("/api/")) {
5306
+ return;
5307
+ }
5308
+ if (!ctx.body || ctx.status !== 200) {
5309
+ return;
5310
+ }
5311
+ const urlMatch = ctx.request.url.match(/^\/api\/([^/?]+)/);
5312
+ if (!urlMatch) return;
5313
+ const apiEndpoint = urlMatch[1];
5314
+ const contentTypeEntry = Object.entries(strapi2.contentTypes).find(
5315
+ ([uid, ct]) => ct.info?.pluralName === apiEndpoint
5316
+ );
5317
+ if (!contentTypeEntry) return;
5318
+ const [contentTypeUid] = contentTypeEntry;
5196
5319
  if (ctx.body.data) {
5197
5320
  if (Array.isArray(ctx.body.data)) {
5198
- ctx.body.data = ctx.body.data.map((item) => enrichWebatlasField(item, contentTypeUid));
5321
+ ctx.body.data = await Promise.all(
5322
+ ctx.body.data.map((item) => enrichRoutePickerFields(item, contentTypeUid))
5323
+ );
5199
5324
  } else {
5200
- ctx.body.data = enrichWebatlasField(ctx.body.data, contentTypeUid);
5325
+ ctx.body.data = await enrichRoutePickerFields(ctx.body.data, contentTypeUid);
5201
5326
  }
5202
5327
  } else if (Array.isArray(ctx.body)) {
5203
- ctx.body = ctx.body.map((item) => enrichWebatlasField(item, contentTypeUid));
5328
+ ctx.body = await Promise.all(
5329
+ ctx.body.map((item) => enrichRoutePickerFields(item, contentTypeUid))
5330
+ );
5204
5331
  } else if (typeof ctx.body === "object") {
5205
- ctx.body = enrichWebatlasField(ctx.body, contentTypeUid);
5332
+ ctx.body = await enrichRoutePickerFields(ctx.body, contentTypeUid);
5206
5333
  }
5207
5334
  };
5208
5335
  };
5209
5336
  const middlewares = {
5210
- addWebatlasField
5337
+ addWebatlasField,
5338
+ enrichRoutePicker
5211
5339
  };
5212
5340
  const bootstrap = async ({ strapi: strapi2 }) => {
5213
5341
  try {
@@ -5221,6 +5349,7 @@ const bootstrap = async ({ strapi: strapi2 }) => {
5221
5349
  documentMiddleware(strapi2, enabledContentTypes, config2);
5222
5350
  webatlasMiddleware(strapi2);
5223
5351
  strapi2.server.use(middlewares.addWebatlasField({ strapi: strapi2 }));
5352
+ strapi2.server.use(middlewares.enrichRoutePicker({ strapi: strapi2 }));
5224
5353
  } catch (error) {
5225
5354
  strapi2.log.error(`Bootstrap failed. ${String(error)}`);
5226
5355
  }
@@ -5228,6 +5357,11 @@ const bootstrap = async ({ strapi: strapi2 }) => {
5228
5357
  const destroy = ({ strapi: strapi2 }) => {
5229
5358
  };
5230
5359
  const register = ({ strapi: strapi2 }) => {
5360
+ strapi2.customFields.register({
5361
+ name: "route-picker",
5362
+ plugin: PLUGIN_ID,
5363
+ type: "string"
5364
+ });
5231
5365
  };
5232
5366
  const config = {
5233
5367
  default: {
@@ -6090,17 +6224,10 @@ const client = ({ strapi: strapi2 }) => ({
6090
6224
  if (!entity) return null;
6091
6225
  let cleanEntity = cleanRootKeys(entity);
6092
6226
  cleanEntity = removeWaFields(cleanEntity);
6093
- const webatlasFields = {
6094
- path: route2.path,
6095
- canonicalPath: route2.canonicalPath,
6096
- slug: route2.slug,
6097
- uidPath: route2.uidPath
6098
- };
6227
+ cleanEntity = await enrichWebatlasData(cleanEntity, route2.relatedContentType);
6228
+ cleanEntity = await enrichRoutePickerFields(cleanEntity, contentTypeKey);
6099
6229
  return {
6100
6230
  contentType: contentType.info.singularName,
6101
- webatlas: {
6102
- ...webatlasFields
6103
- },
6104
6231
  ...cleanEntity
6105
6232
  };
6106
6233
  } catch (e) {
@@ -300,6 +300,9 @@ declare const _default: {
300
300
  addWebatlasField: ({ strapi }: {
301
301
  strapi: import('@strapi/types/dist/core').Strapi;
302
302
  }) => (ctx: any, next: any) => Promise<void>;
303
+ enrichRoutePicker: ({ strapi }: {
304
+ strapi: import('@strapi/types/dist/core').Strapi;
305
+ }) => (ctx: any, next: any) => Promise<void>;
303
306
  };
304
307
  };
305
308
  export default _default;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@mattisvensson/strapi-plugin-webatlas",
3
3
  "description": "A strapi plugin to manage URL routes and navigations.",
4
4
  "license": "MIT",
5
- "version": "0.11.4",
5
+ "version": "0.11.6",
6
6
  "keywords": [
7
7
  "strapi",
8
8
  "plugin",