# 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). For **`DataProviderKey<APIConfiguration>`**, see [API configuration](/docs/misc/api-configuration).

## 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[]`.

In Lit demos, bind HTML attributes from a key’s **`.path`** (single source of truth), and use **`get` / `set` / `dp`** instead of `PublisherManager.get("…")`:

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

export const myFormKey = new DataProviderKey&lt;{ email: string }&gt;("myForm");
set(myFormKey, { email: "a@b.c" });

// template: formDataProvider=${myFormKey.path}
  </template>
</sonic-code>

## 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. See [Dynamic path placeholders](/docs/misc/dynamic-path) for resolution rules and `skipEmptyPlaceholder`. 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>

Dynamic keys are **not** supported by `get`, `set`, or `dp` — those APIs take a snapshot at call time with no component context. For dynamic paths use decorators (`@subscribe`, `@publish`, `@handle`) or **`sub(key)`** in Lit templates (resolves `${…}` from the host component). See [sub()](/docs/directives/sub).

### get / set / dp with static keys

For programmatic access, pass a `DataProviderKey` or a static path string. Dynamic placeholders are rejected:

<sonic-code language="typescript">
  <template>
import { dp, get, set } from "@supersoniks/concorde/utils";
import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
//
const counterKey = new DataProviderKey&lt;{ count: number }&gt;("myCounter");
//
set(counterKey, { count: 0 });
dp(counterKey.count).set(1);
get(counterKey);  // snapshot: { count: 1 }
  </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`, `@handle`
- **Dynamic paths**: reusable keys with `${...}` placeholders
- **Form fields**: form data paths with compile-time checking

## Integration with @subscribe, @publish and @handle

Use `DataProviderKey` with `@subscribe` (read-only), `@publish` (write-only), or `@handle` (method callback on assign). With `@subscribe` / `@publish`, the decorated property **must** match the key’s value type. With `@handle`, the method receives `(value: T)`.

<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>

These decorators support dynamic paths: `"base.${prop}"` in the constructor. A wrong property type (e.g. `number` for `DataProviderKey<string>`) is a TypeScript error. Resolution rules: [Dynamic path placeholders](/docs/misc/dynamic-path). See [@handle](/docs/decorators/handle) for method callbacks.

## 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
