@koordinates/xstate-tree 4.7.0 → 4.8.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.
@@ -69,7 +69,7 @@ function buildCreateRoute(history, basePath) {
69
69
  }
70
70
  return parentRoutes;
71
71
  }
72
- return ({ event, matcher, reverser, paramsSchema, querySchema, redirect, preload, }) => {
72
+ return ({ event, matcher, reverser, paramsSchema, querySchema, redirect, preload, canMatch, }) => {
73
73
  let fullParamsSchema = paramsSchema;
74
74
  let parentRoute = baseRoute;
75
75
  while (fullParamsSchema && parentRoute) {
@@ -86,6 +86,7 @@ function buildCreateRoute(history, basePath) {
86
86
  querySchema,
87
87
  parent: baseRoute,
88
88
  redirect,
89
+ canMatch,
89
90
  matcher: matcher,
90
91
  reverser: reverser,
91
92
  // @ts-ignore :cry:
@@ -95,19 +96,34 @@ function buildCreateRoute(history, basePath) {
95
96
  },
96
97
  // @ts-ignore :cry:
97
98
  matches(suppliedUrl, search) {
98
- var _a, _b, _c;
99
+ var _a, _b, _c, _d, _e;
99
100
  const fullUrl = suppliedUrl.endsWith("/")
100
101
  ? suppliedUrl
101
102
  : suppliedUrl + "/";
102
103
  let url = fullUrl;
103
104
  const parentRoutes = getParentArray();
104
105
  let params = {};
106
+ const parsedQuery = (0, query_string_1.parse)(search);
105
107
  while (parentRoutes.length) {
106
108
  const parentRoute = parentRoutes.shift();
107
109
  const parentMatch = parentRoute.matcher(url, undefined);
108
110
  if (parentMatch === false) {
109
111
  return false;
110
112
  }
113
+ // Evaluate parent's canMatch predicate if provided
114
+ if (parentRoute.canMatch) {
115
+ const accumulatedParams = {
116
+ ...params,
117
+ ...((_a = parentMatch.params) !== null && _a !== void 0 ? _a : {}),
118
+ };
119
+ const canMatchResult = parentRoute.canMatch({
120
+ params: accumulatedParams,
121
+ query: parsedQuery,
122
+ });
123
+ if (!canMatchResult) {
124
+ return false;
125
+ }
126
+ }
111
127
  url = url.slice(parentMatch.matchLength);
112
128
  // All routes assume the url starts with a /
113
129
  // so if the parent route matches the / in the url, which consumes it
@@ -115,7 +131,7 @@ function buildCreateRoute(history, basePath) {
115
131
  if (!url.startsWith("/")) {
116
132
  url = "/" + url;
117
133
  }
118
- params = { ...params, ...((_a = parentMatch.params) !== null && _a !== void 0 ? _a : {}) };
134
+ params = { ...params, ...((_b = parentMatch.params) !== null && _b !== void 0 ? _b : {}) };
119
135
  }
120
136
  const matches = matcher(url, (0, query_string_1.parse)(search));
121
137
  // if there is any URL left after matching this route, the last to match
@@ -125,7 +141,7 @@ function buildCreateRoute(history, basePath) {
125
141
  }
126
142
  const fullParams = {
127
143
  ...params,
128
- ...((_b = matches.params) !== null && _b !== void 0 ? _b : {}),
144
+ ...((_c = matches.params) !== null && _c !== void 0 ? _c : {}),
129
145
  };
130
146
  if (fullParamsSchema) {
131
147
  fullParamsSchema.parse(fullParams);
@@ -133,11 +149,21 @@ function buildCreateRoute(history, basePath) {
133
149
  if (querySchema) {
134
150
  querySchema.parse(matches.query);
135
151
  }
152
+ // Check canMatch predicate if provided
153
+ if (canMatch) {
154
+ const canMatchResult = canMatch({
155
+ params: fullParams,
156
+ query: (_d = matches.query) !== null && _d !== void 0 ? _d : {},
157
+ });
158
+ if (!canMatchResult) {
159
+ return false;
160
+ }
161
+ }
136
162
  return {
137
163
  originalUrl: `${fullUrl}${search}`,
138
164
  type: event,
139
165
  params: fullParams,
140
- query: (_c = matches.query) !== null && _c !== void 0 ? _c : {},
166
+ query: (_e = matches.query) !== null && _e !== void 0 ? _e : {},
141
167
  };
142
168
  },
143
169
  // @ts-ignore :cry:
@@ -60,6 +60,7 @@ export declare type AnyRoute = {
60
60
  matcher: (url: string, query: ParsedQuery<string> | undefined) => any;
61
61
  reverser: any;
62
62
  redirect?: any;
63
+ canMatch?: any;
63
64
  };
64
65
 
65
66
  /**
@@ -129,6 +130,7 @@ export declare function buildCreateRoute(history: () => XstateTreeHistory, baseP
129
130
  meta?: TMeta | undefined;
130
131
  redirect?: RouteRedirect<MergeRouteTypes<RouteParams<TBaseRoute>, ResolveZodType<TParamsSchema>>, ResolveZodType<TQuerySchema>, MergeRouteTypes<RouteMeta<TBaseRoute>, TMeta> & SharedMeta> | undefined;
131
132
  preload?: RouteArgumentFunctions<void, MergeRouteTypes<RouteParams<TBaseRoute>, ResolveZodType<TParamsSchema>>, ResolveZodType<TQuerySchema>, MergeRouteTypes<RouteMeta<TBaseRoute>, TMeta>, RouteArguments<MergeRouteTypes<RouteParams<TBaseRoute>, ResolveZodType<TParamsSchema>>, ResolveZodType<TQuerySchema>, MergeRouteTypes<RouteMeta<TBaseRoute>, TMeta>>> | undefined;
133
+ canMatch?: RouteArgumentFunctions<boolean, MergeRouteTypes<RouteParams<TBaseRoute>, ResolveZodType<TParamsSchema>>, ResolveZodType<TQuerySchema>, MergeRouteTypes<RouteMeta<TBaseRoute>, TMeta> & SharedMeta, RouteArguments<MergeRouteTypes<RouteParams<TBaseRoute>, ResolveZodType<TParamsSchema>>, ResolveZodType<TQuerySchema>, MergeRouteTypes<RouteMeta<TBaseRoute>, TMeta> & SharedMeta>> | undefined;
132
134
  }) => Route<MergeRouteTypes<RouteParams<TBaseRoute>, ResolveZodType<TParamsSchema>>, ResolveZodType<TQuerySchema>, TEvent, MergeRouteTypes<RouteMeta<TBaseRoute>, TMeta> & SharedMeta>;
133
135
  route<TBaseRoute_1 extends AnyRoute>(baseRoute?: TBaseRoute_1 | undefined): <TEvent_1 extends string, TParamsSchema_1 extends Z.ZodObject<any, "strip", Z.ZodTypeAny, {
134
136
  [x: string]: any;
@@ -138,7 +140,7 @@ export declare function buildCreateRoute(history: () => XstateTreeHistory, baseP
138
140
  [x: string]: any;
139
141
  }, {
140
142
  [x: string]: any;
141
- }> | undefined, TMeta_1 extends Record<string, unknown>>({ event, matcher, reverser, paramsSchema, querySchema, redirect, preload, }: {
143
+ }> | undefined, TMeta_1 extends Record<string, unknown>>({ event, matcher, reverser, paramsSchema, querySchema, redirect, preload, canMatch, }: {
142
144
  event: TEvent_1;
143
145
  paramsSchema?: TParamsSchema_1 | undefined;
144
146
  querySchema?: TQuerySchema_1 | undefined;
@@ -160,6 +162,7 @@ export declare function buildCreateRoute(history: () => XstateTreeHistory, baseP
160
162
  */
161
163
  reverser: RouteArgumentFunctions<string, MergeRouteTypes<RouteParams<TBaseRoute_1>, ResolveZodType<TParamsSchema_1>>, ResolveZodType<TQuerySchema_1>, MergeRouteTypes<RouteMeta<TBaseRoute_1>, TMeta_1>, RouteArguments<MergeRouteTypes<RouteParams<TBaseRoute_1>, ResolveZodType<TParamsSchema_1>>, ResolveZodType<TQuerySchema_1>, MergeRouteTypes<RouteMeta<TBaseRoute_1>, TMeta_1>>>;
162
164
  preload?: RouteArgumentFunctions<void, MergeRouteTypes<RouteParams<TBaseRoute_1>, ResolveZodType<TParamsSchema_1>>, ResolveZodType<TQuerySchema_1>, MergeRouteTypes<RouteMeta<TBaseRoute_1>, TMeta_1>, RouteArguments<MergeRouteTypes<RouteParams<TBaseRoute_1>, ResolveZodType<TParamsSchema_1>>, ResolveZodType<TQuerySchema_1>, MergeRouteTypes<RouteMeta<TBaseRoute_1>, TMeta_1>>> | undefined;
165
+ canMatch?: RouteArgumentFunctions<boolean, MergeRouteTypes<RouteParams<TBaseRoute_1>, ResolveZodType<TParamsSchema_1>>, ResolveZodType<TQuerySchema_1>, MergeRouteTypes<RouteMeta<TBaseRoute_1>, TMeta_1> & SharedMeta, RouteArguments<MergeRouteTypes<RouteParams<TBaseRoute_1>, ResolveZodType<TParamsSchema_1>>, ResolveZodType<TQuerySchema_1>, MergeRouteTypes<RouteMeta<TBaseRoute_1>, TMeta_1> & SharedMeta>> | undefined;
163
166
  }) => Route<MergeRouteTypes<RouteParams<TBaseRoute_1>, ResolveZodType<TParamsSchema_1>>, ResolveZodType<TQuerySchema_1>, TEvent_1, MergeRouteTypes<RouteMeta<TBaseRoute_1>, TMeta_1> & SharedMeta>;
164
167
  };
165
168
 
@@ -591,6 +594,12 @@ export declare type Route<TParams, TQuery, TEvent, TMeta> = {
591
594
  paramsSchema?: Z.ZodObject<any>;
592
595
  querySchema?: Z.ZodObject<any>;
593
596
  redirect?: RouteRedirect<TParams, TQuery, TMeta>;
597
+ /**
598
+ * Optional predicate to control whether this route can be matched.
599
+ * Called after URL matching but before the route is considered matched.
600
+ * Useful for access control or conditional routing.
601
+ */
602
+ canMatch?: RouteArgumentFunctions<boolean, TParams, TQuery, TMeta>;
594
603
  };
595
604
 
596
605
  /**
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@koordinates/xstate-tree",
3
3
  "main": "lib/index.js",
4
4
  "types": "lib/xstate-tree.d.ts",
5
- "version": "4.7.0",
5
+ "version": "4.8.1",
6
6
  "license": "MIT",
7
7
  "description": "Build UIs with Actors using xstate and React",
8
8
  "keywords": [