[{"data":1,"prerenderedAt":1944},["ShallowReactive",2],{"navigation":3,"\u002Fdocs\u002Fv1\u002Fserver\u002Fsetup":121,"\u002Fdocs\u002Fv1\u002Fserver\u002Fsetup-surround":1939},[4],{"title":5,"path":6,"stem":7,"children":8,"page":44},"Docs","\u002Fdocs","docs",[9],{"title":10,"path":11,"stem":12,"children":13,"page":44},"V1","\u002Fdocs\u002Fv1","docs\u002Fv1",[14,18,22,45,58,80,93,111],{"title":15,"path":16,"stem":17},"Introduction","\u002Fdocs\u002Fv1\u002Fintroduction","docs\u002Fv1\u002F0.introduction",{"title":19,"path":20,"stem":21},"How It Works","\u002Fdocs\u002Fv1\u002Fhow-it-works","docs\u002Fv1\u002F1.how-it-works",{"title":23,"icon":24,"path":25,"stem":26,"children":27,"page":44},"Client","i-lucide-laptop","\u002Fdocs\u002Fv1\u002Fclient","docs\u002Fv1\u002F2.client",[28,32,36,40],{"title":29,"path":30,"stem":31},"Client Setup","\u002Fdocs\u002Fv1\u002Fclient\u002Fsetup","docs\u002Fv1\u002F2.client\u002F1.setup",{"title":33,"path":34,"stem":35},"Syncing","\u002Fdocs\u002Fv1\u002Fclient\u002Fsyncing","docs\u002Fv1\u002F2.client\u002F2.syncing",{"title":37,"path":38,"stem":39},"Events","\u002Fdocs\u002Fv1\u002Fclient\u002Fevents","docs\u002Fv1\u002F2.client\u002F3.events",{"title":41,"path":42,"stem":43},"Configuration","\u002Fdocs\u002Fv1\u002Fclient\u002Fconfiguration","docs\u002Fv1\u002F2.client\u002F4.configuration",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"Server","i-lucide-server","\u002Fdocs\u002Fv1\u002Fserver","docs\u002Fv1\u002F3.server",[51,55],{"title":52,"path":53,"stem":54},"Server Setup","\u002Fdocs\u002Fv1\u002Fserver\u002Fsetup","docs\u002Fv1\u002F3.server\u002F1.setup",{"title":41,"path":56,"stem":57},"\u002Fdocs\u002Fv1\u002Fserver\u002Fconfiguration","docs\u002Fv1\u002F3.server\u002F2.configuration",{"title":59,"icon":60,"badge":61,"path":62,"stem":63,"children":64,"page":44},"NativePHP","i-lucide-smartphone","Commercial","\u002Fdocs\u002Fv1\u002Fnativephp","docs\u002Fv1\u002F4.nativephp",[65,69,73,76],{"title":66,"path":67,"stem":68},"Overview","\u002Fdocs\u002Fv1\u002Fnativephp\u002Foverview","docs\u002Fv1\u002F4.nativephp\u002F1.overview",{"title":70,"path":71,"stem":72},"Setup","\u002Fdocs\u002Fv1\u002Fnativephp\u002Fsetup","docs\u002Fv1\u002F4.nativephp\u002F2.setup",{"title":41,"path":74,"stem":75},"\u002Fdocs\u002Fv1\u002Fnativephp\u002Fconfiguration","docs\u002Fv1\u002F4.nativephp\u002F3.configuration",{"title":77,"path":78,"stem":79},"Lifecycle & Events","\u002Fdocs\u002Fv1\u002Fnativephp\u002Flifecycle","docs\u002Fv1\u002F4.nativephp\u002F4.lifecycle",{"title":81,"icon":82,"badge":61,"path":83,"stem":84,"children":85,"page":44},"Server Pro","i-lucide-bar-chart-2","\u002Fdocs\u002Fv1\u002Fserver-pro","docs\u002Fv1\u002F5.server-pro",[86,89],{"title":66,"path":87,"stem":88},"\u002Fdocs\u002Fv1\u002Fserver-pro\u002Foverview","docs\u002Fv1\u002F5.server-pro\u002F1.overview",{"title":90,"path":91,"stem":92},"Features","\u002Fdocs\u002Fv1\u002Fserver-pro\u002Ffeatures","docs\u002Fv1\u002F5.server-pro\u002F2.features",{"title":94,"icon":95,"path":96,"stem":97,"children":98,"page":44},"Advanced","i-lucide-settings-2","\u002Fdocs\u002Fv1\u002Fadvanced","docs\u002Fv1\u002F6.advanced",[99,103,107],{"title":100,"path":101,"stem":102},"Service Container","\u002Fdocs\u002Fv1\u002Fadvanced\u002Fservice-container","docs\u002Fv1\u002F6.advanced\u002F0.service-container",{"title":104,"path":105,"stem":106},"Conflict Resolution","\u002Fdocs\u002Fv1\u002Fadvanced\u002Fconflict-resolution","docs\u002Fv1\u002F6.advanced\u002F1.conflict-resolution",{"title":108,"path":109,"stem":110},"Payload Mapping","\u002Fdocs\u002Fv1\u002Fadvanced\u002Fpayload-mapping","docs\u002Fv1\u002F6.advanced\u002F2.payload-mapping",{"title":112,"icon":113,"path":114,"stem":115,"children":116,"page":44},"Examples","i-lucide-book-open","\u002Fdocs\u002Fv1\u002Fexamples","docs\u002Fv1\u002F7.examples",[117],{"title":118,"path":119,"stem":120},"Task Manager","\u002Fdocs\u002Fv1\u002Fexamples\u002Ftask-manager","docs\u002Fv1\u002F7.examples\u002F1.task-manager",{"id":122,"title":52,"body":123,"description":1932,"extension":1933,"links":1934,"meta":1935,"navigation":345,"path":53,"seo":1936,"stem":54,"__hash__":1938},"docs\u002Fdocs\u002Fv1\u002F3.server\u002F1.setup.md",{"type":124,"value":125,"toc":1922},"minimark",[126,139,142,147,194,205,207,211,214,266,269,298,515,518,520,524,527,532,535,542,606,611,710,720,723,1341,1345,1352,1782,1784,1788,1795,1871,1873,1877,1880,1918],[127,128,129,133,134,138],"p",{},[130,131,132],"code",{},"tether\u002Fserver"," installs on your ",[135,136,137],"strong",{},"Laravel server application",". It exposes authoritative sync HTTP endpoints, applies incoming offline client mutations to your Eloquent models, detects conflicts, and generates pull snapshots for client reconciliation.",[140,141],"hr",{},[143,144,146],"h2",{"id":145},"installation","Installation",[148,149,154],"pre",{"className":150,"code":151,"language":152,"meta":153,"style":153},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","composer require tether\u002Fserver\nphp artisan tether:install-server\nphp artisan migrate\n","bash","",[130,155,156,172,184],{"__ignoreMap":153},[157,158,161,165,169],"span",{"class":159,"line":160},"line",1,[157,162,164],{"class":163},"sBMFI","composer",[157,166,168],{"class":167},"sfazB"," require",[157,170,171],{"class":167}," tether\u002Fserver\n",[157,173,175,178,181],{"class":159,"line":174},2,[157,176,177],{"class":163},"php",[157,179,180],{"class":167}," artisan",[157,182,183],{"class":167}," tether:install-server\n",[157,185,187,189,191],{"class":159,"line":186},3,[157,188,177],{"class":163},[157,190,180],{"class":167},[157,192,193],{"class":167}," migrate\n",[127,195,196,197,200,201,204],{},"The installer publishes ",[130,198,199],{},"config\u002Ftether-server.php",". The migration creates ",[130,202,203],{},"tether_server_mutations",", which stores applied push mutations for idempotency and diagnostics.",[140,206],{},[143,208,210],{"id":209},"sync-endpoints","Sync endpoints",[127,212,213],{},"Two routes are registered automatically:",[215,216,217,233],"table",{},[218,219,220],"thead",{},[221,222,223,227,230],"tr",{},[224,225,226],"th",{},"Method",[224,228,229],{},"Path",[224,231,232],{},"Purpose",[234,235,236,252],"tbody",{},[221,237,238,244,249],{},[239,240,241],"td",{},[130,242,243],{},"POST",[239,245,246],{},[130,247,248],{},"\u002Ftether\u002Fpush",[239,250,251],{},"Receive and apply a batch of client mutations",[221,253,254,258,263],{},[239,255,256],{},[130,257,243],{},[239,259,260],{},[130,261,262],{},"\u002Ftether\u002Fpull",[239,264,265],{},"Return server state since a given cursor",[127,267,268],{},"To register routes manually instead:",[148,270,273],{"className":271,"code":272,"language":177,"meta":153,"style":153},"language-php shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F config\u002Ftether-server.php\n'register_routes' => false,\n",[130,274,275,281],{"__ignoreMap":153},[157,276,277],{"class":159,"line":160},[157,278,280],{"class":279},"sHwdD","\u002F\u002F config\u002Ftether-server.php\n",[157,282,283,287,290,292,295],{"class":159,"line":174},[157,284,286],{"class":285},"sMK4o","'",[157,288,289],{"class":167},"register_routes",[157,291,286],{"class":285},[157,293,294],{"class":285}," =>",[157,296,297],{"class":285}," false,\n",[148,299,301],{"className":271,"code":300,"language":177,"meta":153,"style":153},"\u002F\u002F routes\u002Fapi.php\nuse Tether\\Server\\Http\\Controllers\\SyncController;\n\nRoute::middleware(['api', 'auth:sanctum'])\n    ->prefix('tether')\n    ->group(function () {\n        Route::post('\u002Fpush', [SyncController::class, 'push']);\n        Route::post('\u002Fpull', [SyncController::class, 'pull']);\n    });\n",[130,302,303,308,341,347,384,406,426,470,509],{"__ignoreMap":153},[157,304,305],{"class":159,"line":160},[157,306,307],{"class":279},"\u002F\u002F routes\u002Fapi.php\n",[157,309,310,314,318,321,323,325,328,330,333,335,338],{"class":159,"line":174},[157,311,313],{"class":312},"sbssI","use",[157,315,317],{"class":316},"sTEyZ"," Tether",[157,319,320],{"class":285},"\\",[157,322,46],{"class":316},[157,324,320],{"class":285},[157,326,327],{"class":316},"Http",[157,329,320],{"class":285},[157,331,332],{"class":316},"Controllers",[157,334,320],{"class":285},[157,336,337],{"class":316},"SyncController",[157,339,340],{"class":285},";\n",[157,342,343],{"class":159,"line":186},[157,344,346],{"emptyLinePlaceholder":345},true,"\n",[157,348,350,353,356,360,363,365,368,370,373,376,379,381],{"class":159,"line":349},4,[157,351,352],{"class":163},"Route",[157,354,355],{"class":285},"::",[157,357,359],{"class":358},"s2Zo4","middleware",[157,361,362],{"class":285},"([",[157,364,286],{"class":285},[157,366,367],{"class":167},"api",[157,369,286],{"class":285},[157,371,372],{"class":285},",",[157,374,375],{"class":285}," '",[157,377,378],{"class":167},"auth:sanctum",[157,380,286],{"class":285},[157,382,383],{"class":285},"])\n",[157,385,387,390,393,396,398,401,403],{"class":159,"line":386},5,[157,388,389],{"class":285},"    ->",[157,391,392],{"class":358},"prefix",[157,394,395],{"class":285},"(",[157,397,286],{"class":285},[157,399,400],{"class":167},"tether",[157,402,286],{"class":285},[157,404,405],{"class":285},")\n",[157,407,409,411,414,416,420,423],{"class":159,"line":408},6,[157,410,389],{"class":285},[157,412,413],{"class":358},"group",[157,415,395],{"class":285},[157,417,419],{"class":418},"spNyl","function",[157,421,422],{"class":285}," ()",[157,424,425],{"class":285}," {\n",[157,427,429,432,434,437,439,441,444,446,448,451,453,455,458,460,462,465,467],{"class":159,"line":428},7,[157,430,431],{"class":163},"        Route",[157,433,355],{"class":285},[157,435,436],{"class":358},"post",[157,438,395],{"class":285},[157,440,286],{"class":285},[157,442,443],{"class":167},"\u002Fpush",[157,445,286],{"class":285},[157,447,372],{"class":285},[157,449,450],{"class":285}," [",[157,452,337],{"class":163},[157,454,355],{"class":285},[157,456,457],{"class":312},"class",[157,459,372],{"class":285},[157,461,375],{"class":285},[157,463,464],{"class":167},"push",[157,466,286],{"class":285},[157,468,469],{"class":285},"]);\n",[157,471,473,475,477,479,481,483,486,488,490,492,494,496,498,500,502,505,507],{"class":159,"line":472},8,[157,474,431],{"class":163},[157,476,355],{"class":285},[157,478,436],{"class":358},[157,480,395],{"class":285},[157,482,286],{"class":285},[157,484,485],{"class":167},"\u002Fpull",[157,487,286],{"class":285},[157,489,372],{"class":285},[157,491,450],{"class":285},[157,493,337],{"class":163},[157,495,355],{"class":285},[157,497,457],{"class":312},[157,499,372],{"class":285},[157,501,375],{"class":285},[157,503,504],{"class":167},"pull",[157,506,286],{"class":285},[157,508,469],{"class":285},[157,510,512],{"class":159,"line":511},9,[157,513,514],{"class":285},"    });\n",[127,516,517],{},"Package doesn't require any middleware to operate, but you should add authentication middleware in production to protect the endpoints from unauthenticated access.",[140,519],{},[143,521,523],{"id":522},"registering-models","Registering models",[127,525,526],{},"The server must know which models participate in sync. Choose one of two approaches.",[528,529,531],"h3",{"id":530},"option-a-syncable-trait","Option A - Syncable trait",[127,533,534],{},"Add the trait to any model you want clients to sync. It auto-registers with sensible defaults.",[127,536,537,538,541],{},"Every server-side Syncable model must also use standard Laravel timestamps. Tether relies on ",[130,539,540],{},"updated_at"," for pull cursors and default conflict checks.",[148,543,545],{"className":271,"code":544,"language":177,"meta":153,"style":153},"use Tether\\Server\\Traits\\Syncable;\n\nclass Task extends Model\n{\n    use Syncable;\n}\n",[130,546,547,569,573,586,591,601],{"__ignoreMap":153},[157,548,549,551,553,555,557,559,562,564,567],{"class":159,"line":160},[157,550,313],{"class":312},[157,552,317],{"class":316},[157,554,320],{"class":285},[157,556,46],{"class":316},[157,558,320],{"class":285},[157,560,561],{"class":316},"Traits",[157,563,320],{"class":285},[157,565,566],{"class":316},"Syncable",[157,568,340],{"class":285},[157,570,571],{"class":159,"line":174},[157,572,346],{"emptyLinePlaceholder":345},[157,574,575,577,580,583],{"class":159,"line":186},[157,576,457],{"class":418},[157,578,579],{"class":163}," Task",[157,581,582],{"class":418}," extends",[157,584,585],{"class":163}," Model\n",[157,587,588],{"class":159,"line":349},[157,589,590],{"class":285},"{\n",[157,592,593,596,599],{"class":159,"line":386},[157,594,595],{"class":312},"    use",[157,597,598],{"class":316}," Syncable",[157,600,340],{"class":285},[157,602,603],{"class":159,"line":408},[157,604,605],{"class":285},"}\n",[127,607,608],{},[135,609,610],{},"Control which fields clients can write:",[148,612,614],{"className":271,"code":613,"language":177,"meta":153,"style":153},"\u002F\u002F Whitelist - only these fields are accepted from mutation payloads\nprotected array $syncable = ['title', 'status', 'due_date'];\n\n\u002F\u002F Blacklist - all fillable fields except these\nprotected array $syncableExcept = ['internal_notes', 'admin_flags'];\n",[130,615,616,621,668,672,677],{"__ignoreMap":153},[157,617,618],{"class":159,"line":160},[157,619,620],{"class":279},"\u002F\u002F Whitelist - only these fields are accepted from mutation payloads\n",[157,622,623,626,629,632,635,638,640,642,645,647,649,651,654,656,658,660,663,665],{"class":159,"line":174},[157,624,625],{"class":418},"protected",[157,627,628],{"class":312}," array",[157,630,631],{"class":285}," $",[157,633,634],{"class":316},"syncable ",[157,636,637],{"class":285},"=",[157,639,450],{"class":285},[157,641,286],{"class":285},[157,643,644],{"class":167},"title",[157,646,286],{"class":285},[157,648,372],{"class":285},[157,650,375],{"class":285},[157,652,653],{"class":167},"status",[157,655,286],{"class":285},[157,657,372],{"class":285},[157,659,375],{"class":285},[157,661,662],{"class":167},"due_date",[157,664,286],{"class":285},[157,666,667],{"class":285},"];\n",[157,669,670],{"class":159,"line":186},[157,671,346],{"emptyLinePlaceholder":345},[157,673,674],{"class":159,"line":349},[157,675,676],{"class":279},"\u002F\u002F Blacklist - all fillable fields except these\n",[157,678,679,681,683,685,688,690,692,694,697,699,701,703,706,708],{"class":159,"line":386},[157,680,625],{"class":418},[157,682,628],{"class":312},[157,684,631],{"class":285},[157,686,687],{"class":316},"syncableExcept ",[157,689,637],{"class":285},[157,691,450],{"class":285},[157,693,286],{"class":285},[157,695,696],{"class":167},"internal_notes",[157,698,286],{"class":285},[157,700,372],{"class":285},[157,702,375],{"class":285},[157,704,705],{"class":167},"admin_flags",[157,707,286],{"class":285},[157,709,667],{"class":285},[127,711,712,715,716,719],{},[130,713,714],{},"$syncable"," takes precedence if both are set. Setting ",[130,717,718],{},"$syncable = ['*']"," disables filtering.",[127,721,722],{},"Define callbacks as static methods on the model:",[148,724,726],{"className":271,"code":725,"language":177,"meta":153,"style":153},"use Tether\\Server\\Traits\\Syncable;\nuse Tether\\Core\\Conflict\\ConflictResolution;\nuse Tether\\Core\\Mutation\\Mutation;\nuse Tether\\Core\\Sync\\Snapshot;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Http\\Request;\n\nclass Task extends Model\n{\n    use Syncable;\n\n    \u002F\u002F Restrict which records are returned during pull\n    public static function tetherScope(Builder $query, string $clientId, Request $request): Builder\n    {\n        return $query->where('user_id', $request->user()->id);\n    }\n\n    \u002F\u002F Amend snapshot payload before sending to client\n    public static function tetherPullSnapshotMapper(Snapshot $snapshot, Model $row): Snapshot\n    {\n        return $snapshot->withPayload(Arr::except($snapshot->payload, ['internal_notes']));\n    }\n\n    \u002F\u002F Amend incoming mutation payload before applying\n    public static function tetherPushMutationMapper(\n        Mutation $mutation,\n        Request $request,\n    ): Mutation {\n        return $mutation->withPayload(array_merge($mutation->getPayload(), ['user_id' => $request->user()->id]));\n    }\n\n    \u002F\u002F Custom conflict resolution (client wins in this example)\n    public static function tetherConflictResolver(Mutation $mutation, Model $record, Request $request): ConflictResolution\n    {\n        return ConflictResolution::apply($mutation->getPayload());\n    }\n}\n",[130,727,728,748,771,792,814,838,859,876,880,890,895,904,909,915,965,971,1017,1023,1028,1034,1070,1075,1122,1127,1132,1138,1153,1167,1179,1190,1244,1249,1254,1260,1302,1307,1331,1336],{"__ignoreMap":153},[157,729,730,732,734,736,738,740,742,744,746],{"class":159,"line":160},[157,731,313],{"class":312},[157,733,317],{"class":316},[157,735,320],{"class":285},[157,737,46],{"class":316},[157,739,320],{"class":285},[157,741,561],{"class":316},[157,743,320],{"class":285},[157,745,566],{"class":316},[157,747,340],{"class":285},[157,749,750,752,754,756,759,761,764,766,769],{"class":159,"line":174},[157,751,313],{"class":312},[157,753,317],{"class":316},[157,755,320],{"class":285},[157,757,758],{"class":316},"Core",[157,760,320],{"class":285},[157,762,763],{"class":316},"Conflict",[157,765,320],{"class":285},[157,767,768],{"class":316},"ConflictResolution",[157,770,340],{"class":285},[157,772,773,775,777,779,781,783,786,788,790],{"class":159,"line":186},[157,774,313],{"class":312},[157,776,317],{"class":316},[157,778,320],{"class":285},[157,780,758],{"class":316},[157,782,320],{"class":285},[157,784,785],{"class":316},"Mutation",[157,787,320],{"class":285},[157,789,785],{"class":316},[157,791,340],{"class":285},[157,793,794,796,798,800,802,804,807,809,812],{"class":159,"line":349},[157,795,313],{"class":312},[157,797,317],{"class":316},[157,799,320],{"class":285},[157,801,758],{"class":316},[157,803,320],{"class":285},[157,805,806],{"class":316},"Sync",[157,808,320],{"class":285},[157,810,811],{"class":316},"Snapshot",[157,813,340],{"class":285},[157,815,816,818,821,823,826,828,831,833,836],{"class":159,"line":386},[157,817,313],{"class":312},[157,819,820],{"class":316}," Illuminate",[157,822,320],{"class":285},[157,824,825],{"class":316},"Database",[157,827,320],{"class":285},[157,829,830],{"class":316},"Eloquent",[157,832,320],{"class":285},[157,834,835],{"class":316},"Builder",[157,837,340],{"class":285},[157,839,840,842,844,846,848,850,852,854,857],{"class":159,"line":408},[157,841,313],{"class":312},[157,843,820],{"class":316},[157,845,320],{"class":285},[157,847,825],{"class":316},[157,849,320],{"class":285},[157,851,830],{"class":316},[157,853,320],{"class":285},[157,855,856],{"class":316},"Model",[157,858,340],{"class":285},[157,860,861,863,865,867,869,871,874],{"class":159,"line":428},[157,862,313],{"class":312},[157,864,820],{"class":316},[157,866,320],{"class":285},[157,868,327],{"class":316},[157,870,320],{"class":285},[157,872,873],{"class":316},"Request",[157,875,340],{"class":285},[157,877,878],{"class":159,"line":472},[157,879,346],{"emptyLinePlaceholder":345},[157,881,882,884,886,888],{"class":159,"line":511},[157,883,457],{"class":418},[157,885,579],{"class":163},[157,887,582],{"class":418},[157,889,585],{"class":163},[157,891,893],{"class":159,"line":892},10,[157,894,590],{"class":285},[157,896,898,900,902],{"class":159,"line":897},11,[157,899,595],{"class":312},[157,901,598],{"class":316},[157,903,340],{"class":285},[157,905,907],{"class":159,"line":906},12,[157,908,346],{"emptyLinePlaceholder":345},[157,910,912],{"class":159,"line":911},13,[157,913,914],{"class":279},"    \u002F\u002F Restrict which records are returned during pull\n",[157,916,918,921,924,927,930,932,934,936,939,941,944,946,949,951,954,956,959,962],{"class":159,"line":917},14,[157,919,920],{"class":418},"    public",[157,922,923],{"class":418}," static",[157,925,926],{"class":418}," function",[157,928,929],{"class":358}," tetherScope",[157,931,395],{"class":285},[157,933,835],{"class":163},[157,935,631],{"class":285},[157,937,938],{"class":316},"query",[157,940,372],{"class":285},[157,942,943],{"class":312}," string",[157,945,631],{"class":285},[157,947,948],{"class":316},"clientId",[157,950,372],{"class":285},[157,952,953],{"class":163}," Request",[157,955,631],{"class":285},[157,957,958],{"class":316},"request",[157,960,961],{"class":285},"):",[157,963,964],{"class":163}," Builder\n",[157,966,968],{"class":159,"line":967},15,[157,969,970],{"class":285},"    {\n",[157,972,974,978,980,982,985,988,990,992,995,997,999,1001,1003,1005,1008,1011,1014],{"class":159,"line":973},16,[157,975,977],{"class":976},"s7zQu","        return",[157,979,631],{"class":285},[157,981,938],{"class":316},[157,983,984],{"class":285},"->",[157,986,987],{"class":358},"where",[157,989,395],{"class":285},[157,991,286],{"class":285},[157,993,994],{"class":167},"user_id",[157,996,286],{"class":285},[157,998,372],{"class":285},[157,1000,631],{"class":285},[157,1002,958],{"class":316},[157,1004,984],{"class":285},[157,1006,1007],{"class":358},"user",[157,1009,1010],{"class":285},"()->",[157,1012,1013],{"class":316},"id",[157,1015,1016],{"class":285},");\n",[157,1018,1020],{"class":159,"line":1019},17,[157,1021,1022],{"class":285},"    }\n",[157,1024,1026],{"class":159,"line":1025},18,[157,1027,346],{"emptyLinePlaceholder":345},[157,1029,1031],{"class":159,"line":1030},19,[157,1032,1033],{"class":279},"    \u002F\u002F Amend snapshot payload before sending to client\n",[157,1035,1037,1039,1041,1043,1046,1048,1050,1052,1055,1057,1060,1062,1065,1067],{"class":159,"line":1036},20,[157,1038,920],{"class":418},[157,1040,923],{"class":418},[157,1042,926],{"class":418},[157,1044,1045],{"class":358}," tetherPullSnapshotMapper",[157,1047,395],{"class":285},[157,1049,811],{"class":163},[157,1051,631],{"class":285},[157,1053,1054],{"class":316},"snapshot",[157,1056,372],{"class":285},[157,1058,1059],{"class":163}," Model",[157,1061,631],{"class":285},[157,1063,1064],{"class":316},"row",[157,1066,961],{"class":285},[157,1068,1069],{"class":163}," Snapshot\n",[157,1071,1073],{"class":159,"line":1072},21,[157,1074,970],{"class":285},[157,1076,1078,1080,1082,1084,1086,1089,1091,1094,1096,1099,1102,1104,1106,1109,1111,1113,1115,1117,1119],{"class":159,"line":1077},22,[157,1079,977],{"class":976},[157,1081,631],{"class":285},[157,1083,1054],{"class":316},[157,1085,984],{"class":285},[157,1087,1088],{"class":358},"withPayload",[157,1090,395],{"class":285},[157,1092,1093],{"class":163},"Arr",[157,1095,355],{"class":285},[157,1097,1098],{"class":358},"except",[157,1100,1101],{"class":285},"($",[157,1103,1054],{"class":316},[157,1105,984],{"class":285},[157,1107,1108],{"class":316},"payload",[157,1110,372],{"class":285},[157,1112,450],{"class":285},[157,1114,286],{"class":285},[157,1116,696],{"class":167},[157,1118,286],{"class":285},[157,1120,1121],{"class":285},"]));\n",[157,1123,1125],{"class":159,"line":1124},23,[157,1126,1022],{"class":285},[157,1128,1130],{"class":159,"line":1129},24,[157,1131,346],{"emptyLinePlaceholder":345},[157,1133,1135],{"class":159,"line":1134},25,[157,1136,1137],{"class":279},"    \u002F\u002F Amend incoming mutation payload before applying\n",[157,1139,1141,1143,1145,1147,1150],{"class":159,"line":1140},26,[157,1142,920],{"class":418},[157,1144,923],{"class":418},[157,1146,926],{"class":418},[157,1148,1149],{"class":358}," tetherPushMutationMapper",[157,1151,1152],{"class":285},"(\n",[157,1154,1156,1159,1161,1164],{"class":159,"line":1155},27,[157,1157,1158],{"class":163},"        Mutation",[157,1160,631],{"class":285},[157,1162,1163],{"class":316},"mutation",[157,1165,1166],{"class":285},",\n",[157,1168,1170,1173,1175,1177],{"class":159,"line":1169},28,[157,1171,1172],{"class":163},"        Request",[157,1174,631],{"class":285},[157,1176,958],{"class":316},[157,1178,1166],{"class":285},[157,1180,1182,1185,1188],{"class":159,"line":1181},29,[157,1183,1184],{"class":285},"    ):",[157,1186,1187],{"class":163}," Mutation",[157,1189,425],{"class":285},[157,1191,1193,1195,1197,1199,1201,1203,1205,1208,1210,1212,1214,1217,1220,1222,1224,1226,1228,1230,1232,1234,1236,1238,1240,1242],{"class":159,"line":1192},30,[157,1194,977],{"class":976},[157,1196,631],{"class":285},[157,1198,1163],{"class":316},[157,1200,984],{"class":285},[157,1202,1088],{"class":358},[157,1204,395],{"class":285},[157,1206,1207],{"class":358},"array_merge",[157,1209,1101],{"class":285},[157,1211,1163],{"class":316},[157,1213,984],{"class":285},[157,1215,1216],{"class":358},"getPayload",[157,1218,1219],{"class":285},"(),",[157,1221,450],{"class":285},[157,1223,286],{"class":285},[157,1225,994],{"class":167},[157,1227,286],{"class":285},[157,1229,294],{"class":285},[157,1231,631],{"class":285},[157,1233,958],{"class":316},[157,1235,984],{"class":285},[157,1237,1007],{"class":358},[157,1239,1010],{"class":285},[157,1241,1013],{"class":316},[157,1243,1121],{"class":285},[157,1245,1247],{"class":159,"line":1246},31,[157,1248,1022],{"class":285},[157,1250,1252],{"class":159,"line":1251},32,[157,1253,346],{"emptyLinePlaceholder":345},[157,1255,1257],{"class":159,"line":1256},33,[157,1258,1259],{"class":279},"    \u002F\u002F Custom conflict resolution (client wins in this example)\n",[157,1261,1263,1265,1267,1269,1272,1274,1276,1278,1280,1282,1284,1286,1289,1291,1293,1295,1297,1299],{"class":159,"line":1262},34,[157,1264,920],{"class":418},[157,1266,923],{"class":418},[157,1268,926],{"class":418},[157,1270,1271],{"class":358}," tetherConflictResolver",[157,1273,395],{"class":285},[157,1275,785],{"class":163},[157,1277,631],{"class":285},[157,1279,1163],{"class":316},[157,1281,372],{"class":285},[157,1283,1059],{"class":163},[157,1285,631],{"class":285},[157,1287,1288],{"class":316},"record",[157,1290,372],{"class":285},[157,1292,953],{"class":163},[157,1294,631],{"class":285},[157,1296,958],{"class":316},[157,1298,961],{"class":285},[157,1300,1301],{"class":163}," ConflictResolution\n",[157,1303,1305],{"class":159,"line":1304},35,[157,1306,970],{"class":285},[157,1308,1310,1312,1315,1317,1320,1322,1324,1326,1328],{"class":159,"line":1309},36,[157,1311,977],{"class":976},[157,1313,1314],{"class":163}," ConflictResolution",[157,1316,355],{"class":285},[157,1318,1319],{"class":358},"apply",[157,1321,1101],{"class":285},[157,1323,1163],{"class":316},[157,1325,984],{"class":285},[157,1327,1216],{"class":358},[157,1329,1330],{"class":285},"());\n",[157,1332,1334],{"class":159,"line":1333},37,[157,1335,1022],{"class":285},[157,1337,1339],{"class":159,"line":1338},38,[157,1340,605],{"class":285},[528,1342,1344],{"id":1343},"option-b-syncregistry-in-a-service-provider","Option B - SyncRegistry in a service provider",[127,1346,1347,1348,1351],{},"For more explicit control, register models via ",[130,1349,1350],{},"SyncRegistry"," in a service provider. This takes precedence over the trait. You don't need to use the trait if you register the model this way.",[148,1353,1355],{"className":271,"code":1354,"language":177,"meta":153,"style":153},"use Tether\\Server\\SyncRegistry;\nuse Tether\\Core\\Conflict\\ConflictResolution;\nuse Tether\\Core\\Mutation\\Mutation;\nuse Tether\\Core\\Sync\\Snapshot;\nuse Illuminate\\Support\\Arr;\n\npublic function boot(): void\n{\n    app(SyncRegistry::class)->register(\n        modelClass: Task::class,\n        scope: fn ($query, $clientId, $request) => $query->where('user_id', $request->user()->id),\n        pullSnapshotMapper: fn (Snapshot $snapshot, $row) => $snapshot->withPayload(Arr::except($snapshot->payload, ['internal_notes'])),\n        pushMutationMapper: fn (Mutation $mutation, Request $request) => $mutation->withPayload(array_merge(\n            $mutation->getPayload(),\n            ['user_id' => $request->user()->id]\n        )),\n        conflictResolver: fn (Mutation $mutation, $record, $request) => ConflictResolution::reject(),\n    );\n}\n",[130,1356,1357,1373,1393,1413,1433,1450,1454,1470,1474,1495,1511,1576,1641,1684,1698,1726,1731,1773,1778],{"__ignoreMap":153},[157,1358,1359,1361,1363,1365,1367,1369,1371],{"class":159,"line":160},[157,1360,313],{"class":312},[157,1362,317],{"class":316},[157,1364,320],{"class":285},[157,1366,46],{"class":316},[157,1368,320],{"class":285},[157,1370,1350],{"class":316},[157,1372,340],{"class":285},[157,1374,1375,1377,1379,1381,1383,1385,1387,1389,1391],{"class":159,"line":174},[157,1376,313],{"class":312},[157,1378,317],{"class":316},[157,1380,320],{"class":285},[157,1382,758],{"class":316},[157,1384,320],{"class":285},[157,1386,763],{"class":316},[157,1388,320],{"class":285},[157,1390,768],{"class":316},[157,1392,340],{"class":285},[157,1394,1395,1397,1399,1401,1403,1405,1407,1409,1411],{"class":159,"line":186},[157,1396,313],{"class":312},[157,1398,317],{"class":316},[157,1400,320],{"class":285},[157,1402,758],{"class":316},[157,1404,320],{"class":285},[157,1406,785],{"class":316},[157,1408,320],{"class":285},[157,1410,785],{"class":316},[157,1412,340],{"class":285},[157,1414,1415,1417,1419,1421,1423,1425,1427,1429,1431],{"class":159,"line":349},[157,1416,313],{"class":312},[157,1418,317],{"class":316},[157,1420,320],{"class":285},[157,1422,758],{"class":316},[157,1424,320],{"class":285},[157,1426,806],{"class":316},[157,1428,320],{"class":285},[157,1430,811],{"class":316},[157,1432,340],{"class":285},[157,1434,1435,1437,1439,1441,1444,1446,1448],{"class":159,"line":386},[157,1436,313],{"class":312},[157,1438,820],{"class":316},[157,1440,320],{"class":285},[157,1442,1443],{"class":316},"Support",[157,1445,320],{"class":285},[157,1447,1093],{"class":316},[157,1449,340],{"class":285},[157,1451,1452],{"class":159,"line":408},[157,1453,346],{"emptyLinePlaceholder":345},[157,1455,1456,1459,1461,1464,1467],{"class":159,"line":428},[157,1457,1458],{"class":418},"public",[157,1460,926],{"class":418},[157,1462,1463],{"class":358}," boot",[157,1465,1466],{"class":285},"():",[157,1468,1469],{"class":312}," void\n",[157,1471,1472],{"class":159,"line":472},[157,1473,590],{"class":285},[157,1475,1476,1479,1481,1483,1485,1487,1490,1493],{"class":159,"line":511},[157,1477,1478],{"class":358},"    app",[157,1480,395],{"class":285},[157,1482,1350],{"class":163},[157,1484,355],{"class":285},[157,1486,457],{"class":312},[157,1488,1489],{"class":285},")->",[157,1491,1492],{"class":358},"register",[157,1494,1152],{"class":285},[157,1496,1497,1500,1503,1505,1507,1509],{"class":159,"line":892},[157,1498,1499],{"class":163},"        modelClass",[157,1501,1502],{"class":285},":",[157,1504,579],{"class":163},[157,1506,355],{"class":285},[157,1508,457],{"class":312},[157,1510,1166],{"class":285},[157,1512,1513,1516,1518,1521,1524,1526,1528,1530,1532,1534,1536,1538,1541,1543,1545,1547,1549,1551,1553,1555,1557,1559,1561,1563,1565,1567,1569,1571,1573],{"class":159,"line":897},[157,1514,1515],{"class":163},"        scope",[157,1517,1502],{"class":285},[157,1519,1520],{"class":418}," fn",[157,1522,1523],{"class":285}," ($",[157,1525,938],{"class":316},[157,1527,372],{"class":285},[157,1529,631],{"class":285},[157,1531,948],{"class":316},[157,1533,372],{"class":285},[157,1535,631],{"class":285},[157,1537,958],{"class":316},[157,1539,1540],{"class":285},")",[157,1542,294],{"class":285},[157,1544,631],{"class":285},[157,1546,938],{"class":316},[157,1548,984],{"class":285},[157,1550,987],{"class":358},[157,1552,395],{"class":285},[157,1554,286],{"class":285},[157,1556,994],{"class":167},[157,1558,286],{"class":285},[157,1560,372],{"class":285},[157,1562,631],{"class":285},[157,1564,958],{"class":316},[157,1566,984],{"class":285},[157,1568,1007],{"class":358},[157,1570,1010],{"class":285},[157,1572,1013],{"class":316},[157,1574,1575],{"class":285},"),\n",[157,1577,1578,1581,1583,1585,1588,1590,1592,1594,1596,1598,1600,1602,1604,1606,1608,1610,1612,1614,1616,1618,1620,1622,1624,1626,1628,1630,1632,1634,1636,1638],{"class":159,"line":906},[157,1579,1580],{"class":163},"        pullSnapshotMapper",[157,1582,1502],{"class":285},[157,1584,1520],{"class":418},[157,1586,1587],{"class":285}," (",[157,1589,811],{"class":163},[157,1591,631],{"class":285},[157,1593,1054],{"class":316},[157,1595,372],{"class":285},[157,1597,631],{"class":285},[157,1599,1064],{"class":316},[157,1601,1540],{"class":285},[157,1603,294],{"class":285},[157,1605,631],{"class":285},[157,1607,1054],{"class":316},[157,1609,984],{"class":285},[157,1611,1088],{"class":358},[157,1613,395],{"class":285},[157,1615,1093],{"class":163},[157,1617,355],{"class":285},[157,1619,1098],{"class":358},[157,1621,1101],{"class":285},[157,1623,1054],{"class":316},[157,1625,984],{"class":285},[157,1627,1108],{"class":316},[157,1629,372],{"class":285},[157,1631,450],{"class":285},[157,1633,286],{"class":285},[157,1635,696],{"class":167},[157,1637,286],{"class":285},[157,1639,1640],{"class":285},"])),\n",[157,1642,1643,1646,1648,1650,1652,1654,1656,1658,1660,1662,1664,1666,1668,1670,1672,1674,1676,1678,1680,1682],{"class":159,"line":911},[157,1644,1645],{"class":163},"        pushMutationMapper",[157,1647,1502],{"class":285},[157,1649,1520],{"class":418},[157,1651,1587],{"class":285},[157,1653,785],{"class":163},[157,1655,631],{"class":285},[157,1657,1163],{"class":316},[157,1659,372],{"class":285},[157,1661,953],{"class":163},[157,1663,631],{"class":285},[157,1665,958],{"class":316},[157,1667,1540],{"class":285},[157,1669,294],{"class":285},[157,1671,631],{"class":285},[157,1673,1163],{"class":316},[157,1675,984],{"class":285},[157,1677,1088],{"class":358},[157,1679,395],{"class":285},[157,1681,1207],{"class":358},[157,1683,1152],{"class":285},[157,1685,1686,1689,1691,1693,1695],{"class":159,"line":917},[157,1687,1688],{"class":285},"            $",[157,1690,1163],{"class":316},[157,1692,984],{"class":285},[157,1694,1216],{"class":358},[157,1696,1697],{"class":285},"(),\n",[157,1699,1700,1703,1705,1707,1709,1711,1713,1715,1717,1719,1721,1723],{"class":159,"line":967},[157,1701,1702],{"class":285},"            [",[157,1704,286],{"class":285},[157,1706,994],{"class":167},[157,1708,286],{"class":285},[157,1710,294],{"class":285},[157,1712,631],{"class":285},[157,1714,958],{"class":316},[157,1716,984],{"class":285},[157,1718,1007],{"class":358},[157,1720,1010],{"class":285},[157,1722,1013],{"class":316},[157,1724,1725],{"class":285},"]\n",[157,1727,1728],{"class":159,"line":973},[157,1729,1730],{"class":285},"        )),\n",[157,1732,1733,1736,1738,1740,1742,1744,1746,1748,1750,1752,1754,1756,1758,1760,1762,1764,1766,1768,1771],{"class":159,"line":1019},[157,1734,1735],{"class":163},"        conflictResolver",[157,1737,1502],{"class":285},[157,1739,1520],{"class":418},[157,1741,1587],{"class":285},[157,1743,785],{"class":163},[157,1745,631],{"class":285},[157,1747,1163],{"class":316},[157,1749,372],{"class":285},[157,1751,631],{"class":285},[157,1753,1288],{"class":316},[157,1755,372],{"class":285},[157,1757,631],{"class":285},[157,1759,958],{"class":316},[157,1761,1540],{"class":285},[157,1763,294],{"class":285},[157,1765,1314],{"class":163},[157,1767,355],{"class":285},[157,1769,1770],{"class":358},"reject",[157,1772,1697],{"class":285},[157,1774,1775],{"class":159,"line":1025},[157,1776,1777],{"class":285},"    );\n",[157,1779,1780],{"class":159,"line":1030},[157,1781,605],{"class":285},[140,1783],{},[143,1785,1787],{"id":1786},"syncregistry-options","SyncRegistry options",[127,1789,1790,1791,1794],{},"All parameters except ",[130,1792,1793],{},"modelClass"," are optional.",[215,1796,1797,1809],{},[218,1798,1799],{},[221,1800,1801,1804,1807],{},[224,1802,1803],{},"Parameter",[224,1805,1806],{},"Signature",[224,1808,232],{},[234,1810,1811,1826,1841,1856],{},[221,1812,1813,1818,1823],{},[239,1814,1815],{},[130,1816,1817],{},"scope",[239,1819,1820],{},[130,1821,1822],{},"(Builder $q, string $clientId, Request $r): Builder",[239,1824,1825],{},"Filter which rows are returned during pull (e.g. per-user)",[221,1827,1828,1833,1838],{},[239,1829,1830],{},[130,1831,1832],{},"pullSnapshotMapper",[239,1834,1835],{},[130,1836,1837],{},"(Snapshot $snapshot, Model $row): Snapshot",[239,1839,1840],{},"Transform row attributes before sending to client",[221,1842,1843,1848,1853],{},[239,1844,1845],{},[130,1846,1847],{},"pushMutationMapper",[239,1849,1850],{},[130,1851,1852],{},"(Mutation $mutation, Request $request): Mutation",[239,1854,1855],{},"Transform incoming payload before applying (e.g. stamp user ID)",[221,1857,1858,1863,1868],{},[239,1859,1860],{},[130,1861,1862],{},"conflictResolver",[239,1864,1865],{},[130,1866,1867],{},"(Mutation $mutation, Model $record, Request $request): ConflictResolution",[239,1869,1870],{},"Custom per-model conflict resolution",[140,1872],{},[143,1874,1876],{"id":1875},"protecting-the-sync-endpoints","Protecting the sync endpoints",[127,1878,1879],{},"Add authentication middleware that fits your app:",[148,1881,1883],{"className":271,"code":1882,"language":177,"meta":153,"style":153},"\u002F\u002F config\u002Ftether-server.php\n'middleware' => ['api', 'auth:sanctum'],\n",[130,1884,1885,1889],{"__ignoreMap":153},[157,1886,1887],{"class":159,"line":160},[157,1888,280],{"class":279},[157,1890,1891,1893,1895,1897,1899,1901,1903,1905,1907,1909,1911,1913,1915],{"class":159,"line":174},[157,1892,286],{"class":285},[157,1894,359],{"class":167},[157,1896,286],{"class":285},[157,1898,294],{"class":285},[157,1900,450],{"class":285},[157,1902,286],{"class":285},[157,1904,367],{"class":167},[157,1906,286],{"class":285},[157,1908,372],{"class":285},[157,1910,375],{"class":285},[157,1912,378],{"class":167},[157,1914,286],{"class":285},[157,1916,1917],{"class":285},"],\n",[1919,1920,1921],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}",{"title":153,"searchDepth":160,"depth":174,"links":1923},[1924,1925,1926,1930,1931],{"id":145,"depth":174,"text":146},{"id":209,"depth":174,"text":210},{"id":522,"depth":174,"text":523,"children":1927},[1928,1929],{"id":530,"depth":186,"text":531},{"id":1343,"depth":186,"text":1344},{"id":1786,"depth":174,"text":1787},{"id":1875,"depth":174,"text":1876},"Install tether\u002Fserver, register Syncable Eloquent models, expose push and pull endpoints, and reconcile offline client mutations on your Laravel server.","md",null,{},{"title":1937,"description":1932},"Laravel Sync Server Setup - tether\u002Fserver","yXe2aFU7Nrv1hGkdfwQYnF_npryN09hrXMuCpdM0xL8",[1940,1942],{"title":41,"path":42,"stem":43,"description":1941,"children":-1},"Reference every tether\u002Fclient config key for Laravel offline sync, including client identity, HTTP endpoints, queue sync, batching, retries, and cursors.",{"title":41,"path":56,"stem":57,"description":1943,"children":-1},"Configure tether\u002Fserver routes, middleware, sync keys, registered models, mutation storage, duplicate handling, and Laravel sync endpoint behaviour.",1780481013067]