@devpablocristo/platform-browser 0.1.0 → 0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devpablocristo/platform-browser",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
package/src/index.ts CHANGED
@@ -4,3 +4,4 @@ export * from "./search";
4
4
  export * from "./confirm";
5
5
  export * from "./theme";
6
6
  export * from "./observability";
7
+ export * from "./slug";
package/src/slug.ts ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * camelCase ↔ kebab-case helpers for resource slugs in URLs.
3
+ *
4
+ * Internal resource identifiers (e.g. config map keys) are typically
5
+ * camelCase because they double as TypeScript object property names; URLs
6
+ * that end users see are typically kebab-case for readability.
7
+ *
8
+ * This module is purely algorithmic. Consumers that have non-trivial
9
+ * irregular plurals or capitalization quirks (e.g. `bikeWorkOrders` →
10
+ * `bike-work-orders`) supply a custom map via `applySlugMap`.
11
+ */
12
+
13
+ /** camelCase → kebab-case via algorithmic transformation. */
14
+ export function camelToKebab(input: string): string {
15
+ return input
16
+ .replace(/([a-z\d])([A-Z])/g, "$1-$2")
17
+ .replace(/([A-Z])([A-Z][a-z])/g, "$1-$2")
18
+ .toLowerCase();
19
+ }
20
+
21
+ /** kebab-case → camelCase via algorithmic transformation. */
22
+ export function kebabToCamel(input: string): string {
23
+ return input.replace(/-([a-zA-Z])/g, (_, ch: string) => ch.toUpperCase());
24
+ }
25
+
26
+ /**
27
+ * Apply a custom mapping with algorithmic fallback. If `resourceId` is a
28
+ * key of `customMap`, return its value; otherwise transform with
29
+ * `camelToKebab`.
30
+ *
31
+ * const SLUG: Record<string, string> = { bikeWorkOrders: 'bike-work-orders' };
32
+ * applySlugMap('bikeWorkOrders', SLUG); // 'bike-work-orders'
33
+ * applySlugMap('priceLists', SLUG); // 'price-lists' (algorithmic)
34
+ */
35
+ export function applySlugMap(
36
+ resourceId: string,
37
+ customMap: Readonly<Record<string, string>> = {},
38
+ ): string {
39
+ if (resourceId in customMap) return customMap[resourceId];
40
+ return camelToKebab(resourceId);
41
+ }
42
+
43
+ /**
44
+ * Reverse direction: a URL slug → camelCase resourceId. Looks up
45
+ * `customMap`'s VALUES (slug → camel direction); falls back to
46
+ * `kebabToCamel` when no entry matches.
47
+ */
48
+ export function unapplySlugMap(
49
+ slug: string,
50
+ customMap: Readonly<Record<string, string>> = {},
51
+ ): string {
52
+ for (const [camel, mappedSlug] of Object.entries(customMap)) {
53
+ if (mappedSlug === slug) return camel;
54
+ }
55
+ return kebabToCamel(slug);
56
+ }