{"_id":"@dasch-ng/route-signals","_rev":"2-dce44ef14ed43c55072793f476444729","name":"@dasch-ng/route-signals","dist-tags":{"rc":"1.0.0-rc.0","latest":"1.0.0"},"versions":{"1.0.0-rc.0":{"name":"@dasch-ng/route-signals","version":"1.0.0-rc.0","_id":"@dasch-ng/route-signals@1.0.0-rc.0","maintainers":[{"name":"dasch","email":"daniel.schuba@daschmedia.de"}],"dist":{"shasum":"2b7ac7e3aa7b49e9f2d6cf3623128839d40f791a","tarball":"https://registry.npmjs.org/@dasch-ng/route-signals/-/route-signals-1.0.0-rc.0.tgz","fileCount":5,"integrity":"sha512-bjF532U+79SLo5yFFcEtBCGc/s8it5GUumZ/1yOLYuBoAV5UZzw9ODe2rHU2nbfLd3ez5+Mu4bSiNQF872E8Ag==","signatures":[{"sig":"MEYCIQDqHlTtRV1+RUfveazs/6fDhfiURiygO//iy51pMbM49wIhAKAFawP3HBHeGtnXoW4PqLXCCbVFCS+g4ceE2ymdcUPL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":23555},"module":"fesm2022/dasch-ng-route-signals.mjs","exports":{".":{"types":"./types/dasch-ng-route-signals.d.ts","default":"./fesm2022/dasch-ng-route-signals.mjs"},"./package.json":{"default":"./package.json"}},"gitHead":"e2eea39fa0a3d55e50ea9b37b7eebd97aebb3725","typings":"types/dasch-ng-route-signals.d.ts","_npmUser":{"name":"dasch","email":"daniel.schuba@daschmedia.de"},"_npmVersion":"11.7.0","description":"[![npm version](https://img.shields.io/npm/v/@dasch-ng/route-signals.svg)](https://www.npmjs.com/package/@dasch-ng/route-signals)","directories":{},"sideEffects":false,"_nodeVersion":"24.12.0","dependencies":{"tslib":"^2.3.0"},"_hasShrinkwrap":false,"peerDependencies":{"rxjs":"^7.0.0","@angular/core":"^21.0.0","@angular/router":"^21.0.0","@dasch-ng/rxjs-operators":"^0.3.2"},"_npmOperationalInternal":{"tmp":"tmp/route-signals_1.0.0-rc.0_1766669875794_0.08863848240928784","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@dasch-ng/route-signals","version":"1.0.0","publishConfig":{"access":"public"},"peerDependencies":{"@angular/core":"^21.0.0","@angular/router":"^21.0.0","@dasch-ng/rxjs-operators":"^0.3.2 || ^0.4.0 || ^0.5.0","rxjs":"^7.0.0"},"sideEffects":false,"module":"fesm2022/dasch-ng-route-signals.mjs","typings":"types/dasch-ng-route-signals.d.ts","exports":{"./package.json":{"default":"./package.json"},".":{"types":"./types/dasch-ng-route-signals.d.ts","default":"./fesm2022/dasch-ng-route-signals.mjs"}},"dependencies":{"tslib":"^2.3.0"},"gitHead":"6fa2bf5d7add21c6691eb9cb5ce7501387ac33a2","_id":"@dasch-ng/route-signals@1.0.0","description":"[![npm version](https://img.shields.io/npm/v/@dasch-ng/route-signals.svg)](https://www.npmjs.com/package/@dasch-ng/route-signals)","_nodeVersion":"24.12.0","_npmVersion":"11.7.0","dist":{"integrity":"sha512-0eyKo+Kj/Y7cLtQveuLbtzV/qbbkKcOLLtkLD/5zoAYZ/9mctV4fg4A2j+ZpbV/jvQgUPPf6lQVHnQzwYbc5bg==","shasum":"ded83d6817b7bf2b5c5f89377f7e44d0dbc3fb86","tarball":"https://registry.npmjs.org/@dasch-ng/route-signals/-/route-signals-1.0.0.tgz","fileCount":5,"unpackedSize":26084,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCkUDJh3ABnt+zyrKhEX6IX5BBRcnlImjfZM/RddXm7HQIgFVjQMTFMCv/ENyi4JSPCbVeGIehWZb8iquT/RbvFJJE="}]},"_npmUser":{"name":"dasch","email":"daniel.schuba@daschmedia.de"},"directories":{},"maintainers":[{"name":"dasch","email":"daniel.schuba@daschmedia.de"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/route-signals_1.0.0_1771594604646_0.3781269431045322"},"_hasShrinkwrap":false}},"time":{"created":"2025-12-25T13:37:55.706Z","modified":"2026-02-20T13:36:44.914Z","1.0.0-rc.0":"2025-12-25T13:37:55.942Z","1.0.0":"2026-02-20T13:36:44.780Z"},"description":"[![npm version](https://img.shields.io/npm/v/@dasch-ng/route-signals.svg)](https://www.npmjs.com/package/@dasch-ng/route-signals)","maintainers":[{"name":"dasch","email":"daniel.schuba@daschmedia.de"}],"readme":"# @dasch-ng/route-signals\n\n[![npm version](https://img.shields.io/npm/v/@dasch-ng/route-signals.svg)](https://www.npmjs.com/package/@dasch-ng/route-signals)\n\nAngular utilities for working with route parameters, query parameters, and route data as signals.\n\n## Features\n\n- 🎯 **Type-safe signals** - Get route state with full TypeScript support\n- 🔄 **Automatic updates** - Signals update when route changes\n- 🔐 **URL decoding** - Automatically decodes URL-encoded values\n- 🛡️ **Error handling** - Safe decoding with fallback for malformed URLs\n- ⚡ **Zero dependencies** - Only depends on Angular and RxJS\n- 📦 **Tree-shakeable** - Import only what you need\n\n## Installation\n\n```bash\nnpm install @dasch-ng/route-signals\n```\n\n## Usage\n\n### Route Parameters\n\nTrack URL path parameters as signals:\n\n```typescript\nimport { Component } from '@angular/core';\nimport { routeParam } from '@dasch-ng/route-signals';\n\n@Component({\n  selector: 'app-user-detail',\n  template: `\n    <h1>User: {{ userId() }}</h1>\n    <p>Group: {{ groupId() }}</p>\n  `,\n})\nexport class UserDetailComponent {\n  // Route: /users/:userId/groups/:groupId\n  readonly userId = routeParam('userId');\n  readonly groupId = routeParam('groupId');\n\n  constructor() {\n    // URL: /users/john-doe/groups/admin\n    console.log(this.userId()); // \"john-doe\"\n    console.log(this.groupId()); // \"admin\"\n\n    // Automatically decodes URL-encoded values\n    // URL: /users/john%20doe/groups/team%20alpha\n    console.log(this.userId()); // \"john doe\"\n    console.log(this.groupId()); // \"team alpha\"\n  }\n}\n```\n\n### Query Parameters\n\nTrack URL query parameters as signals:\n\n```typescript\nimport { Component } from '@angular/core';\nimport { routeQueryParam } from '@dasch-ng/route-signals';\n\n@Component({\n  selector: 'app-search',\n  template: `\n    <h1>Search: {{ query() }}</h1>\n    <p>Filter: {{ filter() }}</p>\n    <p>Sort: {{ sort() }}</p>\n  `,\n})\nexport class SearchComponent {\n  // URL: /search?q=angular&filter=tutorials&sort=recent\n  readonly query = routeQueryParam('q');\n  readonly filter = routeQueryParam('filter');\n  readonly sort = routeQueryParam('sort');\n\n  constructor() {\n    console.log(this.query()); // \"angular\"\n    console.log(this.filter()); // \"tutorials\"\n    console.log(this.sort()); // \"recent\"\n\n    // Automatically decodes URL-encoded values\n    // URL: /search?q=angular%20signals&filter=a%2Fb\n    console.log(this.query()); // \"angular signals\"\n    console.log(this.filter()); // \"a/b\"\n  }\n}\n```\n\n### Route Data\n\nTrack route data properties as signals:\n\n```typescript\nimport { Component } from '@angular/core';\nimport { routeData } from '@dasch-ng/route-signals';\n\n// In route configuration:\nconst routes = [\n  {\n    path: 'admin',\n    component: AdminComponent,\n    data: { title: 'Admin Dashboard', role: 'admin' },\n  },\n];\n\n@Component({\n  selector: 'app-admin',\n  template: `\n    <h1>{{ title() }}</h1>\n    <p>Required role: {{ role() }}</p>\n  `,\n})\nexport class AdminComponent {\n  readonly title = routeData<string>('title');\n  readonly role = routeData<string>('role');\n\n  constructor() {\n    console.log(this.title()); // \"Admin Dashboard\"\n    console.log(this.role()); // \"admin\"\n  }\n}\n```\n\n### With Resolvers\n\nRoute data works seamlessly with Angular resolvers:\n\n```typescript\nimport { inject } from '@angular/core';\nimport { ActivatedRouteSnapshot } from '@angular/router';\nimport { UserService } from './user.service';\n\n// Resolver\nconst userResolver = (route: ActivatedRouteSnapshot) => {\n  const userService = inject(UserService);\n  const userId = route.paramMap.get('id');\n  return userService.getUser(userId);\n};\n\n// Route configuration\nconst routes = [\n  {\n    path: 'user/:id',\n    component: UserDetailComponent,\n    resolve: { user: userResolver },\n    // Re-run resolver when params change\n    runGuardsAndResolvers: 'paramsChange',\n  },\n];\n\n// Component\n@Component({\n  selector: 'app-user-detail',\n  template: `\n    <h1>{{ user().name }}</h1>\n    <p>{{ user().email }}</p>\n  `,\n})\nexport class UserDetailComponent {\n  readonly user = routeData<User>('user');\n\n  // Signal automatically updates when resolver re-runs\n}\n```\n\n## API\n\n### `routeParam(key: string): Signal<string>`\n\nCreates a signal that tracks a URL-encoded route parameter.\n\n- **Parameters:**\n  - `key`: The name of the route parameter to track\n- **Returns:** A signal containing the decoded parameter value\n- **Throws:** Error if the parameter is not present in the current route\n\n### `routeQueryParam(key: string): Signal<string>`\n\nCreates a signal that tracks a URL-encoded query parameter.\n\n- **Parameters:**\n  - `key`: The name of the query parameter to track\n- **Returns:** A signal containing the decoded query parameter value\n- **Throws:** Error if the query parameter is not present in the current route\n\n### `routeData<T>(key: string): Signal<T>`\n\nCreates a signal that tracks a route data property.\n\n- **Type Parameters:**\n  - `T`: The type of the data property\n- **Parameters:**\n  - `key`: The name of the data property to track\n- **Returns:** A signal containing the route data value\n- **Throws:** Error if the data property is not present in the current route\n\n## URL Decoding\n\nAll functions automatically decode URL-encoded values using a safe decoder that:\n\n- Decodes standard URL encoding (e.g., `%20` → space)\n- Handles special characters (e.g., `%2F` → `/`, `%3D` → `=`)\n- Decodes Unicode characters (e.g., `%E2%9C%93` → `✓`)\n- Safely handles malformed encoding (logs warning and returns original string)\n\n## Error Handling\n\nAll functions throw an error if the requested parameter/data is not present in the route. This helps catch configuration errors early:\n\n```typescript\n// If route doesn't have an 'id' parameter\nconst userId = routeParam('id'); // ❌ Throws: \"id is not in route.\"\n\n// If route doesn't have a 'search' query parameter\nconst search = routeQueryParam('search'); // ❌ Throws: \"Query parameter \"search\" is not in route.\"\n\n// If route data doesn't have a 'title' property\nconst title = routeData('title'); // ❌ Throws: \"Route data property \"title\" is not in route.\"\n```\n\n## Requirements\n\n- Angular 19.0.0 or higher\n- RxJS 7.5.0 or higher\n\n## License\n\nMIT © [Daniel Schuba](https://github.com/DaSchTour)\n\n## Links\n\n- [Documentation](https://dasch.ng/libraries/route-signals.html)\n- [API Reference](https://dasch.ng/api/@dasch-ng/route-signals/)\n- [GitHub](https://github.com/DaSchTour/dasch-ng)\n- [Issues](https://github.com/DaSchTour/dasch-ng/issues)\n","readmeFilename":"README.md"}