@cookbook/urlkit 1.0.0 → 1.1.0

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/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # @cookbook/urlkit
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@cookbook/urlkit.svg)](https://www.npmjs.com/package/@cookbook/urlkit)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@cookbook/urlkit.svg)](https://www.npmjs.com/package/@cookbook/urlkit)
5
+ [![Bundle size](https://img.shields.io/bundlephobia/minzip/@cookbook/urlkit)](https://bundlephobia.com/package/@cookbook/urlkit)
6
+ [![CI](https://github.com/the-cookbook/urlkit/actions/workflows/ci.yml/badge.svg)](https://github.com/the-cookbook/urlkit/actions/workflows/ci.yml)
7
+
3
8
  Framework-agnostic typed URL contracts for parsing, validating, normalizing, matching, and building URL state.
4
9
 
5
10
  URLKit owns typed URL state: path params, search params, hash fragments, request parsing, URL normalization, matching, and href building. It sits between `@cookbook/pathkit` and higher-level router packages, but it does not define routes, route IDs, route trees, loaders, middleware, React hooks, components, or framework adapters.
@@ -176,7 +181,7 @@ ArticleUrl.build({ params: { slug: 'post-1' } });
176
181
  // ArticleUrl.build({ pathname: '/articles/post-1' });
177
182
  ```
178
183
 
179
- Path params are inferred from the pattern. Built-in `int` and `number` path constraints parse to numbers in standalone `url(...)` contracts.
184
+ Path params are inferred from the pattern. Built-in `int`, `decimal` and `range` path constraints parse to numbers in standalone `url(...)` contracts.
180
185
 
181
186
  ```ts
182
187
  const UserUrl = url({ path: '/users/{id:int}' });
@@ -187,7 +192,7 @@ const user = UserUrl.parse('/users/42');
187
192
 
188
193
  ### Custom path constraints
189
194
 
190
- URLKit re-exports PathKit's `createConstraint` and provides global registration helpers for reusable path constraints. Custom constraints infer `string` params by default; built-in `int` and `number` still infer `number`.
195
+ URLKit re-exports PathKit's `createConstraint` and provides global registration helpers for reusable path constraints. Custom constraints infer `string` params by default; built-in `int`, `decimal` and `range` still infer `number`.
191
196
 
192
197
  ```ts
193
198
  import { createConstraint, registerPathConstraint, url } from '@cookbook/urlkit';
@@ -1027,19 +1027,6 @@ function isSearchSchema(input) {
1027
1027
  return typeof input === 'object' && input !== null && !Array.isArray(input);
1028
1028
  }
1029
1029
 
1030
- function getPathParamKind(segment) {
1031
- switch (segment.constraint) {
1032
- case 'int':
1033
- return 'int';
1034
- case 'number':
1035
- return 'number';
1036
- case 'regex':
1037
- return 'regex';
1038
- default:
1039
- return 'string';
1040
- }
1041
- }
1042
-
1043
1030
  function assertPathMatchFailure(pattern, pathname, segments) {
1044
1031
  const pathnameSegments = splitPath(pathname);
1045
1032
  if (pathnameSegments.length !== segments.length) {
@@ -1049,6 +1036,7 @@ function assertPathMatchFailure(pattern, pathname, segments) {
1049
1036
  const segment = segments[index];
1050
1037
  const pathnameSegment = pathnameSegments[index];
1051
1038
  if (!segment || pathnameSegment === undefined) {
1039
+ console.log('ERROR', segment);
1052
1040
  throwPathMismatch(pattern, pathname);
1053
1041
  }
1054
1042
  if (segment.kind === 'literal') {
@@ -1066,46 +1054,31 @@ function assertPathMatchFailure(pattern, pathname, segments) {
1066
1054
  throwPathMismatch(pattern, pathname);
1067
1055
  }
1068
1056
  function splitPath(pathname) {
1069
- if (pathname === '') {
1057
+ if (!pathname) {
1070
1058
  return Object.freeze([]);
1071
1059
  }
1072
1060
  const normalized = pathname.startsWith('/') ? pathname.slice(1) : pathname;
1073
- if (normalized === '') {
1061
+ if (!normalized) {
1074
1062
  return Object.freeze([]);
1075
1063
  }
1076
1064
  return Object.freeze(normalized.split('/'));
1077
1065
  }
1078
1066
  function isValidPathParamSegment(segment, value) {
1079
- if (value === '') {
1067
+ if (!value || !segment.constraint) {
1080
1068
  return false;
1081
1069
  }
1082
- const kind = getPathParamKind(segment);
1083
- if (kind === 'int') {
1084
- return /^\d+$/.test(value);
1085
- }
1086
- if (kind === 'number') {
1087
- return /^-?(?:\d+(?:\.\d+)?|\.\d+)$/.test(value) && Number.isFinite(Number(value));
1070
+ const constraint = getConstraint(segment.constraint);
1071
+ if (!constraint) {
1072
+ return false;
1088
1073
  }
1089
- if (kind === 'regex') {
1090
- const params = segment.constraintParams;
1091
- if (!params) {
1092
- return false;
1093
- }
1094
- return new RegExp(`^(?:${params})$`).test(value);
1074
+ try {
1075
+ constraint(segment.name, value, segment.constraintParams ?? '');
1076
+ console.log(constraint, segment, value);
1077
+ return true;
1095
1078
  }
1096
- if (segment.constraint) {
1097
- const constraint = getConstraint(segment.constraint);
1098
- if (!constraint) {
1099
- return false;
1100
- }
1101
- try {
1102
- return new RegExp(`^(?:${constraint.toRegExp(segment.constraintParams ?? '')})$`).test(value);
1103
- }
1104
- catch {
1105
- return false;
1106
- }
1079
+ catch {
1080
+ return false;
1107
1081
  }
1108
- return true;
1109
1082
  }
1110
1083
  function throwPathMismatch(pattern, pathname) {
1111
1084
  throw new UrlKitError('path-mismatch', `Pathname "${pathname}" does not match pattern "${pattern}".`, {
@@ -1113,6 +1086,20 @@ function throwPathMismatch(pattern, pathname) {
1113
1086
  });
1114
1087
  }
1115
1088
 
1089
+ function getPathParamKind(segment) {
1090
+ switch (segment.constraint) {
1091
+ case 'int':
1092
+ return 'int';
1093
+ case 'decimal':
1094
+ case 'range':
1095
+ return 'number';
1096
+ case 'regex':
1097
+ return 'regex';
1098
+ default:
1099
+ return 'string';
1100
+ }
1101
+ }
1102
+
1116
1103
  function coercePathParam(segment, value, paramsMode) {
1117
1104
  if (paramsMode === 'raw') {
1118
1105
  return value;
@@ -1315,4 +1302,4 @@ function compilePathPattern(pattern) {
1315
1302
  }
1316
1303
 
1317
1304
  export { UrlKitError as U, compilePath as a, boolean as b, compileSearchSchema as c, date as d, enumOf as e, registerPathConstraints as f, handleRuntimeSchemaAbsence as g, hasPathConstraint as h, int as i, createSchemaValueError as j, createRuntimeSchemaValueContext as k, compileRuntimeSchemaValue as l, createRuntimeSchemaBuilder as m, number as n, normalizeRuntimeSchemaValue as o, getRuntimeSchemaInternals as p, runtimeSchemaSymbol as q, registerPathConstraint as r, string as s, compileRuntimeSchema as t, normalizeCompiledRuntimeSchemaValue as u, compileStaticHashDescriptor as v, parseUnixSeconds as w, parseUnixMs as x, parseDateTime as y, parseDate as z };
1318
- //# sourceMappingURL=compile-path-wQfWAzOh.js.map
1305
+ //# sourceMappingURL=compile-path-COU1uxXm.js.map