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