# Frameworks

The script ships with a **bridge layer** (`bridge/framework.lua` and `bridge/inventory.lua`) that auto-detects which framework and inventory your server runs. No framework selection in config is required.

## Auto-detection

On resource start, the framework bridge looks for:

| Detected   | Resource start indicator                                                         |
| ---------- | -------------------------------------------------------------------------------- |
| ESX        | `es_extended` started, `ESX = exports['es_extended']:getSharedObject()` resolves |
| QBCore     | `qb-core` started, `QBCore = exports['qb-core']:GetCoreObject()` resolves        |
| QBox       | `qbx_core` started                                                               |
| Standalone | None of the above — uses the `cleanCashItem` inventory fallback                  |

The inventory bridge looks for:

| Detected      | Indicator                                                        |
| ------------- | ---------------------------------------------------------------- |
| ox\_inventory | `ox_inventory` started                                           |
| qb-inventory  | `qb-inventory` started                                           |
| ESX legacy    | Falls back to `xPlayer.addInventoryItem` / `removeInventoryItem` |

## ESX

Tested against **es\_extended** 1.10+. Money operations route through:

```lua
xPlayer.getInventoryItem(item).count
xPlayer.removeInventoryItem(item, count)
xPlayer.addInventoryItem(item, count)
```

If you're on **ox\_inventory + ESX**, the inventory bridge takes ox's path and bypasses the legacy ESX functions.

## QBCore

Tested against **qb-core** main branch. The bridge resolves `Player.PlayerData.citizenid` as the persistent identifier — that's what `tl_moneylaunder_progress.identifier` is keyed by, so XP/level survives character switches on multi-character servers.

## QBox

Tested against **qbx\_core**. API surface matches QBCore via the QBox compatibility layer, so the bridge uses the same code path.

## Standalone fallback

If no framework is detected:

* The player's `identifier` becomes their license (`license:xxx`).
* Money is exchanged via the `cleanCashItem` inventory (default `'cash'`) — the script removes dirty cash and adds clean cash one-for-one minus the wash rate / fee.
* No level persistence between framework migrations — if you later switch to ESX/QB, run a manual identifier remap.

## Switching framework after launch

If you migrate from one framework to another, **XP and level data won't follow** unless you remap identifiers in `tl_moneylaunder_progress`:

```sql
-- Example: ESX (steam:xxx) → QBCore (citizenid)
UPDATE tl_moneylaunder_progress p
JOIN players c ON c.license = p.identifier
SET p.identifier = c.citizenid;
```

Open a Discord ticket if you're not sure how to map your identifiers — we'll help write the query.

## Notifications

Independent of framework. Set `TL.Config.notifySystem` in `config.lua`:

* `'ox_lib'` (default) — uses `lib.notify`
* `'esx'` — uses `ESX.ShowNotification`
* `'qb'` — uses `QBCore.Functions.Notify`
* `'native'` — uses `BeginTextCommandThefeedPost` / `EndTextCommandThefeedPostTicker`


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://6ly-scripts.gitbook.io/product-docs/scripts/money-launder/frameworks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
