@cookbook/urlkit 1.0.0 → 1.2.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.
@@ -1,6 +1,7 @@
1
1
  import compile from '@cookbook/pathkit/compile';
2
2
  import match from '@cookbook/pathkit/match';
3
- import { hasConstraint, getConstraint, registerConstraint, createConstraint } from '@cookbook/pathkit/constraints';
3
+ import { hasConstraint, getConstraint, registerConstraint } from '@cookbook/pathkit/constraints';
4
+ import tokenize from '@cookbook/pathkit/tokenize';
4
5
 
5
6
  const defaultMessages = {
6
7
  'invalid-url': 'Invalid URL.',
@@ -325,6 +326,209 @@ function validateBooleanDefault(value, context) {
325
326
  });
326
327
  }
327
328
 
329
+ const supportedTokens = ['yyyy', 'SSS', 'MM', 'dd', 'HH', 'mm', 'ss'];
330
+ const dateTokens = new Set(['yyyy', 'MM', 'dd']);
331
+ const dateTimeTokens = new Set(['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'SSS']);
332
+ function parseDateFormatString(input, format, mode, options = {}) {
333
+ const compiled = compileDateFormatString(format, mode);
334
+ const match = compiled.pattern.exec(input);
335
+ if (!match?.groups) {
336
+ throw createDateFormatStringError(`${getFormatLabel(mode)} value must match format "${format}".`, options);
337
+ }
338
+ const parts = readDateParts(match.groups, compiled);
339
+ assertValidDateParts(parts, mode, options);
340
+ const value = new Date(Date.UTC(0, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second, parts.millisecond));
341
+ value.setUTCFullYear(parts.year);
342
+ if (!matchesDateParts(value, parts)) {
343
+ throw createDateFormatStringError(`${getFormatLabel(mode)} value must be a valid UTC calendar ${mode === 'date' ? 'date' : 'instant'}.`, options);
344
+ }
345
+ return value;
346
+ }
347
+ function serializeDateFormatString(input, format, mode, options = {}) {
348
+ if (!(input instanceof Date) || !Number.isFinite(input.getTime())) {
349
+ throw createDateFormatStringError(`${getFormatLabel(mode)} value must be a valid Date.`, options);
350
+ }
351
+ const compiled = compileDateFormatString(format, mode);
352
+ const year = input.getUTCFullYear();
353
+ if (year < 0 || year > 9999) {
354
+ throw createDateFormatStringError(`${getFormatLabel(mode)} value year must be between 0000 and 9999 for yyyy format strings.`, options);
355
+ }
356
+ if (mode === 'date-time' && !compiled.hasMilliseconds && input.getUTCMilliseconds() !== 0) {
357
+ throw createDateFormatStringError('Date-time value cannot be serialized without losing milliseconds. Include SSS in the format or use a Date with zero milliseconds.', options);
358
+ }
359
+ return compiled.parts
360
+ .map((part) => {
361
+ if (part.kind === 'literal') {
362
+ return part.value;
363
+ }
364
+ return serializeToken(input, part.value);
365
+ })
366
+ .join('');
367
+ }
368
+ function validateDateFormatString(format, mode) {
369
+ compileDateFormatString(format, mode);
370
+ }
371
+ function compileDateFormatString(format, mode) {
372
+ const parts = tokenizeDateFormatString(format, mode);
373
+ validateDateFormatParts(format, parts, mode);
374
+ const pattern = new RegExp(`^${parts
375
+ .map((part) => part.kind === 'literal' ? escapeRegExp(part.value) : getTokenPattern(part.value))
376
+ .join('')}$`);
377
+ return {
378
+ format,
379
+ mode,
380
+ parts,
381
+ pattern,
382
+ hasMilliseconds: parts.some((part) => part.kind === 'token' && part.value === 'SSS'),
383
+ };
384
+ }
385
+ function tokenizeDateFormatString(format, mode) {
386
+ if (typeof format !== 'string' || format.length === 0) {
387
+ throw createInvalidDateFormatDescriptorError(`${getFormatLabel(mode)} format must be a non-empty string.`);
388
+ }
389
+ const parts = [];
390
+ let index = 0;
391
+ while (index < format.length) {
392
+ if (format[index] === "'") {
393
+ const literalEnd = format.indexOf("'", index + 1);
394
+ if (literalEnd === -1) {
395
+ throw createInvalidDateFormatDescriptorError(`${getFormatLabel(mode)} format contains an unterminated quoted literal.`);
396
+ }
397
+ const literal = format.slice(index + 1, literalEnd);
398
+ if (literal.length === 0) {
399
+ throw createInvalidDateFormatDescriptorError(`${getFormatLabel(mode)} format contains an empty quoted literal.`);
400
+ }
401
+ parts.push({ kind: 'literal', value: literal });
402
+ index = literalEnd + 1;
403
+ continue;
404
+ }
405
+ const token = findTokenAt(format, index);
406
+ if (token) {
407
+ parts.push({ kind: 'token', value: token });
408
+ index += token.length;
409
+ continue;
410
+ }
411
+ const character = format[index] ?? '';
412
+ if (isAsciiLetter(character)) {
413
+ throw createInvalidDateFormatDescriptorError(`${getFormatLabel(mode)} format contains unsupported token near "${format.slice(index)}".`);
414
+ }
415
+ parts.push({ kind: 'literal', value: character });
416
+ index += 1;
417
+ }
418
+ return mergeAdjacentLiterals(parts);
419
+ }
420
+ function findTokenAt(format, index) {
421
+ return supportedTokens.find((token) => format.startsWith(token, index));
422
+ }
423
+ function mergeAdjacentLiterals(parts) {
424
+ const merged = [];
425
+ for (const part of parts) {
426
+ const previous = merged[merged.length - 1];
427
+ if (part.kind === 'literal' && previous?.kind === 'literal') {
428
+ merged[merged.length - 1] = { kind: 'literal', value: `${previous.value}${part.value}` };
429
+ continue;
430
+ }
431
+ merged.push(part);
432
+ }
433
+ return merged;
434
+ }
435
+ function validateDateFormatParts(format, parts, mode) {
436
+ const allowedTokens = mode === 'date' ? dateTokens : dateTimeTokens;
437
+ const requiredTokens = mode === 'date' ? ['yyyy', 'MM', 'dd'] : ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss'];
438
+ const seenTokens = new Set();
439
+ for (const part of parts) {
440
+ if (part.kind === 'literal') {
441
+ continue;
442
+ }
443
+ if (!allowedTokens.has(part.value)) {
444
+ throw createInvalidDateFormatDescriptorError(`${getFormatLabel(mode)} format "${format}" contains unsupported token "${part.value}".`);
445
+ }
446
+ if (seenTokens.has(part.value)) {
447
+ throw createInvalidDateFormatDescriptorError(`${getFormatLabel(mode)} format "${format}" contains duplicate token "${part.value}".`);
448
+ }
449
+ seenTokens.add(part.value);
450
+ }
451
+ for (const token of requiredTokens) {
452
+ if (!seenTokens.has(token)) {
453
+ throw createInvalidDateFormatDescriptorError(`${getFormatLabel(mode)} format "${format}" is missing required token "${token}".`);
454
+ }
455
+ }
456
+ }
457
+ function readDateParts(groups, compiled) {
458
+ return {
459
+ year: Number(groups.yyyy),
460
+ month: Number(groups.MM),
461
+ day: Number(groups.dd),
462
+ hour: compiled.mode === 'date-time' ? Number(groups.HH) : 0,
463
+ minute: compiled.mode === 'date-time' ? Number(groups.mm) : 0,
464
+ second: compiled.mode === 'date-time' ? Number(groups.ss) : 0,
465
+ millisecond: compiled.hasMilliseconds ? Number(groups.SSS) : 0,
466
+ };
467
+ }
468
+ function assertValidDateParts(parts, mode, options) {
469
+ if (parts.month < 1 ||
470
+ parts.month > 12 ||
471
+ parts.day < 1 ||
472
+ parts.day > 31 ||
473
+ parts.hour < 0 ||
474
+ parts.hour > 23 ||
475
+ parts.minute < 0 ||
476
+ parts.minute > 59 ||
477
+ parts.second < 0 ||
478
+ parts.second > 59 ||
479
+ parts.millisecond < 0 ||
480
+ parts.millisecond > 999) {
481
+ throw createDateFormatStringError(`${getFormatLabel(mode)} value must be a valid UTC calendar ${mode === 'date' ? 'date' : 'instant'}.`, options);
482
+ }
483
+ }
484
+ function matchesDateParts(value, parts) {
485
+ return (value.getUTCFullYear() === parts.year &&
486
+ value.getUTCMonth() === parts.month - 1 &&
487
+ value.getUTCDate() === parts.day &&
488
+ value.getUTCHours() === parts.hour &&
489
+ value.getUTCMinutes() === parts.minute &&
490
+ value.getUTCSeconds() === parts.second &&
491
+ value.getUTCMilliseconds() === parts.millisecond);
492
+ }
493
+ function serializeToken(input, token) {
494
+ switch (token) {
495
+ case 'yyyy':
496
+ return String(input.getUTCFullYear()).padStart(4, '0');
497
+ case 'MM':
498
+ return String(input.getUTCMonth() + 1).padStart(2, '0');
499
+ case 'dd':
500
+ return String(input.getUTCDate()).padStart(2, '0');
501
+ case 'HH':
502
+ return String(input.getUTCHours()).padStart(2, '0');
503
+ case 'mm':
504
+ return String(input.getUTCMinutes()).padStart(2, '0');
505
+ case 'ss':
506
+ return String(input.getUTCSeconds()).padStart(2, '0');
507
+ case 'SSS':
508
+ return String(input.getUTCMilliseconds()).padStart(3, '0');
509
+ }
510
+ }
511
+ function getTokenPattern(token) {
512
+ return `(?<${token}>\\d{${token === 'yyyy' || token === 'SSS' ? token.length.toString() : '2'}})`;
513
+ }
514
+ function escapeRegExp(value) {
515
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
516
+ }
517
+ function isAsciiLetter(value) {
518
+ return /^[A-Za-z]$/.test(value);
519
+ }
520
+ function getFormatLabel(mode) {
521
+ return mode === 'date' ? 'Date' : 'Date-time';
522
+ }
523
+ function createDateFormatStringError(message, options) {
524
+ return new UrlKitError(options.code ?? 'invalid-search', message, {
525
+ path: [...(options.path ?? [])],
526
+ });
527
+ }
528
+ function createInvalidDateFormatDescriptorError(message) {
529
+ return new UrlKitError('invalid-descriptor', message);
530
+ }
531
+
328
532
  const datePattern = /^(\d{4})-(\d{2})-(\d{2})$/;
329
533
  function parseDate(input, options = {}) {
330
534
  const match = datePattern.exec(input);
@@ -596,21 +800,24 @@ const unixMsCodec = {
596
800
  return serializeUnixMs(input, { code: context.errorCode, path: context.path });
597
801
  },
598
802
  };
599
- function date(options = {}) {
600
- const format = resolveDateFormat(options);
803
+ function date(options = {}, formatStringMode = 'date') {
804
+ const format = resolveDateFormat(options, formatStringMode);
601
805
  return createRuntimeSchemaBuilder({
602
806
  kind: 'date',
603
807
  options: { format },
604
- codec: getDateCodec(format),
808
+ codec: getDateCodec(format, formatStringMode),
605
809
  validateDefault(value, context) {
606
810
  validateDateDefault(value, context);
607
811
  },
608
812
  });
609
813
  }
610
- function getDateCodec(format) {
814
+ function getDateCodec(format, formatStringMode) {
611
815
  if (isDateFormatCodec(format)) {
612
816
  return createCustomDateCodec(format);
613
817
  }
818
+ if (isDateFormatString(format) && !isBuiltInDateFormat(format)) {
819
+ return createDateFormatStringCodec(format, formatStringMode);
820
+ }
614
821
  if (format === 'date-time') {
615
822
  return dateTimeCodec;
616
823
  }
@@ -622,6 +829,25 @@ function getDateCodec(format) {
622
829
  }
623
830
  return dateOnlyCodec;
624
831
  }
832
+ function createDateFormatStringCodec(format, mode) {
833
+ return {
834
+ parse(input, context) {
835
+ return parseDateFormatString(input, format, mode, {
836
+ code: context.errorCode,
837
+ path: context.path,
838
+ });
839
+ },
840
+ normalize(input, context) {
841
+ return validateDate(input, context);
842
+ },
843
+ serialize(input, context) {
844
+ return serializeDateFormatString(input, format, mode, {
845
+ code: context.errorCode,
846
+ path: context.path,
847
+ });
848
+ },
849
+ };
850
+ }
625
851
  function createCustomDateCodec(format) {
626
852
  return {
627
853
  parse(input, context) {
@@ -635,7 +861,7 @@ function createCustomDateCodec(format) {
635
861
  },
636
862
  };
637
863
  }
638
- function resolveDateFormat(options) {
864
+ function resolveDateFormat(options, formatStringMode) {
639
865
  if (!isDateOptions(options)) {
640
866
  throw new UrlKitError('invalid-descriptor', 'Date options must be an object.');
641
867
  }
@@ -643,11 +869,18 @@ function resolveDateFormat(options) {
643
869
  if (isBuiltInDateFormat(format) || isDateFormatCodec(format)) {
644
870
  return format;
645
871
  }
646
- throw new UrlKitError('invalid-descriptor', 'Date format must be a supported built-in format or an explicit codec.');
872
+ if (isDateFormatString(format)) {
873
+ validateDateFormatString(format, formatStringMode);
874
+ return format;
875
+ }
876
+ throw new UrlKitError('invalid-descriptor', 'Date format must be a supported built-in format, a supported format string, or an explicit codec.');
647
877
  }
648
878
  function isDateOptions(input) {
649
879
  return typeof input === 'object' && input !== null && !Array.isArray(input);
650
880
  }
881
+ function isDateFormatString(input) {
882
+ return typeof input === 'string';
883
+ }
651
884
  function isBuiltInDateFormat(input) {
652
885
  return (input === 'date' || input === 'date-time' || input === 'unix-seconds' || input === 'unix-ms');
653
886
  }
@@ -1027,19 +1260,6 @@ function isSearchSchema(input) {
1027
1260
  return typeof input === 'object' && input !== null && !Array.isArray(input);
1028
1261
  }
1029
1262
 
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
1263
  function assertPathMatchFailure(pattern, pathname, segments) {
1044
1264
  const pathnameSegments = splitPath(pathname);
1045
1265
  if (pathnameSegments.length !== segments.length) {
@@ -1057,55 +1277,63 @@ function assertPathMatchFailure(pattern, pathname, segments) {
1057
1277
  }
1058
1278
  continue;
1059
1279
  }
1060
- if (!isValidPathParamSegment(segment, pathnameSegment)) {
1061
- throw new UrlKitError('invalid-param', `Path parameter "${segment.name}" is invalid.`, {
1280
+ const validationFailure = getPathParamValidationFailure(segment, pathnameSegment);
1281
+ if (validationFailure) {
1282
+ throw new UrlKitError('invalid-param', validationFailure.message, {
1062
1283
  path: ['params', segment.name],
1284
+ ...(validationFailure.cause === undefined ? {} : { cause: validationFailure.cause }),
1063
1285
  });
1064
1286
  }
1065
1287
  }
1066
1288
  throwPathMismatch(pattern, pathname);
1067
1289
  }
1068
1290
  function splitPath(pathname) {
1069
- if (pathname === '') {
1291
+ if (!pathname) {
1070
1292
  return Object.freeze([]);
1071
1293
  }
1072
1294
  const normalized = pathname.startsWith('/') ? pathname.slice(1) : pathname;
1073
- if (normalized === '') {
1295
+ if (!normalized) {
1074
1296
  return Object.freeze([]);
1075
1297
  }
1076
1298
  return Object.freeze(normalized.split('/'));
1077
1299
  }
1078
- function isValidPathParamSegment(segment, value) {
1079
- if (value === '') {
1080
- return false;
1300
+ function getPathParamValidationFailure(segment, value) {
1301
+ if (!value) {
1302
+ return {
1303
+ message: `Path parameter "${segment.name}" is invalid.`,
1304
+ };
1081
1305
  }
1082
- const kind = getPathParamKind(segment);
1083
- if (kind === 'int') {
1084
- return /^\d+$/.test(value);
1306
+ if (!segment.constraint) {
1307
+ return undefined;
1085
1308
  }
1086
- if (kind === 'number') {
1087
- return /^-?(?:\d+(?:\.\d+)?|\.\d+)$/.test(value) && Number.isFinite(Number(value));
1309
+ const constraint = getConstraint(segment.constraint);
1310
+ if (!constraint) {
1311
+ return {
1312
+ message: `Path constraint "${segment.constraint}" is not registered.`,
1313
+ };
1088
1314
  }
1089
- if (kind === 'regex') {
1090
- const params = segment.constraintParams;
1091
- if (!params) {
1092
- return false;
1093
- }
1094
- return new RegExp(`^(?:${params})$`).test(value);
1315
+ try {
1316
+ constraint(segment.name, value, segment.constraintParams ?? '');
1317
+ return undefined;
1095
1318
  }
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
- }
1319
+ catch (error) {
1320
+ const causeMessage = getCauseMessage(error);
1321
+ return {
1322
+ message: causeMessage
1323
+ ? `Path parameter "${segment.name}" is invalid: ${causeMessage}`
1324
+ : `Path parameter "${segment.name}" is invalid.`,
1325
+ cause: error,
1326
+ };
1327
+ }
1328
+ }
1329
+ function getCauseMessage(error) {
1330
+ if (error instanceof Error && error.message) {
1331
+ return error.message;
1107
1332
  }
1108
- return true;
1333
+ if (typeof error === 'string' && error) {
1334
+ return error;
1335
+ }
1336
+ return undefined;
1109
1337
  }
1110
1338
  function throwPathMismatch(pattern, pathname) {
1111
1339
  throw new UrlKitError('path-mismatch', `Pathname "${pathname}" does not match pattern "${pattern}".`, {
@@ -1113,6 +1341,24 @@ function throwPathMismatch(pattern, pathname) {
1113
1341
  });
1114
1342
  }
1115
1343
 
1344
+ function getPathParamKind(segment) {
1345
+ switch (segment.constraint) {
1346
+ case 'int':
1347
+ return 'int';
1348
+ case 'decimal':
1349
+ return 'decimal';
1350
+ case 'range':
1351
+ return 'range';
1352
+ case 'regex':
1353
+ return 'regex';
1354
+ default:
1355
+ return 'string';
1356
+ }
1357
+ }
1358
+ function isNumericPathParamKind(kind) {
1359
+ return kind === 'int' || kind === 'decimal' || kind === 'range';
1360
+ }
1361
+
1116
1362
  function coercePathParam(segment, value, paramsMode) {
1117
1363
  if (paramsMode === 'raw') {
1118
1364
  return value;
@@ -1127,10 +1373,10 @@ function coercePathParam(segment, value, paramsMode) {
1127
1373
  }
1128
1374
  return parsed;
1129
1375
  }
1130
- if (kind === 'number') {
1376
+ if (isNumericPathParamKind(kind)) {
1131
1377
  const parsed = Number(value);
1132
1378
  if (!Number.isFinite(parsed)) {
1133
- throw new UrlKitError('invalid-param', `Path parameter "${segment.name}" must be a finite number.`, {
1379
+ throw new UrlKitError('invalid-param', `Path parameter "${segment.name}" must be a finite decimal number.`, {
1134
1380
  path: ['params', segment.name],
1135
1381
  });
1136
1382
  }
@@ -1167,79 +1413,57 @@ function isRecord(input) {
1167
1413
  }
1168
1414
 
1169
1415
  function parsePathPattern(pattern) {
1170
- if (pattern === '') {
1171
- return Object.freeze([]);
1172
- }
1173
- const normalized = pattern.startsWith('/') ? pattern.slice(1) : pattern;
1174
- if (normalized === '') {
1175
- return Object.freeze([]);
1176
- }
1177
- return Object.freeze(normalized.split('/').map((segment, index) => parsePathSegment(segment, index)));
1178
- }
1179
- function parsePathSegment(segment, index) {
1180
- if (!segment.startsWith('{') || !segment.endsWith('}')) {
1181
- return Object.freeze({ kind: 'literal', value: segment });
1416
+ try {
1417
+ return Object.freeze(toParsedPathSegments(tokenize(pattern)));
1182
1418
  }
1183
- const token = segment.slice(1, -1);
1184
- const parsed = parseParamToken(token);
1185
- if (!parsed.name) {
1186
- throw new UrlKitError('invalid-descriptor', 'Path parameter name is required.', {
1187
- path: ['path', String(index)],
1419
+ catch (error) {
1420
+ if (error instanceof UrlKitError) {
1421
+ throw error;
1422
+ }
1423
+ const causeMessage = error instanceof Error && error.message ? `: ${error.message}` : '';
1424
+ throw new UrlKitError('invalid-descriptor', `Path pattern is invalid${causeMessage}.`, {
1425
+ path: ['path'],
1426
+ cause: error,
1188
1427
  });
1189
1428
  }
1190
- return Object.freeze({ kind: 'param', ...parsed });
1191
1429
  }
1192
- function parseParamToken(token) {
1193
- const colonIndex = token.indexOf(':');
1194
- if (colonIndex === -1) {
1195
- return { name: token };
1196
- }
1197
- const name = token.slice(0, colonIndex);
1198
- const constraintToken = token.slice(colonIndex + 1);
1199
- const paramsStart = constraintToken.indexOf('(');
1200
- if (paramsStart === -1 || !constraintToken.endsWith(')')) {
1201
- return { name, constraint: constraintToken };
1430
+ function toParsedPathSegments(tokens) {
1431
+ const segments = [];
1432
+ for (const token of tokens) {
1433
+ if (token.type === 'literal') {
1434
+ appendLiteralSegments(segments, token.value);
1435
+ continue;
1436
+ }
1437
+ segments.push(toParsedParamSegment(token));
1202
1438
  }
1203
- return {
1204
- name,
1205
- constraint: constraintToken.slice(0, paramsStart),
1206
- constraintParams: constraintToken.slice(paramsStart + 1, -1),
1207
- };
1439
+ return segments.map((segment) => Object.freeze(segment));
1208
1440
  }
1209
-
1210
- const numberConstraint = createConstraint({
1211
- parse(paramName, value, params) {
1212
- numberConstraint.verify(paramName, params);
1213
- const serialized = String(value);
1214
- if (!isFinitePathNumber(serialized)) {
1215
- throw new UrlKitError('invalid-param', `Path parameter "${paramName}" must be a finite number.`, {
1216
- path: ['params', paramName],
1217
- });
1218
- }
1219
- },
1220
- verify(paramName, params) {
1221
- if (params.trim()) {
1222
- throw new UrlKitError('invalid-descriptor', `Constraint 'number' declared for '${paramName}' parameter does not accept arguments.`, { path: ['path', paramName] });
1441
+ function appendLiteralSegments(segments, value) {
1442
+ if (!value) {
1443
+ return;
1444
+ }
1445
+ for (const segment of value.split('/')) {
1446
+ if (!segment) {
1447
+ continue;
1223
1448
  }
1224
- },
1225
- toRegExp() {
1226
- return '-?(?:\\d+(?:\\.\\d+)?|\\.\\d+)';
1227
- },
1228
- });
1229
- function registerUrlKitPathConstraints() {
1230
- if (!hasConstraint('number')) {
1231
- registerConstraint('number', numberConstraint);
1449
+ segments.push({ kind: 'literal', value: segment });
1232
1450
  }
1233
1451
  }
1234
- function isFinitePathNumber(value) {
1235
- if (!/^-?(?:\d+(?:\.\d+)?|\.\d+)$/.test(value)) {
1236
- return false;
1237
- }
1238
- return Number.isFinite(Number(value));
1452
+ function toParsedParamSegment(token) {
1453
+ const constraint = token.constraints[0];
1454
+ return {
1455
+ kind: 'param',
1456
+ name: token.name,
1457
+ ...(constraint
1458
+ ? {
1459
+ constraint: constraint.type,
1460
+ ...(constraint.params ? { constraintParams: constraint.params } : {}),
1461
+ }
1462
+ : {}),
1463
+ };
1239
1464
  }
1240
1465
 
1241
1466
  function compilePath(pattern, options = {}) {
1242
- registerUrlKitPathConstraints();
1243
1467
  if (options.pathConstraints) {
1244
1468
  registerPathConstraints(options.pathConstraints);
1245
1469
  }
@@ -1314,5 +1538,5 @@ function compilePathPattern(pattern) {
1314
1538
  }
1315
1539
  }
1316
1540
 
1317
- 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
1541
+ export { parseDate as A, 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, validateDateFormatString as v, compileStaticHashDescriptor as w, parseUnixSeconds as x, parseUnixMs as y, parseDateTime as z };
1542
+ //# sourceMappingURL=compile-path-oMMfpXR8.js.map