@websolutespa/bom-mixer-store 1.4.2 → 1.4.3

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/CHANGELOG.md CHANGED
@@ -1,115 +1,121 @@
1
- # @websolutespa/bom-mixer-store
2
-
3
- ## 1.4.2
4
-
5
- ### Patch Changes
6
-
7
- - Added: Decorators, CachedService, RemotePageService, RemoteMenuService, StoreStrategy.DecoratedApi, nextPhase, storeCacheDuration.
8
- - Modified: RemoteStore.
9
- - Updated dependencies
10
- - @websolutespa/bom-core@0.6.4
11
-
12
- ## 1.4.1
13
-
14
- ### Patch Changes
15
-
16
- - Modified: MockPageService.
17
- - Updated dependencies
18
- - @websolutespa/bom-core@0.6.3
19
-
20
- ## 1.4.0
21
-
22
- ### Minor Changes
23
-
24
- - Added: BuildStrategy.
25
-
26
- ## 1.3.0
27
-
28
- ### Minor Changes
29
-
30
- - Added: storeIndexHandler.
31
- - Modified: Store, StoreHandler, RemoteService, MockService, MockPageService, MockMenuService, StaticService.
32
-
33
- ## 1.2.2
34
-
35
- ### Patch Changes
36
-
37
- - Modified: MockMenuService IPageRelationResult.
38
-
39
- ## 1.2.1
40
-
41
- ### Patch Changes
42
-
43
- - Fixing: MockMenuService collectPages.
44
-
45
- ## 1.2.0
46
-
47
- ### Minor Changes
48
-
49
- - Updating: next 13.
50
-
51
- ## 1.1.1
52
-
53
- ### Patch Changes
54
-
55
- - Fixing: check redirects by pattern.
56
-
57
- ## 1.1.0
58
-
59
- ### Minor Changes
60
-
61
- - Modified: store refactor.
62
-
63
- ## 1.0.1
64
-
65
- ### Patch Changes
66
-
67
- - Fixing: localhost url.
68
-
69
- ## 1.0.0
70
-
71
- ### Minor Changes
72
-
73
- - Modified routes, added redirects.
74
-
75
- ## 0.3.2
76
-
77
- ### Patch Changes
78
-
79
- - Modified: MockMenuService.
80
-
81
- ## 0.3.1
82
-
83
- ### Patch Changes
84
-
85
- - Added: source files.
86
-
87
- ## 0.3.0
88
-
89
- ### Minor Changes
90
-
91
- - Added: splat routes.
92
-
93
- ## 0.2.0
94
-
95
- ### Minor Changes
96
-
97
- - Added: @websolutespa/bom-core.
98
-
99
- ## 0.1.1
100
-
101
- ### Patch Changes
102
-
103
- - Modified: store service.
104
-
105
- ## 0.1.0
106
-
107
- ### Minor Changes
108
-
109
- - mock refactor.
110
-
111
- ## 0.0.2
112
-
113
- ### Patch Changes
114
-
115
- - Added: first release.
1
+ # @websolutespa/bom-mixer-store
2
+
3
+ ## 1.4.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Modified: CachedService, Decorators, LocalApi, MockApi, MockStore, RemoteApi, RemoteStore, StoreHandler
8
+
9
+ ## 1.4.2
10
+
11
+ ### Patch Changes
12
+
13
+ - Added: Decorators, CachedService, RemotePageService, RemoteMenuService, StoreStrategy.DecoratedApi, nextPhase, storeCacheDuration
14
+ - Modified: RemoteStore
15
+ - Updated dependencies
16
+ - @websolutespa/bom-core@0.6.4
17
+
18
+ ## 1.4.1
19
+
20
+ ### Patch Changes
21
+
22
+ - Modified: MockPageService
23
+ - Updated dependencies
24
+ - @websolutespa/bom-core@0.6.3
25
+
26
+ ## 1.4.0
27
+
28
+ ### Minor Changes
29
+
30
+ - Added: BuildStrategy
31
+
32
+ ## 1.3.0
33
+
34
+ ### Minor Changes
35
+
36
+ - Added: storeIndexHandler
37
+ - Modified: Store, StoreHandler, RemoteService, MockService, MockPageService, MockMenuService, StaticService
38
+
39
+ ## 1.2.2
40
+
41
+ ### Patch Changes
42
+
43
+ - Modified: MockMenuService IPageRelationResult
44
+
45
+ ## 1.2.1
46
+
47
+ ### Patch Changes
48
+
49
+ - Fixing: MockMenuService collectPages
50
+
51
+ ## 1.2.0
52
+
53
+ ### Minor Changes
54
+
55
+ - Updating: next 13
56
+
57
+ ## 1.1.1
58
+
59
+ ### Patch Changes
60
+
61
+ - Fixing: check redirects by pattern
62
+
63
+ ## 1.1.0
64
+
65
+ ### Minor Changes
66
+
67
+ - Modified: store refactor
68
+
69
+ ## 1.0.1
70
+
71
+ ### Patch Changes
72
+
73
+ - Fixing: localhost url
74
+
75
+ ## 1.0.0
76
+
77
+ ### Minor Changes
78
+
79
+ - Modified routes, added redirects
80
+
81
+ ## 0.3.2
82
+
83
+ ### Patch Changes
84
+
85
+ - Modified: MockMenuService
86
+
87
+ ## 0.3.1
88
+
89
+ ### Patch Changes
90
+
91
+ - Added: source files
92
+
93
+ ## 0.3.0
94
+
95
+ ### Minor Changes
96
+
97
+ - Added: splat routes
98
+
99
+ ## 0.2.0
100
+
101
+ ### Minor Changes
102
+
103
+ - Added: @websolutespa/bom-core
104
+
105
+ ## 0.1.1
106
+
107
+ ### Patch Changes
108
+
109
+ - Modified: store service
110
+
111
+ ## 0.1.0
112
+
113
+ ### Minor Changes
114
+
115
+ - mock refactor
116
+
117
+ ## 0.0.2
118
+
119
+ ### Patch Changes
120
+
121
+ - Added: first release
package/dist/index.d.ts CHANGED
@@ -206,7 +206,8 @@ declare function collectionPatch<T extends IStore>(storePromise: () => Promise<T
206
206
  declare function collectionDelete<T extends IStore>(storePromise: () => Promise<T>): (request: NextApiRequest, response: NextApiResponse) => Promise<void>;
207
207
  declare function routePost(store: IStore, request: NextApiRequest, response: NextApiResponse): Promise<void>;
208
208
  declare function storeIndexHandler(): any;
209
+ declare function findRoute(store: IStore, pathname: string, href: string): Promise<IRoute | undefined>;
209
210
 
210
211
  declare function getStore<T extends IStore>(): Promise<T>;
211
212
 
212
- export { ApiRequestOptions, BuildStrategy, COLLECTIONS, IApiHandler, ICrudMethod, IField, IHandler, KNOWN_COLLECTIONS, MAX_INT, MOCK_COLLECTIONS, MapTo, MockMenuService, MockPageService, MockService, NextPhase, RemoteMenuService, RemotePageService, RemoteService, SERVICE_DEFAULT_DEPTH, STATIC_COLLECTIONS, StaticService, StaticServiceConfig, StaticServiceSrc, StoreStrategy, apiHandler, buildStrategy, collectionDelete, collectionGet, collectionPatch, collectionPost, decorateHref_, decorateHrefs_, decorateMenu_, decorateMenus_, decorateNav_, decorateNavs_, decorateRelation_, decorateRelations_, decorateStaticRelation_, decorateStaticRelations_, defaultApiRequestOptions, errorHandler, fromCache, getDefaultApiRequestOptions, getLocalApiUrl, getMockStore, getMockStoreFromData_, getRemoteStore, getStore, isApiRequest, isNextRequest, isStaticRequest, localApiDelete, localApiFetch, localApiGet, localApiPatch, localApiPost, localApiPut, localizeItem_, localizeItems_, mockApiDelete, mockApiFetch, mockApiGet, mockApiPatch, mockApiPost, mockApiPut, mockHandler, nextPhase, remoteApiDelete, remoteApiFetch, remoteApiGet, remoteApiPatch, remoteApiPost, remoteApiPut, routePost, staticCollections, storeApiDelete, storeApiGet, storeApiPatch, storeApiPost, storeApiPut, storeCacheDuration, storeHandler, storeIndexHandler, storeStrategy };
213
+ export { ApiRequestOptions, BuildStrategy, COLLECTIONS, IApiHandler, ICrudMethod, IField, IHandler, KNOWN_COLLECTIONS, MAX_INT, MOCK_COLLECTIONS, MapTo, MockMenuService, MockPageService, MockService, NextPhase, RemoteMenuService, RemotePageService, RemoteService, SERVICE_DEFAULT_DEPTH, STATIC_COLLECTIONS, StaticService, StaticServiceConfig, StaticServiceSrc, StoreStrategy, apiHandler, buildStrategy, collectionDelete, collectionGet, collectionPatch, collectionPost, decorateHref_, decorateHrefs_, decorateMenu_, decorateMenus_, decorateNav_, decorateNavs_, decorateRelation_, decorateRelations_, decorateStaticRelation_, decorateStaticRelations_, defaultApiRequestOptions, errorHandler, findRoute, fromCache, getDefaultApiRequestOptions, getLocalApiUrl, getMockStore, getMockStoreFromData_, getRemoteStore, getStore, isApiRequest, isNextRequest, isStaticRequest, localApiDelete, localApiFetch, localApiGet, localApiPatch, localApiPost, localApiPut, localizeItem_, localizeItems_, mockApiDelete, mockApiFetch, mockApiGet, mockApiPatch, mockApiPost, mockApiPut, mockHandler, nextPhase, remoteApiDelete, remoteApiFetch, remoteApiGet, remoteApiPatch, remoteApiPost, remoteApiPut, routePost, staticCollections, storeApiDelete, storeApiGet, storeApiPatch, storeApiPost, storeApiPut, storeCacheDuration, storeHandler, storeIndexHandler, storeStrategy };
package/dist/index.js CHANGED
@@ -64,6 +64,7 @@ __export(src_exports, {
64
64
  decorateStaticRelations_: () => decorateStaticRelations_,
65
65
  defaultApiRequestOptions: () => defaultApiRequestOptions,
66
66
  errorHandler: () => errorHandler,
67
+ findRoute: () => findRoute,
67
68
  fromCache: () => fromCache,
68
69
  getDefaultApiRequestOptions: () => getDefaultApiRequestOptions,
69
70
  getLocalApiUrl: () => getLocalApiUrl,
@@ -203,10 +204,6 @@ async function decorateHrefs_(store, items, params = {}) {
203
204
  locale: {
204
205
  equals: params.locale
205
206
  }
206
- /*
207
- ...(params.market ? { market: { equals: params.market } } : {}),
208
- ...(params.locale ? { locale: { equals: params.locale } } : {}),
209
- */
210
207
  }
211
208
  });
212
209
  return items.map((item) => {
@@ -252,7 +249,6 @@ async function decorateMenu_(store, item, params) {
252
249
  const undecoratedPages = collectUndecoratedPages(items);
253
250
  if (undecoratedCategories.length > 0 || undecoratedPages.length > 0) {
254
251
  const routes = await store.route.findMany({
255
- ...params,
256
252
  where: {
257
253
  ...market ? { market: { equals: market } } : {},
258
254
  ...locale ? { locale: { equals: locale } } : {}
@@ -419,7 +415,6 @@ async function decorateNav_(store, item, params = {}, routes) {
419
415
  }
420
416
  async function decorateNavs_(store, items, params = {}) {
421
417
  const routes = await store.route.findMany({
422
- ...params,
423
418
  where: {
424
419
  ...params.market ? { market: { equals: params.market } } : {},
425
420
  ...params.locale ? { locale: { equals: params.locale } } : {}
@@ -521,7 +516,6 @@ var API_BASE = process.env.NEXT_PUBLIC_API || "/api";
521
516
  function getLocalApiUrl(pathname) {
522
517
  let url = `${import_bom_core5.isBrowser ? window.location.origin : API_URL}${API_BASE}${pathname}`;
523
518
  url = import_bom_core5.isBrowser ? url : url.replace("://localhost", "://127.0.0.1");
524
- console.log("getLocalApiUrl", url);
525
519
  return url;
526
520
  }
527
521
  async function localApiFetch(pathname, options = {}, serviceOptions) {
@@ -531,6 +525,7 @@ async function localApiFetch(pathname, options = {}, serviceOptions) {
531
525
  url.searchParams.append("market", serviceOptions.market);
532
526
  }
533
527
  const apiOptions = (0, import_bom_core5.merge)({ ...getDefaultApiRequestOptions() }, options);
528
+ console.log("localApiFetch", apiOptions.method, url.href);
534
529
  const apiResponse = await (0, import_bom_core5.httpFetch)(url.href, apiOptions);
535
530
  return apiResponse;
536
531
  }
@@ -665,12 +660,12 @@ var API_BASE3 = process.env.NEXT_PUBLIC_API || "/api";
665
660
  function getMockApiUrl(pathname) {
666
661
  let url = `${API_URL2}${API_BASE3}/_mock${pathname}`;
667
662
  url = import_bom_core7.isBrowser ? url : url.replace("://localhost", "://127.0.0.1");
668
- console.log("getMockApiUrl", url);
669
663
  return url;
670
664
  }
671
665
  async function mockApiFetch(pathname, options = {}) {
672
666
  const url = getMockApiUrl(pathname);
673
667
  const apiOptions = (0, import_bom_core7.merge)({ ...getDefaultApiRequestOptions() }, options);
668
+ console.log("mockApiFetch", apiOptions.method, url);
674
669
  const apiResponse = await (0, import_bom_core7.httpFetch)(url, apiOptions);
675
670
  return apiResponse;
676
671
  }
@@ -691,7 +686,7 @@ async function mockApiDelete(url, options = {}) {
691
686
  }
692
687
 
693
688
  // src/store/store.handler.ts
694
- var import_bom_core14 = require("@websolutespa/bom-core");
689
+ var import_bom_core13 = require("@websolutespa/bom-core");
695
690
 
696
691
  // src/static/static.ts
697
692
  var staticCollections = [{
@@ -1128,7 +1123,7 @@ function getMockStoreFromData_(data) {
1128
1123
  }
1129
1124
  }
1130
1125
  } else {
1131
- console.log("mock store not found");
1126
+ console.warn("mock store not found");
1132
1127
  }
1133
1128
  staticCollections.forEach((config) => {
1134
1129
  store[config.slug] = new StaticService(config, store);
@@ -1160,13 +1155,11 @@ async function getMockStore() {
1160
1155
  }, import_bom_core9.isDevelopment ? void 0 : "STORE_MOCK_");
1161
1156
  }
1162
1157
 
1163
- // src/remote/remote.store.ts
1164
- var import_bom_core13 = require("@websolutespa/bom-core");
1165
-
1166
1158
  // src/cached/cached.service.ts
1167
1159
  var import_bom_core10 = require("@websolutespa/bom-core");
1168
- var CachedService = class {
1160
+ var CachedService = class extends import_bom_core10.JsonService {
1169
1161
  constructor(service, options = {}) {
1162
+ super([]);
1170
1163
  this.cache_ = /* @__PURE__ */ new Map();
1171
1164
  this.duration_ = storeCacheDuration;
1172
1165
  if (!service) {
@@ -1210,11 +1203,27 @@ var CachedService = class {
1210
1203
  }, key);
1211
1204
  }
1212
1205
  async findMany(params = {}) {
1213
- const serializedParams = (0, import_bom_core10.qsSerialize)(params);
1214
- const key = `findMany?${serializedParams}`;
1215
- return await this.fromCache_(async () => {
1216
- return await this.service_.findMany(params);
1217
- }, key);
1206
+ if (storeStrategy === "decoratedApi" /* DecoratedApi */) {
1207
+ const { market, locale, ...otherParams } = params;
1208
+ const serializedParams = (0, import_bom_core10.qsSerialize)({ market, locale });
1209
+ const key = `findMany?${serializedParams}`;
1210
+ return await this.fromCache_(async () => {
1211
+ let items = await this.service_.findMany({ market, locale });
1212
+ if (otherParams.where) {
1213
+ items = this.whereItems_(items, otherParams.where);
1214
+ }
1215
+ if (otherParams.sort) {
1216
+ items = this.sortCollection_(items, otherParams.sort);
1217
+ }
1218
+ return items;
1219
+ }, key);
1220
+ } else {
1221
+ const serializedParams = (0, import_bom_core10.qsSerialize)(params);
1222
+ const key = `findMany?${serializedParams}`;
1223
+ return await this.fromCache_(async () => {
1224
+ return await this.service_.findMany(params);
1225
+ }, key);
1226
+ }
1218
1227
  }
1219
1228
  async findPaged(params = {}) {
1220
1229
  const serializedParams = (0, import_bom_core10.qsSerialize)(params);
@@ -1244,12 +1253,12 @@ var API_BASE4 = process.env.STORE_API || "/api";
1244
1253
  function getRemoteApiUrl(pathname) {
1245
1254
  let url = `${API_URL3}${API_BASE4}${pathname}`;
1246
1255
  url = import_bom_core11.isBrowser ? url : url.replace("://localhost", "://127.0.0.1");
1247
- console.log("getRemoteApiUrl", url);
1248
1256
  return url;
1249
1257
  }
1250
1258
  async function remoteApiFetch(pathname, options = {}) {
1251
1259
  const url = getRemoteApiUrl(pathname);
1252
1260
  const apiOptions = (0, import_bom_core11.merge)({ ...getDefaultApiRequestOptions() }, options);
1261
+ console.log("remoteApiFetch", apiOptions.method, url);
1253
1262
  const apiResponse = await (0, import_bom_core11.httpFetch)(url, apiOptions);
1254
1263
  return apiResponse;
1255
1264
  }
@@ -1406,7 +1415,6 @@ var RemotePageService = class extends RemoteService {
1406
1415
  var PAGES2 = process.env.PAGES ? process.env.PAGES.split(",") : [];
1407
1416
  async function getRemoteStore_() {
1408
1417
  return fromCache(async () => {
1409
- console.log("getRemoteStore_");
1410
1418
  if (COLLECTIONS.length === 0) {
1411
1419
  console.error("RemoteStore.getRemoteStore_ no collections");
1412
1420
  throw "RemoteStore.getRemoteStore_ no collections";
@@ -1426,7 +1434,7 @@ async function getRemoteStore_() {
1426
1434
  store[key] = new CachedService(
1427
1435
  new RemoteMenuService(key, store)
1428
1436
  );
1429
- } else if (KNOWN_COLLECTIONS.includes(key) && !import_bom_core13.isDevelopment) {
1437
+ } else if (KNOWN_COLLECTIONS.includes(key)) {
1430
1438
  store[key] = new CachedService(
1431
1439
  new RemoteService(key, store)
1432
1440
  );
@@ -1508,7 +1516,7 @@ function collectionGet(storePromise) {
1508
1516
  const collection = collections[0];
1509
1517
  const id = collections[1];
1510
1518
  const qs = Object.entries(query).map((kv) => kv.join("=")).join("&");
1511
- const params = (0, import_bom_core14.qsDeserialize)(qs);
1519
+ const params = (0, import_bom_core13.qsDeserialize)(qs);
1512
1520
  if (!COLLECTIONS.includes(collection)) {
1513
1521
  console.error("storeHandler.collectionGet", "method not allowed", collection);
1514
1522
  return response.status(405).send("method not allowed");
@@ -1520,7 +1528,7 @@ function collectionGet(storePromise) {
1520
1528
  return response.status(405).send("method not allowed");
1521
1529
  }
1522
1530
  if (id) {
1523
- const item = await service.findOne((0, import_bom_core14.asEquatable)(id), params);
1531
+ const item = await service.findOne((0, import_bom_core13.asEquatable)(id), params);
1524
1532
  if (item) {
1525
1533
  return response.status(200).json(item);
1526
1534
  } else {
@@ -1612,87 +1620,20 @@ async function routePost(store, request, response) {
1612
1620
  if (request.body.pathname && request.body.href) {
1613
1621
  try {
1614
1622
  const { pathname, href } = request.body;
1615
- const routes = await store.route.findMany({
1616
- where: {
1617
- id: {
1618
- equals: pathname
1619
- }
1620
- }
1621
- });
1622
- if (routes.length > 0) {
1623
- return response.status(200).json(routes[0]);
1624
- } else {
1625
- const splatRoutes = await store.route.findMany({
1626
- where: {
1627
- useSplat: {
1628
- equals: true
1629
- }
1630
- }
1631
- });
1632
- const splatRoute = splatRoutes.reduce((p, c) => {
1633
- const match = pathname.indexOf(c.id) === 0;
1634
- if (match && (!p || p.id.length < c.id.length)) {
1635
- return { ...c, splat: pathname.substring(c.id.length, pathname.length) };
1636
- } else {
1637
- return p;
1638
- }
1639
- }, void 0);
1640
- if (splatRoute) {
1641
- return response.status(200).json(splatRoute);
1642
- }
1643
- const redirects = await store.redirect.findMany({
1644
- where: {
1645
- isActive: {
1646
- equals: true
1647
- }
1648
- }
1649
- });
1650
- const exactMatchResolver = (redirect) => {
1651
- const status = parseInt(redirect.status) || 307;
1652
- if (status < 400) {
1653
- const redirectUrl = redirect.to;
1654
- return response.status(status).send({
1655
- redirectUrl
1656
- });
1657
- } else {
1658
- return response.status(status).send("gone");
1659
- }
1660
- };
1661
- const exactMatchRedirect = redirects.find((x) => x.from === href);
1662
- if (exactMatchRedirect !== void 0) {
1663
- return exactMatchResolver(exactMatchRedirect);
1623
+ const route = await findRoute(store, pathname, href);
1624
+ return response.status(200).json(route);
1625
+ } catch (error) {
1626
+ if (error.status < 500) {
1627
+ if (error.status < 400) {
1628
+ return response.status(error.status).send({
1629
+ redirectUrl: error.redirectUrl
1630
+ });
1664
1631
  }
1665
- const patternMatchResolver = (redirect) => {
1666
- const status = parseInt(redirect.status) || 307;
1667
- if (status < 400) {
1668
- const regexp = new RegExp(redirect.from);
1669
- let i = 0;
1670
- let redirectUrl = redirect.to.replace(/\*/g, (...rest) => {
1671
- return `$${++i}`;
1672
- });
1673
- redirectUrl = href.replace(regexp, redirectUrl);
1674
- return response.status(status).send({
1675
- redirectUrl
1676
- });
1677
- } else {
1678
- return response.status(status).send("gone");
1679
- }
1680
- };
1681
- redirects.forEach((x) => {
1682
- let expression = x.from;
1683
- expression = expression.replace(/\*(?!\))/g, "(.*)");
1684
- expression = `^${expression.replace(/(^\^)|(\$$)/g, "")}$`;
1685
- x.from = expression;
1686
- });
1687
- const firstMatch = redirects.find((x) => href.match(x.from));
1688
- if (firstMatch) {
1689
- return patternMatchResolver(firstMatch);
1632
+ if (error.status === 404) {
1633
+ return response.status(404).send("not found");
1634
+ } else {
1635
+ return response.status(error.status).send("gone");
1690
1636
  }
1691
- return response.status(404).send("not found");
1692
- }
1693
- } catch (error) {
1694
- if (error.status === 404) {
1695
- return response.status(404).send("not found");
1696
1637
  } else {
1697
1638
  console.error("storeHandler.error.500", error);
1698
1639
  throw error;
@@ -1706,8 +1647,8 @@ function storeIndexHandler() {
1706
1647
  return apiHandler({
1707
1648
  get: async (request, response) => {
1708
1649
  try {
1709
- const market = import_bom_core14.defaultMarket;
1710
- const locale = import_bom_core14.defaultLocale;
1650
+ const market = import_bom_core13.defaultMarket;
1651
+ const locale = import_bom_core13.defaultLocale;
1711
1652
  if (!Array.isArray(COLLECTIONS) || COLLECTIONS?.length === 0) {
1712
1653
  console.error("apiHome.collectionGet", "collections not found", COLLECTIONS);
1713
1654
  return response.status(405).send("method not allowed");
@@ -1833,6 +1774,82 @@ function storeIndexHandler() {
1833
1774
  }
1834
1775
  });
1835
1776
  }
1777
+ async function findRoute(store, pathname, href) {
1778
+ const routes = await store.route.findMany({
1779
+ where: {
1780
+ id: {
1781
+ equals: pathname
1782
+ }
1783
+ }
1784
+ });
1785
+ if (routes.length > 0) {
1786
+ return routes[0];
1787
+ } else {
1788
+ const splatRoutes = await store.route.findMany({
1789
+ where: {
1790
+ useSplat: {
1791
+ equals: true
1792
+ }
1793
+ }
1794
+ });
1795
+ const splatRoute = splatRoutes.reduce((p, c) => {
1796
+ const match = pathname.indexOf(c.id) === 0;
1797
+ if (match && (!p || p.id.length < c.id.length)) {
1798
+ return { ...c, splat: pathname.substring(c.id.length, pathname.length) };
1799
+ } else {
1800
+ return p;
1801
+ }
1802
+ }, void 0);
1803
+ if (splatRoute) {
1804
+ return splatRoute;
1805
+ }
1806
+ const redirects = await store.redirect.findMany({
1807
+ where: {
1808
+ isActive: {
1809
+ equals: true
1810
+ }
1811
+ }
1812
+ });
1813
+ const exactMatchResolver = (redirect) => {
1814
+ const status = parseInt(redirect.status) || 307;
1815
+ if (status < 400) {
1816
+ const redirectUrl = redirect.to;
1817
+ throw { status, redirectUrl };
1818
+ } else {
1819
+ throw { status };
1820
+ }
1821
+ };
1822
+ const exactMatchRedirect = redirects.find((x) => x.from === href);
1823
+ if (exactMatchRedirect !== void 0) {
1824
+ return exactMatchResolver(exactMatchRedirect);
1825
+ }
1826
+ const patternMatchResolver = (redirect) => {
1827
+ const status = parseInt(redirect.status) || 307;
1828
+ if (status < 400) {
1829
+ const regexp = new RegExp(redirect.from);
1830
+ let i = 0;
1831
+ let redirectUrl = redirect.to.replace(/\*/g, (...rest) => {
1832
+ return `$${++i}`;
1833
+ });
1834
+ redirectUrl = href.replace(regexp, redirectUrl);
1835
+ throw { status, redirectUrl };
1836
+ } else {
1837
+ throw { status };
1838
+ }
1839
+ };
1840
+ redirects.forEach((x) => {
1841
+ let expression = x.from;
1842
+ expression = expression.replace(/\*(?!\))/g, "(.*)");
1843
+ expression = `^${expression.replace(/(^\^)|(\$$)/g, "")}$`;
1844
+ x.from = expression;
1845
+ });
1846
+ const firstMatch = redirects.find((x) => href.match(x.from));
1847
+ if (firstMatch) {
1848
+ return patternMatchResolver(firstMatch);
1849
+ }
1850
+ throw { status: 404 };
1851
+ }
1852
+ }
1836
1853
 
1837
1854
  // src/mock/mock.handler.ts
1838
1855
  function mockHandler() {
@@ -1892,6 +1909,7 @@ async function storeApiDelete(url, options = {}) {
1892
1909
  decorateStaticRelations_,
1893
1910
  defaultApiRequestOptions,
1894
1911
  errorHandler,
1912
+ findRoute,
1895
1913
  fromCache,
1896
1914
  getDefaultApiRequestOptions,
1897
1915
  getLocalApiUrl,
package/dist/index.mjs CHANGED
@@ -99,10 +99,6 @@ async function decorateHrefs_(store, items, params = {}) {
99
99
  locale: {
100
100
  equals: params.locale
101
101
  }
102
- /*
103
- ...(params.market ? { market: { equals: params.market } } : {}),
104
- ...(params.locale ? { locale: { equals: params.locale } } : {}),
105
- */
106
102
  }
107
103
  });
108
104
  return items.map((item) => {
@@ -152,7 +148,6 @@ async function decorateMenu_(store, item, params) {
152
148
  const undecoratedPages = collectUndecoratedPages(items);
153
149
  if (undecoratedCategories.length > 0 || undecoratedPages.length > 0) {
154
150
  const routes = await store.route.findMany({
155
- ...params,
156
151
  where: {
157
152
  ...market ? { market: { equals: market } } : {},
158
153
  ...locale ? { locale: { equals: locale } } : {}
@@ -319,7 +314,6 @@ async function decorateNav_(store, item, params = {}, routes) {
319
314
  }
320
315
  async function decorateNavs_(store, items, params = {}) {
321
316
  const routes = await store.route.findMany({
322
- ...params,
323
317
  where: {
324
318
  ...params.market ? { market: { equals: params.market } } : {},
325
319
  ...params.locale ? { locale: { equals: params.locale } } : {}
@@ -421,7 +415,6 @@ var API_BASE = process.env.NEXT_PUBLIC_API || "/api";
421
415
  function getLocalApiUrl(pathname) {
422
416
  let url = `${isBrowser ? window.location.origin : API_URL}${API_BASE}${pathname}`;
423
417
  url = isBrowser ? url : url.replace("://localhost", "://127.0.0.1");
424
- console.log("getLocalApiUrl", url);
425
418
  return url;
426
419
  }
427
420
  async function localApiFetch(pathname, options = {}, serviceOptions) {
@@ -431,6 +424,7 @@ async function localApiFetch(pathname, options = {}, serviceOptions) {
431
424
  url.searchParams.append("market", serviceOptions.market);
432
425
  }
433
426
  const apiOptions = merge({ ...getDefaultApiRequestOptions() }, options);
427
+ console.log("localApiFetch", apiOptions.method, url.href);
434
428
  const apiResponse = await httpFetch(url.href, apiOptions);
435
429
  return apiResponse;
436
430
  }
@@ -565,12 +559,12 @@ var API_BASE3 = process.env.NEXT_PUBLIC_API || "/api";
565
559
  function getMockApiUrl(pathname) {
566
560
  let url = `${API_URL2}${API_BASE3}/_mock${pathname}`;
567
561
  url = isBrowser2 ? url : url.replace("://localhost", "://127.0.0.1");
568
- console.log("getMockApiUrl", url);
569
562
  return url;
570
563
  }
571
564
  async function mockApiFetch(pathname, options = {}) {
572
565
  const url = getMockApiUrl(pathname);
573
566
  const apiOptions = merge2({ ...getDefaultApiRequestOptions() }, options);
567
+ console.log("mockApiFetch", apiOptions.method, url);
574
568
  const apiResponse = await httpFetch2(url, apiOptions);
575
569
  return apiResponse;
576
570
  }
@@ -1028,7 +1022,7 @@ function getMockStoreFromData_(data) {
1028
1022
  }
1029
1023
  }
1030
1024
  } else {
1031
- console.log("mock store not found");
1025
+ console.warn("mock store not found");
1032
1026
  }
1033
1027
  staticCollections.forEach((config) => {
1034
1028
  store[config.slug] = new StaticService(config, store);
@@ -1060,13 +1054,11 @@ async function getMockStore() {
1060
1054
  }, isDevelopment ? void 0 : "STORE_MOCK_");
1061
1055
  }
1062
1056
 
1063
- // src/remote/remote.store.ts
1064
- import { isDevelopment as isDevelopment2 } from "@websolutespa/bom-core";
1065
-
1066
1057
  // src/cached/cached.service.ts
1067
- import { qsSerialize } from "@websolutespa/bom-core";
1068
- var CachedService = class {
1058
+ import { JsonService as JsonService3, qsSerialize } from "@websolutespa/bom-core";
1059
+ var CachedService = class extends JsonService3 {
1069
1060
  constructor(service, options = {}) {
1061
+ super([]);
1070
1062
  this.cache_ = /* @__PURE__ */ new Map();
1071
1063
  this.duration_ = storeCacheDuration;
1072
1064
  if (!service) {
@@ -1110,11 +1102,27 @@ var CachedService = class {
1110
1102
  }, key);
1111
1103
  }
1112
1104
  async findMany(params = {}) {
1113
- const serializedParams = qsSerialize(params);
1114
- const key = `findMany?${serializedParams}`;
1115
- return await this.fromCache_(async () => {
1116
- return await this.service_.findMany(params);
1117
- }, key);
1105
+ if (storeStrategy === "decoratedApi" /* DecoratedApi */) {
1106
+ const { market, locale, ...otherParams } = params;
1107
+ const serializedParams = qsSerialize({ market, locale });
1108
+ const key = `findMany?${serializedParams}`;
1109
+ return await this.fromCache_(async () => {
1110
+ let items = await this.service_.findMany({ market, locale });
1111
+ if (otherParams.where) {
1112
+ items = this.whereItems_(items, otherParams.where);
1113
+ }
1114
+ if (otherParams.sort) {
1115
+ items = this.sortCollection_(items, otherParams.sort);
1116
+ }
1117
+ return items;
1118
+ }, key);
1119
+ } else {
1120
+ const serializedParams = qsSerialize(params);
1121
+ const key = `findMany?${serializedParams}`;
1122
+ return await this.fromCache_(async () => {
1123
+ return await this.service_.findMany(params);
1124
+ }, key);
1125
+ }
1118
1126
  }
1119
1127
  async findPaged(params = {}) {
1120
1128
  const serializedParams = qsSerialize(params);
@@ -1144,12 +1152,12 @@ var API_BASE4 = process.env.STORE_API || "/api";
1144
1152
  function getRemoteApiUrl(pathname) {
1145
1153
  let url = `${API_URL3}${API_BASE4}${pathname}`;
1146
1154
  url = isBrowser3 ? url : url.replace("://localhost", "://127.0.0.1");
1147
- console.log("getRemoteApiUrl", url);
1148
1155
  return url;
1149
1156
  }
1150
1157
  async function remoteApiFetch(pathname, options = {}) {
1151
1158
  const url = getRemoteApiUrl(pathname);
1152
1159
  const apiOptions = merge3({ ...getDefaultApiRequestOptions() }, options);
1160
+ console.log("remoteApiFetch", apiOptions.method, url);
1153
1161
  const apiResponse = await httpFetch3(url, apiOptions);
1154
1162
  return apiResponse;
1155
1163
  }
@@ -1306,7 +1314,6 @@ var RemotePageService = class extends RemoteService {
1306
1314
  var PAGES2 = process.env.PAGES ? process.env.PAGES.split(",") : [];
1307
1315
  async function getRemoteStore_() {
1308
1316
  return fromCache(async () => {
1309
- console.log("getRemoteStore_");
1310
1317
  if (COLLECTIONS.length === 0) {
1311
1318
  console.error("RemoteStore.getRemoteStore_ no collections");
1312
1319
  throw "RemoteStore.getRemoteStore_ no collections";
@@ -1326,7 +1333,7 @@ async function getRemoteStore_() {
1326
1333
  store[key] = new CachedService(
1327
1334
  new RemoteMenuService(key, store)
1328
1335
  );
1329
- } else if (KNOWN_COLLECTIONS.includes(key) && !isDevelopment2) {
1336
+ } else if (KNOWN_COLLECTIONS.includes(key)) {
1330
1337
  store[key] = new CachedService(
1331
1338
  new RemoteService(key, store)
1332
1339
  );
@@ -1512,87 +1519,20 @@ async function routePost(store, request, response) {
1512
1519
  if (request.body.pathname && request.body.href) {
1513
1520
  try {
1514
1521
  const { pathname, href } = request.body;
1515
- const routes = await store.route.findMany({
1516
- where: {
1517
- id: {
1518
- equals: pathname
1519
- }
1520
- }
1521
- });
1522
- if (routes.length > 0) {
1523
- return response.status(200).json(routes[0]);
1524
- } else {
1525
- const splatRoutes = await store.route.findMany({
1526
- where: {
1527
- useSplat: {
1528
- equals: true
1529
- }
1530
- }
1531
- });
1532
- const splatRoute = splatRoutes.reduce((p, c) => {
1533
- const match = pathname.indexOf(c.id) === 0;
1534
- if (match && (!p || p.id.length < c.id.length)) {
1535
- return { ...c, splat: pathname.substring(c.id.length, pathname.length) };
1536
- } else {
1537
- return p;
1538
- }
1539
- }, void 0);
1540
- if (splatRoute) {
1541
- return response.status(200).json(splatRoute);
1542
- }
1543
- const redirects = await store.redirect.findMany({
1544
- where: {
1545
- isActive: {
1546
- equals: true
1547
- }
1548
- }
1549
- });
1550
- const exactMatchResolver = (redirect) => {
1551
- const status = parseInt(redirect.status) || 307;
1552
- if (status < 400) {
1553
- const redirectUrl = redirect.to;
1554
- return response.status(status).send({
1555
- redirectUrl
1556
- });
1557
- } else {
1558
- return response.status(status).send("gone");
1559
- }
1560
- };
1561
- const exactMatchRedirect = redirects.find((x) => x.from === href);
1562
- if (exactMatchRedirect !== void 0) {
1563
- return exactMatchResolver(exactMatchRedirect);
1522
+ const route = await findRoute(store, pathname, href);
1523
+ return response.status(200).json(route);
1524
+ } catch (error) {
1525
+ if (error.status < 500) {
1526
+ if (error.status < 400) {
1527
+ return response.status(error.status).send({
1528
+ redirectUrl: error.redirectUrl
1529
+ });
1564
1530
  }
1565
- const patternMatchResolver = (redirect) => {
1566
- const status = parseInt(redirect.status) || 307;
1567
- if (status < 400) {
1568
- const regexp = new RegExp(redirect.from);
1569
- let i = 0;
1570
- let redirectUrl = redirect.to.replace(/\*/g, (...rest) => {
1571
- return `$${++i}`;
1572
- });
1573
- redirectUrl = href.replace(regexp, redirectUrl);
1574
- return response.status(status).send({
1575
- redirectUrl
1576
- });
1577
- } else {
1578
- return response.status(status).send("gone");
1579
- }
1580
- };
1581
- redirects.forEach((x) => {
1582
- let expression = x.from;
1583
- expression = expression.replace(/\*(?!\))/g, "(.*)");
1584
- expression = `^${expression.replace(/(^\^)|(\$$)/g, "")}$`;
1585
- x.from = expression;
1586
- });
1587
- const firstMatch = redirects.find((x) => href.match(x.from));
1588
- if (firstMatch) {
1589
- return patternMatchResolver(firstMatch);
1531
+ if (error.status === 404) {
1532
+ return response.status(404).send("not found");
1533
+ } else {
1534
+ return response.status(error.status).send("gone");
1590
1535
  }
1591
- return response.status(404).send("not found");
1592
- }
1593
- } catch (error) {
1594
- if (error.status === 404) {
1595
- return response.status(404).send("not found");
1596
1536
  } else {
1597
1537
  console.error("storeHandler.error.500", error);
1598
1538
  throw error;
@@ -1733,6 +1673,82 @@ function storeIndexHandler() {
1733
1673
  }
1734
1674
  });
1735
1675
  }
1676
+ async function findRoute(store, pathname, href) {
1677
+ const routes = await store.route.findMany({
1678
+ where: {
1679
+ id: {
1680
+ equals: pathname
1681
+ }
1682
+ }
1683
+ });
1684
+ if (routes.length > 0) {
1685
+ return routes[0];
1686
+ } else {
1687
+ const splatRoutes = await store.route.findMany({
1688
+ where: {
1689
+ useSplat: {
1690
+ equals: true
1691
+ }
1692
+ }
1693
+ });
1694
+ const splatRoute = splatRoutes.reduce((p, c) => {
1695
+ const match = pathname.indexOf(c.id) === 0;
1696
+ if (match && (!p || p.id.length < c.id.length)) {
1697
+ return { ...c, splat: pathname.substring(c.id.length, pathname.length) };
1698
+ } else {
1699
+ return p;
1700
+ }
1701
+ }, void 0);
1702
+ if (splatRoute) {
1703
+ return splatRoute;
1704
+ }
1705
+ const redirects = await store.redirect.findMany({
1706
+ where: {
1707
+ isActive: {
1708
+ equals: true
1709
+ }
1710
+ }
1711
+ });
1712
+ const exactMatchResolver = (redirect) => {
1713
+ const status = parseInt(redirect.status) || 307;
1714
+ if (status < 400) {
1715
+ const redirectUrl = redirect.to;
1716
+ throw { status, redirectUrl };
1717
+ } else {
1718
+ throw { status };
1719
+ }
1720
+ };
1721
+ const exactMatchRedirect = redirects.find((x) => x.from === href);
1722
+ if (exactMatchRedirect !== void 0) {
1723
+ return exactMatchResolver(exactMatchRedirect);
1724
+ }
1725
+ const patternMatchResolver = (redirect) => {
1726
+ const status = parseInt(redirect.status) || 307;
1727
+ if (status < 400) {
1728
+ const regexp = new RegExp(redirect.from);
1729
+ let i = 0;
1730
+ let redirectUrl = redirect.to.replace(/\*/g, (...rest) => {
1731
+ return `$${++i}`;
1732
+ });
1733
+ redirectUrl = href.replace(regexp, redirectUrl);
1734
+ throw { status, redirectUrl };
1735
+ } else {
1736
+ throw { status };
1737
+ }
1738
+ };
1739
+ redirects.forEach((x) => {
1740
+ let expression = x.from;
1741
+ expression = expression.replace(/\*(?!\))/g, "(.*)");
1742
+ expression = `^${expression.replace(/(^\^)|(\$$)/g, "")}$`;
1743
+ x.from = expression;
1744
+ });
1745
+ const firstMatch = redirects.find((x) => href.match(x.from));
1746
+ if (firstMatch) {
1747
+ return patternMatchResolver(firstMatch);
1748
+ }
1749
+ throw { status: 404 };
1750
+ }
1751
+ }
1736
1752
 
1737
1753
  // src/mock/mock.handler.ts
1738
1754
  function mockHandler() {
@@ -1791,6 +1807,7 @@ export {
1791
1807
  decorateStaticRelations_,
1792
1808
  defaultApiRequestOptions,
1793
1809
  errorHandler,
1810
+ findRoute,
1794
1811
  fromCache,
1795
1812
  getDefaultApiRequestOptions,
1796
1813
  getLocalApiUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@websolutespa/bom-mixer-store",
3
- "version": "1.4.2",
3
+ "version": "1.4.3",
4
4
  "description": "Mixer Store module of the BOM Repository",
5
5
  "keywords": [
6
6
  "bom",
@@ -1,5 +1,5 @@
1
- import { IEntity, IEquatable, IPagedResult, IQuerable, QueryParams, qsSerialize } from '@websolutespa/bom-core';
2
- import { storeCacheDuration } from '../store/store';
1
+ import { IEntity, IEquatable, IPagedResult, IQuerable, JsonService, QueryParams, qsSerialize } from '@websolutespa/bom-core';
2
+ import { StoreStrategy, storeCacheDuration, storeStrategy } from '../store/store';
3
3
 
4
4
  export type ICacheResult<T extends IEntity = IEntity> = T | T[] | IPagedResult<T> | undefined;
5
5
 
@@ -12,13 +12,15 @@ export type InMemoryCacheOptions = {
12
12
  duration?: number | false;
13
13
  };
14
14
 
15
- export class CachedService<T extends IEntity = IEntity> implements IQuerable<T> {
15
+ // export class CachedService<T extends IEntity = IEntity> implements IQuerable<T> {
16
+ export class CachedService<T extends IEntity = IEntity> extends JsonService<T> {
16
17
 
17
18
  private service_: IQuerable<T>;
18
19
  private cache_: Map<string, ICache<T>> = new Map<string, ICache<T>>();
19
20
  private duration_: number | false = storeCacheDuration;
20
21
 
21
22
  constructor(service: IQuerable<T>, options: InMemoryCacheOptions = {}) {
23
+ super([]);
22
24
  if (!service) {
23
25
  throw new Error('CachedService: service is required');
24
26
  }
@@ -67,11 +69,27 @@ export class CachedService<T extends IEntity = IEntity> implements IQuerable<T>
67
69
  }
68
70
 
69
71
  async findMany<B extends T = T>(params: QueryParams = {}): Promise<B[]> {
70
- const serializedParams = qsSerialize(params);
71
- const key = `findMany?${serializedParams}`;
72
- return await this.fromCache_(async () => {
73
- return await this.service_.findMany(params);
74
- }, key) as B[];
72
+ if (storeStrategy === StoreStrategy.DecoratedApi) {
73
+ const { market, locale, ...otherParams } = params;
74
+ const serializedParams = qsSerialize({ market, locale });
75
+ const key = `findMany?${serializedParams}`;
76
+ return await this.fromCache_(async () => {
77
+ let items = await this.service_.findMany({ market, locale });
78
+ if (otherParams.where) {
79
+ items = this.whereItems_(items, otherParams.where);
80
+ }
81
+ if (otherParams.sort) {
82
+ items = this.sortCollection_(items, otherParams.sort);
83
+ }
84
+ return items;
85
+ }, key) as B[];
86
+ } else {
87
+ const serializedParams = qsSerialize(params);
88
+ const key = `findMany?${serializedParams}`;
89
+ return await this.fromCache_(async () => {
90
+ return await this.service_.findMany(params);
91
+ }, key) as B[];
92
+ }
75
93
  }
76
94
 
77
95
  async findPaged<B extends T = T>(params: QueryParams = {}): Promise<IPagedResult<B>> {
@@ -35,10 +35,6 @@ export async function decorateHrefs_<T extends ISchema = ISchema>(store: IStore,
35
35
  locale: {
36
36
  equals: params.locale,
37
37
  },
38
- /*
39
- ...(params.market ? { market: { equals: params.market } } : {}),
40
- ...(params.locale ? { locale: { equals: params.locale } } : {}),
41
- */
42
38
  },
43
39
  });
44
40
  return items.map(item => {
@@ -22,7 +22,6 @@ export async function decorateMenu_(store: IStore, item: IUndecoratedMenu, param
22
22
  undecoratedPages.length > 0
23
23
  ) {
24
24
  const routes = await store.route.findMany<IRoute>({
25
- ...params,
26
25
  where: {
27
26
  ...(market ? { market: { equals: market } } : {}),
28
27
  ...(locale ? { locale: { equals: locale } } : {}),
@@ -19,7 +19,6 @@ export async function decorateNav_<T extends ISchema = ISchema>(store: IStore, i
19
19
 
20
20
  export async function decorateNavs_<T extends ISchema = ISchema>(store: IStore, items: T[], params: QueryParams = {}): Promise<T[]> {
21
21
  const routes = await store.route.findMany<IRoute>({
22
- ...params,
23
22
  where: {
24
23
  ...(params.market ? { market: { equals: params.market } } : {}),
25
24
  ...(params.locale ? { locale: { equals: params.locale } } : {}),
@@ -16,7 +16,7 @@ export function getLocalApiUrl(pathname: string): string {
16
16
  let url = `${isBrowser ? window.location.origin : API_URL}${API_BASE}${pathname}`;
17
17
  // !!! node 18 issue -> require('dns').setDefaultResultOrder('ipv4first');
18
18
  url = isBrowser ? url : url.replace('://localhost', '://127.0.0.1');
19
- console.log('getLocalApiUrl', url);
19
+ // console.log('getLocalApiUrl', url);
20
20
  return url;
21
21
  }
22
22
 
@@ -28,7 +28,7 @@ export async function localApiFetch<T = unknown>(pathname: string, options: ApiR
28
28
  url.searchParams.append('market', serviceOptions.market);
29
29
  }
30
30
  const apiOptions = merge({ ...getDefaultApiRequestOptions() }, options);
31
- // console.log('localApiFetch', url.href, apiOptions);
31
+ console.log('localApiFetch', apiOptions.method, url.href);
32
32
  const apiResponse = await httpFetch(url.href, apiOptions);
33
33
  // console.log('localApiFetch', pathname, apiResponse);
34
34
  return apiResponse;
@@ -12,13 +12,14 @@ function getMockApiUrl(pathname: string): string {
12
12
  let url = `${API_URL}${API_BASE}/_mock${pathname}`;
13
13
  // !!! node 18 issue -> require('dns').setDefaultResultOrder('ipv4first');
14
14
  url = isBrowser ? url : url.replace('://localhost', '://127.0.0.1');
15
- console.log('getMockApiUrl', url);
15
+ // console.log('getMockApiUrl', url);
16
16
  return url;
17
17
  }
18
18
 
19
19
  export async function mockApiFetch<T = unknown>(pathname: string, options: ApiRequestOptions = {}): Promise<T> {
20
20
  const url = getMockApiUrl(pathname);
21
21
  const apiOptions = merge({ ...getDefaultApiRequestOptions() }, options);
22
+ console.log('mockApiFetch', apiOptions.method, url);
22
23
  const apiResponse = await httpFetch(url, apiOptions);
23
24
  return apiResponse;
24
25
  }
@@ -39,7 +39,7 @@ export function getMockStoreFromData_<T extends IStore>(data?: IMemoryStore): T
39
39
  }
40
40
  }
41
41
  } else {
42
- console.log('mock store not found');
42
+ console.warn('mock store not found');
43
43
  }
44
44
  staticCollections.forEach(config => {
45
45
  store[config.slug] = new StaticService(config, store);
@@ -12,13 +12,14 @@ function getRemoteApiUrl(pathname: string): string {
12
12
  let url = `${API_URL}${API_BASE}${pathname}`;
13
13
  // !!! node 18 issue -> require('dns').setDefaultResultOrder('ipv4first');
14
14
  url = isBrowser ? url : url.replace('://localhost', '://127.0.0.1');
15
- console.log('getRemoteApiUrl', url);
15
+ // console.log('getRemoteApiUrl', url);
16
16
  return url;
17
17
  }
18
18
 
19
19
  export async function remoteApiFetch<T = unknown>(pathname: string, options: ApiRequestOptions = {}): Promise<T> {
20
20
  const url = getRemoteApiUrl(pathname);
21
21
  const apiOptions = merge({ ...getDefaultApiRequestOptions() }, options);
22
+ console.log('remoteApiFetch', apiOptions.method, url);
22
23
  const apiResponse = await httpFetch(url, apiOptions);
23
24
  return apiResponse;
24
25
  }
@@ -1,4 +1,4 @@
1
- import { IEntity, IMemoryStore, IQuerable, IStore, isDevelopment } from '@websolutespa/bom-core';
1
+ import { IEntity, IMemoryStore, IQuerable, IStore } from '@websolutespa/bom-core';
2
2
  import { fromCache } from '../cache/cache.service';
3
3
  import { CachedService } from '../cached/cached.service';
4
4
  import { MockService } from '../mock/mock.service';
@@ -13,7 +13,7 @@ const PAGES: string[] = process.env.PAGES ? process.env.PAGES.split(',') : [];
13
13
 
14
14
  async function getRemoteStore_<T extends IStore>(): Promise<T> {
15
15
  return fromCache<T>(async () => {
16
- console.log('getRemoteStore_');
16
+ // console.log('getRemoteStore_');
17
17
  if (COLLECTIONS.length === 0) {
18
18
  console.error('RemoteStore.getRemoteStore_ no collections');
19
19
  throw ('RemoteStore.getRemoteStore_ no collections');
@@ -33,7 +33,7 @@ async function getRemoteStore_<T extends IStore>(): Promise<T> {
33
33
  store[key] = new CachedService(
34
34
  new RemoteMenuService(key, store)
35
35
  );
36
- } else if (KNOWN_COLLECTIONS.includes(key) && !isDevelopment) {
36
+ } else if (KNOWN_COLLECTIONS.includes(key)) {
37
37
  store[key] = new CachedService(
38
38
  new RemoteService(key, store)
39
39
  );
@@ -42,8 +42,8 @@ async function getRemoteStore_<T extends IStore>(): Promise<T> {
42
42
  }
43
43
  });
44
44
  return store as T;
45
- // }, isDevelopment ? undefined : 'STORE_RUNTIME_');
46
45
  }, 'STORE_RUNTIME_');
46
+ // }, isDevelopment ? undefined : 'STORE_RUNTIME_');
47
47
  }
48
48
 
49
49
  async function getRemoteStoreStaticBuild_<T extends IStore>(): Promise<T> {
@@ -155,6 +155,9 @@ export async function routePost(store: IStore, request: NextApiRequest, response
155
155
  if (request.body.pathname && request.body.href) {
156
156
  try {
157
157
  const { pathname, href } = request.body;
158
+ const route = await findRoute(store, pathname, href);
159
+ return response.status(200).json(route);
160
+ /*
158
161
  // here we use findMany because route id would break api url
159
162
  const routes = await store.route.findMany<IRoute>({
160
163
  where: {
@@ -221,7 +224,7 @@ export async function routePost(store: IStore, request: NextApiRequest, response
221
224
  if (status < 400) {
222
225
  const regexp = new RegExp(redirect.from);
223
226
  let i = 0;
224
- let redirectUrl = (redirect.to as string).replace(/\*/g, (...rest) => {
227
+ let redirectUrl = (redirect.to as string).replace(//g, (...rest) => {
225
228
  return `$${++i}`;
226
229
  });
227
230
  redirectUrl = href.replace(regexp, redirectUrl);
@@ -249,7 +252,24 @@ export async function routePost(store: IStore, request: NextApiRequest, response
249
252
  // console.error('not found', collection);
250
253
  return response.status(404).send('not found');
251
254
  }
255
+ */
252
256
  } catch (error: any) {
257
+ if (error.status < 500) {
258
+ if (error.status < 400) {
259
+ return response.status(error.status).send({
260
+ redirectUrl: error.redirectUrl,
261
+ });
262
+ }
263
+ if (error.status === 404) {
264
+ return response.status(404).send('not found');
265
+ } else {
266
+ return response.status(error.status).send('gone');
267
+ }
268
+ } else {
269
+ console.error('storeHandler.error.500', error);
270
+ throw error;
271
+ }
272
+ /*
253
273
  if (error.status === 404) {
254
274
  // console.error('not found', collection);
255
275
  return response.status(404).send('not found');
@@ -257,6 +277,7 @@ export async function routePost(store: IStore, request: NextApiRequest, response
257
277
  console.error('storeHandler.error.500', error);
258
278
  throw error;
259
279
  }
280
+ */
260
281
  }
261
282
  } else {
262
283
  return response.status(400).send('bad request');
@@ -397,3 +418,97 @@ export function storeIndexHandler() {
397
418
  },
398
419
  });
399
420
  }
421
+
422
+
423
+
424
+ export async function findRoute(store: IStore, pathname: string, href: string): Promise<IRoute | undefined> {
425
+ // here we use findMany because route id would break api url
426
+ const routes = await store.route.findMany<IRoute>({
427
+ where: {
428
+ id: {
429
+ equals: pathname,
430
+ },
431
+ },
432
+ });
433
+ // console.log('pathname', pathname);
434
+ if (routes.length > 0) {
435
+ return routes[0];
436
+ } else {
437
+ // exact route match not found
438
+ // let's try if match splat routes
439
+ const splatRoutes = await store.route.findMany<IRoute>({
440
+ where: {
441
+ useSplat: {
442
+ equals: true,
443
+ },
444
+ },
445
+ });
446
+ // console.log('splatRoutes', splatRoutes);
447
+ // find best match splat route
448
+ const splatRoute = splatRoutes.reduce((p: (IRoute & { splat: string }) | undefined, c: IRoute) => {
449
+ const match = pathname.indexOf(c.id) === 0;
450
+ if (match && (!p || p.id.length < c.id.length)) {
451
+ return { ...c, splat: pathname.substring(c.id.length, pathname.length) };
452
+ } else {
453
+ return p;
454
+ }
455
+ }, undefined);
456
+ if (splatRoute) {
457
+ return splatRoute;
458
+ }
459
+ // collect redirects
460
+ const redirects = await store.redirect.findMany<IRedirect>({
461
+ where: {
462
+ isActive: {
463
+ equals: true,
464
+ },
465
+ },
466
+ });
467
+ // redirects.sort((a, b) => a.order - b.order);
468
+ // exact match redirect resolver
469
+ const exactMatchResolver = (redirect: IRedirect) => {
470
+ const status: number = parseInt(redirect.status) || 307;
471
+ if (status < 400) {
472
+ const redirectUrl = redirect.to;
473
+ throw { status, redirectUrl };
474
+ } else {
475
+ throw { status };
476
+ }
477
+ };
478
+ // check exact match redirects
479
+ const exactMatchRedirect = redirects.find(x => x.from === href);
480
+ if (exactMatchRedirect !== undefined) {
481
+ return exactMatchResolver(exactMatchRedirect);
482
+ }
483
+ // pattern match redirect resolver
484
+ const patternMatchResolver = (redirect: IRedirect) => {
485
+ const status: number = parseInt(redirect.status) || 307;
486
+ if (status < 400) {
487
+ const regexp = new RegExp(redirect.from);
488
+ let i = 0;
489
+ let redirectUrl = (redirect.to as string).replace(/\*/g, (...rest) => {
490
+ return `$${++i}`;
491
+ });
492
+ redirectUrl = href.replace(regexp, redirectUrl);
493
+ throw { status, redirectUrl };
494
+ } else {
495
+ throw { status };
496
+ }
497
+ };
498
+ // check redirects by pattern
499
+ redirects.forEach(x => {
500
+ let expression = x.from;
501
+ // wrapping catchall in (.*)
502
+ expression = expression.replace(/\*(?!\))/g, '(.*)');
503
+ // wrapping expression in ^...$
504
+ expression = `^${expression.replace(/(^\^)|(\$$)/g, '')}$`;
505
+ x.from = expression;
506
+ });
507
+ // check if a regex pattern redirect does match
508
+ const firstMatch = redirects.find(x => href.match(x.from));
509
+ if (firstMatch) {
510
+ return patternMatchResolver(firstMatch);
511
+ }
512
+ throw { status: 404 };
513
+ }
514
+ }