@route-forge/core 1.2.2 → 1.3.1

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/index.js CHANGED
@@ -465,6 +465,39 @@ async function resolveAdapter(opts) {
465
465
  return createBuiltinHttp(opts.forgeInterceptors);
466
466
  }
467
467
 
468
+ // src/resolveRouteName.ts
469
+ async function resolveRouteName(forge, level, prefix, suffix, separator = ".") {
470
+ if (!suffix) return prefix;
471
+ const joined = `${prefix}${separator}${suffix}`;
472
+ if (!suffix.startsWith(`${prefix}${separator}`)) return joined;
473
+ await forge.load(level);
474
+ if (forge.hasRoute(level, joined)) return joined;
475
+ if (forge.hasRoute(level, suffix)) return suffix;
476
+ throw new UnknownRouteError(joined, level);
477
+ }
478
+ function resolveRouteNameSync(forge, level, prefix, suffix, separator = ".") {
479
+ if (!suffix) return prefix;
480
+ const joined = `${prefix}${separator}${suffix}`;
481
+ if (!suffix.startsWith(`${prefix}${separator}`)) return joined;
482
+ if (forge.hasRoute(level, joined)) return joined;
483
+ if (forge.hasRoute(level, suffix)) return suffix;
484
+ throw new UnknownRouteError(joined, level);
485
+ }
486
+
487
+ // src/defineImmutableProps.ts
488
+ function defineImmutableProps(target, props) {
489
+ for (const key of Object.keys(props)) {
490
+ const val = props[key];
491
+ Object.defineProperty(target, key, {
492
+ value: val !== null && typeof val === "object" ? Object.freeze(val) : val,
493
+ writable: false,
494
+ enumerable: false,
495
+ configurable: false
496
+ });
497
+ }
498
+ return target;
499
+ }
500
+
468
501
  // src/loading.ts
469
502
  var LoadingTracker = class {
470
503
  constructor() {
@@ -631,7 +664,6 @@ function createRouteForge(options) {
631
664
  const readyPromise = new Promise((resolve) => {
632
665
  resolveReady = resolve;
633
666
  });
634
- const levelLoadedListeners = /* @__PURE__ */ new Map();
635
667
  const cacheTtl = cacheOpts.ttl ?? DEFAULT_CACHE_TTL;
636
668
  const cacheStorage = cacheOpts.storage ?? "memory";
637
669
  const cache = new RouteCache({ storage: cacheStorage, ttl: cacheTtl });
@@ -683,7 +715,7 @@ function createRouteForge(options) {
683
715
  function assertDiscoveryReady() {
684
716
  if (!autoDiscoveryCompleted && !explicitLevels?.length) {
685
717
  throw new ForgeError(
686
- "Route data not available. Auto-discovery has not completed. Use onSummaryReady callback to mount app, or await forge.ready / forge.load(level) first.",
718
+ "Route data not available. Auto-discovery has not completed. Use forge.ready() or forge.use(level) first.",
687
719
  { code: "RF_FE_010" }
688
720
  );
689
721
  }
@@ -736,15 +768,11 @@ function createRouteForge(options) {
736
768
  routes[r.name] = { ...r, level: UNASSIGNED_LEVEL };
737
769
  }
738
770
  cache.set({ level: UNASSIGNED_LEVEL, routes, cache: null });
739
- const listeners = levelLoadedListeners.get(level);
740
- if (listeners) listeners.forEach((cb) => cb());
741
771
  return;
742
772
  }
743
773
  const resp = await fetchLevel(level);
744
774
  if ((invalidationGens.get(level) ?? 0) === gen) {
745
775
  cache.set(resp);
746
- const listeners = levelLoadedListeners.get(level);
747
- if (listeners) listeners.forEach((cb) => cb());
748
776
  }
749
777
  } finally {
750
778
  inflight.delete(level);
@@ -818,23 +846,40 @@ function createRouteForge(options) {
818
846
  }
819
847
  return void 0;
820
848
  }
821
- async function api(level, name, params = {}) {
822
- await autoDiscoveryPromise;
823
- await load(level);
824
- const meta = findRouteMeta(level, name);
825
- if (!meta) {
826
- throw new UnknownRouteError(name, level);
827
- }
828
- return doApiCall(meta, params);
849
+ function api(level, name, params = {}) {
850
+ let ctrl;
851
+ let abortedBeforeInit = false;
852
+ let abortReason;
853
+ const work = (async () => {
854
+ ctrl = new AbortController();
855
+ if (abortedBeforeInit) {
856
+ ctrl.abort(abortReason);
857
+ }
858
+ await autoDiscoveryPromise;
859
+ await load(level);
860
+ const meta = findRouteMeta(level, name);
861
+ if (!meta) {
862
+ throw new UnknownRouteError(name, level);
863
+ }
864
+ return doApiCall(meta, params, ctrl.signal);
865
+ })();
866
+ const request = work;
867
+ request.abort = () => {
868
+ if (ctrl) {
869
+ ctrl.abort();
870
+ } else {
871
+ abortedBeforeInit = true;
872
+ }
873
+ };
874
+ return request;
829
875
  }
830
- async function doApiCall(meta, params) {
876
+ async function doApiCall(meta, params, signal) {
831
877
  const {
832
878
  pathParams,
833
879
  query,
834
880
  body,
835
881
  headers,
836
- timeout: perCallTimeout,
837
- signal
882
+ timeout: perCallTimeout
838
883
  } = resolveApiParams(params);
839
884
  if (signal?.aborted) {
840
885
  throw new RequestAbortedError(meta.name, meta.level, signal.reason);
@@ -878,7 +923,7 @@ function createRouteForge(options) {
878
923
  },
879
924
  (err) => {
880
925
  if (err instanceof ForgeError) throw err;
881
- if (isAbortError2(err, finalConfig.signal)) {
926
+ if (isAbortError2(err, signal)) {
882
927
  throw new RequestAbortedError(meta.name, meta.level, err);
883
928
  }
884
929
  throw new NetworkError(
@@ -946,6 +991,7 @@ function createRouteForge(options) {
946
991
  return result;
947
992
  }
948
993
  void autoDiscoveryPromise.then(() => {
994
+ autoDiscoveryCompleted = true;
949
995
  options.onSummaryReady?.();
950
996
  if (effectiveEager.length > 0) {
951
997
  return Promise.all(effectiveEager.map((lvl) => load(lvl))).catch((e) => {
@@ -953,11 +999,74 @@ function createRouteForge(options) {
953
999
  });
954
1000
  }
955
1001
  }).then(() => {
956
- autoDiscoveryCompleted = true;
957
- resolveReady();
1002
+ resolveReady(forgeInstance);
958
1003
  }).catch(() => {
959
1004
  });
960
- return {
1005
+ function ready(onFulfilled, onRejected) {
1006
+ if (onFulfilled) {
1007
+ const p = readyPromise.then(onFulfilled, onRejected);
1008
+ return p.then(() => forgeInstance);
1009
+ }
1010
+ return readyPromise;
1011
+ }
1012
+ const forgeResolver = { load, hasRoute };
1013
+ function createBoundForge(level, prefix) {
1014
+ const levelLoadedPromise = load(level).catch(() => {
1015
+ });
1016
+ const apiFn = prefix ? (name, params) => resolveRouteName(forgeResolver, level, prefix, name).then(
1017
+ (resolved) => api(level, resolved, params)
1018
+ ) : (name, params) => api(level, name, params);
1019
+ const callable = apiFn;
1020
+ defineImmutableProps(callable, {
1021
+ level,
1022
+ ...prefix !== void 0 ? { prefix } : {},
1023
+ api: apiFn,
1024
+ route: prefix ? (name, params) => route(level, resolveRouteNameSync(forgeResolver, level, prefix, name), params) : (name, params) => route(level, name, params),
1025
+ url: prefix ? (name, params) => route(level, resolveRouteNameSync(forgeResolver, level, prefix, name), params) : (name, params) => route(level, name, params),
1026
+ hasRoute: (name) => hasRoute(level, name),
1027
+ getRoutes: () => getRoutes(level),
1028
+ load: () => load(level),
1029
+ invalidate: () => invalidate(level),
1030
+ isLoaded: () => isLoaded(level),
1031
+ isLoading: () => loadingTracker.isLoading(),
1032
+ onLoadingChange: (cb) => loadingTracker.subscribe(cb)
1033
+ });
1034
+ Object.defineProperty(callable, "levelLoaded", {
1035
+ value: levelLoadedPromise,
1036
+ writable: false,
1037
+ enumerable: true,
1038
+ configurable: true
1039
+ });
1040
+ return callable;
1041
+ }
1042
+ function boundOnLevelLoaded(bound, levelLoadedPromise, onFulfilled, onRejected) {
1043
+ const p = levelLoadedPromise.then(() => bound);
1044
+ if (onFulfilled) {
1045
+ return p.then(onFulfilled, onRejected).then(() => bound);
1046
+ }
1047
+ return p;
1048
+ }
1049
+ function attachBoundMethods(bound, levelLoadedPromise, level) {
1050
+ Object.defineProperty(bound, "onLevelLoaded", {
1051
+ value: (onFulfilled, onRejected) => boundOnLevelLoaded(bound, levelLoadedPromise, onFulfilled, onRejected),
1052
+ writable: false,
1053
+ enumerable: false,
1054
+ configurable: false
1055
+ });
1056
+ Object.defineProperty(bound, "useRoutePrefix", {
1057
+ value: (prefix) => createBoundForgeWithMethods(level, prefix),
1058
+ writable: false,
1059
+ enumerable: false,
1060
+ configurable: false
1061
+ });
1062
+ }
1063
+ function createBoundForgeWithMethods(level, prefix) {
1064
+ const bound = createBoundForge(level, prefix);
1065
+ const levelLoadedPromise = bound.levelLoaded;
1066
+ attachBoundMethods(bound, levelLoadedPromise, level);
1067
+ return bound;
1068
+ }
1069
+ const forgeInstance = {
961
1070
  api,
962
1071
  load,
963
1072
  route,
@@ -972,15 +1081,13 @@ function createRouteForge(options) {
972
1081
  request: requestInterceptors,
973
1082
  response: responseInterceptors
974
1083
  },
975
- ready: readyPromise,
976
- onLevelLoaded(level, cb) {
977
- if (!levelLoadedListeners.has(level)) {
978
- levelLoadedListeners.set(level, /* @__PURE__ */ new Set());
979
- }
980
- levelLoadedListeners.get(level).add(cb);
981
- return () => levelLoadedListeners.get(level)?.delete(cb);
1084
+ ready,
1085
+ use(level, prefix) {
1086
+ if (level === void 0) return forgeInstance;
1087
+ return createBoundForgeWithMethods(level, prefix);
982
1088
  }
983
1089
  };
1090
+ return forgeInstance;
984
1091
  }
985
1092
  function pickMethod(meta) {
986
1093
  const m = meta.methods.find((x) => x.toUpperCase() !== "HEAD");
@@ -1004,7 +1111,6 @@ function resolveApiParams(input) {
1004
1111
  body: rawBody,
1005
1112
  headers: rawHeaders,
1006
1113
  timeout: perCallTimeout,
1007
- signal,
1008
1114
  ...flatRest
1009
1115
  } = input;
1010
1116
  const pathParams = explicitParams ? { ...explicitParams } : {};
@@ -1037,7 +1143,7 @@ function resolveApiParams(input) {
1037
1143
  pathParams.headers = rawHeaders;
1038
1144
  }
1039
1145
  }
1040
- return { pathParams, query, body, headers, timeout: perCallTimeout, signal };
1146
+ return { pathParams, query, body, headers, timeout: perCallTimeout };
1041
1147
  }
1042
1148
  function isAbortError2(err, signal) {
1043
1149
  if (signal?.aborted) return true;
@@ -1048,25 +1154,6 @@ function isAbortError2(err, signal) {
1048
1154
  return false;
1049
1155
  }
1050
1156
 
1051
- // src/resolveRouteName.ts
1052
- async function resolveRouteName(forge, level, prefix, suffix, separator = ".") {
1053
- if (!suffix) return prefix;
1054
- const joined = `${prefix}${separator}${suffix}`;
1055
- if (!suffix.startsWith(`${prefix}${separator}`)) return joined;
1056
- await forge.load(level);
1057
- if (forge.hasRoute(level, joined)) return joined;
1058
- if (forge.hasRoute(level, suffix)) return suffix;
1059
- throw new UnknownRouteError(joined, level);
1060
- }
1061
- function resolveRouteNameSync(forge, level, prefix, suffix, separator = ".") {
1062
- if (!suffix) return prefix;
1063
- const joined = `${prefix}${separator}${suffix}`;
1064
- if (!suffix.startsWith(`${prefix}${separator}`)) return joined;
1065
- if (forge.hasRoute(level, joined)) return joined;
1066
- if (forge.hasRoute(level, suffix)) return suffix;
1067
- throw new UnknownRouteError(joined, level);
1068
- }
1069
-
1070
1157
  export { AdapterNotFoundError, ForgeError, HTTPError, InterceptorManagerImpl, InvalidInterceptorReturnError, LoadingTracker, MissingRouteParamError, NetworkError, RequestAbortedError, RouteCache, UnknownLevelError, UnknownRouteError, createInterceptorManager, createRouteForge, resolveRouteName, resolveRouteNameSync };
1071
1158
  //# sourceMappingURL=index.js.map
1072
1159
  //# sourceMappingURL=index.js.map