@hyeonqyu/typed-router-core 1.5.2 → 1.5.4

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.d.mts CHANGED
@@ -7,6 +7,11 @@ type SearchParamsStringOptions = {
7
7
  };
8
8
  declare const toSearchParamsString: (searchParams: SearchParams, options?: SearchParamsStringOptions) => string;
9
9
  declare const findObjectPath: <T>(root: Record<string, unknown>, target: T, splitter?: string, path?: string) => string | undefined;
10
- declare const replaceDynamicSegments: (pathname: string, params?: SearchParams) => string;
10
+ type ReplaceDynamicSegmentsResult = {
11
+ pathname: string;
12
+ remainingParams: SearchParams;
13
+ };
14
+ declare const extractDynamicSegmentKeys: (pathname: string) => string[];
15
+ declare const replaceDynamicSegments: (pathname: string, params?: SearchParams) => ReplaceDynamicSegmentsResult;
11
16
 
12
- export { PathValue, Paths, SearchParams, type SearchParamsStringOptions, findObjectPath, getSafely, replaceDynamicSegments, toSearchParamsString };
17
+ export { PathValue, Paths, type ReplaceDynamicSegmentsResult, SearchParams, type SearchParamsStringOptions, extractDynamicSegmentKeys, findObjectPath, getSafely, replaceDynamicSegments, toSearchParamsString };
package/dist/index.d.ts CHANGED
@@ -7,6 +7,11 @@ type SearchParamsStringOptions = {
7
7
  };
8
8
  declare const toSearchParamsString: (searchParams: SearchParams, options?: SearchParamsStringOptions) => string;
9
9
  declare const findObjectPath: <T>(root: Record<string, unknown>, target: T, splitter?: string, path?: string) => string | undefined;
10
- declare const replaceDynamicSegments: (pathname: string, params?: SearchParams) => string;
10
+ type ReplaceDynamicSegmentsResult = {
11
+ pathname: string;
12
+ remainingParams: SearchParams;
13
+ };
14
+ declare const extractDynamicSegmentKeys: (pathname: string) => string[];
15
+ declare const replaceDynamicSegments: (pathname: string, params?: SearchParams) => ReplaceDynamicSegmentsResult;
11
16
 
12
- export { PathValue, Paths, SearchParams, type SearchParamsStringOptions, findObjectPath, getSafely, replaceDynamicSegments, toSearchParamsString };
17
+ export { PathValue, Paths, type ReplaceDynamicSegmentsResult, SearchParams, type SearchParamsStringOptions, extractDynamicSegmentKeys, findObjectPath, getSafely, replaceDynamicSegments, toSearchParamsString };
package/dist/index.js CHANGED
@@ -51,15 +51,35 @@ var findObjectPath = /* @__PURE__ */ __name((root, target, splitter = "/", path
51
51
  }
52
52
  return void 0;
53
53
  }, "findObjectPath");
54
+ var DYNAMIC_SEGMENT_REGEX = /\[([^\]]+)\]/g;
55
+ var extractDynamicSegmentKeys = /* @__PURE__ */ __name((pathname) => {
56
+ const matches = Array.from(pathname.matchAll(DYNAMIC_SEGMENT_REGEX));
57
+ return matches.map((match) => match[1]);
58
+ }, "extractDynamicSegmentKeys");
54
59
  var replaceDynamicSegments = /* @__PURE__ */ __name((pathname, params) => {
55
- if (!params) return pathname;
56
- return pathname.replace(/\[([^\]]+)\]/g, (_, key) => {
60
+ if (!params) {
61
+ return {
62
+ pathname,
63
+ remainingParams: {}
64
+ };
65
+ }
66
+ const usedKeys = /* @__PURE__ */ new Set();
67
+ const replacedPathname = pathname.replace(DYNAMIC_SEGMENT_REGEX, (_, key) => {
68
+ usedKeys.add(key);
57
69
  const value = params[key.toString()];
58
70
  if (value === void 0 || value === null) return `[${key}]`;
59
71
  return Array.isArray(value) ? value.join(",") : value.toString();
60
72
  });
73
+ const remainingParams = Object.fromEntries(
74
+ Object.entries(params).filter(([key]) => !usedKeys.has(key))
75
+ );
76
+ return {
77
+ pathname: replacedPathname,
78
+ remainingParams
79
+ };
61
80
  }, "replaceDynamicSegments");
62
81
 
82
+ exports.extractDynamicSegmentKeys = extractDynamicSegmentKeys;
63
83
  exports.findObjectPath = findObjectPath;
64
84
  exports.getSafely = getSafely;
65
85
  exports.replaceDynamicSegments = replaceDynamicSegments;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/object.utils.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { Paths, PathValue } from './path.types';\nimport { SearchParams } from './query.types';\n\nexport const getSafely = <TObject, TSplitter extends string, TPath extends string & Paths<TObject, TSplitter>>(\n splitter: TSplitter,\n obj: TObject,\n path: TPath,\n): PathValue<TObject, TPath, TSplitter> => {\n if (path === '' || path === splitter) return obj as PathValue<TObject, TPath, TSplitter>;\n\n const keys = (path as string).split(splitter).filter((key) => key);\n let value: unknown = obj;\n\n for (const key of keys) {\n if (typeof value === 'object' && value !== null && key in value) {\n value = (value as Record<string, unknown>)[key];\n } else {\n return undefined as PathValue<TObject, TPath, TSplitter>;\n }\n }\n\n return value as PathValue<TObject, TPath, TSplitter>;\n};\n\nexport type SearchParamsStringOptions = {\n includeQuestionMark?: boolean;\n};\n\nexport const toSearchParamsString = (\n searchParams: SearchParams,\n options: SearchParamsStringOptions = { includeQuestionMark: true },\n): string => {\n const params: string[] = [];\n\n Object.entries(searchParams).forEach(([key, value]) => {\n if (value === undefined || value === null) {\n return;\n }\n\n const encodedKey = encodeURIComponent(key);\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n params.push(`${encodedKey}=${encodeURIComponent(String(item))}`);\n }\n });\n } else {\n params.push(`${encodedKey}=${encodeURIComponent(String(value))}`);\n }\n });\n\n if (params.length === 0) {\n return '';\n }\n\n const searchParamsString = params.join('&');\n return options.includeQuestionMark ? `?${searchParamsString}` : searchParamsString;\n};\n\nexport const findObjectPath = <T>(\n root: Record<string, unknown>,\n target: T,\n splitter = '/',\n path = splitter,\n): string | undefined => {\n for (const key in root) {\n const current = root[key];\n\n if (current === target) return path + key;\n\n if (typeof current === 'object' && current !== null) {\n const result = findObjectPath(current as Record<string, unknown>, target, splitter, path + key + splitter);\n if (result) return result;\n }\n }\n\n return undefined;\n};\n\nexport const replaceDynamicSegments = (\n pathname: string,\n params?: SearchParams,\n): string => {\n if (!params) return pathname;\n\n return pathname.replace(/\\[([^\\]]+)\\]/g, (_, key) => {\n const value = params[key.toString()];\n if (value === undefined || value === null) return `[${key}]`;\n return Array.isArray(value) ? value.join(',') : value.toString();\n });\n};\n"]}
1
+ {"version":3,"sources":["../src/object.utils.ts"],"names":[],"mappings":";;;;;;AAGO,IAAM,SAAA,mBAAY,MAAA,CAAA,CACvB,QAAA,EACA,GAAA,EACA,IAAA,KACyC;AACzC,EAAA,IAAI,IAAA,KAAS,EAAA,IAAM,IAAA,KAAS,QAAA,EAAU,OAAO,GAAA;AAE7C,EAAA,MAAM,IAAA,GAAQ,KAAgB,KAAA,CAAM,QAAQ,EAAE,MAAA,CAAO,CAAC,QAAQ,GAAG,CAAA;AACjE,EAAA,IAAI,KAAA,GAAiB,GAAA;AAErB,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,EAAO;AAC/D,MAAA,KAAA,GAAS,MAAkC,GAAG,CAAA;AAAA,IAChD,CAAA,MAAO;AACL,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT,CAAA,EAnByB,WAAA;AAyBlB,IAAM,uCAAuB,MAAA,CAAA,CAClC,YAAA,EACA,UAAqC,EAAE,mBAAA,EAAqB,MAAK,KACtD;AACX,EAAA,MAAM,SAAmB,EAAC;AAE1B,EAAA,MAAA,CAAO,OAAA,CAAQ,YAAY,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM;AACrD,IAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AACzC,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,UAAA,GAAa,mBAAmB,GAAG,CAAA;AAEzC,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxB,MAAA,KAAA,CAAM,OAAA,CAAQ,CAAC,IAAA,KAAS;AACtB,QAAA,IAAI,IAAA,KAAS,MAAA,IAAa,IAAA,KAAS,IAAA,EAAM;AACvC,UAAA,MAAA,CAAO,IAAA,CAAK,GAAG,UAAU,CAAA,CAAA,EAAI,mBAAmB,MAAA,CAAO,IAAI,CAAC,CAAC,CAAA,CAAE,CAAA;AAAA,QACjE;AAAA,MACF,CAAC,CAAA;AAAA,IACH,CAAA,MAAO;AACL,MAAA,MAAA,CAAO,IAAA,CAAK,GAAG,UAAU,CAAA,CAAA,EAAI,mBAAmB,MAAA,CAAO,KAAK,CAAC,CAAC,CAAA,CAAE,CAAA;AAAA,IAClE;AAAA,EACF,CAAC,CAAA;AAED,EAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,IAAA,OAAO,EAAA;AAAA,EACT;AAEA,EAAA,MAAM,kBAAA,GAAqB,MAAA,CAAO,IAAA,CAAK,GAAG,CAAA;AAC1C,EAAA,OAAO,OAAA,CAAQ,mBAAA,GAAsB,CAAA,CAAA,EAAI,kBAAkB,CAAA,CAAA,GAAK,kBAAA;AAClE,CAAA,EA9BoC,sBAAA;AAgC7B,IAAM,iCAAiB,MAAA,CAAA,CAC5B,IAAA,EACA,QACA,QAAA,GAAW,GAAA,EACX,OAAO,QAAA,KACgB;AACvB,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,MAAM,OAAA,GAAU,KAAK,GAAG,CAAA;AAExB,IAAA,IAAI,OAAA,KAAY,MAAA,EAAQ,OAAO,IAAA,GAAO,GAAA;AAEtC,IAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,OAAA,KAAY,IAAA,EAAM;AACnD,MAAA,MAAM,SAAS,cAAA,CAAe,OAAA,EAAoC,QAAQ,QAAA,EAAU,IAAA,GAAO,MAAM,QAAQ,CAAA;AACzG,MAAA,IAAI,QAAQ,OAAO,MAAA;AAAA,IACrB;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT,CAAA,EAlB8B,gBAAA;AAyB9B,IAAM,qBAAA,GAAwB,eAAA;AAEvB,IAAM,yBAAA,2BAA6B,QAAA,KAA+B;AACvE,EAAA,MAAM,UAAU,KAAA,CAAM,IAAA,CAAK,QAAA,CAAS,QAAA,CAAS,qBAAqB,CAAC,CAAA;AACnE,EAAA,OAAO,QAAQ,GAAA,CAAI,CAAC,KAAA,KAAU,KAAA,CAAM,CAAC,CAAC,CAAA;AACxC,CAAA,EAHyC,2BAAA;AAKlC,IAAM,sBAAA,mBAAyB,MAAA,CAAA,CACpC,QAAA,EACA,MAAA,KACiC;AACjC,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,OAAO;AAAA,MACL,QAAA;AAAA,MACA,iBAAiB;AAAC,KACpB;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAY;AAEjC,EAAA,MAAM,mBAAmB,QAAA,CAAS,OAAA,CAAQ,qBAAA,EAAuB,CAAC,GAAG,GAAA,KAAQ;AAC3E,IAAA,QAAA,CAAS,IAAI,GAAG,CAAA;AAChB,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,GAAA,CAAI,QAAA,EAAU,CAAA;AACnC,IAAA,IAAI,UAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM,OAAO,IAAI,GAAG,CAAA,CAAA,CAAA;AACzD,IAAA,OAAO,KAAA,CAAM,QAAQ,KAAK,CAAA,GAAI,MAAM,IAAA,CAAK,GAAG,CAAA,GAAI,KAAA,CAAM,QAAA,EAAS;AAAA,EACjE,CAAC,CAAA;AAED,EAAA,MAAM,kBAAkB,MAAA,CAAO,WAAA;AAAA,IAC7B,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,GAAG,CAAA,KAAM,CAAC,QAAA,CAAS,GAAA,CAAI,GAAG,CAAC;AAAA,GAC7D;AAEA,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,gBAAA;AAAA,IACV;AAAA,GACF;AACF,CAAA,EA5BsC,wBAAA","file":"index.js","sourcesContent":["import { Paths, PathValue } from './path.types';\nimport { SearchParams } from './query.types';\n\nexport const getSafely = <TObject, TSplitter extends string, TPath extends string & Paths<TObject, TSplitter>>(\n splitter: TSplitter,\n obj: TObject,\n path: TPath,\n): PathValue<TObject, TPath, TSplitter> => {\n if (path === '' || path === splitter) return obj as PathValue<TObject, TPath, TSplitter>;\n\n const keys = (path as string).split(splitter).filter((key) => key);\n let value: unknown = obj;\n\n for (const key of keys) {\n if (typeof value === 'object' && value !== null && key in value) {\n value = (value as Record<string, unknown>)[key];\n } else {\n return undefined as PathValue<TObject, TPath, TSplitter>;\n }\n }\n\n return value as PathValue<TObject, TPath, TSplitter>;\n};\n\nexport type SearchParamsStringOptions = {\n includeQuestionMark?: boolean;\n};\n\nexport const toSearchParamsString = (\n searchParams: SearchParams,\n options: SearchParamsStringOptions = { includeQuestionMark: true },\n): string => {\n const params: string[] = [];\n\n Object.entries(searchParams).forEach(([key, value]) => {\n if (value === undefined || value === null) {\n return;\n }\n\n const encodedKey = encodeURIComponent(key);\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n params.push(`${encodedKey}=${encodeURIComponent(String(item))}`);\n }\n });\n } else {\n params.push(`${encodedKey}=${encodeURIComponent(String(value))}`);\n }\n });\n\n if (params.length === 0) {\n return '';\n }\n\n const searchParamsString = params.join('&');\n return options.includeQuestionMark ? `?${searchParamsString}` : searchParamsString;\n};\n\nexport const findObjectPath = <T>(\n root: Record<string, unknown>,\n target: T,\n splitter = '/',\n path = splitter,\n): string | undefined => {\n for (const key in root) {\n const current = root[key];\n\n if (current === target) return path + key;\n\n if (typeof current === 'object' && current !== null) {\n const result = findObjectPath(current as Record<string, unknown>, target, splitter, path + key + splitter);\n if (result) return result;\n }\n }\n\n return undefined;\n};\n\nexport type ReplaceDynamicSegmentsResult = {\n pathname: string;\n remainingParams: SearchParams;\n};\n\nconst DYNAMIC_SEGMENT_REGEX = /\\[([^\\]]+)\\]/g;\n\nexport const extractDynamicSegmentKeys = (pathname: string): string[] => {\n const matches = Array.from(pathname.matchAll(DYNAMIC_SEGMENT_REGEX));\n return matches.map((match) => match[1]);\n};\n\nexport const replaceDynamicSegments = (\n pathname: string,\n params?: SearchParams,\n): ReplaceDynamicSegmentsResult => {\n if (!params) {\n return {\n pathname,\n remainingParams: {},\n };\n }\n\n const usedKeys = new Set<string>();\n\n const replacedPathname = pathname.replace(DYNAMIC_SEGMENT_REGEX, (_, key) => {\n usedKeys.add(key);\n const value = params[key.toString()];\n if (value === undefined || value === null) return `[${key}]`;\n return Array.isArray(value) ? value.join(',') : value.toString();\n });\n\n const remainingParams = Object.fromEntries(\n Object.entries(params).filter(([key]) => !usedKeys.has(key))\n ) as SearchParams;\n\n return {\n pathname: replacedPathname,\n remainingParams,\n };\n};\n"]}
package/dist/index.mjs CHANGED
@@ -49,15 +49,34 @@ var findObjectPath = /* @__PURE__ */ __name((root, target, splitter = "/", path
49
49
  }
50
50
  return void 0;
51
51
  }, "findObjectPath");
52
+ var DYNAMIC_SEGMENT_REGEX = /\[([^\]]+)\]/g;
53
+ var extractDynamicSegmentKeys = /* @__PURE__ */ __name((pathname) => {
54
+ const matches = Array.from(pathname.matchAll(DYNAMIC_SEGMENT_REGEX));
55
+ return matches.map((match) => match[1]);
56
+ }, "extractDynamicSegmentKeys");
52
57
  var replaceDynamicSegments = /* @__PURE__ */ __name((pathname, params) => {
53
- if (!params) return pathname;
54
- return pathname.replace(/\[([^\]]+)\]/g, (_, key) => {
58
+ if (!params) {
59
+ return {
60
+ pathname,
61
+ remainingParams: {}
62
+ };
63
+ }
64
+ const usedKeys = /* @__PURE__ */ new Set();
65
+ const replacedPathname = pathname.replace(DYNAMIC_SEGMENT_REGEX, (_, key) => {
66
+ usedKeys.add(key);
55
67
  const value = params[key.toString()];
56
68
  if (value === void 0 || value === null) return `[${key}]`;
57
69
  return Array.isArray(value) ? value.join(",") : value.toString();
58
70
  });
71
+ const remainingParams = Object.fromEntries(
72
+ Object.entries(params).filter(([key]) => !usedKeys.has(key))
73
+ );
74
+ return {
75
+ pathname: replacedPathname,
76
+ remainingParams
77
+ };
59
78
  }, "replaceDynamicSegments");
60
79
 
61
- export { findObjectPath, getSafely, replaceDynamicSegments, toSearchParamsString };
80
+ export { extractDynamicSegmentKeys, findObjectPath, getSafely, replaceDynamicSegments, toSearchParamsString };
62
81
  //# sourceMappingURL=index.mjs.map
63
82
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/object.utils.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { Paths, PathValue } from './path.types';\nimport { SearchParams } from './query.types';\n\nexport const getSafely = <TObject, TSplitter extends string, TPath extends string & Paths<TObject, TSplitter>>(\n splitter: TSplitter,\n obj: TObject,\n path: TPath,\n): PathValue<TObject, TPath, TSplitter> => {\n if (path === '' || path === splitter) return obj as PathValue<TObject, TPath, TSplitter>;\n\n const keys = (path as string).split(splitter).filter((key) => key);\n let value: unknown = obj;\n\n for (const key of keys) {\n if (typeof value === 'object' && value !== null && key in value) {\n value = (value as Record<string, unknown>)[key];\n } else {\n return undefined as PathValue<TObject, TPath, TSplitter>;\n }\n }\n\n return value as PathValue<TObject, TPath, TSplitter>;\n};\n\nexport type SearchParamsStringOptions = {\n includeQuestionMark?: boolean;\n};\n\nexport const toSearchParamsString = (\n searchParams: SearchParams,\n options: SearchParamsStringOptions = { includeQuestionMark: true },\n): string => {\n const params: string[] = [];\n\n Object.entries(searchParams).forEach(([key, value]) => {\n if (value === undefined || value === null) {\n return;\n }\n\n const encodedKey = encodeURIComponent(key);\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n params.push(`${encodedKey}=${encodeURIComponent(String(item))}`);\n }\n });\n } else {\n params.push(`${encodedKey}=${encodeURIComponent(String(value))}`);\n }\n });\n\n if (params.length === 0) {\n return '';\n }\n\n const searchParamsString = params.join('&');\n return options.includeQuestionMark ? `?${searchParamsString}` : searchParamsString;\n};\n\nexport const findObjectPath = <T>(\n root: Record<string, unknown>,\n target: T,\n splitter = '/',\n path = splitter,\n): string | undefined => {\n for (const key in root) {\n const current = root[key];\n\n if (current === target) return path + key;\n\n if (typeof current === 'object' && current !== null) {\n const result = findObjectPath(current as Record<string, unknown>, target, splitter, path + key + splitter);\n if (result) return result;\n }\n }\n\n return undefined;\n};\n\nexport const replaceDynamicSegments = (\n pathname: string,\n params?: SearchParams,\n): string => {\n if (!params) return pathname;\n\n return pathname.replace(/\\[([^\\]]+)\\]/g, (_, key) => {\n const value = params[key.toString()];\n if (value === undefined || value === null) return `[${key}]`;\n return Array.isArray(value) ? value.join(',') : value.toString();\n });\n};\n"]}
1
+ {"version":3,"sources":["../src/object.utils.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { Paths, PathValue } from './path.types';\nimport { SearchParams } from './query.types';\n\nexport const getSafely = <TObject, TSplitter extends string, TPath extends string & Paths<TObject, TSplitter>>(\n splitter: TSplitter,\n obj: TObject,\n path: TPath,\n): PathValue<TObject, TPath, TSplitter> => {\n if (path === '' || path === splitter) return obj as PathValue<TObject, TPath, TSplitter>;\n\n const keys = (path as string).split(splitter).filter((key) => key);\n let value: unknown = obj;\n\n for (const key of keys) {\n if (typeof value === 'object' && value !== null && key in value) {\n value = (value as Record<string, unknown>)[key];\n } else {\n return undefined as PathValue<TObject, TPath, TSplitter>;\n }\n }\n\n return value as PathValue<TObject, TPath, TSplitter>;\n};\n\nexport type SearchParamsStringOptions = {\n includeQuestionMark?: boolean;\n};\n\nexport const toSearchParamsString = (\n searchParams: SearchParams,\n options: SearchParamsStringOptions = { includeQuestionMark: true },\n): string => {\n const params: string[] = [];\n\n Object.entries(searchParams).forEach(([key, value]) => {\n if (value === undefined || value === null) {\n return;\n }\n\n const encodedKey = encodeURIComponent(key);\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n params.push(`${encodedKey}=${encodeURIComponent(String(item))}`);\n }\n });\n } else {\n params.push(`${encodedKey}=${encodeURIComponent(String(value))}`);\n }\n });\n\n if (params.length === 0) {\n return '';\n }\n\n const searchParamsString = params.join('&');\n return options.includeQuestionMark ? `?${searchParamsString}` : searchParamsString;\n};\n\nexport const findObjectPath = <T>(\n root: Record<string, unknown>,\n target: T,\n splitter = '/',\n path = splitter,\n): string | undefined => {\n for (const key in root) {\n const current = root[key];\n\n if (current === target) return path + key;\n\n if (typeof current === 'object' && current !== null) {\n const result = findObjectPath(current as Record<string, unknown>, target, splitter, path + key + splitter);\n if (result) return result;\n }\n }\n\n return undefined;\n};\n\nexport type ReplaceDynamicSegmentsResult = {\n pathname: string;\n remainingParams: SearchParams;\n};\n\nconst DYNAMIC_SEGMENT_REGEX = /\\[([^\\]]+)\\]/g;\n\nexport const extractDynamicSegmentKeys = (pathname: string): string[] => {\n const matches = Array.from(pathname.matchAll(DYNAMIC_SEGMENT_REGEX));\n return matches.map((match) => match[1]);\n};\n\nexport const replaceDynamicSegments = (\n pathname: string,\n params?: SearchParams,\n): ReplaceDynamicSegmentsResult => {\n if (!params) {\n return {\n pathname,\n remainingParams: {},\n };\n }\n\n const usedKeys = new Set<string>();\n\n const replacedPathname = pathname.replace(DYNAMIC_SEGMENT_REGEX, (_, key) => {\n usedKeys.add(key);\n const value = params[key.toString()];\n if (value === undefined || value === null) return `[${key}]`;\n return Array.isArray(value) ? value.join(',') : value.toString();\n });\n\n const remainingParams = Object.fromEntries(\n Object.entries(params).filter(([key]) => !usedKeys.has(key))\n ) as SearchParams;\n\n return {\n pathname: replacedPathname,\n remainingParams,\n };\n};\n"]}
@@ -31,13 +31,28 @@ var findObjectPath = /* @__PURE__ */ __name((root, target, splitter = "/", path
31
31
  }
32
32
  return void 0;
33
33
  }, "findObjectPath");
34
+ var DYNAMIC_SEGMENT_REGEX = /\[([^\]]+)\]/g;
34
35
  var replaceDynamicSegments = /* @__PURE__ */ __name((pathname, params) => {
35
- if (!params) return pathname;
36
- return pathname.replace(/\[([^\]]+)\]/g, (_, key) => {
36
+ if (!params) {
37
+ return {
38
+ pathname,
39
+ remainingParams: {}
40
+ };
41
+ }
42
+ const usedKeys = /* @__PURE__ */ new Set();
43
+ const replacedPathname = pathname.replace(DYNAMIC_SEGMENT_REGEX, (_, key) => {
44
+ usedKeys.add(key);
37
45
  const value = params[key.toString()];
38
46
  if (value === void 0 || value === null) return `[${key}]`;
39
47
  return Array.isArray(value) ? value.join(",") : value.toString();
40
48
  });
49
+ const remainingParams = Object.fromEntries(
50
+ Object.entries(params).filter(([key]) => !usedKeys.has(key))
51
+ );
52
+ return {
53
+ pathname: replacedPathname,
54
+ remainingParams
55
+ };
41
56
  }, "replaceDynamicSegments");
42
57
  var createAppRoutes = /* @__PURE__ */ __name(() => (appRoutes) => {
43
58
  const ROUTE_NODE_COMMON_KEY = "_metadata";
@@ -73,7 +88,8 @@ var createAppRoutes = /* @__PURE__ */ __name(() => (appRoutes) => {
73
88
  const getPathnameFromNode = /* @__PURE__ */ __name((targetNode, params) => {
74
89
  const pathname = findObjectPath(frozenAppRoutes, targetNode);
75
90
  if (!pathname) return void 0;
76
- return replaceDynamicSegments(pathname, params);
91
+ const { pathname: replacedPathname } = replaceDynamicSegments(pathname, params);
92
+ return replacedPathname;
77
93
  }, "getPathnameFromNode");
78
94
  return {
79
95
  AppRoutesProvider,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/object.utils.ts","../src/routes.utils.tsx"],"names":["createContext","useContext"],"mappings":";;;;;;;;;AAGO,IAAM,SAAA,mBAAY,MAAA,CAAA,CACvB,QAAA,EACA,GAAA,EACA,IAAA,KACyC;AACzC,EAAA,IAAI,IAAA,KAAS,EAAA,IAAM,IAAA,KAAS,QAAA,EAAU,OAAO,GAAA;AAE7C,EAAA,MAAM,IAAA,GAAQ,KAAgB,KAAA,CAAM,QAAQ,EAAE,MAAA,CAAO,CAAC,QAAQ,GAAG,CAAA;AACjE,EAAA,IAAI,KAAA,GAAiB,GAAA;AAErB,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,EAAO;AAC/D,MAAA,KAAA,GAAS,MAAkC,GAAG,CAAA;AAAA,IAChD,CAAA,MAAO;AACL,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT,CAAA,EAnByB,WAAA,CAAA;AAyDlB,IAAM,iCAAiB,MAAA,CAAA,CAC5B,IAAA,EACA,QACA,QAAA,GAAW,GAAA,EACX,OAAO,QAAA,KACgB;AACvB,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,MAAM,OAAA,GAAU,KAAK,GAAG,CAAA;AAExB,IAAA,IAAI,OAAA,KAAY,MAAA,EAAQ,OAAO,IAAA,GAAO,GAAA;AAEtC,IAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,OAAA,KAAY,IAAA,EAAM;AACnD,MAAA,MAAM,SAAS,cAAA,CAAe,OAAA,EAAoC,QAAQ,QAAA,EAAU,IAAA,GAAO,MAAM,QAAQ,CAAA;AACzG,MAAA,IAAI,QAAQ,OAAO,MAAA;AAAA,IACrB;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT,CAAA,EAlB8B,gBAAA,CAAA;AAoBvB,IAAM,sBAAA,mBAAyB,MAAA,CAAA,CACpC,QAAA,EACA,MAAA,KACW;AACX,EAAA,IAAI,CAAC,QAAQ,OAAO,QAAA;AAEpB,EAAA,OAAO,QAAA,CAAS,OAAA,CAAQ,eAAA,EAAiB,CAAC,GAAG,GAAA,KAAQ;AACnD,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,GAAA,CAAI,QAAA,EAAU,CAAA;AACnC,IAAA,IAAI,UAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM,OAAO,IAAI,GAAG,CAAA,CAAA,CAAA;AACzD,IAAA,OAAO,KAAA,CAAM,QAAQ,KAAK,CAAA,GAAI,MAAM,IAAA,CAAK,GAAG,CAAA,GAAI,KAAA,CAAM,QAAA,EAAS;AAAA,EACjE,CAAC,CAAA;AACH,CAAA,EAXsC,wBAAA,CAAA;AC3E/B,IAAM,eAAA,mBACX,MAAA,CAAA,MACE,CAA2D,SAAA,KAAuE;AAKhI,EAAA,MAAM,qBAAA,GAA4C,WAAA;AAElD,EAAA,MAAM,kCAAkB,MAAA,CAAA,MAAM;AAC5B,IAAA,MAAM,eAAA,GAAkB,EAAE,GAAG,SAAA,EAAU;AAEvC,IAAA,MAAM,MAAA,2BAAU,GAAA,KAAiC;AAC/C,MAAA,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM;AAC5C,QAAA,MAAA,CAAO,cAAA,CAAe,KAAK,GAAA,EAAK;AAAA,UAC9B,QAAA,EAAU,KAAA;AAAA,UACV,YAAY,GAAA,KAAQ,qBAAA;AAAA,UACpB,YAAA,EAAc;AAAA,SACf,CAAA;AAED,QAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA,EAAM;AAC/C,UAAA,MAAA,CAAO,KAAgC,CAAA;AAAA,QACzC;AAAA,MACF,CAAC,CAAA;AAAA,IACH,CAAA,EAZe,QAAA,CAAA;AAcf,IAAA,MAAA,CAAO,eAA0C,CAAA;AACjD,IAAA,OAAO,eAAA;AAAA,EACT,CAAA,EAnBwB,iBAAA,CAAA;AAqBxB,EAAA,MAAM,kBAAkB,eAAA,EAAgB;AAExC,EAAA,MAAM,OAAA,GAAUA,oBAAsB,eAAe,CAAA;AAErD,EAAA,MAAM,+BAAe,MAAA,CAAA,MAAc;AACjC,IAAA,OAAOC,iBAAW,OAAO,CAAA;AAAA,EAC3B,CAAA,EAFqB,cAAA,CAAA;AAIrB,EAAA,MAAM,mBAAA,2BAA+C,QAAA,KAAoB;AACvE,IAAA,MAAM,MAAA,GAASA,iBAAW,OAAO,CAAA;AACjC,IAAA,OAAO,SAAA,CAAU,GAAA,EAAK,MAAA,EAAQ,QAAQ,CAAA;AAAA,EACxC,CAAA,EAH4B,qBAAA,CAAA;AAK5B,EAAA,MAAM,iBAAA,mBAAoB,MAAA,CAAA,CAAC,EAAE,QAAA,EAAS,KAA+B;AACnE,IAAA,sCAAQ,OAAA,CAAQ,QAAA,EAAR,EAAiB,KAAA,EAAO,iBAAkB,QAAA,EAAS,CAAA;AAAA,EAC7D,CAAA,EAF0B,mBAAA,CAAA;AAI1B,EAAA,MAAM,mBAAA,mBAAsB,MAAA,CAAA,CAAC,UAAA,EAA0B,MAAA,KAA8C;AACnG,IAAA,MAAM,QAAA,GAAW,cAAA,CAAe,eAAA,EAAiB,UAAU,CAAA;AAC3D,IAAA,IAAI,CAAC,UAAU,OAAO,MAAA;AACtB,IAAA,OAAO,sBAAA,CAAuB,UAAU,MAAM,CAAA;AAAA,EAChD,CAAA,EAJ4B,qBAAA,CAAA;AAM5B,EAAA,OAAO;AAAA,IACL,iBAAA;AAAA,IACA,YAAA;AAAA,IACA,mBAAA;AAAA,IACA,mBAAA;AAAA,IACA,eAAA;AAAA,IACA,QAAQ;AAAC,GAMX;AACF,CAAA,EAjEF,iBAAA","file":"routes.utils.js","sourcesContent":["import { Paths, PathValue } from './path.types';\nimport { SearchParams } from './query.types';\n\nexport const getSafely = <TObject, TSplitter extends string, TPath extends string & Paths<TObject, TSplitter>>(\n splitter: TSplitter,\n obj: TObject,\n path: TPath,\n): PathValue<TObject, TPath, TSplitter> => {\n if (path === '' || path === splitter) return obj as PathValue<TObject, TPath, TSplitter>;\n\n const keys = (path as string).split(splitter).filter((key) => key);\n let value: unknown = obj;\n\n for (const key of keys) {\n if (typeof value === 'object' && value !== null && key in value) {\n value = (value as Record<string, unknown>)[key];\n } else {\n return undefined as PathValue<TObject, TPath, TSplitter>;\n }\n }\n\n return value as PathValue<TObject, TPath, TSplitter>;\n};\n\nexport type SearchParamsStringOptions = {\n includeQuestionMark?: boolean;\n};\n\nexport const toSearchParamsString = (\n searchParams: SearchParams,\n options: SearchParamsStringOptions = { includeQuestionMark: true },\n): string => {\n const params: string[] = [];\n\n Object.entries(searchParams).forEach(([key, value]) => {\n if (value === undefined || value === null) {\n return;\n }\n\n const encodedKey = encodeURIComponent(key);\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n params.push(`${encodedKey}=${encodeURIComponent(String(item))}`);\n }\n });\n } else {\n params.push(`${encodedKey}=${encodeURIComponent(String(value))}`);\n }\n });\n\n if (params.length === 0) {\n return '';\n }\n\n const searchParamsString = params.join('&');\n return options.includeQuestionMark ? `?${searchParamsString}` : searchParamsString;\n};\n\nexport const findObjectPath = <T>(\n root: Record<string, unknown>,\n target: T,\n splitter = '/',\n path = splitter,\n): string | undefined => {\n for (const key in root) {\n const current = root[key];\n\n if (current === target) return path + key;\n\n if (typeof current === 'object' && current !== null) {\n const result = findObjectPath(current as Record<string, unknown>, target, splitter, path + key + splitter);\n if (result) return result;\n }\n }\n\n return undefined;\n};\n\nexport const replaceDynamicSegments = (\n pathname: string,\n params?: SearchParams,\n): string => {\n if (!params) return pathname;\n\n return pathname.replace(/\\[([^\\]]+)\\]/g, (_, key) => {\n const value = params[key.toString()];\n if (value === undefined || value === null) return `[${key}]`;\n return Array.isArray(value) ? value.join(',') : value.toString();\n });\n};\n","import { createContext, ReactNode, useContext } from 'react';\nimport { findObjectPath, getSafely, replaceDynamicSegments } from './object.utils';\nimport { SearchParams } from './query.types';\nimport { BaseMetadata, PartialRouteTree, ResolvedRouteTree, RouteNode, RoutePathname, RouteTree } from './routes.types';\n\nexport const createAppRoutes =\n <TMetadata extends BaseMetadata, TContext>() =>\n <TRouteTree extends PartialRouteTree<TMetadata, TContext>>(appRoutes: TRouteTree & RouteTree<TMetadata, TContext, TRouteTree>) => {\n type Routes = ResolvedRouteTree<TMetadata, TContext, TRouteTree>;\n type Pathname = RoutePathname<TMetadata, TContext, TRouteTree>;\n type AppRouteNode = RouteNode<TMetadata, TContext>;\n\n const ROUTE_NODE_COMMON_KEY: keyof AppRouteNode = '_metadata';\n\n const freezeAppRoutes = () => {\n const copiedAppRoutes = { ...appRoutes };\n\n const freeze = (obj: Record<string, unknown>) => {\n Object.entries(obj).forEach(([key, value]) => {\n Object.defineProperty(obj, key, {\n writable: false,\n enumerable: key !== ROUTE_NODE_COMMON_KEY,\n configurable: false,\n });\n\n if (typeof value === 'object' && value !== null) {\n freeze(value as Record<string, unknown>);\n }\n });\n };\n\n freeze(copiedAppRoutes as Record<string, unknown>);\n return copiedAppRoutes as Routes;\n };\n\n const frozenAppRoutes = freezeAppRoutes();\n\n const Context = createContext<Routes>(frozenAppRoutes);\n\n const useAppRoutes = (): Routes => {\n return useContext(Context);\n };\n\n const useCurrentRouteNode = <TPath extends Pathname>(pathname: TPath) => {\n const routes = useContext(Context);\n return getSafely('/', routes, pathname) as RouteNode<TMetadata, TContext>;\n };\n\n const AppRoutesProvider = ({ children }: { children: ReactNode }) => {\n return <Context.Provider value={frozenAppRoutes}>{children}</Context.Provider>;\n };\n\n const getPathnameFromNode = (targetNode: AppRouteNode, params?: SearchParams): string | undefined => {\n const pathname = findObjectPath(frozenAppRoutes, targetNode);\n if (!pathname) return undefined;\n return replaceDynamicSegments(pathname, params);\n };\n\n return {\n AppRoutesProvider,\n useAppRoutes,\n useCurrentRouteNode,\n getPathnameFromNode,\n freezeAppRoutes,\n _types: {} as {\n AppRoutesMetadata: TMetadata;\n AppRoutesContext: TContext;\n AppRoutesPathname: Pathname;\n AppRouteNode: AppRouteNode;\n },\n };\n };\n"]}
1
+ {"version":3,"sources":["../src/object.utils.ts","../src/routes.utils.tsx"],"names":["createContext","useContext"],"mappings":"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","file":"routes.utils.js","sourcesContent":["import { Paths, PathValue } from './path.types';\nimport { SearchParams } from './query.types';\n\nexport const getSafely = <TObject, TSplitter extends string, TPath extends string & Paths<TObject, TSplitter>>(\n splitter: TSplitter,\n obj: TObject,\n path: TPath,\n): PathValue<TObject, TPath, TSplitter> => {\n if (path === '' || path === splitter) return obj as PathValue<TObject, TPath, TSplitter>;\n\n const keys = (path as string).split(splitter).filter((key) => key);\n let value: unknown = obj;\n\n for (const key of keys) {\n if (typeof value === 'object' && value !== null && key in value) {\n value = (value as Record<string, unknown>)[key];\n } else {\n return undefined as PathValue<TObject, TPath, TSplitter>;\n }\n }\n\n return value as PathValue<TObject, TPath, TSplitter>;\n};\n\nexport type SearchParamsStringOptions = {\n includeQuestionMark?: boolean;\n};\n\nexport const toSearchParamsString = (\n searchParams: SearchParams,\n options: SearchParamsStringOptions = { includeQuestionMark: true },\n): string => {\n const params: string[] = [];\n\n Object.entries(searchParams).forEach(([key, value]) => {\n if (value === undefined || value === null) {\n return;\n }\n\n const encodedKey = encodeURIComponent(key);\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n params.push(`${encodedKey}=${encodeURIComponent(String(item))}`);\n }\n });\n } else {\n params.push(`${encodedKey}=${encodeURIComponent(String(value))}`);\n }\n });\n\n if (params.length === 0) {\n return '';\n }\n\n const searchParamsString = params.join('&');\n return options.includeQuestionMark ? `?${searchParamsString}` : searchParamsString;\n};\n\nexport const findObjectPath = <T>(\n root: Record<string, unknown>,\n target: T,\n splitter = '/',\n path = splitter,\n): string | undefined => {\n for (const key in root) {\n const current = root[key];\n\n if (current === target) return path + key;\n\n if (typeof current === 'object' && current !== null) {\n const result = findObjectPath(current as Record<string, unknown>, target, splitter, path + key + splitter);\n if (result) return result;\n }\n }\n\n return undefined;\n};\n\nexport type ReplaceDynamicSegmentsResult = {\n pathname: string;\n remainingParams: SearchParams;\n};\n\nconst DYNAMIC_SEGMENT_REGEX = /\\[([^\\]]+)\\]/g;\n\nexport const extractDynamicSegmentKeys = (pathname: string): string[] => {\n const matches = Array.from(pathname.matchAll(DYNAMIC_SEGMENT_REGEX));\n return matches.map((match) => match[1]);\n};\n\nexport const replaceDynamicSegments = (\n pathname: string,\n params?: SearchParams,\n): ReplaceDynamicSegmentsResult => {\n if (!params) {\n return {\n pathname,\n remainingParams: {},\n };\n }\n\n const usedKeys = new Set<string>();\n\n const replacedPathname = pathname.replace(DYNAMIC_SEGMENT_REGEX, (_, key) => {\n usedKeys.add(key);\n const value = params[key.toString()];\n if (value === undefined || value === null) return `[${key}]`;\n return Array.isArray(value) ? value.join(',') : value.toString();\n });\n\n const remainingParams = Object.fromEntries(\n Object.entries(params).filter(([key]) => !usedKeys.has(key))\n ) as SearchParams;\n\n return {\n pathname: replacedPathname,\n remainingParams,\n };\n};\n","import { createContext, ReactNode, useContext } from 'react';\nimport { findObjectPath, getSafely, replaceDynamicSegments } from './object.utils';\nimport { SearchParams } from './query.types';\nimport { BaseMetadata, PartialRouteTree, ResolvedRouteTree, RouteNode, RoutePathname, RouteTree } from './routes.types';\n\nexport const createAppRoutes =\n <TMetadata extends BaseMetadata, TContext>() =>\n <TRouteTree extends PartialRouteTree<TMetadata, TContext>>(appRoutes: TRouteTree & RouteTree<TMetadata, TContext, TRouteTree>) => {\n type Routes = ResolvedRouteTree<TMetadata, TContext, TRouteTree>;\n type Pathname = RoutePathname<TMetadata, TContext, TRouteTree>;\n type AppRouteNode = RouteNode<TMetadata, TContext>;\n\n const ROUTE_NODE_COMMON_KEY: keyof AppRouteNode = '_metadata';\n\n const freezeAppRoutes = () => {\n const copiedAppRoutes = { ...appRoutes };\n\n const freeze = (obj: Record<string, unknown>) => {\n Object.entries(obj).forEach(([key, value]) => {\n Object.defineProperty(obj, key, {\n writable: false,\n enumerable: key !== ROUTE_NODE_COMMON_KEY,\n configurable: false,\n });\n\n if (typeof value === 'object' && value !== null) {\n freeze(value as Record<string, unknown>);\n }\n });\n };\n\n freeze(copiedAppRoutes as Record<string, unknown>);\n return copiedAppRoutes as Routes;\n };\n\n const frozenAppRoutes = freezeAppRoutes();\n\n const Context = createContext<Routes>(frozenAppRoutes);\n\n const useAppRoutes = (): Routes => {\n return useContext(Context);\n };\n\n const useCurrentRouteNode = <TPath extends Pathname>(pathname: TPath) => {\n const routes = useContext(Context);\n return getSafely('/', routes, pathname) as RouteNode<TMetadata, TContext>;\n };\n\n const AppRoutesProvider = ({ children }: { children: ReactNode }) => {\n return <Context.Provider value={frozenAppRoutes}>{children}</Context.Provider>;\n };\n\n const getPathnameFromNode = (targetNode: AppRouteNode, params?: SearchParams): string | undefined => {\n const pathname = findObjectPath(frozenAppRoutes, targetNode);\n if (!pathname) return undefined;\n const { pathname: replacedPathname } = replaceDynamicSegments(pathname, params);\n return replacedPathname;\n };\n\n return {\n AppRoutesProvider,\n useAppRoutes,\n useCurrentRouteNode,\n getPathnameFromNode,\n freezeAppRoutes,\n _types: {} as {\n AppRoutesMetadata: TMetadata;\n AppRoutesContext: TContext;\n AppRoutesPathname: Pathname;\n AppRouteNode: AppRouteNode;\n },\n };\n };\n"]}
@@ -29,13 +29,28 @@ var findObjectPath = /* @__PURE__ */ __name((root, target, splitter = "/", path
29
29
  }
30
30
  return void 0;
31
31
  }, "findObjectPath");
32
+ var DYNAMIC_SEGMENT_REGEX = /\[([^\]]+)\]/g;
32
33
  var replaceDynamicSegments = /* @__PURE__ */ __name((pathname, params) => {
33
- if (!params) return pathname;
34
- return pathname.replace(/\[([^\]]+)\]/g, (_, key) => {
34
+ if (!params) {
35
+ return {
36
+ pathname,
37
+ remainingParams: {}
38
+ };
39
+ }
40
+ const usedKeys = /* @__PURE__ */ new Set();
41
+ const replacedPathname = pathname.replace(DYNAMIC_SEGMENT_REGEX, (_, key) => {
42
+ usedKeys.add(key);
35
43
  const value = params[key.toString()];
36
44
  if (value === void 0 || value === null) return `[${key}]`;
37
45
  return Array.isArray(value) ? value.join(",") : value.toString();
38
46
  });
47
+ const remainingParams = Object.fromEntries(
48
+ Object.entries(params).filter(([key]) => !usedKeys.has(key))
49
+ );
50
+ return {
51
+ pathname: replacedPathname,
52
+ remainingParams
53
+ };
39
54
  }, "replaceDynamicSegments");
40
55
  var createAppRoutes = /* @__PURE__ */ __name(() => (appRoutes) => {
41
56
  const ROUTE_NODE_COMMON_KEY = "_metadata";
@@ -71,7 +86,8 @@ var createAppRoutes = /* @__PURE__ */ __name(() => (appRoutes) => {
71
86
  const getPathnameFromNode = /* @__PURE__ */ __name((targetNode, params) => {
72
87
  const pathname = findObjectPath(frozenAppRoutes, targetNode);
73
88
  if (!pathname) return void 0;
74
- return replaceDynamicSegments(pathname, params);
89
+ const { pathname: replacedPathname } = replaceDynamicSegments(pathname, params);
90
+ return replacedPathname;
75
91
  }, "getPathnameFromNode");
76
92
  return {
77
93
  AppRoutesProvider,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/object.utils.ts","../src/routes.utils.tsx"],"names":[],"mappings":"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","file":"routes.utils.mjs","sourcesContent":["import { Paths, PathValue } from './path.types';\nimport { SearchParams } from './query.types';\n\nexport const getSafely = <TObject, TSplitter extends string, TPath extends string & Paths<TObject, TSplitter>>(\n splitter: TSplitter,\n obj: TObject,\n path: TPath,\n): PathValue<TObject, TPath, TSplitter> => {\n if (path === '' || path === splitter) return obj as PathValue<TObject, TPath, TSplitter>;\n\n const keys = (path as string).split(splitter).filter((key) => key);\n let value: unknown = obj;\n\n for (const key of keys) {\n if (typeof value === 'object' && value !== null && key in value) {\n value = (value as Record<string, unknown>)[key];\n } else {\n return undefined as PathValue<TObject, TPath, TSplitter>;\n }\n }\n\n return value as PathValue<TObject, TPath, TSplitter>;\n};\n\nexport type SearchParamsStringOptions = {\n includeQuestionMark?: boolean;\n};\n\nexport const toSearchParamsString = (\n searchParams: SearchParams,\n options: SearchParamsStringOptions = { includeQuestionMark: true },\n): string => {\n const params: string[] = [];\n\n Object.entries(searchParams).forEach(([key, value]) => {\n if (value === undefined || value === null) {\n return;\n }\n\n const encodedKey = encodeURIComponent(key);\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n params.push(`${encodedKey}=${encodeURIComponent(String(item))}`);\n }\n });\n } else {\n params.push(`${encodedKey}=${encodeURIComponent(String(value))}`);\n }\n });\n\n if (params.length === 0) {\n return '';\n }\n\n const searchParamsString = params.join('&');\n return options.includeQuestionMark ? `?${searchParamsString}` : searchParamsString;\n};\n\nexport const findObjectPath = <T>(\n root: Record<string, unknown>,\n target: T,\n splitter = '/',\n path = splitter,\n): string | undefined => {\n for (const key in root) {\n const current = root[key];\n\n if (current === target) return path + key;\n\n if (typeof current === 'object' && current !== null) {\n const result = findObjectPath(current as Record<string, unknown>, target, splitter, path + key + splitter);\n if (result) return result;\n }\n }\n\n return undefined;\n};\n\nexport const replaceDynamicSegments = (\n pathname: string,\n params?: SearchParams,\n): string => {\n if (!params) return pathname;\n\n return pathname.replace(/\\[([^\\]]+)\\]/g, (_, key) => {\n const value = params[key.toString()];\n if (value === undefined || value === null) return `[${key}]`;\n return Array.isArray(value) ? value.join(',') : value.toString();\n });\n};\n","import { createContext, ReactNode, useContext } from 'react';\nimport { findObjectPath, getSafely, replaceDynamicSegments } from './object.utils';\nimport { SearchParams } from './query.types';\nimport { BaseMetadata, PartialRouteTree, ResolvedRouteTree, RouteNode, RoutePathname, RouteTree } from './routes.types';\n\nexport const createAppRoutes =\n <TMetadata extends BaseMetadata, TContext>() =>\n <TRouteTree extends PartialRouteTree<TMetadata, TContext>>(appRoutes: TRouteTree & RouteTree<TMetadata, TContext, TRouteTree>) => {\n type Routes = ResolvedRouteTree<TMetadata, TContext, TRouteTree>;\n type Pathname = RoutePathname<TMetadata, TContext, TRouteTree>;\n type AppRouteNode = RouteNode<TMetadata, TContext>;\n\n const ROUTE_NODE_COMMON_KEY: keyof AppRouteNode = '_metadata';\n\n const freezeAppRoutes = () => {\n const copiedAppRoutes = { ...appRoutes };\n\n const freeze = (obj: Record<string, unknown>) => {\n Object.entries(obj).forEach(([key, value]) => {\n Object.defineProperty(obj, key, {\n writable: false,\n enumerable: key !== ROUTE_NODE_COMMON_KEY,\n configurable: false,\n });\n\n if (typeof value === 'object' && value !== null) {\n freeze(value as Record<string, unknown>);\n }\n });\n };\n\n freeze(copiedAppRoutes as Record<string, unknown>);\n return copiedAppRoutes as Routes;\n };\n\n const frozenAppRoutes = freezeAppRoutes();\n\n const Context = createContext<Routes>(frozenAppRoutes);\n\n const useAppRoutes = (): Routes => {\n return useContext(Context);\n };\n\n const useCurrentRouteNode = <TPath extends Pathname>(pathname: TPath) => {\n const routes = useContext(Context);\n return getSafely('/', routes, pathname) as RouteNode<TMetadata, TContext>;\n };\n\n const AppRoutesProvider = ({ children }: { children: ReactNode }) => {\n return <Context.Provider value={frozenAppRoutes}>{children}</Context.Provider>;\n };\n\n const getPathnameFromNode = (targetNode: AppRouteNode, params?: SearchParams): string | undefined => {\n const pathname = findObjectPath(frozenAppRoutes, targetNode);\n if (!pathname) return undefined;\n return replaceDynamicSegments(pathname, params);\n };\n\n return {\n AppRoutesProvider,\n useAppRoutes,\n useCurrentRouteNode,\n getPathnameFromNode,\n freezeAppRoutes,\n _types: {} as {\n AppRoutesMetadata: TMetadata;\n AppRoutesContext: TContext;\n AppRoutesPathname: Pathname;\n AppRouteNode: AppRouteNode;\n },\n };\n };\n"]}
1
+ {"version":3,"sources":["../src/object.utils.ts","../src/routes.utils.tsx"],"names":[],"mappings":"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","file":"routes.utils.mjs","sourcesContent":["import { Paths, PathValue } from './path.types';\nimport { SearchParams } from './query.types';\n\nexport const getSafely = <TObject, TSplitter extends string, TPath extends string & Paths<TObject, TSplitter>>(\n splitter: TSplitter,\n obj: TObject,\n path: TPath,\n): PathValue<TObject, TPath, TSplitter> => {\n if (path === '' || path === splitter) return obj as PathValue<TObject, TPath, TSplitter>;\n\n const keys = (path as string).split(splitter).filter((key) => key);\n let value: unknown = obj;\n\n for (const key of keys) {\n if (typeof value === 'object' && value !== null && key in value) {\n value = (value as Record<string, unknown>)[key];\n } else {\n return undefined as PathValue<TObject, TPath, TSplitter>;\n }\n }\n\n return value as PathValue<TObject, TPath, TSplitter>;\n};\n\nexport type SearchParamsStringOptions = {\n includeQuestionMark?: boolean;\n};\n\nexport const toSearchParamsString = (\n searchParams: SearchParams,\n options: SearchParamsStringOptions = { includeQuestionMark: true },\n): string => {\n const params: string[] = [];\n\n Object.entries(searchParams).forEach(([key, value]) => {\n if (value === undefined || value === null) {\n return;\n }\n\n const encodedKey = encodeURIComponent(key);\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n if (item !== undefined && item !== null) {\n params.push(`${encodedKey}=${encodeURIComponent(String(item))}`);\n }\n });\n } else {\n params.push(`${encodedKey}=${encodeURIComponent(String(value))}`);\n }\n });\n\n if (params.length === 0) {\n return '';\n }\n\n const searchParamsString = params.join('&');\n return options.includeQuestionMark ? `?${searchParamsString}` : searchParamsString;\n};\n\nexport const findObjectPath = <T>(\n root: Record<string, unknown>,\n target: T,\n splitter = '/',\n path = splitter,\n): string | undefined => {\n for (const key in root) {\n const current = root[key];\n\n if (current === target) return path + key;\n\n if (typeof current === 'object' && current !== null) {\n const result = findObjectPath(current as Record<string, unknown>, target, splitter, path + key + splitter);\n if (result) return result;\n }\n }\n\n return undefined;\n};\n\nexport type ReplaceDynamicSegmentsResult = {\n pathname: string;\n remainingParams: SearchParams;\n};\n\nconst DYNAMIC_SEGMENT_REGEX = /\\[([^\\]]+)\\]/g;\n\nexport const extractDynamicSegmentKeys = (pathname: string): string[] => {\n const matches = Array.from(pathname.matchAll(DYNAMIC_SEGMENT_REGEX));\n return matches.map((match) => match[1]);\n};\n\nexport const replaceDynamicSegments = (\n pathname: string,\n params?: SearchParams,\n): ReplaceDynamicSegmentsResult => {\n if (!params) {\n return {\n pathname,\n remainingParams: {},\n };\n }\n\n const usedKeys = new Set<string>();\n\n const replacedPathname = pathname.replace(DYNAMIC_SEGMENT_REGEX, (_, key) => {\n usedKeys.add(key);\n const value = params[key.toString()];\n if (value === undefined || value === null) return `[${key}]`;\n return Array.isArray(value) ? value.join(',') : value.toString();\n });\n\n const remainingParams = Object.fromEntries(\n Object.entries(params).filter(([key]) => !usedKeys.has(key))\n ) as SearchParams;\n\n return {\n pathname: replacedPathname,\n remainingParams,\n };\n};\n","import { createContext, ReactNode, useContext } from 'react';\nimport { findObjectPath, getSafely, replaceDynamicSegments } from './object.utils';\nimport { SearchParams } from './query.types';\nimport { BaseMetadata, PartialRouteTree, ResolvedRouteTree, RouteNode, RoutePathname, RouteTree } from './routes.types';\n\nexport const createAppRoutes =\n <TMetadata extends BaseMetadata, TContext>() =>\n <TRouteTree extends PartialRouteTree<TMetadata, TContext>>(appRoutes: TRouteTree & RouteTree<TMetadata, TContext, TRouteTree>) => {\n type Routes = ResolvedRouteTree<TMetadata, TContext, TRouteTree>;\n type Pathname = RoutePathname<TMetadata, TContext, TRouteTree>;\n type AppRouteNode = RouteNode<TMetadata, TContext>;\n\n const ROUTE_NODE_COMMON_KEY: keyof AppRouteNode = '_metadata';\n\n const freezeAppRoutes = () => {\n const copiedAppRoutes = { ...appRoutes };\n\n const freeze = (obj: Record<string, unknown>) => {\n Object.entries(obj).forEach(([key, value]) => {\n Object.defineProperty(obj, key, {\n writable: false,\n enumerable: key !== ROUTE_NODE_COMMON_KEY,\n configurable: false,\n });\n\n if (typeof value === 'object' && value !== null) {\n freeze(value as Record<string, unknown>);\n }\n });\n };\n\n freeze(copiedAppRoutes as Record<string, unknown>);\n return copiedAppRoutes as Routes;\n };\n\n const frozenAppRoutes = freezeAppRoutes();\n\n const Context = createContext<Routes>(frozenAppRoutes);\n\n const useAppRoutes = (): Routes => {\n return useContext(Context);\n };\n\n const useCurrentRouteNode = <TPath extends Pathname>(pathname: TPath) => {\n const routes = useContext(Context);\n return getSafely('/', routes, pathname) as RouteNode<TMetadata, TContext>;\n };\n\n const AppRoutesProvider = ({ children }: { children: ReactNode }) => {\n return <Context.Provider value={frozenAppRoutes}>{children}</Context.Provider>;\n };\n\n const getPathnameFromNode = (targetNode: AppRouteNode, params?: SearchParams): string | undefined => {\n const pathname = findObjectPath(frozenAppRoutes, targetNode);\n if (!pathname) return undefined;\n const { pathname: replacedPathname } = replaceDynamicSegments(pathname, params);\n return replacedPathname;\n };\n\n return {\n AppRoutesProvider,\n useAppRoutes,\n useCurrentRouteNode,\n getPathnameFromNode,\n freezeAppRoutes,\n _types: {} as {\n AppRoutesMetadata: TMetadata;\n AppRoutesContext: TContext;\n AppRoutesPathname: Pathname;\n AppRouteNode: AppRouteNode;\n },\n };\n };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyeonqyu/typed-router-core",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "description": "Core types and utilities for typed-router",
5
5
  "author": "hyeonQyu <dhk0561@naver.com>",
6
6
  "license": "MIT",