# DataProviderKey

The `DataProviderKey<T>` utility provides type-safe navigation through composite data structures. Each property or index access extends the path, and the final key can be retrieved via `toString()` or the `path` property.

For a **single HTTP path string** (no dot-syntax), see [Endpoint](#docs/_misc/endpoint.md/endpoint).

## Principle

`DataProviderKey` uses a Proxy to intercept property access and build a cumulative path string. TypeScript infers the nested type at each level, so `myKey.items[0]` is correctly typed as `DataProviderKey<Item>` when `items` is `Item[]`.

## Usage

### Import

<sonic-code language="typescript">
  <template>
import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
  </template>
</sonic-code>

### Basic example

<sonic-code language="typescript">
  <template>
type Item = { id: string; name: string };
//
type Data = {
  items: Item[];
  count: number;
};
//
const myKey = new DataProviderKey&lt;Data&gt;("data").items[0];
// Equivalent to: new DataProviderKey&lt;Item&gt;("data.items.0")
myKey.toString();  // "data.items.0"
myKey.path;        // same value
// myKey is typed as DataProviderKey&lt;Item&gt;
  </template>
</sonic-code>

### Object property access

<sonic-code language="typescript">
  <template>
const key = new DataProviderKey&lt;Data&gt;("data");
const countKey = key.count;
countKey.path;        // "data.count"
countKey.toString();  // "data.count"
  </template>
</sonic-code>

### Array index access

<sonic-code language="typescript">
  <template>
const itemsKey = new DataProviderKey&lt;Data&gt;("data").items;
itemsKey.path;  // "data.items"
// itemsKey is DataProviderKey&lt;Item[]&gt;
//
const firstItem = itemsKey[0];
firstItem.path;  // "data.items.0"
// firstItem is DataProviderKey&lt;Item&gt;
  </template>
</sonic-code>

### Dynamic paths

Use placeholders `${prop}` or `{$prop}` in the path string. The path is resolved at runtime from the component's properties. The type remains declarative:

<sonic-code language="typescript">
  <template>
type User = { name: string; email: string };
//
// Path resolved from component.userIndex at runtime
@subscribe(new DataProviderKey&lt;User&gt;("users.${userIndex}"))
@state()
user: User | null = null;
  </template>
</sonic-code>

## Path retrieval

The final path is built by concatenating each accessed property with a dot:

- `new DataProviderKey<T>("base")` → `"base"`
- `key.prop` → `"base.prop"`
- `key.items[0]` → `"base.items.0"`

Use `toString()` or `path` to get the full path string:

<sonic-code language="typescript">
  <template>
const key = new DataProviderKey&lt;Data&gt;("data").count;
const pathString = key.toString();  // "data.count"
const pathProp = key.path;           // "data.count"
  </template>
</sonic-code>

## Use cases

- **Type-safe bindings**: paths for `@bind`, `@subscribe`, `@publish`
- **Dynamic paths**: reusable keys with `${...}` placeholders
- **Form fields**: form data paths with compile-time checking

## Integration with @subscribe and @publish

Use `DataProviderKey` with `@subscribe` (read-only) or `@publish` (write-only). The decorated property **must** match the key’s value type:

<sonic-code language="typescript">
  <template>
import { subscribe } from "@supersoniks/concorde/decorators";
import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
//
type FormData = { email: string };
const formKey = new DataProviderKey&lt;FormData&gt;("formData");
//
@customElement("user-form")
export class UserForm extends LitElement {
  @subscribe(formKey.email)
  @state()
  email = "";
//
  render() {
    return html`&lt;input .value=${this.email} @input=${(e) => this.email = (e.target as HTMLInputElement).value}&gt;`;
  }
}
  </template>
</sonic-code>

Both decorators support dynamic paths: `"base.${prop}"` in the constructor. A wrong property type (e.g. `number` for `DataProviderKey<string>`) is a TypeScript error.

## Notes

- Function properties are excluded from navigation (no `key.method()` chaining)
- Primitives have no navigable properties
- The `path` property and `toString()` are equivalent for retrieving the key
