Compare commits

...

2 commits

Author SHA1 Message Date
KodeStar
9e2f1f1650 Start on getting it to work 2022-03-10 15:56:36 +00:00
KodeStar
3712eb750b initial changes 2022-03-10 13:41:17 +00:00
49 changed files with 49522 additions and 12168 deletions

View file

@ -14,6 +14,8 @@ use App\SupportedApps;
use App\Jobs\ProcessApps;
use App\Search;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
class ItemController extends Controller
{
@ -36,9 +38,12 @@ class ItemController extends Controller
$query->where('id', 0);
})->orWhere('type', 1)->orderBy('order', 'asc')->get();
$data['current_user'] = User::currentUser();
//$data['all_apps'] = Item::doesntHave('parents')->get();
//die(print_r($data['apps']));
return view('welcome', $data);
// return view('welcome', $data);
return Inertia::render('Index', $data);
}
/**

View file

@ -35,6 +35,7 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\HandleInertiaRequests::class,
],
'api' => [

View file

@ -0,0 +1,43 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Inertia\Middleware;
class HandleInertiaRequests extends Middleware
{
/**
* The root template that's loaded on the first page visit.
*
* @see https://inertiajs.com/server-side-setup#root-template
* @var string
*/
protected $rootView = 'app';
/**
* Determines the current asset version.
*
* @see https://inertiajs.com/asset-versioning
* @param \Illuminate\Http\Request $request
* @return string|null
*/
public function version(Request $request): ?string
{
return parent::version($request);
}
/**
* Defines the props that are shared by default.
*
* @see https://inertiajs.com/shared-data
* @param \Illuminate\Http\Request $request
* @return array
*/
public function share(Request $request): array
{
return array_merge(parent::share($request), [
//
]);
}
}

View file

@ -9,6 +9,7 @@
"fideloper/proxy": "^4.0",
"graham-campbell/github": "^10.5",
"guzzlehttp/guzzle": "^7.4",
"inertiajs/inertia-laravel": "^0.5.4",
"laravel/framework": "^7.0",
"laravel/tinker": "^2.0",
"laravel/ui": "^2.4",

71
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "7c2e9180811d1213f10aedcef74bf8dd",
"content-hash": "f1a775595186b33c151fe16e35298fdc",
"packages": [
{
"name": "brick/math",
@ -1038,6 +1038,75 @@
],
"time": "2021-10-06T17:43:30+00:00"
},
{
"name": "inertiajs/inertia-laravel",
"version": "v0.5.4",
"source": {
"type": "git",
"url": "https://github.com/inertiajs/inertia-laravel.git",
"reference": "6a050ce04a710ac4809161558ac09fe49f13075e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/6a050ce04a710ac4809161558ac09fe49f13075e",
"reference": "6a050ce04a710ac4809161558ac09fe49f13075e",
"shasum": ""
},
"require": {
"ext-json": "*",
"laravel/framework": "^6.0|^7.0|^8.74|^9.0",
"php": "^7.2|~8.0.0|~8.1.0"
},
"require-dev": {
"mockery/mockery": "^1.3.3",
"orchestra/testbench": "^4.0|^5.0|^6.4|^7.0",
"phpunit/phpunit": "^8.0|^9.5.8",
"roave/security-advisories": "dev-master"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Inertia\\ServiceProvider"
]
}
},
"autoload": {
"files": [
"./helpers.php"
],
"psr-4": {
"Inertia\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jonathan Reinink",
"email": "jonathan@reinink.ca",
"homepage": "https://reinink.ca"
}
],
"description": "The Laravel adapter for Inertia.js.",
"keywords": [
"inertia",
"laravel"
],
"support": {
"issues": "https://github.com/inertiajs/inertia-laravel/issues",
"source": "https://github.com/inertiajs/inertia-laravel/tree/v0.5.4"
},
"funding": [
{
"url": "https://github.com/reinink",
"type": "github"
}
],
"time": "2022-01-18T10:59:08+00:00"
},
{
"name": "knplabs/github-api",
"version": "v3.5.1",

9486
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,22 +2,20 @@
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
"production": "mix --production"
},
"devDependencies": {
"bootstrap-sass": "^3.4.1",
"cross-env": "^5.2.0",
"jquery": "^3.4.1",
"laravel-mix": "^4.0.16",
"sass": "^1.21.0",
"sass-loader": "7.*"
"@inertiajs/inertia": "^0.11.0",
"@inertiajs/inertia-vue3": "^0.6.0",
"laravel-mix": "^6.0.43",
"vue": "^3.2.31",
"vue-loader": "^16.1.2",
"vue-template-compiler": "^2.6.14"
},
"dependencies": {
"select2": "^4.0.7"
}
"dependencies": {}
}

43288
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,3 @@
{
"/css/app.css": "/css/app.css?id=e4ac968c3a08d246c3b0",
"/js/app.js": "/js/app.js?id=f5edf362209887248205"
"/js/app.js": "/js/app.js"
}

View file

@ -0,0 +1,54 @@
<template>
<div class="flex flex-center">
<div class="noapps" v-if="apps.length === 0">
{{ $t('no_apps') }}
<button style="margin-top: 20px" unelevated color="cyan-8" to="/account">
{{ $t('manage_apps') }}
</button>
</div>
<Tile v-else v-for="application in apps" :key="application.id" v-bind="application" :application="application" />
</div>
</template>
<script>
import Tile from '../components/Tile'
export default {
name: 'PageIndex',
props: ['apps', 'searchfilter'],
components: {
Tile
},
computed: {
/*applications: function () {
let tiles = null
if (this.filter === null) {
tiles = this.$store.state.tiles.active
} else {
console.log('active')
console.log(this.$store.state.tiles.active)
const notNull = this.$store.state.tiles.active.filter(a => a.tags !== null)
console.log('notnull')
console.log(notNull)
tiles = notNull.filter(a => a.tags.find(name => name === this.filter))
}
// console.log(tiles)
if (this.searchfilter === null) {
return tiles
} else {
return tiles.filter(v => v.title.toLowerCase().indexOf(this.searchfilter) > -1)
}
}*/
},
data() {
return {}
},
mounted() {
console.log(apps)
}
}
</script>

View file

@ -1,195 +1,11 @@
$.when( $.ready ).then(function() {
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/inertia-vue3'
var base = (document.querySelector('base') || {}).href;
if($('.message-container').length) {
setTimeout(
function()
{
$('.message-container').fadeOut();
}, 3500);
}
// from https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
// Set the name of the hidden property and the change event for visibility
var hidden, visibilityChange;
if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support
hidden = "hidden";
visibilityChange = "visibilitychange";
} else if (typeof document.msHidden !== "undefined") {
hidden = "msHidden";
visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
hidden = "webkitHidden";
visibilityChange = "webkitvisibilitychange";
}
var livestatsRefreshTimeouts = [];
var livestatsFuncs = [];
var livestatsContainers = $('.livestats-container');
function stopLivestatsRefresh() {
for (var timeoutId of livestatsRefreshTimeouts) {
window.clearTimeout(timeoutId);
}
}
function startLivestatsRefresh() {
for (var fun of livestatsFuncs) {
fun();
}
}
if (livestatsContainers.length > 0) {
if (typeof document.addEventListener === "undefined" || hidden === undefined) {
console.log("This browser does not support visibilityChange");
} else {
document.addEventListener(visibilityChange, function() {
if (document[hidden]) {
stopLivestatsRefresh();
} else {
startLivestatsRefresh();
}
}, false);
}
livestatsContainers.each(function(index){
var id = $(this).data('id');
var dataonly = $(this).data('dataonly');
var increaseby = (dataonly == 1) ? 20000 : 1000;
var container = $(this);
var max_timer = 30000;
var timer = 5000;
var fun = function worker() {
$.ajax({
url: base+'/get_stats/'+id,
dataType: 'json',
success: function(data) {
container.html(data.html);
if(data.status == 'active') timer = increaseby;
else {
if(timer < max_timer) timer += 2000;
}
},
complete: function() {
// Schedule the next request when the current one's complete
livestatsRefreshTimeouts[index] = window.setTimeout(worker, timer);
}
});
};
livestatsFuncs[index] = fun;
fun();
});
}
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
$('#appimage img').attr('src', e.target.result);
};
reader.readAsDataURL(input.files[0]);
}
}
$('#upload').change(function() {
readURL(this);
});
/*$(".droppable").droppable({
tolerance: "intersect",
drop: function( event, ui ) {
var tag = $( this ).data('id');
var item = $( ui.draggable ).data('id');
$.get('tag/add/'+tag+'/'+item, function(data) {
if(data == 1) {
$( ui.draggable ).remove();
} else {
alert('not added');
}
});
}
});*/
$( '#sortable' ).sortable({
stop: function (event, ui) {
var idsInOrder = $('#sortable').sortable('toArray', {
attribute: 'data-id'
});
$.post(
base+'/order',
{ order:idsInOrder }
);
}
});
$('#sortable').sortable('disable');
$('#app').on('click', '#config-button', function(e) {
e.preventDefault();
var app = $('#app');
var active = (app.hasClass('header'));
app.toggleClass('header');
if(active) {
$('.add-item').hide();
$('.item-edit').hide();
$('#app').removeClass('sidebar');
$('#sortable').sortable('disable');
} else {
$('#sortable').sortable('enable');
setTimeout(function() {
$('.add-item').fadeIn();
$('.item-edit').fadeIn();
}, 350);
}
}).on('click', '#add-item, #pin-item', function(e) {
e.preventDefault();
var app = $('#app');
var active = (app.hasClass('sidebar'));
app.toggleClass('sidebar');
}).on('click', '.close-sidenav', function(e) {
e.preventDefault();
var app = $('#app');
app.removeClass('sidebar');
}).on('click', '#test_config', function(e) {
e.preventDefault();
var apiurl = $('#create input[name=url]').val();
var override_url = $('#create input[name="config[override_url]"]').val();
if(override_url.length && override_url != '') {
apiurl = override_url;
}
var data = {};
data['url'] = apiurl;
$('.config-item').each(function(index){
var config = $(this).data('config');
data[config] = $(this).val();
});
$.post(base+'/test_config', { data: data }, function(data) {
alert(data);
});
});
$('#pinlist').on('click', 'a', function(e) {
e.preventDefault();
var current = $(this);
var id = current.data('id');
var tag = current.data('tag');
$.get(base+'/items/pintoggle/'+id+'/true/'+tag, function(data) {
var inner = $(data).filter('#sortable').html();
$('#sortable').html(inner);
current.toggleClass('active');
});
});
});
createInertiaApp({
resolve: name => require(`./Pages/${name}`),
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.mount(el)
},
})

View file

@ -0,0 +1,201 @@
<template>
<section class="item-container ui-sortable-handle" :class="{ preview: preview }" :data-id="this.$attrs.id">
<div class="item" :style="'background-color: ' + bgColor + '; color: ' + textColor + ''">
<img class="app-icon" :src="appIcon" />
<div class="details">
<div class="title white">{{ application.title }}</div>
<!--<div v-if="application.config.enhancedType !== 'disabled'" class="livestats-container white">
<ul class="livestats">
<li v-if="application.config.stat1.name">
<span class="title">{{ application.config.stat1.name }}</span>
<strong>{{ this.stat1value }}</strong>
</li>
<li v-if="application.config.stat2.name">
<span class="title">{{ application.config.stat2.name }}</span>
<strong>{{ this.stat2value }}</strong>
</li>
</ul>
</div>-->
</div>
<a :style="'color: ' + textColor" class="link white" :href="application.url" v-bind:target="application.link_tab === 'default' ? settings.default_link_tab : application.link_tab">
<svg class="svg-inline--fa fa-arrow-alt-to-right fa-w-14" aria-hidden="true" data-prefix="fas" data-icon="arrow-alt-to-right" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg="">
<path fill="currentColor" d="M448 88v336c0 13.3-10.7 24-24 24h-24c-13.3 0-24-10.7-24-24V88c0-13.3 10.7-24 24-24h24c13.3 0 24 10.7 24 24zM24 320h136v87.7c0 17.8 21.5 26.7 34.1 14.1l152.2-152.2c7.5-7.5 7.5-19.8 0-27.3L194.1 90.1c-12.6-12.6-34.1-3.7-34.1 14.1V192H24c-13.3 0-24 10.7-24 24v80c0 13.3 10.7 24 24 24z"></path>
</svg>
<!-- <i class="fas fa-arrow-alt-to-right"></i> -->
</a>
<div v-if="application.description !== ''" content-class="tooltip-content" max-width="500px" anchor="top middle" self="bottom middle">{{ application.description }}</div>
<!--<div v-if="application.config.enhancedType !== 'disabled'" @click="refreshData" class="tile-actions refresh">
<q-icon class="" name="refresh"></q-icon>
{{ $t('refresh_stats') }}
</div>-->
</div>
</section>
</template>
<script>
import EnhancedApps from '../plugins/EnhancedApps'
import _ from 'lodash'
export default {
name: 'Tile',
props: ['application', 'stat1valueinit', 'stat2valueinit'],
components: {},
computed: {
textColor() {
return '#000000'
const bgColor = this.bgColor
const lightColor = '#ffffff'
const darkColor = '#000000'
const color = bgColor.charAt(0) === '#' ? bgColor.substring(1, 7) : bgColor
const alpha = bgColor.charAt(0) === '#' ? bgColor.substring(7, 9) : bgColor.substring(6, 8)
const r = parseInt(color.substring(0, 2), 16) // hexToR
const g = parseInt(color.substring(2, 4), 16) // hexToG
const b = parseInt(color.substring(4, 6), 16) // hexToB
const a = parseFloat(parseInt((parseInt(alpha, 16) / 255) * 1000) / 1000)
const brightness = r * 0.299 + g * 0.587 + b * 0.114 + (1 - a) * 255
return brightness > 186 ? darkColor : lightColor
},
bgColor() {
if (this.application.color !== 'null' && this.application.color !== null) {
return this.application.color
}
return '#222222'
},
preview() {
return this.application.preview
},
running() {
// return this.$store.state.tiles.running
},
settings() {
// return this.$store.state.app.settings
},
appIcon() {
return '';
}
},
watch: {
application: function (newdata, olddata) {
clearTimeout(this.check)
if (newdata.config.enhancedType !== 'disabled') {
// this.checkVisible()
}
},
running: function (newdata, olddata) {
if (newdata === false) {
clearTimeout(this.check)
}
if (olddata === false && newdata === true) {
this.timedChecks()
}
},
stat1valueinit: function (newdata, olddata) {
this.stat1value = newdata
},
stat2valueinit: function (newdata, olddata) {
this.stat2value = newdata
}
},
asyncComputed: {
/*async appIcon() {
if (this.application.icon === null) {
return '/heimdall-logo-white.svg'
}
// Is a file upload
if (typeof this.application.icon === 'object') {
// work out how to preview the image
console.log(this.application.icon)
}
// Is an image stored online
if (this.application.icon && this.application.icon.startsWith('http')) {
}
return this.application.icon
}*/
},
data() {
return {
icon: this.$attrs.icon || '/heimdall-logo-white.svg',
stat1value: this.stat1valueinit || null,
stat2value: this.stat2valueinit || null,
check: null,
active: 2000,
maxTimer: 45000,
timer: 5000
}
},
mounted() {
this.refreshData()
},
methods: {
async timedChecks() {
const current1 = this.stat1value
const current2 = this.stat2value
const data = await this.checkForData()
if (!data) {
return
}
if (data.stat1 !== current1 && this.application.config.stat1.updateOnChange === 'Yes') {
this.timer = this.active
} else if (data.stat2 !== current2 && this.application.config.stat2.updateOnChange === 'Yes') {
// There has been a change to the data
this.timer = this.active
} else {
if (this.timer < this.maxTimer) this.timer += 5000
}
this.stat1value = data.stat1
this.stat2value = data.stat2
clearTimeout(this.check) // Make sure timer is cleared, it should be, but shouldn't hurt to make sure
if (
// check if update on change is set on at least 1 stat
this.application.config.stat1.updateOnChange === 'Yes' ||
this.application.config.stat2.updateOnChange === 'Yes'
) {
console.log('timer: ' + this.timer)
this.check = setTimeout(this.timedChecks, this.timer)
}
},
async refreshData() {
await this.timedChecks()
this.$q.notify({
message: this.$t('updated'),
type: 'positive',
progress: true,
position: 'bottom',
timeout: 1500
})
},
forceCheck() {
clearTimeout(this.check)
this.checkForData()
},
async checkForData() {
if (this.application.config.enhancedType && this.application.config.enhancedType !== 'disabled') {
const enhanced = new EnhancedApps(this.application)
let call
try {
call = await enhanced.call()
} catch (e) {
console.error(e)
}
if (!call) {
return
}
const stat1 = this.application.config.stat1.key !== null && this.application.config.stat1.key !== '' ? _.get(call.data.result.stat1, this.application.config.stat1.key, null) : call.data.result.stat1
const stat2 = this.application.config.stat2.key !== null && this.application.config.stat2.key !== '' ? _.get(call.data.result.stat2, this.application.config.stat2.key, null) : call.data.result.stat2
return {
stat1: enhanced.filter(stat1, this.application.config.stat1),
stat2: enhanced.filter(stat2, this.application.config.stat2)
}
}
}
}
}
</script>

View file

@ -0,0 +1,112 @@
import axios from 'axios'
// import { i18n } from 'boot/i18n.js'
export default class EnhancedApps {
constructor(application) {
this.data = application.config
this.id = application.id
}
async call() {
const request = await axios.get('enhanced/' + this.id)
return request
}
async test() {
try {
// console.log(this.data)
const test = await axios.post('enhanced/test', this.data)
/*Notify.create({
type: 'positive',
message: i18n.t('api_test_success') + ': ' + JSON.stringify(test.data),
progress: true,
position: 'bottom',
timeout: 1500
})*/
return test
} catch (e) {
console.log(e.response)
/*Notify.create({
type: 'negative',
message: i18n.t('api_test_failure') + ': ' + JSON.stringify(e.response.data.result),
progress: true,
position: 'bottom',
timeout: 1500
})*/
}
}
static types() {
return [
{
id: 'disabled',
value: 'disabled'
},
{
id: 'none',
value: 'none'
},
{
id: 'apikey',
value: 'apikey'
},
{
id: 'cookie',
value: 'cookie'
},
{
id: 'basic_auth',
value: 'basic_auth'
}
]
}
humanFileSize(size) {
const i = size === 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024))
return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i]
}
filter(value, stat) {
switch (stat.filter) {
case 'count':
return value.length
case 'size':
return this.humanFileSize(value)
case 'filter':
return value.filter(val => {
return val[stat.filterBy].toString() === stat.filterByValue.toString()
}).length
}
return value
}
static filters() {
return [
{
id: 'none',
value: 'none'
},
{
id: 'size',
value: 'size'
},
{
id: 'speed',
value: 'speed'
},
{
id: 'count',
value: 'count'
},
{
id: 'filter',
value: 'filter'
}
]
}
fields() {
if (this.data.type === 1) {
return [{}]
}
}
}

View file

@ -0,0 +1,42 @@
<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ config('app.name') }}</title>
<link rel="apple-touch-icon" sizes="57x57" href="{{ asset('apple-icon-57x57.png') }}">
<link rel="apple-touch-icon" sizes="60x60" href="{{ asset('apple-icon-60x60.png') }}">
<link rel="apple-touch-icon" sizes="72x72" href="{{ asset('apple-icon-72x72.png') }}">
<link rel="apple-touch-icon" sizes="76x76" href="{{ asset('apple-icon-76x76.png') }}">
<link rel="apple-touch-icon" sizes="114x114" href="{{ asset('apple-icon-114x114.png') }}">
<link rel="apple-touch-icon" sizes="120x120" href="{{ asset('apple-icon-120x120.png') }}">
<link rel="apple-touch-icon" sizes="144x144" href="{{ asset('apple-icon-144x144.png') }}">
<link rel="apple-touch-icon" sizes="152x152" href="{{ asset('apple-icon-152x152.png') }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ asset('apple-icon-180x180.png') }}">
<link rel="icon" type="image/png" sizes="192x192" href="{{ asset('android-icon-192x192.png') }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ asset('favicon-32x32.png') }}">
<link rel="icon" type="image/png" sizes="96x96" href="{{ asset('favicon-96x96.png') }}">
<link rel="icon" type="image/png" sizes="16x16" href="{{ asset('favicon-16x16.png') }}">
<link rel="mask-icon" href="{{ asset('img/heimdall-logo-small.svg') }}" color="black">
<link rel="manifest" href="{{ asset('manifest.json') }}">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="{{ asset('ms-icon-144x144.png') }}">
<meta name="theme-color" content="#ffffff">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<script src="{{ asset('js/fontawesome.js') }}"></script>
<link href="{{ asset('/css/app.css') }}" rel="stylesheet" />
<script src="{{ asset('/js/app.js') }}" defer></script>
@if(config('app.url') !== 'http://localhost')
<base href="{{ config('app.url') }}">
@else
<base href="{{ url('') }}">
@endif
@inertiaHead
</head>
<body>
@inertia
</body>
</html>

View file

@ -2405,6 +2405,26 @@ return array(
'Illuminate\\View\\ViewFinderInterface' => $vendorDir . '/laravel/framework/src/Illuminate/View/ViewFinderInterface.php',
'Illuminate\\View\\ViewName' => $vendorDir . '/laravel/framework/src/Illuminate/View/ViewName.php',
'Illuminate\\View\\ViewServiceProvider' => $vendorDir . '/laravel/framework/src/Illuminate/View/ViewServiceProvider.php',
'Inertia\\Console\\CreateMiddleware' => $vendorDir . '/inertiajs/inertia-laravel/src/Console/CreateMiddleware.php',
'Inertia\\Controller' => $vendorDir . '/inertiajs/inertia-laravel/src/Controller.php',
'Inertia\\Directive' => $vendorDir . '/inertiajs/inertia-laravel/src/Directive.php',
'Inertia\\Inertia' => $vendorDir . '/inertiajs/inertia-laravel/src/Inertia.php',
'Inertia\\LazyProp' => $vendorDir . '/inertiajs/inertia-laravel/src/LazyProp.php',
'Inertia\\Middleware' => $vendorDir . '/inertiajs/inertia-laravel/src/Middleware.php',
'Inertia\\Response' => $vendorDir . '/inertiajs/inertia-laravel/src/Response.php',
'Inertia\\ResponseFactory' => $vendorDir . '/inertiajs/inertia-laravel/src/ResponseFactory.php',
'Inertia\\ServiceProvider' => $vendorDir . '/inertiajs/inertia-laravel/src/ServiceProvider.php',
'Inertia\\Ssr\\Gateway' => $vendorDir . '/inertiajs/inertia-laravel/src/Ssr/Gateway.php',
'Inertia\\Ssr\\HttpGateway' => $vendorDir . '/inertiajs/inertia-laravel/src/Ssr/HttpGateway.php',
'Inertia\\Ssr\\Response' => $vendorDir . '/inertiajs/inertia-laravel/src/Ssr/Response.php',
'Inertia\\Testing\\Assert' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/Assert.php',
'Inertia\\Testing\\AssertableInertia' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/AssertableInertia.php',
'Inertia\\Testing\\Concerns\\Debugging' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/Concerns/Debugging.php',
'Inertia\\Testing\\Concerns\\Has' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/Concerns/Has.php',
'Inertia\\Testing\\Concerns\\Interaction' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/Concerns/Interaction.php',
'Inertia\\Testing\\Concerns\\Matching' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/Concerns/Matching.php',
'Inertia\\Testing\\Concerns\\PageObject' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/Concerns/PageObject.php',
'Inertia\\Testing\\TestResponseMacros' => $vendorDir . '/inertiajs/inertia-laravel/src/Testing/TestResponseMacros.php',
'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
'Laravel\\Tinker\\ClassAliasAutoloader' => $vendorDir . '/laravel/tinker/src/ClassAliasAutoloader.php',
'Laravel\\Tinker\\Console\\TinkerCommand' => $vendorDir . '/laravel/tinker/src/Console/TinkerCommand.php',

View file

@ -19,19 +19,20 @@ return array(
'9c67151ae59aff4788964ce8eb2a0f43' => $vendorDir . '/clue/stream-filter/src/functions_include.php',
'8cff32064859f4559445b89279f3199c' => $vendorDir . '/php-http/message/src/filters.php',
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
'801c31d8ed748cfa537fa45402288c95' => $vendorDir . '/psy/psysh/src/functions.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
'2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
'23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php',
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'538ca81a9a966a6716601ecf48f4eaef' => $vendorDir . '/opis/closure/functions.php',
'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'f0906e6318348a765ffb6eb24e0d0938' => $vendorDir . '/laravel/framework/src/Illuminate/Foundation/helpers.php',
'58571171fd5812e6e447dce228f52f4d' => $vendorDir . '/laravel/framework/src/Illuminate/Support/helpers.php',
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
'f18cc91337d49233e5754e93f3ed9ec3' => $vendorDir . '/laravelcollective/html/src/helpers.php',
'98caa11a197f6516a8e48aa4abb5ccc6' => $vendorDir . '/inertiajs/inertia-laravel/helpers.php',
'e617b14322a074392076a2f38eaf6115' => $baseDir . '/app/Helper.php',
);

View file

@ -63,6 +63,7 @@ return array(
'League\\CommonMark\\' => array($vendorDir . '/league/commonmark/src'),
'Laravel\\Ui\\' => array($vendorDir . '/laravel/ui/src'),
'Laravel\\Tinker\\' => array($vendorDir . '/laravel/tinker/src'),
'Inertia\\' => array($vendorDir . '/inertiajs/inertia-laravel/src'),
'Illuminate\\Foundation\\Auth\\' => array($vendorDir . '/laravel/ui/auth-backend'),
'Illuminate\\' => array($vendorDir . '/laravel/framework/src/Illuminate'),
'Http\\Promise\\' => array($vendorDir . '/php-http/promise/src'),

View file

@ -20,20 +20,21 @@ class ComposerStaticInit4b6fb9210a1ea37c2db27b8ff53a1ecf
'9c67151ae59aff4788964ce8eb2a0f43' => __DIR__ . '/..' . '/clue/stream-filter/src/functions_include.php',
'8cff32064859f4559445b89279f3199c' => __DIR__ . '/..' . '/php-http/message/src/filters.php',
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
'801c31d8ed748cfa537fa45402288c95' => __DIR__ . '/..' . '/psy/psysh/src/functions.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
'2c102faa651ef8ea5874edb585946bce' => __DIR__ . '/..' . '/swiftmailer/swiftmailer/lib/swift_required.php',
'23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php',
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'538ca81a9a966a6716601ecf48f4eaef' => __DIR__ . '/..' . '/opis/closure/functions.php',
'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'f0906e6318348a765ffb6eb24e0d0938' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Foundation/helpers.php',
'58571171fd5812e6e447dce228f52f4d' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Support/helpers.php',
'6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
'f18cc91337d49233e5754e93f3ed9ec3' => __DIR__ . '/..' . '/laravelcollective/html/src/helpers.php',
'98caa11a197f6516a8e48aa4abb5ccc6' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/helpers.php',
'e617b14322a074392076a2f38eaf6115' => __DIR__ . '/../..' . '/app/Helper.php',
);
@ -127,6 +128,7 @@ class ComposerStaticInit4b6fb9210a1ea37c2db27b8ff53a1ecf
),
'I' =>
array (
'Inertia\\' => 8,
'Illuminate\\Foundation\\Auth\\' => 27,
'Illuminate\\' => 11,
),
@ -416,6 +418,10 @@ class ComposerStaticInit4b6fb9210a1ea37c2db27b8ff53a1ecf
array (
0 => __DIR__ . '/..' . '/laravel/tinker/src',
),
'Inertia\\' =>
array (
0 => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src',
),
'Illuminate\\Foundation\\Auth\\' =>
array (
0 => __DIR__ . '/..' . '/laravel/ui/auth-backend',
@ -2949,6 +2955,26 @@ class ComposerStaticInit4b6fb9210a1ea37c2db27b8ff53a1ecf
'Illuminate\\View\\ViewFinderInterface' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/View/ViewFinderInterface.php',
'Illuminate\\View\\ViewName' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/View/ViewName.php',
'Illuminate\\View\\ViewServiceProvider' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/View/ViewServiceProvider.php',
'Inertia\\Console\\CreateMiddleware' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Console/CreateMiddleware.php',
'Inertia\\Controller' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Controller.php',
'Inertia\\Directive' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Directive.php',
'Inertia\\Inertia' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Inertia.php',
'Inertia\\LazyProp' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/LazyProp.php',
'Inertia\\Middleware' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Middleware.php',
'Inertia\\Response' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Response.php',
'Inertia\\ResponseFactory' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/ResponseFactory.php',
'Inertia\\ServiceProvider' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/ServiceProvider.php',
'Inertia\\Ssr\\Gateway' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Ssr/Gateway.php',
'Inertia\\Ssr\\HttpGateway' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Ssr/HttpGateway.php',
'Inertia\\Ssr\\Response' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Ssr/Response.php',
'Inertia\\Testing\\Assert' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/Assert.php',
'Inertia\\Testing\\AssertableInertia' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/AssertableInertia.php',
'Inertia\\Testing\\Concerns\\Debugging' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/Concerns/Debugging.php',
'Inertia\\Testing\\Concerns\\Has' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/Concerns/Has.php',
'Inertia\\Testing\\Concerns\\Interaction' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/Concerns/Interaction.php',
'Inertia\\Testing\\Concerns\\Matching' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/Concerns/Matching.php',
'Inertia\\Testing\\Concerns\\PageObject' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/Concerns/PageObject.php',
'Inertia\\Testing\\TestResponseMacros' => __DIR__ . '/..' . '/inertiajs/inertia-laravel/src/Testing/TestResponseMacros.php',
'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
'Laravel\\Tinker\\ClassAliasAutoloader' => __DIR__ . '/..' . '/laravel/tinker/src/ClassAliasAutoloader.php',
'Laravel\\Tinker\\Console\\TinkerCommand' => __DIR__ . '/..' . '/laravel/tinker/src/Console/TinkerCommand.php',

View file

@ -1330,6 +1330,78 @@
},
"install-path": "../hamcrest/hamcrest-php"
},
{
"name": "inertiajs/inertia-laravel",
"version": "v0.5.4",
"version_normalized": "0.5.4.0",
"source": {
"type": "git",
"url": "https://github.com/inertiajs/inertia-laravel.git",
"reference": "6a050ce04a710ac4809161558ac09fe49f13075e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/6a050ce04a710ac4809161558ac09fe49f13075e",
"reference": "6a050ce04a710ac4809161558ac09fe49f13075e",
"shasum": ""
},
"require": {
"ext-json": "*",
"laravel/framework": "^6.0|^7.0|^8.74|^9.0",
"php": "^7.2|~8.0.0|~8.1.0"
},
"require-dev": {
"mockery/mockery": "^1.3.3",
"orchestra/testbench": "^4.0|^5.0|^6.4|^7.0",
"phpunit/phpunit": "^8.0|^9.5.8",
"roave/security-advisories": "dev-master"
},
"time": "2022-01-18T10:59:08+00:00",
"type": "library",
"extra": {
"laravel": {
"providers": [
"Inertia\\ServiceProvider"
]
}
},
"installation-source": "dist",
"autoload": {
"files": [
"./helpers.php"
],
"psr-4": {
"Inertia\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jonathan Reinink",
"email": "jonathan@reinink.ca",
"homepage": "https://reinink.ca"
}
],
"description": "The Laravel adapter for Inertia.js.",
"keywords": [
"inertia",
"laravel"
],
"support": {
"issues": "https://github.com/inertiajs/inertia-laravel/issues",
"source": "https://github.com/inertiajs/inertia-laravel/tree/v0.5.4"
},
"funding": [
{
"url": "https://github.com/reinink",
"type": "github"
}
],
"install-path": "../inertiajs/inertia-laravel"
},
{
"name": "knplabs/github-api",
"version": "v3.5.1",

View file

@ -5,7 +5,7 @@
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '61a5a1a8b023771e0ff7c056add5537d20737e51',
'reference' => 'f9a19fce91446a34aa433bb1426491fbb2005611',
'name' => 'laravel/laravel',
'dev' => true,
),
@ -349,6 +349,15 @@
0 => 'v7.30.6',
),
),
'inertiajs/inertia-laravel' => array(
'pretty_version' => 'v0.5.4',
'version' => '0.5.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/../inertiajs/inertia-laravel',
'aliases' => array(),
'reference' => '6a050ce04a710ac4809161558ac09fe49f13075e',
'dev_requirement' => false,
),
'knplabs/github-api' => array(
'pretty_version' => 'v3.5.1',
'version' => '3.5.1.0',
@ -379,7 +388,7 @@
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '61a5a1a8b023771e0ff7c056add5537d20737e51',
'reference' => 'f9a19fce91446a34aa433bb1426491fbb2005611',
'dev_requirement' => false,
),
'laravel/tinker' => array(

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) Jonathan Reinink <jonathan@reinink.ca>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,31 @@
<?php
/** @noinspection PhpUndefinedClassInspection */
/** @noinspection PhpFullyQualifiedNameUsageInspection */
/** @noinspection PhpUnusedAliasInspection */
namespace Illuminate\Testing {
/**
* @see \Inertia\Testing\TestResponseMacros
*
* @method self assertInertia(\Closure $assert = null)
*/
class TestResponse
{
//
}
}
namespace Illuminate\Foundation\Testing {
/**
* @see \Inertia\Testing\TestResponseMacros
*
* @method self assertInertia(\Closure $assert = null)
*/
class TestResponse
{
//
}
}

View file

@ -0,0 +1,47 @@
{
"name": "inertiajs/inertia-laravel",
"type": "library",
"description": "The Laravel adapter for Inertia.js.",
"keywords": ["laravel", "inertia"],
"license": "MIT",
"authors": [
{
"name": "Jonathan Reinink",
"email": "jonathan@reinink.ca",
"homepage": "https://reinink.ca"
}
],
"autoload": {
"psr-4": {
"Inertia\\": "src"
},
"files": [
"./helpers.php"
]
},
"autoload-dev": {
"psr-4": {
"Inertia\\Tests\\": "tests/"
}
},
"require": {
"php": "^7.2|~8.0.0|~8.1.0",
"ext-json": "*",
"laravel/framework": "^6.0|^7.0|^8.74|^9.0"
},
"require-dev": {
"roave/security-advisories": "dev-master",
"orchestra/testbench": "^4.0|^5.0|^6.4|^7.0",
"mockery/mockery": "^1.3.3",
"phpunit/phpunit": "^8.0|^9.5.8"
},
"extra": {
"laravel": {
"providers": [
"Inertia\\ServiceProvider"
]
}
},
"minimum-stability": "dev",
"prefer-stable": true
}

View file

@ -0,0 +1,62 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Server Side Rendering
|--------------------------------------------------------------------------
|
| These options configures if and how Inertia uses Server Side Rendering
| to pre-render the initial visits made to your application's pages.
|
| Do note that enabling these options will NOT automatically make SSR work,
| as a separate rendering service needs to be available. To learn more,
| please visit https://inertiajs.com/server-side-rendering
|
*/
'ssr' => [
'enabled' => false,
'url' => 'http://127.0.0.1:13714/render',
],
/*
|--------------------------------------------------------------------------
| Testing
|--------------------------------------------------------------------------
|
| The values described here are used to locate Inertia components on the
| filesystem. For instance, when using `assertInertia`, the assertion
| attempts to locate the component as a file relative to any of the
| paths AND with any of the extensions specified here.
|
*/
'testing' => [
'ensure_pages_exist' => true,
'page_paths' => [
resource_path('js/Pages'),
],
'page_extensions' => [
'js',
'jsx',
'svelte',
'ts',
'tsx',
'vue',
],
],
];

View file

@ -0,0 +1,21 @@
<?php
if (! function_exists('inertia')) {
/**
* Inertia helper.
*
* @param null|string $component
* @param array|\Illuminate\Contracts\Support\Arrayable $props
* @return \Inertia\ResponseFactory|\Inertia\Response
*/
function inertia($component = null, $props = [])
{
$instance = \Inertia\Inertia::getFacadeRoot();
if ($component) {
return $instance->render($component, $props);
}
return $instance;
}
}

View file

@ -0,0 +1,18 @@
# Inertia.js Laravel Adapter
<p align="left">
<a href="https://github.com/inertiajs/inertia-laravel/releases">
<img src="https://img.shields.io/github/release/inertiajs/inertia-laravel.svg?style=flat-square" alt="Latest Version">
</a>
<a href="https://github.com/inertiajs/inertia-laravel/actions?query=workflow%3Atests+branch%3Amaster">
<img src="https://img.shields.io/github/workflow/status/inertiajs/inertia-laravel/tests/master.svg?style=flat-square" alt="Build Status">
</a>
<a href="https://styleci.io/repos/174395905"><img src="https://styleci.io/repos/174395905/shield" alt="StyleCI"></a>
<a href="https://packagist.org/packages/inertiajs/inertia-laravel">
<img src="https://img.shields.io/packagist/dt/inertiajs/inertia-laravel.svg?style=flat-square" alt="Total Downloads">
</a>
</p>
---
Visit [inertiajs.com](https://inertiajs.com/) to learn more.

View file

@ -0,0 +1,75 @@
<?php
namespace Inertia\Console;
use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Input\InputOption;
class CreateMiddleware extends GeneratorCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'inertia:middleware';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new Inertia middleware';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Middleware';
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub(): string
{
return __DIR__.'/../../stubs/middleware.stub';
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace): string
{
return $rootNamespace.'\Http\Middleware';
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments(): array
{
return [
['name', InputOption::VALUE_REQUIRED, 'Name of the Middleware that should be created', 'HandleInertiaRequests'],
];
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions(): array
{
return [
['force', null, InputOption::VALUE_NONE, 'Create the class even if the Middleware already exists'],
];
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace Inertia;
use Illuminate\Http\Request;
class Controller
{
public function __invoke(Request $request): Response
{
return Inertia::render(
$request->route()->defaults['component'],
$request->route()->defaults['props']
);
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace Inertia;
class Directive
{
/**
* Compiles the "@inertia" directive.
*
* @param string $expression
* @return string
*/
public static function compile($expression = ''): string
{
$id = trim(trim($expression), "\'\"") ?: 'app';
$template = '<?php
if (!isset($__inertiaSsr)) {
$__inertiaSsr = app(\Inertia\Ssr\Gateway::class)->dispatch($page);
}
if ($__inertiaSsr instanceof \Inertia\Ssr\Response) {
echo $__inertiaSsr->body;
} else {
?><div id="'.$id.'" data-page="{{ json_encode($page) }}"></div><?php
}
?>';
return implode(' ', array_map('trim', explode("\n", $template)));
}
/**
* Compiles the "@inertiaHead" directive.
*
* @param string $expression
* @return string
*/
public static function compileHead($expression = ''): string
{
$template = '<?php
if (!isset($__inertiaSsr)) {
$__inertiaSsr = app(\Inertia\Ssr\Gateway::class)->dispatch($page);
}
if ($__inertiaSsr instanceof \Inertia\Ssr\Response) {
echo $__inertiaSsr->head;
}
?>';
return implode(' ', array_map('trim', explode("\n", $template)));
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace Inertia;
use Illuminate\Support\Facades\Facade;
/**
* @method static void setRootView(string $name)
* @method static void share($key, $value = null)
* @method static array getShared(string $key = null, $default = null)
* @method static array flushShared()
* @method static void version($version)
* @method static int|string getVersion()
* @method static LazyProp lazy(callable $callback)
* @method static Response render($component, array $props = [])
* @method static \Illuminate\Http\Response location(string $url)
*
* @see \Inertia\ResponseFactory
*/
class Inertia extends Facade
{
protected static function getFacadeAccessor(): string
{
return ResponseFactory::class;
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace Inertia;
use Illuminate\Support\Facades\App;
class LazyProp
{
protected $callback;
public function __construct(callable $callback)
{
$this->callback = $callback;
}
public function __invoke()
{
return App::call($this->callback);
}
}

View file

@ -0,0 +1,167 @@
<?php
namespace Inertia;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class Middleware
{
/**
* The root template that's loaded on the first page visit.
*
* @see https://inertiajs.com/server-side-setup#root-template
*
* @var string
*/
protected $rootView = 'app';
/**
* Determines the current asset version.
*
* @see https://inertiajs.com/asset-versioning
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
public function version(Request $request)
{
if (config('app.asset_url')) {
return md5(config('app.asset_url'));
}
if (file_exists($manifest = public_path('mix-manifest.json'))) {
return md5_file($manifest);
}
return null;
}
/**
* Defines the props that are shared by default.
*
* @see https://inertiajs.com/shared-data
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function share(Request $request)
{
return [
'errors' => function () use ($request) {
return $this->resolveValidationErrors($request);
},
];
}
/**
* Sets the root template that's loaded on the first page visit.
*
* @see https://inertiajs.com/server-side-setup#root-template
*
* @param Request $request
* @return string
*/
public function rootView(Request $request)
{
return $this->rootView;
}
/**
* Handle the incoming request.
*
* @param \Illuminate\Http\Request $request
* @param Closure $next
* @return Response
*/
public function handle(Request $request, Closure $next)
{
Inertia::version(function () use ($request) {
return $this->version($request);
});
Inertia::share($this->share($request));
Inertia::setRootView($this->rootView($request));
$response = $next($request);
$response = $this->checkVersion($request, $response);
return $this->changeRedirectCode($request, $response);
}
/**
* In the event that the assets change, initiate a
* client-side location visit to force an update.
*
* @param Request $request
* @param Response $response
* @return Response
*/
public function checkVersion(Request $request, Response $response)
{
if ($request->header('X-Inertia') &&
$request->method() === 'GET' &&
$request->header('X-Inertia-Version', '') !== Inertia::getVersion()
) {
if ($request->hasSession()) {
$request->session()->reflash();
}
return Inertia::location($request->fullUrl());
}
return $response;
}
/**
* Changes the status code during redirects, ensuring they are made as
* GET requests, preventing "MethodNotAllowedHttpException" errors.
*
* @param Request $request
* @param Response $response
* @return Response
*/
public function changeRedirectCode(Request $request, Response $response)
{
if ($request->header('X-Inertia') &&
$response->getStatusCode() === 302 &&
in_array($request->method(), ['PUT', 'PATCH', 'DELETE'])
) {
$response->setStatusCode(303);
}
return $response;
}
/**
* Resolves and prepares validation errors in such
* a way that they are easier to use client-side.
*
* @param Request $request
* @return object
*/
public function resolveValidationErrors(Request $request)
{
if (! $request->session()->has('errors')) {
return (object) [];
}
return (object) collect($request->session()->get('errors')->getBags())->map(function ($bag) {
return (object) collect($bag->messages())->map(function ($errors) {
return $errors[0];
})->toArray();
})->pipe(function ($bags) use ($request) {
if ($bags->has('default') && $request->header('x-inertia-error-bag')) {
return [$request->header('x-inertia-error-bag') => $bags->get('default')];
}
if ($bags->has('default')) {
return $bags->get('default');
}
return $bags->toArray();
});
}
}

View file

@ -0,0 +1,161 @@
<?php
namespace Inertia;
use Closure;
use GuzzleHttp\Promise\PromiseInterface;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\Json\ResourceResponse;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Response as ResponseFactory;
use Illuminate\Support\Traits\Macroable;
class Response implements Responsable
{
use Macroable;
protected $component;
protected $props;
protected $rootView;
protected $version;
protected $viewData = [];
/**
* @param string $component
* @param array|Arrayable $props
* @param string $rootView
* @param string $version
*/
public function __construct(string $component, $props, string $rootView = 'app', string $version = '')
{
$this->component = $component;
$this->props = $props instanceof Arrayable ? $props->toArray() : $props;
$this->rootView = $rootView;
$this->version = $version;
}
/**
* @param string|array $key
* @param mixed|null $value
* @return $this
*/
public function with($key, $value = null): self
{
if (is_array($key)) {
$this->props = array_merge($this->props, $key);
} else {
$this->props[$key] = $value;
}
return $this;
}
/**
* @param string|array $key
* @param mixed|null $value
* @return $this
*/
public function withViewData($key, $value = null): self
{
if (is_array($key)) {
$this->viewData = array_merge($this->viewData, $key);
} else {
$this->viewData[$key] = $value;
}
return $this;
}
public function rootView(string $rootView): self
{
$this->rootView = $rootView;
return $this;
}
/**
* Create an HTTP response that represents the object.
*
* @param \Illuminate\Http\Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function toResponse($request)
{
$only = array_filter(explode(',', $request->header('X-Inertia-Partial-Data', '')));
$props = ($only && $request->header('X-Inertia-Partial-Component') === $this->component)
? Arr::only($this->props, $only)
: array_filter($this->props, static function ($prop) {
return ! ($prop instanceof LazyProp);
});
$props = $this->resolvePropertyInstances($props, $request);
$page = [
'component' => $this->component,
'props' => $props,
'url' => $request->getBaseUrl().$request->getRequestUri(),
'version' => $this->version,
];
if ($request->header('X-Inertia')) {
return new JsonResponse($page, 200, [
'Vary' => 'Accept',
'X-Inertia' => 'true',
]);
}
return ResponseFactory::view($this->rootView, $this->viewData + ['page' => $page]);
}
/**
* Resolve all necessary class instances in the given props.
*
* @param array $props
* @param \Illuminate\Http\Request $request
* @param bool $unpackDotProps
* @return array
*/
public function resolvePropertyInstances(array $props, Request $request, bool $unpackDotProps = true): array
{
foreach ($props as $key => $value) {
if ($value instanceof Closure) {
$value = App::call($value);
}
if ($value instanceof LazyProp) {
$value = App::call($value);
}
if ($value instanceof PromiseInterface) {
$value = $value->wait();
}
if ($value instanceof ResourceResponse || $value instanceof JsonResource) {
$value = $value->toResponse($request)->getData(true);
}
if ($value instanceof Arrayable) {
$value = $value->toArray();
}
if (is_array($value)) {
$value = $this->resolvePropertyInstances($value, $request, false);
}
if ($unpackDotProps && str_contains($key, '.')) {
Arr::set($props, $key, $value);
unset($props[$key]);
} else {
$props[$key] = $value;
}
}
return $props;
}
}

View file

@ -0,0 +1,122 @@
<?php
namespace Inertia;
use Closure;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Response as BaseResponse;
use Illuminate\Support\Traits\Macroable;
class ResponseFactory
{
use Macroable;
/** @var string */
protected $rootView = 'app';
/** @var array */
protected $sharedProps = [];
/** @var Closure|string|null */
protected $version;
public function setRootView(string $name): void
{
$this->rootView = $name;
}
/**
* @param string|array|Arrayable $key
* @param mixed|null $value
*/
public function share($key, $value = null): void
{
if (is_array($key)) {
$this->sharedProps = array_merge($this->sharedProps, $key);
} elseif ($key instanceof Arrayable) {
$this->sharedProps = array_merge($this->sharedProps, $key->toArray());
} else {
Arr::set($this->sharedProps, $key, $value);
}
}
/**
* @param string|null $key
* @param null|mixed $default
* @return mixed
*/
public function getShared(string $key = null, $default = null)
{
if ($key) {
return Arr::get($this->sharedProps, $key, $default);
}
return $this->sharedProps;
}
public function flushShared(): void
{
$this->sharedProps = [];
}
/**
* @param Closure|string|null $version
*/
public function version($version): void
{
$this->version = $version;
}
public function getVersion(): string
{
$version = $this->version instanceof Closure
? App::call($this->version)
: $this->version;
return (string) $version;
}
public function lazy(callable $callback): LazyProp
{
return new LazyProp($callback);
}
/**
* @param string $component
* @param array|Arrayable $props
* @return Response
*/
public function render(string $component, $props = []): Response
{
if ($props instanceof Arrayable) {
$props = $props->toArray();
}
return new Response(
$component,
array_merge($this->sharedProps, $props),
$this->rootView,
$this->getVersion()
);
}
/**
* @param string|RedirectResponse $url
*/
public function location($url): \Symfony\Component\HttpFoundation\Response
{
if ($url instanceof RedirectResponse) {
$url = $url->getTargetUrl();
}
if (Request::inertia()) {
return BaseResponse::make('', 409, ['X-Inertia-Location' => $url]);
}
return new RedirectResponse($url);
}
}

View file

@ -0,0 +1,106 @@
<?php
namespace Inertia;
use Illuminate\Foundation\Testing\TestResponse as LegacyTestResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider as BaseServiceProvider;
use Illuminate\Testing\TestResponse;
use Illuminate\View\FileViewFinder;
use Inertia\Ssr\Gateway;
use Inertia\Ssr\HttpGateway;
use Inertia\Testing\TestResponseMacros;
use LogicException;
use ReflectionException;
class ServiceProvider extends BaseServiceProvider
{
public function register(): void
{
$this->app->singleton(ResponseFactory::class);
$this->app->bind(Gateway::class, HttpGateway::class);
$this->mergeConfigFrom(
__DIR__.'/../config/inertia.php',
'inertia'
);
$this->registerRequestMacro();
$this->registerRouterMacro();
$this->registerTestingMacros();
$this->app->bind('inertia.testing.view-finder', function ($app) {
return new FileViewFinder(
$app['files'],
$app['config']->get('inertia.testing.page_paths'),
$app['config']->get('inertia.testing.page_extensions')
);
});
}
public function boot(): void
{
$this->registerBladeDirectives();
$this->registerConsoleCommands();
$this->publishes([
__DIR__.'/../config/inertia.php' => config_path('inertia.php'),
]);
}
protected function registerBladeDirectives(): void
{
Blade::directive('inertia', [Directive::class, 'compile']);
Blade::directive('inertiaHead', [Directive::class, 'compileHead']);
}
protected function registerConsoleCommands(): void
{
if (! $this->app->runningInConsole()) {
return;
}
$this->commands([
Console\CreateMiddleware::class,
]);
}
protected function registerRequestMacro(): void
{
Request::macro('inertia', function () {
return (bool) $this->header('X-Inertia');
});
}
protected function registerRouterMacro(): void
{
Router::macro('inertia', function ($uri, $component, $props = []) {
return $this->match(['GET', 'HEAD'], $uri, Controller::class)
->defaults('component', $component)
->defaults('props', $props);
});
}
/**
* @throws ReflectionException|LogicException
*/
protected function registerTestingMacros(): void
{
if (class_exists(TestResponse::class)) {
TestResponse::mixin(new TestResponseMacros());
return;
}
// Laravel <= 6.0
if (class_exists(LegacyTestResponse::class)) {
LegacyTestResponse::mixin(new TestResponseMacros());
return;
}
throw new LogicException('Could not detect TestResponse class.');
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace Inertia\Ssr;
interface Gateway
{
/**
* Dispatch the Inertia page to the Server Side Rendering engine.
*
* @param array $page
* @return Response|null
*/
public function dispatch(array $page): ?Response;
}

View file

@ -0,0 +1,40 @@
<?php
namespace Inertia\Ssr;
use Exception;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;
class HttpGateway implements Gateway
{
/**
* Dispatch the Inertia page to the Server Side Rendering engine.
*
* @param array $page
* @return Response|null
*/
public function dispatch(array $page): ?Response
{
if (! Config::get('inertia.ssr.enabled', false)) {
return null;
}
$url = Config::get('inertia.ssr.url', 'http://127.0.0.1:13714/render');
try {
$response = Http::post($url, $page)->throw()->json();
} catch (Exception $e) {
return null;
}
if (is_null($response)) {
return null;
}
return new Response(
implode("\n", $response['head']),
$response['body']
);
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Inertia\Ssr;
class Response
{
/**
* @var string
*/
public $head;
/**
* @var string
*/
public $body;
/**
* Prepare the Inertia Server Side Rendering (SSR) response.
*
* @param string $head
* @param string $body
*/
public function __construct(string $head, string $body)
{
$this->head = $head;
$this->body = $body;
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace Inertia\Testing;
use Closure;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Traits\Macroable;
use PHPUnit\Framework\Assert as PHPUnit;
use PHPUnit\Framework\AssertionFailedError;
class Assert implements Arrayable
{
use Concerns\Has,
Concerns\Matching,
Concerns\Debugging,
Concerns\PageObject,
Concerns\Interaction,
Macroable;
/** @var string */
private $component;
/** @var array */
private $props;
/** @var string */
private $url;
/** @var string|null */
private $version;
/** @var string */
private $path;
protected function __construct(string $component, array $props, string $url, string $version = null, string $path = null)
{
echo "\033[0;31mInertia's built-in 'Assert' library will be removed in a future version of inertia-laravel:\033[0m\n";
echo "\033[0;31m - If you are seeing this error while using \$response->assertInertia(...), please upgrade to Laravel 8.32.0 or higher.\033[0m\n";
echo "\033[0;31m - If you are using the 'Assert' class directly, please adapt your tests to use the 'AssertableInertia' class instead.\033[0m\n";
echo "\033[0;31mFor more information and questions, please see https://github.com/inertiajs/inertia-laravel/pull/338 \033[0m\n\n";
@trigger_error("Inertia's built-in 'Assert' library will be removed in a future version of inertia-laravel: https://github.com/inertiajs/inertia-laravel/pull/338", \E_USER_DEPRECATED);
$this->path = $path;
$this->component = $component;
$this->props = $props;
$this->url = $url;
$this->version = $version;
}
protected function dotPath(string $key): string
{
if (is_null($this->path)) {
return $key;
}
return implode('.', [$this->path, $key]);
}
protected function scope(string $key, Closure $callback): self
{
$props = $this->prop($key);
$path = $this->dotPath($key);
PHPUnit::assertIsArray($props, sprintf('Inertia property [%s] is not scopeable.', $path));
$scope = new self($this->component, $props, $this->url, $this->version, $path);
$callback($scope);
$scope->interacted();
return $this;
}
public static function fromTestResponse($response): self
{
try {
$response->assertViewHas('page');
$page = json_decode(json_encode($response->viewData('page')), true);
PHPUnit::assertIsArray($page);
PHPUnit::assertArrayHasKey('component', $page);
PHPUnit::assertArrayHasKey('props', $page);
PHPUnit::assertArrayHasKey('url', $page);
PHPUnit::assertArrayHasKey('version', $page);
} catch (AssertionFailedError $e) {
PHPUnit::fail('Not a valid Inertia response.');
}
return new self($page['component'], $page['props'], $page['url'], $page['version']);
}
}

View file

@ -0,0 +1,83 @@
<?php
namespace Inertia\Testing;
use Illuminate\Testing\Fluent\AssertableJson;
use Illuminate\Testing\TestResponse;
use InvalidArgumentException;
use PHPUnit\Framework\Assert as PHPUnit;
use PHPUnit\Framework\AssertionFailedError;
class AssertableInertia extends AssertableJson
{
/** @var string */
private $component;
/** @var string */
private $url;
/** @var string|null */
private $version;
public static function fromTestResponse(TestResponse $response): self
{
try {
$response->assertViewHas('page');
$page = json_decode(json_encode($response->viewData('page')), true);
PHPUnit::assertIsArray($page);
PHPUnit::assertArrayHasKey('component', $page);
PHPUnit::assertArrayHasKey('props', $page);
PHPUnit::assertArrayHasKey('url', $page);
PHPUnit::assertArrayHasKey('version', $page);
} catch (AssertionFailedError $e) {
PHPUnit::fail('Not a valid Inertia response.');
}
$instance = static::fromArray($page['props']);
$instance->component = $page['component'];
$instance->url = $page['url'];
$instance->version = $page['version'];
return $instance;
}
public function component(string $value = null, $shouldExist = null): self
{
PHPUnit::assertSame($value, $this->component, 'Unexpected Inertia page component.');
if ($shouldExist || (is_null($shouldExist) && config('inertia.testing.ensure_pages_exist', true))) {
try {
app('inertia.testing.view-finder')->find($value);
} catch (InvalidArgumentException $exception) {
PHPUnit::fail(sprintf('Inertia page component file [%s] does not exist.', $value));
}
}
return $this;
}
public function url(string $value): self
{
PHPUnit::assertSame($value, $this->url, 'Unexpected Inertia page url.');
return $this;
}
public function version(string $value): self
{
PHPUnit::assertSame($value, $this->version, 'Unexpected Inertia asset version.');
return $this;
}
public function toArray()
{
return [
'component' => $this->component,
'props' => $this->prop(),
'url' => $this->url,
'version' => $this->version,
];
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace Inertia\Testing\Concerns;
trait Debugging
{
public function dump(string $prop = null): self
{
dump($this->prop($prop));
return $this;
}
public function dd(string $prop = null): void
{
dd($this->prop($prop));
}
abstract protected function prop(string $key = null);
}

View file

@ -0,0 +1,119 @@
<?php
namespace Inertia\Testing\Concerns;
use Closure;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use PHPUnit\Framework\Assert as PHPUnit;
trait Has
{
protected function count(string $key, int $length): self
{
PHPUnit::assertCount(
$length,
$this->prop($key),
sprintf('Inertia property [%s] does not have the expected size.', $this->dotPath($key))
);
return $this;
}
public function hasAll($key): self
{
$keys = is_array($key) ? $key : func_get_args();
foreach ($keys as $prop => $count) {
if (is_int($prop)) {
$this->has($count);
} else {
$this->has($prop, $count);
}
}
return $this;
}
/**
* @param string $key
* @param mixed|null $value
* @param Closure|null $scope
* @return $this
*/
public function has(string $key, $value = null, Closure $scope = null): self
{
PHPUnit::assertTrue(
Arr::has($this->prop(), $key),
sprintf('Inertia property [%s] does not exist.', $this->dotPath($key))
);
$this->interactsWith($key);
if (is_int($value) && ! is_null($scope)) {
$path = $this->dotPath($key);
$prop = $this->prop($key);
if ($prop instanceof Collection) {
$prop = $prop->all();
}
PHPUnit::assertTrue($value > 0, sprintf('Cannot scope directly onto the first entry of property [%s] when asserting that it has a size of 0.', $path));
PHPUnit::assertIsArray($prop, sprintf('Direct scoping is currently unsupported for non-array like properties such as [%s].', $path));
$this->count($key, $value);
return $this->scope($key.'.'.array_keys($prop)[0], $scope);
}
if (is_callable($value)) {
$this->scope($key, $value);
} elseif (! is_null($value)) {
$this->count($key, $value);
}
return $this;
}
public function missingAll($key): self
{
$keys = is_array($key) ? $key : func_get_args();
foreach ($keys as $prop) {
$this->misses($prop);
}
return $this;
}
public function missing(string $key): self
{
$this->interactsWith($key);
PHPUnit::assertNotTrue(
Arr::has($this->prop(), $key),
sprintf('Inertia property [%s] was found while it was expected to be missing.', $this->dotPath($key))
);
return $this;
}
public function missesAll($key): self
{
return $this->missingAll(
is_array($key) ? $key : func_get_args()
);
}
public function misses(string $key): self
{
return $this->missing($key);
}
abstract protected function prop(string $key = null);
abstract protected function dotPath(string $key): string;
abstract protected function interactsWith(string $key): void;
abstract protected function scope(string $key, Closure $callback);
}

View file

@ -0,0 +1,41 @@
<?php
namespace Inertia\Testing\Concerns;
use Illuminate\Support\Str;
use PHPUnit\Framework\Assert as PHPUnit;
trait Interaction
{
/** @var array */
protected $interacted = [];
protected function interactsWith(string $key): void
{
$prop = Str::before($key, '.');
if (! in_array($prop, $this->interacted, true)) {
$this->interacted[] = $prop;
}
}
public function interacted(): void
{
PHPUnit::assertSame(
[],
array_diff(array_keys($this->prop()), $this->interacted),
$this->path
? sprintf('Unexpected Inertia properties were found in scope [%s].', $this->path)
: 'Unexpected Inertia properties were found on the root level.'
);
}
public function etc(): self
{
$this->interacted = array_keys($this->prop());
return $this;
}
abstract protected function prop(string $key = null);
}

View file

@ -0,0 +1,74 @@
<?php
namespace Inertia\Testing\Concerns;
use Closure;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\Json\ResourceResponse;
use Illuminate\Support\Collection;
use PHPUnit\Framework\Assert as PHPUnit;
trait Matching
{
public function whereAll(array $bindings): self
{
foreach ($bindings as $key => $value) {
$this->where($key, $value);
}
return $this;
}
public function where(string $key, $expected): self
{
$this->has($key);
$actual = $this->prop($key);
if ($expected instanceof Closure) {
PHPUnit::assertTrue(
$expected(is_array($actual) ? Collection::make($actual) : $actual),
sprintf('Inertia property [%s] was marked as invalid using a closure.', $this->dotPath($key))
);
return $this;
}
if ($expected instanceof Arrayable) {
$expected = $expected->toArray();
} elseif ($expected instanceof ResourceResponse || $expected instanceof JsonResource) {
$expected = json_decode(json_encode($expected->toResponse(request())->getData()), true);
}
$this->ensureSorted($expected);
$this->ensureSorted($actual);
PHPUnit::assertSame(
$expected,
$actual,
sprintf('Inertia property [%s] does not match the expected value.', $this->dotPath($key))
);
return $this;
}
protected function ensureSorted(&$value): void
{
if (! is_array($value)) {
return;
}
foreach ($value as &$arg) {
$this->ensureSorted($arg);
}
ksort($value);
}
abstract protected function dotPath(string $key): string;
abstract protected function prop(string $key = null);
abstract public function has(string $key, $value = null, Closure $scope = null);
}

View file

@ -0,0 +1,54 @@
<?php
namespace Inertia\Testing\Concerns;
use Illuminate\Support\Arr;
use InvalidArgumentException;
use PHPUnit\Framework\Assert as PHPUnit;
trait PageObject
{
public function component(string $value = null, $shouldExist = null): self
{
PHPUnit::assertSame($value, $this->component, 'Unexpected Inertia page component.');
if ($shouldExist || (is_null($shouldExist) && config('inertia.testing.ensure_pages_exist', true))) {
try {
app('inertia.testing.view-finder')->find($value);
} catch (InvalidArgumentException $exception) {
PHPUnit::fail(sprintf('Inertia page component file [%s] does not exist.', $value));
}
}
return $this;
}
protected function prop(string $key = null)
{
return Arr::get($this->props, $key);
}
public function url(string $value): self
{
PHPUnit::assertSame($value, $this->url, 'Unexpected Inertia page url.');
return $this;
}
public function version(string $value): self
{
PHPUnit::assertSame($value, $this->version, 'Unexpected Inertia asset version.');
return $this;
}
public function toArray(): array
{
return [
'component' => $this->component,
'props' => $this->props,
'url' => $this->url,
'version' => $this->version,
];
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace Inertia\Testing;
use Closure;
use Illuminate\Testing\Fluent\AssertableJson;
class TestResponseMacros
{
public function assertInertia()
{
return function (Closure $callback = null) {
if (class_exists(AssertableJson::class)) {
$assert = AssertableInertia::fromTestResponse($this);
} else {
$assert = Assert::fromTestResponse($this);
}
if (is_null($callback)) {
return $this;
}
$callback($assert);
return $this;
};
}
public function inertiaPage()
{
return function () {
if (class_exists(AssertableJson::class)) {
return AssertableInertia::fromTestResponse($this)->toArray();
}
return Assert::fromTestResponse($this)->toArray();
};
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace {{ namespace }};
use Illuminate\Http\Request;
use Inertia\Middleware;
class {{ class }} extends Middleware
{
/**
* The root template that's loaded on the first page visit.
*
* @see https://inertiajs.com/server-side-setup#root-template
* @var string
*/
protected $rootView = 'app';
/**
* Determines the current asset version.
*
* @see https://inertiajs.com/asset-versioning
* @param \Illuminate\Http\Request $request
* @return string|null
*/
public function version(Request $request): ?string
{
return parent::version($request);
}
/**
* Defines the props that are shared by default.
*
* @see https://inertiajs.com/shared-data
* @param \Illuminate\Http\Request $request
* @return array
*/
public function share(Request $request): array
{
return array_merge(parent::share($request), [
//
]);
}
}

11
webpack.mix.js vendored
View file

@ -11,11 +11,6 @@ let mix = require('laravel-mix');
|
*/
mix.babel([
//'resources/assets/js/jquery-ui.min.js',
'resources/assets/js/huebee.js',
'resources/assets/js/app.js'
], 'public/js/app.js')
.sass('resources/assets/sass/app.scss', 'public/css').options({
processCssUrls: false
}).version();
mix.js('resources/assets/js/app.js', 'public/js').vue({ version: 3 })

6400
yarn.lock

File diff suppressed because it is too large Load diff