From 2bb0c017d760f04739c551951ea3611fcf432266 Mon Sep 17 00:00:00 2001 From: Kelvin Oghenerhoro Omereshone Date: Tue, 19 May 2026 19:18:52 +0100 Subject: [PATCH 1/6] feat(inertia-sails): support Inertia v3 protocol metadata --- packages/inertia-sails/README.md | 25 ++- .../lib/helpers/build-page-object.js | 33 +++- .../lib/helpers/inertia-headers.js | 5 +- .../lib/helpers/resolve-asset-version.js | 4 + .../inertia-sails/lib/props/merge-prop.js | 2 +- .../inertia-sails/lib/props/mergeable-prop.js | 118 +++++++++++++ .../lib/props/resolve-merge-props.js | 86 ++++++++-- .../lib/props/resolve-scroll-props.js | 2 +- .../inertia-sails/lib/props/scroll-prop.js | 8 +- packages/inertia-sails/lib/render.js | 47 +++++- packages/inertia-sails/package.json | 2 +- .../tests/helpers/build-page-object.test.js | 105 ++++++++++++ .../tests/props/resolve-merge-props.test.js | 147 ++++++++++++++++ .../tests/props/resolve-scroll-props.test.js | 51 ++++++ packages/inertia-sails/tests/render.test.js | 158 ++++++++++++++++++ templates/ascent-react/views/app.ejs | 5 +- templates/ascent-vue/views/app.ejs | 5 +- templates/mellow-react/views/app.ejs | 5 +- templates/mellow-svelte/views/app.ejs | 5 +- templates/mellow-vue/views/app.ejs | 5 +- 20 files changed, 772 insertions(+), 46 deletions(-) create mode 100644 packages/inertia-sails/lib/helpers/resolve-asset-version.js create mode 100644 packages/inertia-sails/tests/helpers/build-page-object.test.js create mode 100644 packages/inertia-sails/tests/props/resolve-merge-props.test.js create mode 100644 packages/inertia-sails/tests/props/resolve-scroll-props.test.js create mode 100644 packages/inertia-sails/tests/render.test.js diff --git a/packages/inertia-sails/README.md b/packages/inertia-sails/README.md index dd7ba073..698dd92d 100644 --- a/packages/inertia-sails/README.md +++ b/packages/inertia-sails/README.md @@ -38,7 +38,10 @@ module.exports.inertia = { <%- shipwright.styles() %> -
+
+ <%- shipwright.scripts() %> @@ -199,13 +202,29 @@ Merge with existing client-side data (useful for infinite scroll): // Shallow merge messages: sails.inertia.merge(() => newMessages) +// Prepend new items instead of appending +notifications: sails.inertia.merge(() => newNotifications).prepend() + +// Merge a nested array inside a paginated object +users: sails.inertia.merge(() => paginatedUsers).append('data') + +// Match existing items by ID when merging +users: sails.inertia + .merge(() => paginatedUsers) + .append('data', { + matchOn: 'id' + }) + // Deep merge (nested objects) settings: sails.inertia.deepMerge(() => updatedSettings) + +// Deep merge with item matching +chat: sails.inertia.deepMerge(() => chatState).matchOn('messages.id') ``` ### Infinite Scroll -Paginate data with automatic merge behavior. Works with Inertia.js v2's `` component: +Paginate data with automatic merge behavior. Works with Inertia's `` component: ```js // Controller @@ -244,6 +263,8 @@ defineProps({ invoices: Object }) ``` +`scroll()` targets the wrapped array for merging, such as `invoices.data`, and follows Inertia's infinite-scroll merge intent header so previous-page requests prepend while next-page requests append. + ### History Encryption Encrypt sensitive data in browser history: diff --git a/packages/inertia-sails/lib/helpers/build-page-object.js b/packages/inertia-sails/lib/helpers/build-page-object.js index 816ed93a..0ba044ce 100644 --- a/packages/inertia-sails/lib/helpers/build-page-object.js +++ b/packages/inertia-sails/lib/helpers/build-page-object.js @@ -4,6 +4,7 @@ const resolveMergeProps = require('../props/resolve-merge-props') const { resolveOncePropsMetadata } = require('../props/resolve-once-props') const resolvePageProps = require('../props/resolve-page-props') const resolveScrollProps = require('../props/resolve-scroll-props') +const resolveAssetVersion = require('./resolve-asset-version') /** * @typedef {Object} InertiaPageObject @@ -11,10 +12,13 @@ const resolveScrollProps = require('../props/resolve-scroll-props') * @property {string} url - The current URL * @property {string|number} version - Asset version for cache busting * @property {Object.} props - Resolved page props - * @property {boolean} clearHistory - Whether to clear browser history - * @property {boolean} encryptHistory - Whether to encrypt history state + * @property {boolean} [clearHistory] - Whether to clear browser history + * @property {boolean} [encryptHistory] - Whether to encrypt history state + * @property {string[]} [sharedProps] - Shared prop keys included in this response * @property {string[]} [mergeProps] - Props that should be merged on client + * @property {string[]} [prependProps] - Props that should be prepended on client * @property {string[]} [deepMergeProps] - Props that should be deep merged + * @property {string[]} [matchPropsOn] - Prop paths to use for matching merge items * @property {Object.} [deferredProps] - Deferred props by group * @property {Object.} [onceProps] - Once-prop metadata * @property {Object.} [scrollProps] - Scroll props for InfiniteScroll component @@ -38,18 +42,21 @@ const resolveScrollProps = require('../props/resolve-scroll-props') module.exports = async function buildPageObject(req, component, pageProps) { const sails = req._sails let url = req.url || req.originalUrl - const assetVersion = sails.config.inertia.version - const currentVersion = - typeof assetVersion === 'function' ? assetVersion() : assetVersion + const currentVersion = resolveAssetVersion(sails) + + const sharedProps = sails.inertia.getShared() + const sharedPropKeys = Object.keys(sharedProps) // Merge props: global shared → request-scoped shared → page-specific // This ensures user-specific data (from share()) doesn't leak between requests const allProps = { - ...sails.inertia.getShared(), // Merges global + request-scoped + ...sharedProps, // Merges global + request-scoped ...pageProps } const propsToResolve = pickPropsToResolve(req, component, allProps) + const clearHistory = sails.inertia.shouldClearHistory() + const encryptHistory = sails.inertia.shouldEncryptHistory() // Build the page object with all metadata // Use request-scoped history settings (prevents race conditions) @@ -58,14 +65,24 @@ module.exports = async function buildPageObject(req, component, pageProps) { url, version: currentVersion, props: await resolvePageProps(propsToResolve), - clearHistory: sails.inertia.shouldClearHistory(), - encryptHistory: sails.inertia.shouldEncryptHistory(), ...resolveMergeProps(req, allProps), ...resolveDeferredProps(req, component, allProps), ...resolveOncePropsMetadata(allProps), ...resolveScrollProps(allProps) } + if (clearHistory) { + page.clearHistory = true + } + + if (encryptHistory) { + page.encryptHistory = true + } + + if (sharedPropKeys.length > 0) { + page.sharedProps = sharedPropKeys + } + // Consume flash data from session and add to props // Flash data is included in props.flash so it's accessible via usePage().props.flash // Note: Unlike regular props, flash data should NOT be persisted in browser history diff --git a/packages/inertia-sails/lib/helpers/inertia-headers.js b/packages/inertia-sails/lib/helpers/inertia-headers.js index 679848a3..a8db8701 100644 --- a/packages/inertia-sails/lib/helpers/inertia-headers.js +++ b/packages/inertia-sails/lib/helpers/inertia-headers.js @@ -23,6 +23,8 @@ const PARTIAL_COMPONENT = 'X-Inertia-Partial-Component' const LOCATION = 'X-Inertia-Location' /** @type {string} - Comma-separated list of once-props client already has */ const EXCEPT_ONCE_PROPS = 'X-Inertia-Except-Once-Props' +/** @type {string} - InfiniteScroll merge direction */ +const INFINITE_SCROLL_MERGE_INTENT = 'X-Inertia-Infinite-Scroll-Merge-Intent' module.exports = { INERTIA, @@ -33,5 +35,6 @@ module.exports = { RESET, PARTIAL_COMPONENT, LOCATION, - EXCEPT_ONCE_PROPS + EXCEPT_ONCE_PROPS, + INFINITE_SCROLL_MERGE_INTENT } diff --git a/packages/inertia-sails/lib/helpers/resolve-asset-version.js b/packages/inertia-sails/lib/helpers/resolve-asset-version.js new file mode 100644 index 00000000..51909ff1 --- /dev/null +++ b/packages/inertia-sails/lib/helpers/resolve-asset-version.js @@ -0,0 +1,4 @@ +module.exports = function resolveAssetVersion(sails) { + const assetVersion = sails.config.inertia.version + return typeof assetVersion === 'function' ? assetVersion() : assetVersion +} diff --git a/packages/inertia-sails/lib/props/merge-prop.js b/packages/inertia-sails/lib/props/merge-prop.js index a6424244..eae5e32d 100644 --- a/packages/inertia-sails/lib/props/merge-prop.js +++ b/packages/inertia-sails/lib/props/merge-prop.js @@ -29,7 +29,7 @@ module.exports = class MergeProp extends MergeableProp { /** @type {Function} */ this.callback = callback /** @type {boolean} */ - this.shouldMerge = true + this.merge() } /** diff --git a/packages/inertia-sails/lib/props/mergeable-prop.js b/packages/inertia-sails/lib/props/mergeable-prop.js index 3cc8267d..fa2f277d 100644 --- a/packages/inertia-sails/lib/props/mergeable-prop.js +++ b/packages/inertia-sails/lib/props/mergeable-prop.js @@ -12,6 +12,10 @@ module.exports = class MergeableProp { this.shouldMerge = false /** @type {boolean} - Whether to deep merge this prop */ this.shouldDeepMerge = false + /** @type {{direction: string, path: string|null, matchOn: string|null, isDefault?: boolean}[]} */ + this.mergeOperations = [] + /** @type {string[]} - Paths to use when matching merge items */ + this.matchOnPaths = [] } /** @@ -21,9 +25,38 @@ module.exports = class MergeableProp { */ merge() { this.shouldMerge = true + this.shouldDeepMerge = false + if (this.mergeOperations.length === 0) { + this.mergeOperations.push({ + direction: 'append', + path: null, + matchOn: null, + isDefault: true + }) + } return this } + /** + * Append this prop, or one or more nested paths, during partial reloads. + * @param {string|string[]|Object|null} [paths] - Path(s) to append, or a path-to-matchOn map + * @param {Object|string} [options] - Options, or a matchOn string + * @returns {MergeableProp} - Returns this for chaining + */ + append(paths = null, options = {}) { + return this._addMergeOperations('append', paths, options) + } + + /** + * Prepend this prop, or one or more nested paths, during partial reloads. + * @param {string|string[]|Object|null} [paths] - Path(s) to prepend, or a path-to-matchOn map + * @param {Object|string} [options] - Options, or a matchOn string + * @returns {MergeableProp} - Returns this for chaining + */ + prepend(paths = null, options = {}) { + return this._addMergeOperations('prepend', paths, options) + } + /** * Enable deep merging for this prop. * Recursively merges nested objects instead of replacing them. @@ -32,6 +65,91 @@ module.exports = class MergeableProp { deepMerge() { this.shouldMerge = true this.shouldDeepMerge = true + this.mergeOperations = [] + return this + } + + /** + * Configure one or more match-on paths for merge operations. + * @param {string|string[]} paths - Path(s) ending with the field to match on + * @returns {MergeableProp} - Returns this for chaining + */ + matchOn(paths) { + const normalizedPaths = Array.isArray(paths) ? paths : [paths] + normalizedPaths.filter(Boolean).forEach((path) => { + this.matchOnPaths.push(path) + }) + return this + } + + _addMergeOperations(direction, paths, options) { + const normalizedOptions = + typeof options === 'string' ? { matchOn: options } : options || {} + + this.shouldMerge = true + this.shouldDeepMerge = false + this._clearDefaultMergeOperation() + + this._normalizeMergeTargets(paths, normalizedOptions).forEach((target) => { + this.mergeOperations.push({ + direction, + path: target.path, + matchOn: target.matchOn + }) + }) + return this } + + _clearDefaultMergeOperation() { + if ( + this.mergeOperations.length === 1 && + this.mergeOperations[0].isDefault + ) { + this.mergeOperations = [] + } + } + + _normalizeMergeTargets(paths, options) { + if (paths === null || paths === undefined) { + return [ + { + path: null, + matchOn: options.matchOn || null + } + ] + } + + if (Array.isArray(paths)) { + return paths.map((path) => ({ + path: this._normalizePath(path), + matchOn: this._resolveMatchOn(path, options) + })) + } + + if (typeof paths === 'object') { + return Object.entries(paths).map(([path, matchOn]) => ({ + path: this._normalizePath(path), + matchOn + })) + } + + return [ + { + path: this._normalizePath(paths), + matchOn: this._resolveMatchOn(paths, options) + } + ] + } + + _normalizePath(path) { + return path === '' ? null : path + } + + _resolveMatchOn(path, options) { + if (!options.matchOn) return null + if (typeof options.matchOn === 'object') + return options.matchOn[path] || null + return options.matchOn + } } diff --git a/packages/inertia-sails/lib/props/resolve-merge-props.js b/packages/inertia-sails/lib/props/resolve-merge-props.js index cccdace7..06efe2d5 100644 --- a/packages/inertia-sails/lib/props/resolve-merge-props.js +++ b/packages/inertia-sails/lib/props/resolve-merge-props.js @@ -1,5 +1,9 @@ -const { RESET } = require('../helpers/inertia-headers') +const { + INFINITE_SCROLL_MERGE_INTENT, + RESET +} = require('../helpers/inertia-headers') const MergeableProp = require('./mergeable-prop') +const ScrollProp = require('./scroll-prop') /** * Resolve merge props metadata for the page response. @@ -11,27 +15,75 @@ const MergeableProp = require('./mergeable-prop') module.exports = function resolveMergeProps(req, pageProps) { const inertiaResetHeader = req.get(RESET) const resetProps = new Set(inertiaResetHeader?.split(',').filter(Boolean)) + const infiniteScrollMergeIntent = req.get(INFINITE_SCROLL_MERGE_INTENT) - const mergeableEntries = Object.entries(pageProps || {}).filter( - ([key, value]) => - value instanceof MergeableProp && - value.shouldMerge && - !resetProps.has(key) - ) + const mergeProps = [] + const prependProps = [] + const deepMergeProps = [] + const matchPropsOn = [] - // Props that should be shallow merged (appended) - const mergeProps = mergeableEntries - .filter(([_, value]) => !value.shouldDeepMerge) - .map(([key]) => key) + Object.entries(pageProps || {}).forEach(([key, value]) => { + if (!(value instanceof MergeableProp) || !value.shouldMerge) return + if (resetProps.has(key)) return - // Props that should be deep merged - const deepMergeProps = mergeableEntries - .filter(([_, value]) => value.shouldDeepMerge) - .map(([key]) => key) + if (value instanceof ScrollProp) { + const propPath = resolvePropPath(key, value.wrapper) + if (resetProps.has(propPath)) return + + if (infiniteScrollMergeIntent === 'prepend') { + prependProps.push(propPath) + } else { + mergeProps.push(propPath) + } + + if (value.matchOn) { + matchPropsOn.push(resolvePropPath(propPath, value.matchOn)) + } + + return + } + + if (value.shouldDeepMerge) { + deepMergeProps.push(key) + value.matchOnPaths.forEach((path) => { + matchPropsOn.push(resolvePropPath(key, path)) + }) + return + } + + value.mergeOperations.forEach((operation) => { + const propPath = resolvePropPath(key, operation.path) + if (resetProps.has(propPath)) return + + if (operation.direction === 'prepend') { + prependProps.push(propPath) + } else { + mergeProps.push(propPath) + } + + if (operation.matchOn) { + matchPropsOn.push(resolvePropPath(propPath, operation.matchOn)) + } + }) + + value.matchOnPaths.forEach((path) => { + matchPropsOn.push(resolvePropPath(key, path)) + }) + }) const result = {} - if (mergeProps.length) result.mergeProps = mergeProps - if (deepMergeProps.length) result.deepMergeProps = deepMergeProps + if (mergeProps.length) result.mergeProps = unique(mergeProps) + if (prependProps.length) result.prependProps = unique(prependProps) + if (deepMergeProps.length) result.deepMergeProps = unique(deepMergeProps) + if (matchPropsOn.length) result.matchPropsOn = unique(matchPropsOn) return result } + +function resolvePropPath(key, path) { + return path ? `${key}.${path}` : key +} + +function unique(values) { + return [...new Set(values)] +} diff --git a/packages/inertia-sails/lib/props/resolve-scroll-props.js b/packages/inertia-sails/lib/props/resolve-scroll-props.js index bdf7cba3..5908947b 100644 --- a/packages/inertia-sails/lib/props/resolve-scroll-props.js +++ b/packages/inertia-sails/lib/props/resolve-scroll-props.js @@ -3,7 +3,7 @@ const ScrollProp = require('./scroll-prop') /** * Resolve scroll props metadata for the page response. * Extracts ScrollProp instances and builds the scrollProps object - * expected by Inertia.js v2's component. + * expected by Inertia's component. * * @param {Object} pageProps - The page props * @returns {Object} - Object with scrollProps if any exist diff --git a/packages/inertia-sails/lib/props/scroll-prop.js b/packages/inertia-sails/lib/props/scroll-prop.js index ce1a7506..0ec07d54 100644 --- a/packages/inertia-sails/lib/props/scroll-prop.js +++ b/packages/inertia-sails/lib/props/scroll-prop.js @@ -27,6 +27,7 @@ class ScrollProp extends MergeProp { * @param {number} [options.total=0] - Total number of items * @param {string} [options.pageName='page'] - Query parameter name for pagination * @param {string} [options.wrapper='data'] - Key to wrap the data in + * @param {string} [options.matchOn] - Optional field used to match items when merging */ constructor(callback, options = {}) { const { @@ -34,7 +35,8 @@ class ScrollProp extends MergeProp { perPage = 10, total = 0, pageName = 'page', - wrapper = 'data' + wrapper = 'data', + matchOn = null } = options // Calculate pagination metadata @@ -66,12 +68,16 @@ class ScrollProp extends MergeProp { super(wrappedCallback) + // InfiniteScroll uses request headers to decide append vs prepend. + this.mergeOperations = [] + // Store metadata for potential access this.page = page this.perPage = perPage this.total = total this.pageName = pageName this.wrapper = wrapper + this.matchOn = matchOn this.totalPages = totalPages this.currentPage = currentPage } diff --git a/packages/inertia-sails/lib/render.js b/packages/inertia-sails/lib/render.js index 719afbe1..cee2d8ef 100644 --- a/packages/inertia-sails/lib/render.js +++ b/packages/inertia-sails/lib/render.js @@ -2,6 +2,33 @@ const { encode } = require('querystring') const inertiaHeaders = require('./helpers/inertia-headers') const buildPageObject = require('./helpers/build-page-object') const requestContext = require('./helpers/request-context') +const resolveAssetVersion = require('./helpers/resolve-asset-version') + +function getRequestUrl(req) { + let url = req.url || req.originalUrl + const queryParams = req.query || {} + + if (req.method === 'GET' && Object.keys(queryParams).length) { + // Only append query params if the URL doesn't already contain them + // This prevents duplication when redirecting with query parameters + if (!url.includes('?')) { + url += `?${encode(queryParams)}` + } + } + + return url +} + +function hasAssetVersionMismatch(req, currentVersion) { + const requestVersion = req.get(inertiaHeaders.VERSION) + + if (req.method !== 'GET') return false + if (!req.get(inertiaHeaders.INERTIA)) return false + if (requestVersion === undefined || requestVersion === null) return false + if (currentVersion === undefined || currentVersion === null) return false + + return String(requestVersion) !== String(currentVersion) +} module.exports = async function render(req, res, data) { const sails = req._sails @@ -15,20 +42,22 @@ module.exports = async function render(req, res, data) { ...data.locals } - let page = await buildPageObject(req, data.page, data.props) + const currentVersion = resolveAssetVersion(sails) + const requestUrl = getRequestUrl(req) - const queryParams = req.query - if (req.method === 'GET' && Object.keys(queryParams).length) { - // Only append query params if the URL doesn't already contain them - // This prevents duplication when redirecting with query parameters - if (!page.url.includes('?')) { - page.url += `?${encode(queryParams)}` - } + if (hasAssetVersionMismatch(req, currentVersion)) { + res.set('Vary', 'X-Inertia') + res.set(inertiaHeaders.LOCATION, requestUrl) + return res.status(409).end() } + let page = await buildPageObject(req, data.page, data.props) + + page.url = requestUrl + if (req.get(inertiaHeaders.INERTIA)) { res.set(inertiaHeaders.INERTIA, true) - res.set('Vary', 'Accept') + res.set('Vary', 'X-Inertia') return res.json(page) } else { // Implements full page reload diff --git a/packages/inertia-sails/package.json b/packages/inertia-sails/package.json index aebb39c0..e39e4caf 100644 --- a/packages/inertia-sails/package.json +++ b/packages/inertia-sails/package.json @@ -8,7 +8,7 @@ "hookName": "inertia" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "node --test tests/*.test.js tests/**/*.test.js" }, "repository": { "type": "git", diff --git a/packages/inertia-sails/tests/helpers/build-page-object.test.js b/packages/inertia-sails/tests/helpers/build-page-object.test.js new file mode 100644 index 00000000..df28d2e3 --- /dev/null +++ b/packages/inertia-sails/tests/helpers/build-page-object.test.js @@ -0,0 +1,105 @@ +const { describe, it } = require('node:test') +const assert = require('node:assert/strict') +const buildPageObject = require('../../lib/helpers/build-page-object') + +function createRequest({ + sharedProps = {}, + pageProps = {}, + clearHistory = false, + encryptHistory = false, + flash = {}, + headers = {}, + version = 'test-version', + url = '/dashboard' +} = {}) { + const normalizedHeaders = Object.fromEntries( + Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]) + ) + + return { + url, + get(header) { + return normalizedHeaders[header.toLowerCase()] + }, + _sails: { + config: { + inertia: { + version + } + }, + inertia: { + getShared() { + return sharedProps + }, + shouldClearHistory() { + return clearHistory + }, + shouldEncryptHistory() { + return encryptHistory + }, + consumeFlash() { + return flash + } + } + }, + pageProps + } +} + +describe('buildPageObject', function () { + it('omits false history flags from the page object', async function () { + const req = createRequest() + const page = await buildPageObject(req, 'dashboard/index', {}) + + assert.equal(Object.hasOwn(page, 'clearHistory'), false) + assert.equal(Object.hasOwn(page, 'encryptHistory'), false) + }) + + it('includes true history flags in the page object', async function () { + const req = createRequest({ + clearHistory: true, + encryptHistory: true + }) + const page = await buildPageObject(req, 'dashboard/index', {}) + + assert.equal(page.clearHistory, true) + assert.equal(page.encryptHistory, true) + }) + + it('adds sharedProps metadata for shared prop keys', async function () { + const req = createRequest({ + sharedProps: { + auth: { user: { id: 1 } }, + app: { name: 'Boring Stack' } + } + }) + const page = await buildPageObject(req, 'dashboard/index', { + stats: { users: 10 } + }) + + assert.deepEqual(page.sharedProps, ['auth', 'app']) + assert.deepEqual(page.props.auth, { user: { id: 1 } }) + assert.deepEqual(page.props.stats, { users: 10 }) + }) + + it('keeps sharedProps metadata when page props override a shared key', async function () { + const req = createRequest({ + sharedProps: { + auth: { user: { id: 1 } } + } + }) + const page = await buildPageObject(req, 'dashboard/index', { + auth: { user: { id: 2 } } + }) + + assert.deepEqual(page.sharedProps, ['auth']) + assert.deepEqual(page.props.auth, { user: { id: 2 } }) + }) + + it('omits sharedProps when no shared props are present', async function () { + const req = createRequest() + const page = await buildPageObject(req, 'dashboard/index', {}) + + assert.equal(Object.hasOwn(page, 'sharedProps'), false) + }) +}) diff --git a/packages/inertia-sails/tests/props/resolve-merge-props.test.js b/packages/inertia-sails/tests/props/resolve-merge-props.test.js new file mode 100644 index 00000000..5322b81e --- /dev/null +++ b/packages/inertia-sails/tests/props/resolve-merge-props.test.js @@ -0,0 +1,147 @@ +const { describe, it } = require('node:test') +const assert = require('node:assert/strict') +const resolveMergeProps = require('../../lib/props/resolve-merge-props') +const MergeProp = require('../../lib/props/merge-prop') +const DeferProp = require('../../lib/props/defer-prop') +const ScrollProp = require('../../lib/props/scroll-prop') +const { + INFINITE_SCROLL_MERGE_INTENT, + RESET +} = require('../../lib/helpers/inertia-headers') + +function createRequest(headers = {}) { + const normalizedHeaders = Object.fromEntries( + Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]) + ) + + return { + get(header) { + return normalizedHeaders[header.toLowerCase()] + } + } +} + +describe('resolveMergeProps', function () { + it('marks merge props as appendable root props by default', function () { + const result = resolveMergeProps(createRequest(), { + notifications: new MergeProp(() => []) + }) + + assert.deepEqual(result, { + mergeProps: ['notifications'] + }) + }) + + it('supports root-level prepend props', function () { + const result = resolveMergeProps(createRequest(), { + notifications: new MergeProp(() => []).prepend() + }) + + assert.deepEqual(result, { + prependProps: ['notifications'] + }) + }) + + it('supports nested append props with match-on metadata', function () { + const result = resolveMergeProps(createRequest(), { + users: new MergeProp(() => ({})).append('data', { matchOn: 'id' }) + }) + + assert.deepEqual(result, { + mergeProps: ['users.data'], + matchPropsOn: ['users.data.id'] + }) + }) + + it('supports several nested append and prepend paths', function () { + const result = resolveMergeProps(createRequest(), { + dashboard: new MergeProp(() => ({})) + .append({ + 'users.data': 'id', + messages: 'uuid' + }) + .prepend('announcements') + }) + + assert.deepEqual(result, { + mergeProps: ['dashboard.users.data', 'dashboard.messages'], + prependProps: ['dashboard.announcements'], + matchPropsOn: ['dashboard.users.data.id', 'dashboard.messages.uuid'] + }) + }) + + it('supports deep merge props with match-on metadata', function () { + const result = resolveMergeProps(createRequest(), { + chat: new MergeProp(() => ({})).deepMerge().matchOn('messages.id') + }) + + assert.deepEqual(result, { + deepMergeProps: ['chat'], + matchPropsOn: ['chat.messages.id'] + }) + }) + + it('supports merge metadata on deferred props', function () { + const result = resolveMergeProps(createRequest(), { + results: new DeferProp(() => ({}), 'default').append('data', 'id') + }) + + assert.deepEqual(result, { + mergeProps: ['results.data'], + matchPropsOn: ['results.data.id'] + }) + }) + + it('appends infinite scroll data by wrapper path by default', function () { + const result = resolveMergeProps(createRequest(), { + users: new ScrollProp(() => [], { page: 0, perPage: 10, total: 25 }) + }) + + assert.deepEqual(result, { + mergeProps: ['users.data'] + }) + }) + + it('prepends infinite scroll data when requested by the client', function () { + const result = resolveMergeProps( + createRequest({ + [INFINITE_SCROLL_MERGE_INTENT]: 'prepend' + }), + { + users: new ScrollProp(() => [], { page: 1, perPage: 10, total: 25 }) + } + ) + + assert.deepEqual(result, { + prependProps: ['users.data'] + }) + }) + + it('supports custom infinite scroll wrappers and match-on metadata', function () { + const result = resolveMergeProps(createRequest(), { + feed: new ScrollProp(() => [], { + wrapper: 'items', + matchOn: 'id' + }) + }) + + assert.deepEqual(result, { + mergeProps: ['feed.items'], + matchPropsOn: ['feed.items.id'] + }) + }) + + it('skips merge metadata for reset props', function () { + const result = resolveMergeProps( + createRequest({ + [RESET]: 'results,notifications.data' + }), + { + results: new MergeProp(() => []), + notifications: new MergeProp(() => ({})).append('data', 'id') + } + ) + + assert.deepEqual(result, {}) + }) +}) diff --git a/packages/inertia-sails/tests/props/resolve-scroll-props.test.js b/packages/inertia-sails/tests/props/resolve-scroll-props.test.js new file mode 100644 index 00000000..a4c2b32e --- /dev/null +++ b/packages/inertia-sails/tests/props/resolve-scroll-props.test.js @@ -0,0 +1,51 @@ +const { describe, it } = require('node:test') +const assert = require('node:assert/strict') +const resolveScrollProps = require('../../lib/props/resolve-scroll-props') +const ScrollProp = require('../../lib/props/scroll-prop') + +describe('resolveScrollProps', function () { + it('emits Inertia scroll metadata for scroll props', function () { + const result = resolveScrollProps({ + users: new ScrollProp(() => [], { + page: 1, + perPage: 10, + total: 25 + }) + }) + + assert.deepEqual(result, { + scrollProps: { + users: { + pageName: 'page', + currentPage: 2, + previousPage: 1, + nextPage: 3, + reset: false + } + } + }) + }) + + it('supports custom page names', function () { + const result = resolveScrollProps({ + orders: new ScrollProp(() => [], { + page: 0, + pageName: 'orders', + perPage: 15, + total: 20 + }) + }) + + assert.deepEqual(result, { + scrollProps: { + orders: { + pageName: 'orders', + currentPage: 1, + previousPage: null, + nextPage: 2, + reset: false + } + } + }) + }) +}) diff --git a/packages/inertia-sails/tests/render.test.js b/packages/inertia-sails/tests/render.test.js new file mode 100644 index 00000000..b761d0f4 --- /dev/null +++ b/packages/inertia-sails/tests/render.test.js @@ -0,0 +1,158 @@ +const { describe, it } = require('node:test') +const assert = require('node:assert/strict') +const render = require('../lib/render') +const { INERTIA, VERSION, LOCATION } = require('../lib/helpers/inertia-headers') + +function createRequest({ + method = 'GET', + url = '/dashboard', + query = {}, + headers = {}, + version = 'current-version', + sharedProps = {}, + locals = {} +} = {}) { + const normalizedHeaders = Object.fromEntries( + Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]) + ) + + return { + method, + url, + query, + get(header) { + return normalizedHeaders[header.toLowerCase()] + }, + _sails: { + config: { + inertia: { + rootView: 'app', + version + } + }, + inertia: { + getLocals() { + return locals + }, + getShared() { + return sharedProps + }, + shouldClearHistory() { + return false + }, + shouldEncryptHistory() { + return false + }, + consumeFlash() { + return {} + } + } + } + } +} + +function createResponse() { + return { + headers: {}, + statusCode: null, + body: null, + viewName: null, + viewData: null, + ended: false, + set(header, value) { + this.headers[header] = value + return this + }, + status(statusCode) { + this.statusCode = statusCode + return this + }, + end() { + this.ended = true + return this + }, + json(body) { + this.body = body + return this + }, + view(viewName, viewData) { + this.viewName = viewName + this.viewData = viewData + return this + } + } +} + +describe('render', function () { + it('returns a 409 location response on GET asset version mismatch', async function () { + const req = createRequest({ + url: '/dashboard', + query: { tab: 'billing' }, + headers: { + [INERTIA]: 'true', + [VERSION]: 'old-version' + } + }) + const res = createResponse() + let resolvedProps = false + + await render(req, res, { + page: 'dashboard/index', + props: { + expensive: async () => { + resolvedProps = true + return 'should not resolve' + } + } + }) + + assert.equal(res.statusCode, 409) + assert.equal(res.ended, true) + assert.equal(res.headers.Vary, 'X-Inertia') + assert.equal(res.headers[LOCATION], '/dashboard?tab=billing') + assert.equal(resolvedProps, false) + }) + + it('does not return a 409 location response for non-GET mismatches', async function () { + const req = createRequest({ + method: 'POST', + headers: { + [INERTIA]: 'true', + [VERSION]: 'old-version' + } + }) + const res = createResponse() + + await render(req, res, { + page: 'dashboard/index', + props: { + saved: true + } + }) + + assert.equal(res.statusCode, null) + assert.equal(res.headers[LOCATION], undefined) + assert.equal(res.body.component, 'dashboard/index') + }) + + it('uses the v3 Vary header for Inertia JSON responses', async function () { + const req = createRequest({ + headers: { + [INERTIA]: 'true', + [VERSION]: 'current-version' + } + }) + const res = createResponse() + + await render(req, res, { + page: 'dashboard/index', + props: { + stats: { users: 10 } + } + }) + + assert.equal(res.headers[INERTIA], true) + assert.equal(res.headers.Vary, 'X-Inertia') + assert.deepEqual(res.body.props.stats, { users: 10 }) + }) +}) diff --git a/templates/ascent-react/views/app.ejs b/templates/ascent-react/views/app.ejs index fc7131e4..50d53fa0 100644 --- a/templates/ascent-react/views/app.ejs +++ b/templates/ascent-react/views/app.ejs @@ -7,7 +7,10 @@ <%- shipwright.styles() %> -
+
+ <%- shipwright.scripts() %> diff --git a/templates/ascent-vue/views/app.ejs b/templates/ascent-vue/views/app.ejs index fc7131e4..50d53fa0 100644 --- a/templates/ascent-vue/views/app.ejs +++ b/templates/ascent-vue/views/app.ejs @@ -7,7 +7,10 @@ <%- shipwright.styles() %> -
+
+ <%- shipwright.scripts() %> diff --git a/templates/mellow-react/views/app.ejs b/templates/mellow-react/views/app.ejs index fc7131e4..50d53fa0 100644 --- a/templates/mellow-react/views/app.ejs +++ b/templates/mellow-react/views/app.ejs @@ -7,7 +7,10 @@ <%- shipwright.styles() %> -
+
+ <%- shipwright.scripts() %> diff --git a/templates/mellow-svelte/views/app.ejs b/templates/mellow-svelte/views/app.ejs index fc7131e4..50d53fa0 100644 --- a/templates/mellow-svelte/views/app.ejs +++ b/templates/mellow-svelte/views/app.ejs @@ -7,7 +7,10 @@ <%- shipwright.styles() %> -
+
+ <%- shipwright.scripts() %> diff --git a/templates/mellow-vue/views/app.ejs b/templates/mellow-vue/views/app.ejs index fc7131e4..50d53fa0 100644 --- a/templates/mellow-vue/views/app.ejs +++ b/templates/mellow-vue/views/app.ejs @@ -7,7 +7,10 @@ <%- shipwright.styles() %> -
+
+ <%- shipwright.scripts() %> From de5cfe390998f889fe3bd073f2e97d51a682220f Mon Sep 17 00:00:00 2001 From: Kelvin Oghenerhoro Omereshone Date: Wed, 20 May 2026 09:07:09 +0100 Subject: [PATCH 2/6] feat(inertia): complete v3 template migration --- package-lock.json | 22 +- .../inertia-sails/lib/props/merge-targets.js | 76 ++++++ .../inertia-sails/lib/props/mergeable-prop.js | 61 +---- .../lib/props/resolve-merge-props.js | 9 +- packages/inertia-sails/package.json | 2 +- .../tests/props/merge-targets.test.js | 74 ++++++ .../assets/js/pages/billing/pricing.jsx | 2 +- .../ascent-react/assets/js/pages/blog.jsx | 2 +- .../assets/js/pages/dashboard/index.jsx | 6 +- .../ascent-react/assets/js/pages/features.jsx | 2 +- .../ascent-react/assets/js/pages/index.jsx | 2 +- .../assets/js/pages/settings/billing.jsx | 9 +- .../assets/js/pages/settings/profile.jsx | 9 +- .../assets/js/pages/settings/security.jsx | 9 +- .../assets/js/pages/settings/team.jsx | 6 +- templates/ascent-react/package-lock.json | 182 ++++++++------ templates/ascent-react/package.json | 9 +- templates/ascent-vue/package-lock.json | 178 ++++++++------ templates/ascent-vue/package.json | 9 +- .../assets/js/pages/dashboard/index.jsx | 2 +- .../assets/js/pages/dashboard/profile.jsx | 2 +- .../mellow-react/assets/js/pages/index.jsx | 2 +- templates/mellow-react/package-lock.json | 155 +++++++----- templates/mellow-react/package.json | 9 +- templates/mellow-svelte/package-lock.json | 225 ++++++++---------- templates/mellow-svelte/package.json | 9 +- templates/mellow-vue/package-lock.json | 157 +++++++----- templates/mellow-vue/package.json | 9 +- 28 files changed, 731 insertions(+), 508 deletions(-) create mode 100644 packages/inertia-sails/lib/props/merge-targets.js create mode 100644 packages/inertia-sails/tests/props/merge-targets.test.js diff --git a/package-lock.json b/package-lock.json index d2774b99..49e59fff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "boring-stack", - "version": "0.5.4", + "version": "1.2.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "boring-stack", - "version": "0.5.4", + "version": "1.2.3", "license": "MIT", "workspaces": [ "packages/*" @@ -66,18 +66,6 @@ "sisteransi": "^1.0.5" } }, - "node_modules/@clack/prompts/node_modules/is-unicode-supported": { - "version": "1.3.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@commitlint/cli": { "version": "17.8.1", "dev": true, @@ -6235,7 +6223,7 @@ } }, "packages/create-sails": { - "version": "1.0.4", + "version": "1.2.1", "license": "MIT", "dependencies": { "@clack/prompts": "^0.6.3", @@ -6252,7 +6240,7 @@ } }, "packages/create-sails-generator": { - "version": "0.1.0", + "version": "1.0.0", "license": "MIT", "devDependencies": {}, "peerDependencies": { @@ -6260,7 +6248,7 @@ } }, "packages/inertia-sails": { - "version": "0.3.3", + "version": "1.4.0", "license": "MIT", "devDependencies": {}, "peerDependencies": { diff --git a/packages/inertia-sails/lib/props/merge-targets.js b/packages/inertia-sails/lib/props/merge-targets.js new file mode 100644 index 00000000..240365b6 --- /dev/null +++ b/packages/inertia-sails/lib/props/merge-targets.js @@ -0,0 +1,76 @@ +/** + * Utilities for describing how mergeable props should be merged by the client. + */ + +function createDefaultMergeOperation() { + return { + direction: 'append', + path: null, + matchOn: null, + isDefault: true + } +} + +function normalizeMergeOptions(options) { + return typeof options === 'string' ? { matchOn: options } : options || {} +} + +function normalizeMergeTargets(paths, options = {}) { + const normalizedOptions = normalizeMergeOptions(options) + + if (paths === null || paths === undefined) { + return [ + { + path: null, + matchOn: normalizedOptions.matchOn || null + } + ] + } + + if (Array.isArray(paths)) { + return paths.map((path) => ({ + path: normalizePath(path), + matchOn: resolveTargetMatchOn(path, normalizedOptions) + })) + } + + if (typeof paths === 'object') { + return Object.entries(paths).map(([path, matchOn]) => ({ + path: normalizePath(path), + matchOn + })) + } + + return [ + { + path: normalizePath(paths), + matchOn: resolveTargetMatchOn(paths, normalizedOptions) + } + ] +} + +function normalizePath(path) { + return path === '' ? null : path +} + +function resolveTargetMatchOn(path, options) { + if (!options.matchOn) return null + if (typeof options.matchOn === 'object') return options.matchOn[path] || null + return options.matchOn +} + +function resolvePropPath(key, path) { + return path ? `${key}.${path}` : key +} + +function unique(values) { + return [...new Set(values)] +} + +module.exports = { + createDefaultMergeOperation, + normalizeMergeOptions, + normalizeMergeTargets, + resolvePropPath, + unique +} diff --git a/packages/inertia-sails/lib/props/mergeable-prop.js b/packages/inertia-sails/lib/props/mergeable-prop.js index fa2f277d..740565f9 100644 --- a/packages/inertia-sails/lib/props/mergeable-prop.js +++ b/packages/inertia-sails/lib/props/mergeable-prop.js @@ -1,3 +1,9 @@ +const { + createDefaultMergeOperation, + normalizeMergeOptions, + normalizeMergeTargets +} = require('./merge-targets') + /** * MergeableProp - Base class for props that can be merged during partial reloads. * @@ -27,12 +33,7 @@ module.exports = class MergeableProp { this.shouldMerge = true this.shouldDeepMerge = false if (this.mergeOperations.length === 0) { - this.mergeOperations.push({ - direction: 'append', - path: null, - matchOn: null, - isDefault: true - }) + this.mergeOperations.push(createDefaultMergeOperation()) } return this } @@ -83,14 +84,13 @@ module.exports = class MergeableProp { } _addMergeOperations(direction, paths, options) { - const normalizedOptions = - typeof options === 'string' ? { matchOn: options } : options || {} + const normalizedOptions = normalizeMergeOptions(options) this.shouldMerge = true this.shouldDeepMerge = false this._clearDefaultMergeOperation() - this._normalizeMergeTargets(paths, normalizedOptions).forEach((target) => { + normalizeMergeTargets(paths, normalizedOptions).forEach((target) => { this.mergeOperations.push({ direction, path: target.path, @@ -109,47 +109,4 @@ module.exports = class MergeableProp { this.mergeOperations = [] } } - - _normalizeMergeTargets(paths, options) { - if (paths === null || paths === undefined) { - return [ - { - path: null, - matchOn: options.matchOn || null - } - ] - } - - if (Array.isArray(paths)) { - return paths.map((path) => ({ - path: this._normalizePath(path), - matchOn: this._resolveMatchOn(path, options) - })) - } - - if (typeof paths === 'object') { - return Object.entries(paths).map(([path, matchOn]) => ({ - path: this._normalizePath(path), - matchOn - })) - } - - return [ - { - path: this._normalizePath(paths), - matchOn: this._resolveMatchOn(paths, options) - } - ] - } - - _normalizePath(path) { - return path === '' ? null : path - } - - _resolveMatchOn(path, options) { - if (!options.matchOn) return null - if (typeof options.matchOn === 'object') - return options.matchOn[path] || null - return options.matchOn - } } diff --git a/packages/inertia-sails/lib/props/resolve-merge-props.js b/packages/inertia-sails/lib/props/resolve-merge-props.js index 06efe2d5..bf176b25 100644 --- a/packages/inertia-sails/lib/props/resolve-merge-props.js +++ b/packages/inertia-sails/lib/props/resolve-merge-props.js @@ -3,6 +3,7 @@ const { RESET } = require('../helpers/inertia-headers') const MergeableProp = require('./mergeable-prop') +const { resolvePropPath, unique } = require('./merge-targets') const ScrollProp = require('./scroll-prop') /** @@ -79,11 +80,3 @@ module.exports = function resolveMergeProps(req, pageProps) { return result } - -function resolvePropPath(key, path) { - return path ? `${key}.${path}` : key -} - -function unique(values) { - return [...new Set(values)] -} diff --git a/packages/inertia-sails/package.json b/packages/inertia-sails/package.json index e39e4caf..172c1a16 100644 --- a/packages/inertia-sails/package.json +++ b/packages/inertia-sails/package.json @@ -1,6 +1,6 @@ { "name": "inertia-sails", - "version": "1.3.3", + "version": "1.4.0", "description": "The Sails adapter for Inertia.", "main": "index.js", "sails": { diff --git a/packages/inertia-sails/tests/props/merge-targets.test.js b/packages/inertia-sails/tests/props/merge-targets.test.js new file mode 100644 index 00000000..43edae5b --- /dev/null +++ b/packages/inertia-sails/tests/props/merge-targets.test.js @@ -0,0 +1,74 @@ +const { describe, it } = require('node:test') +const assert = require('node:assert/strict') +const { + createDefaultMergeOperation, + normalizeMergeOptions, + normalizeMergeTargets, + resolvePropPath, + unique +} = require('../../lib/props/merge-targets') + +describe('merge-targets', function () { + it('creates the default root append operation', function () { + assert.deepEqual(createDefaultMergeOperation(), { + direction: 'append', + path: null, + matchOn: null, + isDefault: true + }) + }) + + it('normalizes string options as match-on metadata', function () { + assert.deepEqual(normalizeMergeOptions('id'), { matchOn: 'id' }) + }) + + it('normalizes root targets', function () { + assert.deepEqual(normalizeMergeTargets(null, { matchOn: 'id' }), [ + { + path: null, + matchOn: 'id' + } + ]) + }) + + it('normalizes array targets with shared match-on metadata', function () { + assert.deepEqual(normalizeMergeTargets(['users', 'messages'], 'id'), [ + { + path: 'users', + matchOn: 'id' + }, + { + path: 'messages', + matchOn: 'id' + } + ]) + }) + + it('normalizes object targets as path-to-match maps', function () { + assert.deepEqual( + normalizeMergeTargets({ + 'users.data': 'id', + messages: 'uuid' + }), + [ + { + path: 'users.data', + matchOn: 'id' + }, + { + path: 'messages', + matchOn: 'uuid' + } + ] + ) + }) + + it('resolves prop paths and unique values', function () { + assert.equal(resolvePropPath('users', 'data'), 'users.data') + assert.equal(resolvePropPath('users', null), 'users') + assert.deepEqual(unique(['users', 'users', 'messages']), [ + 'users', + 'messages' + ]) + }) +}) diff --git a/templates/ascent-react/assets/js/pages/billing/pricing.jsx b/templates/ascent-react/assets/js/pages/billing/pricing.jsx index 088e6238..a282c984 100644 --- a/templates/ascent-react/assets/js/pages/billing/pricing.jsx +++ b/templates/ascent-react/assets/js/pages/billing/pricing.jsx @@ -2,7 +2,7 @@ import { Head, Link } from '@inertiajs/react' import AppLayout from '@/layouts/AppLayout.jsx' import { useState } from 'react' -Pricing.layout = (page) => +Pricing.layout = AppLayout export default function Pricing({ plans }) { const [billingCycle, setBillingCycle] = useState('monthly') diff --git a/templates/ascent-react/assets/js/pages/blog.jsx b/templates/ascent-react/assets/js/pages/blog.jsx index 9334a4dc..18025c6f 100644 --- a/templates/ascent-react/assets/js/pages/blog.jsx +++ b/templates/ascent-react/assets/js/pages/blog.jsx @@ -1,7 +1,7 @@ import { Head, Link } from '@inertiajs/react' import AppLayout from '@/layouts/AppLayout.jsx' -Blog.layout = (page) => +Blog.layout = AppLayout export default function Blog({ appName, blogPosts }) { return ( diff --git a/templates/ascent-react/assets/js/pages/dashboard/index.jsx b/templates/ascent-react/assets/js/pages/dashboard/index.jsx index ee3e3043..bf1c2b14 100644 --- a/templates/ascent-react/assets/js/pages/dashboard/index.jsx +++ b/templates/ascent-react/assets/js/pages/dashboard/index.jsx @@ -1,11 +1,7 @@ import { Link, Head, usePage } from '@inertiajs/react' import DashboardLayout from '@/layouts/DashboardLayout.jsx' -Dashboard.layout = (page) => ( - - {page} - -) +Dashboard.layout = [DashboardLayout, { title: 'Dashboard', maxWidth: 'wide' }] export default function Dashboard() { const page = usePage() const loggedInUser = page.props.loggedInUser diff --git a/templates/ascent-react/assets/js/pages/features.jsx b/templates/ascent-react/assets/js/pages/features.jsx index ef178135..c6fdd0b2 100644 --- a/templates/ascent-react/assets/js/pages/features.jsx +++ b/templates/ascent-react/assets/js/pages/features.jsx @@ -1,7 +1,7 @@ import { Head, Link } from '@inertiajs/react' import AppLayout from '@/layouts/AppLayout.jsx' -Features.layout = (page) => +Features.layout = AppLayout export default function Features() { return ( <> diff --git a/templates/ascent-react/assets/js/pages/index.jsx b/templates/ascent-react/assets/js/pages/index.jsx index 0c62d1e6..5a6cbac5 100644 --- a/templates/ascent-react/assets/js/pages/index.jsx +++ b/templates/ascent-react/assets/js/pages/index.jsx @@ -4,7 +4,7 @@ import { useState } from 'react' import { Message } from 'primereact/message' import '~/css/homepage.css' -Index.layout = (page) => +Index.layout = AppLayout export default function Index() { const [isWaitlistActive] = useState(true) const [shouldShake, setShouldShake] = useState(false) diff --git a/templates/ascent-react/assets/js/pages/settings/billing.jsx b/templates/ascent-react/assets/js/pages/settings/billing.jsx index fe7f6447..88ac4029 100644 --- a/templates/ascent-react/assets/js/pages/settings/billing.jsx +++ b/templates/ascent-react/assets/js/pages/settings/billing.jsx @@ -9,11 +9,10 @@ import { ProgressBar } from 'primereact/progressbar' import DashboardLayout from '@/layouts/DashboardLayout' import SettingsLayout from '@/layouts/SettingsLayout.jsx' -BillingSettings.layout = (page) => ( - - {page} - -) +BillingSettings.layout = [ + DashboardLayout, + { title: 'Billing', maxWidth: 'narrow' } +] export default function BillingSettings({ subscription, plans }) { const isSubscribed = !!subscription diff --git a/templates/ascent-react/assets/js/pages/settings/profile.jsx b/templates/ascent-react/assets/js/pages/settings/profile.jsx index 746461ff..81051e6a 100644 --- a/templates/ascent-react/assets/js/pages/settings/profile.jsx +++ b/templates/ascent-react/assets/js/pages/settings/profile.jsx @@ -12,11 +12,10 @@ import { ConfirmDialog } from 'primereact/confirmdialog' import { confirmDialog } from 'primereact/confirmdialog' import ImageUpload from '@/components/ImageUpload' -ProfileSettings.layout = (page) => ( - - {page} - -) +ProfileSettings.layout = [ + DashboardLayout, + { title: 'Profile', maxWidth: 'narrow' } +] export default function ProfileSettings() { const loggedInUser = usePage().props.loggedInUser diff --git a/templates/ascent-react/assets/js/pages/settings/security.jsx b/templates/ascent-react/assets/js/pages/settings/security.jsx index 55f7eb00..9371574e 100644 --- a/templates/ascent-react/assets/js/pages/settings/security.jsx +++ b/templates/ascent-react/assets/js/pages/settings/security.jsx @@ -15,11 +15,10 @@ import BackupCodesModal from '@/components/BackupCodesModal.jsx' import EmailTwoFactorSetupModal from '@/components/EmailTwoFactorSetupModal.jsx' import ManagePasskeysModal from '@/components/ManagePasskeysModal.jsx' -SecuritySettings.layout = (page) => ( - - {page} - -) +SecuritySettings.layout = [ + DashboardLayout, + { title: 'Security', maxWidth: 'narrow' } +] export default function SecuritySettings({ loggedInUser, diff --git a/templates/ascent-react/assets/js/pages/settings/team.jsx b/templates/ascent-react/assets/js/pages/settings/team.jsx index 6af15dc2..7ce8c861 100644 --- a/templates/ascent-react/assets/js/pages/settings/team.jsx +++ b/templates/ascent-react/assets/js/pages/settings/team.jsx @@ -18,11 +18,7 @@ import { Dropdown } from 'primereact/dropdown' import { useCopyToClipboard } from '@/hooks/useCopyToClipboard' import ImageUpload from '@/components/ImageUpload' -TeamSettings.layout = (page) => ( - - {page} - -) +TeamSettings.layout = [DashboardLayout, { title: 'Team', maxWidth: 'narrow' }] export default function TeamSettings({ team, diff --git a/templates/ascent-react/package-lock.json b/templates/ascent-react/package-lock.json index ed3aaf10..f410c957 100644 --- a/templates/ascent-react/package-lock.json +++ b/templates/ascent-react/package-lock.json @@ -8,14 +8,15 @@ "name": "ascent-react", "version": "0.0.0", "dependencies": { - "@inertiajs/react": "^2.2.15", + "@inertiajs/react": "^3.1.1", "@sails-pay/lemonsqueezy": "^0.0.2", "@sailshq/connect-redis": "^6.1.3", "@sailshq/lodash": "^3.10.7", "@sailshq/socket.io-redis": "^6.1.2", "@simplewebauthn/browser": "^13.2.2", "@simplewebauthn/server": "^13.2.2", - "inertia-sails": "^1.1.0", + "axios": "^1.16.1", + "inertia-sails": "^1.4.0", "nodemailer": "^7.0.10", "primeicons": "^7.0.0", "primereact": "^10.9.7", @@ -49,6 +50,7 @@ "sails-hook-shipwright": "^1.1.0", "sails.io.js": "^1.2.1", "socket.io-client": "^4.8.1", + "sounding": "^0.0.1", "tailwindcss": "^3.4.18" }, "engines": { @@ -118,29 +120,37 @@ "license": "MIT" }, "node_modules/@inertiajs/core": { - "version": "2.2.15", - "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-2.2.15.tgz", - "integrity": "sha512-0hj2oBWzj2Z2+UMTrqBMrRrlgoPZsqru7E9FEiR+zkOTywhuV0izJi/rAbmGBIxPAz9v3zMN/wrAy4293xBZoQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-3.1.1.tgz", + "integrity": "sha512-l8NfuI6xaeLcSTUH67fk/RPKGr5Pm+oyeWJy6mBfWxHIiNpMctPQtha5/NLQ+RqGekJdmJ7cHH8l3JDkvoPXYg==", "license": "MIT", "dependencies": { - "@types/lodash-es": "^4.17.12", - "axios": "^1.12.2", - "lodash-es": "^4.17.21", - "qs": "^6.14.0" + "@jridgewell/trace-mapping": "^0.3.31", + "es-toolkit": "^1.33.0", + "laravel-precognition": "^2.0.0" + }, + "peerDependencies": { + "axios": "^1.15.2" + }, + "peerDependenciesMeta": { + "axios": { + "optional": true + } } }, "node_modules/@inertiajs/react": { - "version": "2.2.15", - "resolved": "https://registry.npmjs.org/@inertiajs/react/-/react-2.2.15.tgz", - "integrity": "sha512-b6BaGtW18TqyADr/dwTSe5FHYTo6iCGkTmVLxJyyIUqKE08YXXzxfFPWbjhLsfP5SDvpg/uqlVVfMP8cIQbCtw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@inertiajs/react/-/react-3.1.1.tgz", + "integrity": "sha512-Z1MhuMaRC1rv7hdK8p8I4u88seku358Je4q7OPVG+FRSZAfnUGXGRBYaHq34HYDjoz1axb+/irkk1WdJFs37Rg==", "license": "MIT", "dependencies": { - "@inertiajs/core": "2.2.15", - "@types/lodash-es": "^4.17.12", - "lodash-es": "^4.17.21" + "@inertiajs/core": "3.1.1", + "es-toolkit": "^1.33.0", + "laravel-precognition": "^2.0.0" }, "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "react": "^19.0.0", + "react-dom": "^19.0.0" } }, "node_modules/@isaacs/cliui": { @@ -261,7 +271,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -271,14 +280,12 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1091,21 +1098,6 @@ "@types/node": "*" } }, - "node_modules/@types/lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", - "license": "MIT" - }, - "node_modules/@types/lodash-es": { - "version": "4.17.12", - "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", - "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", - "license": "MIT", - "dependencies": { - "@types/lodash": "*" - } - }, "node_modules/@types/node": { "version": "24.10.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", @@ -1168,6 +1160,18 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1362,14 +1366,15 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, "node_modules/b64": { @@ -2435,6 +2440,16 @@ "node": ">= 0.4" } }, + "node_modules/es-toolkit": { + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.46.1.tgz", + "integrity": "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2944,9 +2959,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -2990,9 +3005,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -3347,6 +3362,19 @@ "npm": ">=1.3.7" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/i18n-2": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/i18n-2/-/i18n-2-0.7.3.tgz", @@ -3407,9 +3435,9 @@ } }, "node_modules/inertia-sails": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/inertia-sails/-/inertia-sails-1.1.0.tgz", - "integrity": "sha512-HxeDiX1Bw5xVcd9wsLYnGrsjlCYP/HKyxDx9pF0GxICOjFN1Z+BKNX3cC3rwyJCSg2NbaQodZ8jLDZViFp/OQA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/inertia-sails/-/inertia-sails-1.4.0.tgz", + "integrity": "sha512-RqIOCEKW3dy8g3ojSWJH/6pVHw7t7zIaGPF4c7gBbdJh73lNSk8QtugyIFJbxjyWeN3lrnoCKgBBZDYKYl/g==", "license": "MIT", "peerDependencies": { "sails": ">=1", @@ -3704,6 +3732,23 @@ "graceful-fs": "^4.1.9" } }, + "node_modules/laravel-precognition": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/laravel-precognition/-/laravel-precognition-2.0.0.tgz", + "integrity": "sha512-dmA4HGc9m+TsVNsJs9/XQBI8u6j7coilN+qKkBuhuXQzH3HypwS/c5dFQ4UqUGjBbcxIM7zdk91kM/SRZwIvWQ==", + "license": "MIT", + "dependencies": { + "es-toolkit": "^1.32.0" + }, + "peerDependencies": { + "axios": "^1.4.0" + }, + "peerDependenciesMeta": { + "axios": { + "optional": true + } + } + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -3751,12 +3796,6 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -4916,10 +4955,13 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/psl": { "version": "1.15.0", @@ -4987,21 +5029,6 @@ "node": ">=10.13.0" } }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -6292,6 +6319,13 @@ "@sailshq/lodash": "^3.10.2" } }, + "node_modules/sounding": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/sounding/-/sounding-0.0.1.tgz", + "integrity": "sha512-fwjwSgZYU8umo2IDD+8T45MurA7XLw0sledowT3MATfivMTryfbK+WLJAQj6e7mBXeumPrgnj357Zp87/ejmcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", diff --git a/templates/ascent-react/package.json b/templates/ascent-react/package.json index 0eb83275..cf111dac 100644 --- a/templates/ascent-react/package.json +++ b/templates/ascent-react/package.json @@ -12,14 +12,15 @@ "javascript" ], "dependencies": { - "@inertiajs/react": "^2.2.15", + "@inertiajs/react": "^3.1.1", "@sails-pay/lemonsqueezy": "^0.0.2", "@sailshq/connect-redis": "^6.1.3", "@sailshq/lodash": "^3.10.7", "@sailshq/socket.io-redis": "^6.1.2", "@simplewebauthn/browser": "^13.2.2", "@simplewebauthn/server": "^13.2.2", - "inertia-sails": "^1.1.0", + "axios": "^1.16.1", + "inertia-sails": "^1.4.0", "nodemailer": "^7.0.10", "primeicons": "^7.0.0", "primereact": "^10.9.7", @@ -53,8 +54,8 @@ "sails-hook-shipwright": "^1.1.0", "sails.io.js": "^1.2.1", "socket.io-client": "^4.8.1", - "tailwindcss": "^3.4.18", - "sounding": "^0.0.1" + "sounding": "^0.0.1", + "tailwindcss": "^3.4.18" }, "scripts": { "dev": "node --watch-path=api --watch-path=config app.js", diff --git a/templates/ascent-vue/package-lock.json b/templates/ascent-vue/package-lock.json index 9922e1a8..17e4106c 100644 --- a/templates/ascent-vue/package-lock.json +++ b/templates/ascent-vue/package-lock.json @@ -8,15 +8,16 @@ "name": "ascent-vue", "version": "0.0.0", "dependencies": { - "@inertiajs/vue3": "^2.2.15", + "@inertiajs/vue3": "^3.1.1", "@sails-pay/lemonsqueezy": "^0.0.2", "@sailshq/connect-redis": "^6.1.3", "@sailshq/lodash": "^3.10.7", "@sailshq/socket.io-redis": "^6.1.2", "@simplewebauthn/browser": "^13.2.0", "@simplewebauthn/server": "^13.2.1", + "axios": "^1.16.1", "floating-vue": "^5.2.2", - "inertia-sails": "^1.1.0", + "inertia-sails": "^1.4.0", "nodemailer": "^7.0.10", "primeicons": "^7.0.0", "primevue": "^4.4.1", @@ -51,6 +52,7 @@ "sails-hook-shipwright": "^1.1.0", "sails.io.js": "^1.2.1", "socket.io-client": "^4.8.1", + "sounding": "^0.0.1", "tailwindcss": "^4.1.17", "tailwindcss-primeui": "^0.6.1" }, @@ -182,26 +184,33 @@ "license": "MIT" }, "node_modules/@inertiajs/core": { - "version": "2.2.15", - "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-2.2.15.tgz", - "integrity": "sha512-0hj2oBWzj2Z2+UMTrqBMrRrlgoPZsqru7E9FEiR+zkOTywhuV0izJi/rAbmGBIxPAz9v3zMN/wrAy4293xBZoQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-3.1.1.tgz", + "integrity": "sha512-l8NfuI6xaeLcSTUH67fk/RPKGr5Pm+oyeWJy6mBfWxHIiNpMctPQtha5/NLQ+RqGekJdmJ7cHH8l3JDkvoPXYg==", "license": "MIT", "dependencies": { - "@types/lodash-es": "^4.17.12", - "axios": "^1.12.2", - "lodash-es": "^4.17.21", - "qs": "^6.14.0" + "@jridgewell/trace-mapping": "^0.3.31", + "es-toolkit": "^1.33.0", + "laravel-precognition": "^2.0.0" + }, + "peerDependencies": { + "axios": "^1.15.2" + }, + "peerDependenciesMeta": { + "axios": { + "optional": true + } } }, "node_modules/@inertiajs/vue3": { - "version": "2.2.15", - "resolved": "https://registry.npmjs.org/@inertiajs/vue3/-/vue3-2.2.15.tgz", - "integrity": "sha512-DINuy5KeubhrEtAeAXEroh7+sxBZvXKLTJfo9UEOMgg0FQgM0qbbhhOKDZjWe2iGDP2hGq6A4zMfma4u0huNBQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@inertiajs/vue3/-/vue3-3.1.1.tgz", + "integrity": "sha512-k2xdcRIxbflGW9Doo6pg4pUfBK9t7PgODyHJFaf1p9AxOFRdBIKrxxvaGFQTT5L4GjOUdZ9trfDLxZ2IIaCabA==", "license": "MIT", "dependencies": { - "@inertiajs/core": "2.2.15", - "@types/lodash-es": "^4.17.12", - "lodash-es": "^4.17.21" + "@inertiajs/core": "3.1.1", + "es-toolkit": "^1.33.0", + "laravel-precognition": "^2.0.0" }, "peerDependencies": { "vue": "^3.0.0" @@ -233,7 +242,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -260,7 +268,6 @@ "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1408,21 +1415,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", - "license": "MIT" - }, - "node_modules/@types/lodash-es": { - "version": "4.17.12", - "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", - "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", - "license": "MIT", - "dependencies": { - "@types/lodash": "*" - } - }, "node_modules/@types/node": { "version": "24.9.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz", @@ -1741,6 +1733,18 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1949,14 +1953,15 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, "node_modules/b64": { @@ -3027,6 +3032,16 @@ "node": ">= 0.4" } }, + "node_modules/es-toolkit": { + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.46.1.tgz", + "integrity": "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3622,9 +3637,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -3651,9 +3666,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -3998,6 +4013,19 @@ "npm": ">=1.3.7" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/i18n-2": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/i18n-2/-/i18n-2-0.7.3.tgz", @@ -4058,9 +4086,9 @@ } }, "node_modules/inertia-sails": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/inertia-sails/-/inertia-sails-1.1.0.tgz", - "integrity": "sha512-HxeDiX1Bw5xVcd9wsLYnGrsjlCYP/HKyxDx9pF0GxICOjFN1Z+BKNX3cC3rwyJCSg2NbaQodZ8jLDZViFp/OQA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/inertia-sails/-/inertia-sails-1.4.0.tgz", + "integrity": "sha512-RqIOCEKW3dy8g3ojSWJH/6pVHw7t7zIaGPF4c7gBbdJh73lNSk8QtugyIFJbxjyWeN3lrnoCKgBBZDYKYl/g==", "license": "MIT", "peerDependencies": { "sails": ">=1", @@ -4345,6 +4373,23 @@ "graceful-fs": "^4.1.9" } }, + "node_modules/laravel-precognition": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/laravel-precognition/-/laravel-precognition-2.0.0.tgz", + "integrity": "sha512-dmA4HGc9m+TsVNsJs9/XQBI8u6j7coilN+qKkBuhuXQzH3HypwS/c5dFQ4UqUGjBbcxIM7zdk91kM/SRZwIvWQ==", + "license": "MIT", + "dependencies": { + "es-toolkit": "^1.32.0" + }, + "peerDependencies": { + "axios": "^1.4.0" + }, + "peerDependenciesMeta": { + "axios": { + "optional": true + } + } + }, "node_modules/lightningcss": { "version": "1.30.2", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", @@ -4647,12 +4692,6 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -5570,10 +5609,13 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/psl": { "version": "1.15.0", @@ -5641,21 +5683,6 @@ "node": ">=10.13.0" } }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -6992,6 +7019,13 @@ "@sailshq/lodash": "^3.10.2" } }, + "node_modules/sounding": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/sounding/-/sounding-0.0.1.tgz", + "integrity": "sha512-fwjwSgZYU8umo2IDD+8T45MurA7XLw0sledowT3MATfivMTryfbK+WLJAQj6e7mBXeumPrgnj357Zp87/ejmcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/templates/ascent-vue/package.json b/templates/ascent-vue/package.json index 6b3ab5fb..efdd5ad3 100644 --- a/templates/ascent-vue/package.json +++ b/templates/ascent-vue/package.json @@ -12,15 +12,16 @@ "javascript" ], "dependencies": { - "@inertiajs/vue3": "^2.2.15", + "@inertiajs/vue3": "^3.1.1", "@sails-pay/lemonsqueezy": "^0.0.2", "@sailshq/connect-redis": "^6.1.3", "@sailshq/lodash": "^3.10.7", "@sailshq/socket.io-redis": "^6.1.2", "@simplewebauthn/browser": "^13.2.0", "@simplewebauthn/server": "^13.2.1", + "axios": "^1.16.1", "floating-vue": "^5.2.2", - "inertia-sails": "^1.1.0", + "inertia-sails": "^1.4.0", "nodemailer": "^7.0.10", "primeicons": "^7.0.0", "primevue": "^4.4.1", @@ -55,9 +56,9 @@ "sails-hook-shipwright": "^1.1.0", "sails.io.js": "^1.2.1", "socket.io-client": "^4.8.1", + "sounding": "^0.0.1", "tailwindcss": "^4.1.17", - "tailwindcss-primeui": "^0.6.1", - "sounding": "^0.0.1" + "tailwindcss-primeui": "^0.6.1" }, "scripts": { "dev": "node --watch-path=api --watch-path=config app.js", diff --git a/templates/mellow-react/assets/js/pages/dashboard/index.jsx b/templates/mellow-react/assets/js/pages/dashboard/index.jsx index 2a540802..2569ec68 100644 --- a/templates/mellow-react/assets/js/pages/dashboard/index.jsx +++ b/templates/mellow-react/assets/js/pages/dashboard/index.jsx @@ -1,7 +1,7 @@ import { Link, Head, usePage } from '@inertiajs/react' import AppLayout from '@/layouts/AppLayout.jsx' -Dashboard.layout = (page) => +Dashboard.layout = AppLayout export default function Dashboard() { const page = usePage() const loggedInUser = page.props.loggedInUser diff --git a/templates/mellow-react/assets/js/pages/dashboard/profile.jsx b/templates/mellow-react/assets/js/pages/dashboard/profile.jsx index 93e1b74b..d19e3b79 100644 --- a/templates/mellow-react/assets/js/pages/dashboard/profile.jsx +++ b/templates/mellow-react/assets/js/pages/dashboard/profile.jsx @@ -7,7 +7,7 @@ import InputPassword from '@/components/InputPassword.jsx' import InputEmail from '@/components/InputEmail.jsx' import InputButton from '@/components/InputButton.jsx' -Profile.layout = (page) => +Profile.layout = AppLayout export default function Profile() { const loggedInUser = usePage().props.loggedInUser const { data, setData, ...form } = useForm({ diff --git a/templates/mellow-react/assets/js/pages/index.jsx b/templates/mellow-react/assets/js/pages/index.jsx index e8c0a2a5..baad1f4b 100644 --- a/templates/mellow-react/assets/js/pages/index.jsx +++ b/templates/mellow-react/assets/js/pages/index.jsx @@ -2,7 +2,7 @@ import { Head } from '@inertiajs/react' import AppLayout from '@/layouts/AppLayout.jsx' import '~/css/homepage.css' -Index.layout = (page) => +Index.layout = AppLayout export default function Index() { return ( <> diff --git a/templates/mellow-react/package-lock.json b/templates/mellow-react/package-lock.json index 0c8eef4e..fca483fb 100644 --- a/templates/mellow-react/package-lock.json +++ b/templates/mellow-react/package-lock.json @@ -8,11 +8,12 @@ "name": "mellow-react", "version": "0.0.0", "dependencies": { - "@inertiajs/react": "^2.1.2", + "@inertiajs/react": "^3.1.1", "@sailshq/connect-redis": "^6.1.3", "@sailshq/lodash": "^3.10.7", "@sailshq/socket.io-redis": "^6.1.2", - "inertia-sails": "^1.1.0", + "axios": "^1.16.1", + "inertia-sails": "^1.4.0", "react": "^19.1.1", "react-dom": "^19.1.1", "sails": "^1.5.15", @@ -34,6 +35,7 @@ "sails-hook-shipwright": "^1.1.0", "sails.io.js": "^1.2.1", "socket.io-client": "^4.7.5", + "sounding": "^0.0.1", "tailwindcss": "^4.1.16" }, "engines": { @@ -88,27 +90,37 @@ } }, "node_modules/@inertiajs/core": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-2.1.2.tgz", - "integrity": "sha512-fS3bDanwIZMEhtndhs1NvDvFN7y9Nx+FPkuBLSjIvYXFVmwieZmj+q2SYLXVl/jKt0qg69GwfLVrNm+gFiFbMg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-3.1.1.tgz", + "integrity": "sha512-l8NfuI6xaeLcSTUH67fk/RPKGr5Pm+oyeWJy6mBfWxHIiNpMctPQtha5/NLQ+RqGekJdmJ7cHH8l3JDkvoPXYg==", "license": "MIT", "dependencies": { - "axios": "^1.8.2", - "es-toolkit": "^1.34.1", - "qs": "^6.9.0" + "@jridgewell/trace-mapping": "^0.3.31", + "es-toolkit": "^1.33.0", + "laravel-precognition": "^2.0.0" + }, + "peerDependencies": { + "axios": "^1.15.2" + }, + "peerDependenciesMeta": { + "axios": { + "optional": true + } } }, "node_modules/@inertiajs/react": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@inertiajs/react/-/react-2.1.2.tgz", - "integrity": "sha512-hh3dQxoEumdjSRyMajYkEnG3fb3xkyexBD8tTSjo5OeulE/VteEjS7ZM8tNseM7ya/jb3G6ccoc5MSlYEh6atg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@inertiajs/react/-/react-3.1.1.tgz", + "integrity": "sha512-Z1MhuMaRC1rv7hdK8p8I4u88seku358Je4q7OPVG+FRSZAfnUGXGRBYaHq34HYDjoz1axb+/irkk1WdJFs37Rg==", "license": "MIT", "dependencies": { - "@inertiajs/core": "2.1.2", - "es-toolkit": "^1.33.0" + "@inertiajs/core": "3.1.1", + "es-toolkit": "^1.33.0", + "laravel-precognition": "^2.0.0" }, "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "react": "^19.0.0", + "react-dom": "^19.0.0" } }, "node_modules/@jridgewell/gen-mapping": { @@ -137,7 +149,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -147,14 +158,12 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1060,6 +1069,18 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1158,14 +1179,15 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, "node_modules/balanced-match": { @@ -1936,9 +1958,9 @@ } }, "node_modules/es-toolkit": { - "version": "1.39.10", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.10.tgz", - "integrity": "sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==", + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.46.1.tgz", + "integrity": "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==", "license": "MIT", "workspaces": [ "docs", @@ -2379,9 +2401,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -2408,9 +2430,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -2722,6 +2744,19 @@ "npm": ">=1.3.7" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/i18n-2": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/i18n-2/-/i18n-2-0.7.3.tgz", @@ -2776,9 +2811,9 @@ } }, "node_modules/inertia-sails": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/inertia-sails/-/inertia-sails-1.1.0.tgz", - "integrity": "sha512-HxeDiX1Bw5xVcd9wsLYnGrsjlCYP/HKyxDx9pF0GxICOjFN1Z+BKNX3cC3rwyJCSg2NbaQodZ8jLDZViFp/OQA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/inertia-sails/-/inertia-sails-1.4.0.tgz", + "integrity": "sha512-RqIOCEKW3dy8g3ojSWJH/6pVHw7t7zIaGPF4c7gBbdJh73lNSk8QtugyIFJbxjyWeN3lrnoCKgBBZDYKYl/g==", "license": "MIT", "peerDependencies": { "sails": ">=1", @@ -2967,6 +3002,23 @@ "graceful-fs": "^4.1.9" } }, + "node_modules/laravel-precognition": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/laravel-precognition/-/laravel-precognition-2.0.0.tgz", + "integrity": "sha512-dmA4HGc9m+TsVNsJs9/XQBI8u6j7coilN+qKkBuhuXQzH3HypwS/c5dFQ4UqUGjBbcxIM7zdk91kM/SRZwIvWQ==", + "license": "MIT", + "dependencies": { + "es-toolkit": "^1.32.0" + }, + "peerDependencies": { + "axios": "^1.4.0" + }, + "peerDependenciesMeta": { + "axios": { + "optional": true + } + } + }, "node_modules/lightningcss": { "version": "1.30.2", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", @@ -3973,10 +4025,13 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/psl": { "version": "1.15.0", @@ -3999,21 +4054,6 @@ "node": ">=6" } }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -4943,6 +4983,13 @@ "@sailshq/lodash": "^3.10.2" } }, + "node_modules/sounding": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/sounding/-/sounding-0.0.1.tgz", + "integrity": "sha512-fwjwSgZYU8umo2IDD+8T45MurA7XLw0sledowT3MATfivMTryfbK+WLJAQj6e7mBXeumPrgnj357Zp87/ejmcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", diff --git a/templates/mellow-react/package.json b/templates/mellow-react/package.json index 3da39925..41d231f7 100644 --- a/templates/mellow-react/package.json +++ b/templates/mellow-react/package.json @@ -5,11 +5,12 @@ "description": "a Sails application", "keywords": [], "dependencies": { - "@inertiajs/react": "^2.1.2", + "@inertiajs/react": "^3.1.1", "@sailshq/connect-redis": "^6.1.3", "@sailshq/lodash": "^3.10.7", "@sailshq/socket.io-redis": "^6.1.2", - "inertia-sails": "^1.1.0", + "axios": "^1.16.1", + "inertia-sails": "^1.4.0", "react": "^19.1.1", "react-dom": "^19.1.1", "sails": "^1.5.15", @@ -31,8 +32,8 @@ "sails-hook-shipwright": "^1.1.0", "sails.io.js": "^1.2.1", "socket.io-client": "^4.7.5", - "tailwindcss": "^4.1.16", - "sounding": "^0.0.1" + "sounding": "^0.0.1", + "tailwindcss": "^4.1.16" }, "scripts": { "dev": "node --watch-path=api --watch-path=config app.js", diff --git a/templates/mellow-svelte/package-lock.json b/templates/mellow-svelte/package-lock.json index 8a58c3d0..c2ea32ea 100644 --- a/templates/mellow-svelte/package-lock.json +++ b/templates/mellow-svelte/package-lock.json @@ -8,11 +8,12 @@ "name": "mellow-svelte", "version": "0.0.0", "dependencies": { - "@inertiajs/svelte": "^2.0.3", + "@inertiajs/svelte": "^3.1.1", "@sailshq/connect-redis": "^6.1.3", "@sailshq/lodash": "^3.10.6", "@sailshq/socket.io-redis": "^6.1.2", - "inertia-sails": "^1.1.0", + "axios": "^1.16.1", + "inertia-sails": "^1.4.0", "nodemailer": "^6.9.15", "sails": "^1.5.12", "sails-flash": "^0.0.1", @@ -37,6 +38,7 @@ "sails-hook-shipwright": "^1.1.0", "sails.io.js": "^1.2.1", "socket.io-client": "^4.8.0", + "sounding": "^0.0.1", "tailwindcss": "^4.1.16" }, "engines": { @@ -104,28 +106,36 @@ } }, "node_modules/@inertiajs/core": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-2.0.17.tgz", - "integrity": "sha512-tvYoqiouQSJrP7i7zVq61yyuEjlL96UU4nkkOWtOajXZlubGN4XrgRpnygpDk1KBO8V2yBab3oUZm+aZImwTHg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-3.1.1.tgz", + "integrity": "sha512-l8NfuI6xaeLcSTUH67fk/RPKGr5Pm+oyeWJy6mBfWxHIiNpMctPQtha5/NLQ+RqGekJdmJ7cHH8l3JDkvoPXYg==", "license": "MIT", "dependencies": { - "axios": "^1.8.2", - "es-toolkit": "^1.34.1", - "qs": "^6.9.0" + "@jridgewell/trace-mapping": "^0.3.31", + "es-toolkit": "^1.33.0", + "laravel-precognition": "^2.0.0" + }, + "peerDependencies": { + "axios": "^1.15.2" + }, + "peerDependenciesMeta": { + "axios": { + "optional": true + } } }, "node_modules/@inertiajs/svelte": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@inertiajs/svelte/-/svelte-2.0.17.tgz", - "integrity": "sha512-QhhzqznNAXkQsOT/QLUz7555xPzu55HNJS2218WFyAxOdiCxSYtiMlBzD7skDbsaZmoD1EqRAnGjvE/dS8rEVA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@inertiajs/svelte/-/svelte-3.1.1.tgz", + "integrity": "sha512-Q8TcrQ6+6JxaapcBQ3I7fklgg5oiUi7Sjs2eVqaaQoMJt3utB945eAV4Sn374dGItUvg0soJuVimUsQYXAZM9w==", "license": "MIT", "dependencies": { - "@inertiajs/core": "2.0.17", + "@inertiajs/core": "3.1.1", "es-toolkit": "^1.33.0", - "html-escape": "^2.0.0" + "laravel-precognition": "^2.0.0" }, "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0 || ^5.0.0-next.244" + "svelte": "^5.0.0" } }, "node_modules/@jridgewell/gen-mapping": { @@ -163,9 +173,9 @@ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1053,6 +1063,18 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1170,14 +1192,15 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, "node_modules/axobject-query": { @@ -2041,9 +2064,9 @@ } }, "node_modules/es-toolkit": { - "version": "1.39.8", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.8.tgz", - "integrity": "sha512-A8QO9TfF+rltS8BXpdu8OS+rpGgEdnRhqIVxO/ZmNvnXBYgOdSsxukT55ELyP94gZIntWJ+Li9QRrT2u1Kitpg==", + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.46.1.tgz", + "integrity": "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==", "license": "MIT", "workspaces": [ "docs", @@ -2553,9 +2576,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -2582,9 +2605,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -2847,12 +2870,6 @@ "dev": true, "license": "ISC" }, - "node_modules/html-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/html-escape/-/html-escape-2.0.0.tgz", - "integrity": "sha512-BYh0wceM2Vm4/Q8TNfnKaHXs4DCv2DuYVS87DR40elSvFc+8a6B9mE9ej+8nCOkdqPx7puEx9+hm+GoJ3f9PzA==", - "license": "Public Domain" - }, "node_modules/http-errors": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", @@ -2902,6 +2919,19 @@ "npm": ">=1.3.7" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/i18n-2": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/i18n-2/-/i18n-2-0.7.3.tgz", @@ -2969,9 +2999,9 @@ } }, "node_modules/inertia-sails": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/inertia-sails/-/inertia-sails-1.1.0.tgz", - "integrity": "sha512-HxeDiX1Bw5xVcd9wsLYnGrsjlCYP/HKyxDx9pF0GxICOjFN1Z+BKNX3cC3rwyJCSg2NbaQodZ8jLDZViFp/OQA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/inertia-sails/-/inertia-sails-1.4.0.tgz", + "integrity": "sha512-RqIOCEKW3dy8g3ojSWJH/6pVHw7t7zIaGPF4c7gBbdJh73lNSk8QtugyIFJbxjyWeN3lrnoCKgBBZDYKYl/g==", "license": "MIT", "peerDependencies": { "sails": ">=1", @@ -3224,6 +3254,23 @@ "graceful-fs": "^4.1.9" } }, + "node_modules/laravel-precognition": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/laravel-precognition/-/laravel-precognition-2.0.0.tgz", + "integrity": "sha512-dmA4HGc9m+TsVNsJs9/XQBI8u6j7coilN+qKkBuhuXQzH3HypwS/c5dFQ4UqUGjBbcxIM7zdk91kM/SRZwIvWQ==", + "license": "MIT", + "dependencies": { + "es-toolkit": "^1.32.0" + }, + "peerDependencies": { + "axios": "^1.4.0" + }, + "peerDependenciesMeta": { + "axios": { + "optional": true + } + } + }, "node_modules/lightningcss": { "version": "1.30.2", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", @@ -4270,54 +4317,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "optional": true, - "peer": true, - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", - "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">=14" - } - }, "node_modules/prettier": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", @@ -4482,10 +4481,13 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/psl": { "version": "1.15.0", @@ -4508,21 +4510,6 @@ "node": ">=6" } }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -5501,6 +5488,13 @@ "@sailshq/lodash": "^3.10.2" } }, + "node_modules/sounding": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/sounding/-/sounding-0.0.1.tgz", + "integrity": "sha512-fwjwSgZYU8umo2IDD+8T45MurA7XLw0sledowT3MATfivMTryfbK+WLJAQj6e7mBXeumPrgnj357Zp87/ejmcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -6278,21 +6272,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, - "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, "node_modules/yargs": { "version": "3.4.5", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.4.5.tgz", diff --git a/templates/mellow-svelte/package.json b/templates/mellow-svelte/package.json index e42433f7..001e8c09 100644 --- a/templates/mellow-svelte/package.json +++ b/templates/mellow-svelte/package.json @@ -5,11 +5,12 @@ "description": "a Sails application", "keywords": [], "dependencies": { - "@inertiajs/svelte": "^2.0.3", + "@inertiajs/svelte": "^3.1.1", "@sailshq/connect-redis": "^6.1.3", "@sailshq/lodash": "^3.10.6", "@sailshq/socket.io-redis": "^6.1.2", - "inertia-sails": "^1.1.0", + "axios": "^1.16.1", + "inertia-sails": "^1.4.0", "nodemailer": "^6.9.15", "sails": "^1.5.12", "sails-flash": "^0.0.1", @@ -34,8 +35,8 @@ "sails-hook-shipwright": "^1.1.0", "sails.io.js": "^1.2.1", "socket.io-client": "^4.8.0", - "tailwindcss": "^4.1.16", - "sounding": "^0.0.1" + "sounding": "^0.0.1", + "tailwindcss": "^4.1.16" }, "scripts": { "dev": "node --watch-path=api --watch-path=config app.js", diff --git a/templates/mellow-vue/package-lock.json b/templates/mellow-vue/package-lock.json index ca636fac..3f4bb6b8 100644 --- a/templates/mellow-vue/package-lock.json +++ b/templates/mellow-vue/package-lock.json @@ -8,11 +8,12 @@ "name": "mellow-vue", "version": "0.0.0", "dependencies": { - "@inertiajs/vue3": "^2.0.3", + "@inertiajs/vue3": "^3.1.1", "@sailshq/connect-redis": "^6.1.3", "@sailshq/lodash": "^3.10.3", "@sailshq/socket.io-redis": "^6.1.2", - "inertia-sails": "^1.0.1", + "axios": "^1.16.1", + "inertia-sails": "^1.4.0", "nodemailer": "^6.9.15", "sails": "^1.5.14", "sails-flash": "^0.0.1", @@ -35,6 +36,7 @@ "sails-hook-shipwright": "^1.1.0", "sails.io.js": "^1.2.1", "socket.io-client": "^4.7.5", + "sounding": "^0.0.1", "tailwindcss": "^4.1.16" }, "engines": { @@ -135,24 +137,33 @@ } }, "node_modules/@inertiajs/core": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-2.0.17.tgz", - "integrity": "sha512-tvYoqiouQSJrP7i7zVq61yyuEjlL96UU4nkkOWtOajXZlubGN4XrgRpnygpDk1KBO8V2yBab3oUZm+aZImwTHg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-3.1.1.tgz", + "integrity": "sha512-l8NfuI6xaeLcSTUH67fk/RPKGr5Pm+oyeWJy6mBfWxHIiNpMctPQtha5/NLQ+RqGekJdmJ7cHH8l3JDkvoPXYg==", "license": "MIT", "dependencies": { - "axios": "^1.8.2", - "es-toolkit": "^1.34.1", - "qs": "^6.9.0" + "@jridgewell/trace-mapping": "^0.3.31", + "es-toolkit": "^1.33.0", + "laravel-precognition": "^2.0.0" + }, + "peerDependencies": { + "axios": "^1.15.2" + }, + "peerDependenciesMeta": { + "axios": { + "optional": true + } } }, "node_modules/@inertiajs/vue3": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@inertiajs/vue3/-/vue3-2.0.17.tgz", - "integrity": "sha512-Al0IMHQSj5aTQBLUAkljFEMCw4YRwSiOSKzN8LAbvJpKwvJFgc/wSj3wVVpr/AO9y9mz1w2mtvjnDoOzsntPLw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@inertiajs/vue3/-/vue3-3.1.1.tgz", + "integrity": "sha512-k2xdcRIxbflGW9Doo6pg4pUfBK9t7PgODyHJFaf1p9AxOFRdBIKrxxvaGFQTT5L4GjOUdZ9trfDLxZ2IIaCabA==", "license": "MIT", "dependencies": { - "@inertiajs/core": "2.0.17", - "es-toolkit": "^1.33.0" + "@inertiajs/core": "3.1.1", + "es-toolkit": "^1.33.0", + "laravel-precognition": "^2.0.0" }, "peerDependencies": { "vue": "^3.0.0" @@ -184,7 +195,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -208,10 +218,9 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1419,6 +1428,18 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1569,14 +1590,15 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, "node_modules/balanced-match": { @@ -2525,9 +2547,9 @@ } }, "node_modules/es-toolkit": { - "version": "1.39.8", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.8.tgz", - "integrity": "sha512-A8QO9TfF+rltS8BXpdu8OS+rpGgEdnRhqIVxO/ZmNvnXBYgOdSsxukT55ELyP94gZIntWJ+Li9QRrT2u1Kitpg==", + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.46.1.tgz", + "integrity": "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==", "license": "MIT", "workspaces": [ "docs", @@ -3098,9 +3120,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -3127,9 +3149,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -3456,6 +3478,19 @@ "npm": ">=1.3.7" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/i18n-2": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/i18n-2/-/i18n-2-0.7.3.tgz", @@ -3523,9 +3558,9 @@ } }, "node_modules/inertia-sails": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/inertia-sails/-/inertia-sails-1.0.1.tgz", - "integrity": "sha512-+G2EQPvgfN6y575MMjBVx2X11QXs1FwJ2WIbbKDUcVmkwif4fbipeaM53iB5uN+JumDtvYqRlepVfVNIz3d1ag==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/inertia-sails/-/inertia-sails-1.4.0.tgz", + "integrity": "sha512-RqIOCEKW3dy8g3ojSWJH/6pVHw7t7zIaGPF4c7gBbdJh73lNSk8QtugyIFJbxjyWeN3lrnoCKgBBZDYKYl/g==", "license": "MIT", "peerDependencies": { "sails": ">=1", @@ -3795,6 +3830,23 @@ "graceful-fs": "^4.1.9" } }, + "node_modules/laravel-precognition": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/laravel-precognition/-/laravel-precognition-2.0.0.tgz", + "integrity": "sha512-dmA4HGc9m+TsVNsJs9/XQBI8u6j7coilN+qKkBuhuXQzH3HypwS/c5dFQ4UqUGjBbcxIM7zdk91kM/SRZwIvWQ==", + "license": "MIT", + "dependencies": { + "es-toolkit": "^1.32.0" + }, + "peerDependencies": { + "axios": "^1.4.0" + }, + "peerDependenciesMeta": { + "axios": { + "optional": true + } + } + }, "node_modules/lightningcss": { "version": "1.30.2", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", @@ -4987,10 +5039,13 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/psl": { "version": "1.15.0", @@ -5013,21 +5068,6 @@ "node": ">=6" } }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -6237,6 +6277,13 @@ "@sailshq/lodash": "^3.10.2" } }, + "node_modules/sounding": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/sounding/-/sounding-0.0.1.tgz", + "integrity": "sha512-fwjwSgZYU8umo2IDD+8T45MurA7XLw0sledowT3MATfivMTryfbK+WLJAQj6e7mBXeumPrgnj357Zp87/ejmcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/templates/mellow-vue/package.json b/templates/mellow-vue/package.json index 1756046c..0048e79e 100644 --- a/templates/mellow-vue/package.json +++ b/templates/mellow-vue/package.json @@ -5,11 +5,12 @@ "description": "a Sails application", "keywords": [], "dependencies": { - "@inertiajs/vue3": "^2.0.3", + "@inertiajs/vue3": "^3.1.1", "@sailshq/connect-redis": "^6.1.3", "@sailshq/lodash": "^3.10.3", "@sailshq/socket.io-redis": "^6.1.2", - "inertia-sails": "^1.0.1", + "axios": "^1.16.1", + "inertia-sails": "^1.4.0", "nodemailer": "^6.9.15", "sails": "^1.5.14", "sails-flash": "^0.0.1", @@ -32,8 +33,8 @@ "sails-hook-shipwright": "^1.1.0", "sails.io.js": "^1.2.1", "socket.io-client": "^4.7.5", - "tailwindcss": "^4.1.16", - "sounding": "^0.0.1" + "sounding": "^0.0.1", + "tailwindcss": "^4.1.16" }, "scripts": { "dev": "node --watch-path=api --watch-path=config app.js", From dc2f8d0683edb6edb8221fda33b62360b9534298 Mon Sep 17 00:00:00 2001 From: Kelvin Oghenerhoro Omereshone Date: Fri, 22 May 2026 21:36:45 +0100 Subject: [PATCH 3/6] test(ascent-vue): restore template smoke checks --- .../api/controllers/security/disable-2fa.js | 4 +- .../helpers/passkey/verify-authentication.js | 5 +- .../ascent-vue/api/hooks/custom/index.js | 5 +- templates/ascent-vue/api/models/Team.js | 5 +- .../assets/js/components/BackupCodesModal.vue | 4 +- .../ascent-vue/assets/js/components/Chips.vue | 2 +- .../components/EmailTwoFactorSetupModal.vue | 2 +- .../assets/js/components/ImageUpload.vue | 2 +- .../js/components/ManagePasskeysModal.vue | 2 +- .../assets/js/components/Sidebar.vue | 14 +- .../assets/js/components/TotpSetupModal.vue | 6 +- .../assets/js/components/UserMenu.vue | 8 +- .../assets/js/layouts/AppLayout.vue | 20 +- .../assets/js/layouts/DashboardLayout.vue | 8 +- .../assets/js/pages/auth/check-email.vue | 6 +- .../assets/js/pages/auth/forgot-password.vue | 16 +- .../ascent-vue/assets/js/pages/auth/login.vue | 38 ++-- .../assets/js/pages/auth/reset-password.vue | 18 +- .../assets/js/pages/auth/signup.vue | 38 ++-- .../assets/js/pages/auth/verify-2fa.vue | 20 +- .../assets/js/pages/billing/pricing.vue | 158 ++++++++-------- templates/ascent-vue/assets/js/pages/blog.vue | 38 ++-- .../ascent-vue/assets/js/pages/contact.vue | 22 +-- .../assets/js/pages/dashboard/index.vue | 4 +- .../ascent-vue/assets/js/pages/features.vue | 172 +++++++++--------- .../ascent-vue/assets/js/pages/index.vue | 136 +++++++------- .../assets/js/pages/settings/billing.vue | 12 +- .../assets/js/pages/settings/security.vue | 28 +-- .../assets/js/pages/settings/team.vue | 8 +- .../assets/js/pages/team/invite.vue | 20 +- templates/ascent-vue/assets/js/volt/Chip.vue | 2 +- .../assets/js/volt/ConfirmDialog.vue | 8 +- .../ascent-vue/assets/js/volt/Password.vue | 4 +- .../ascent-vue/assets/js/volt/Select.vue | 2 +- templates/ascent-vue/config/env/production.js | 2 +- templates/ascent-vue/prettier.config.js | 1 - .../unit/helpers/get-user-initials.test.js | 4 +- 37 files changed, 420 insertions(+), 424 deletions(-) diff --git a/templates/ascent-vue/api/controllers/security/disable-2fa.js b/templates/ascent-vue/api/controllers/security/disable-2fa.js index 766d9a02..263714ef 100644 --- a/templates/ascent-vue/api/controllers/security/disable-2fa.js +++ b/templates/ascent-vue/api/controllers/security/disable-2fa.js @@ -65,8 +65,8 @@ module.exports = { method === 'all' ? 'All two-factor authentication methods' : method === 'totp' - ? 'Authenticator app (TOTP)' - : 'Email verification (2FA)' + ? 'Authenticator app (TOTP)' + : 'Email verification (2FA)' this.req.flash('success', `${methodDisplayName} disabled successfully`) return '/settings/security' diff --git a/templates/ascent-vue/api/helpers/passkey/verify-authentication.js b/templates/ascent-vue/api/helpers/passkey/verify-authentication.js index 030ecf35..598eb7ef 100644 --- a/templates/ascent-vue/api/helpers/passkey/verify-authentication.js +++ b/templates/ascent-vue/api/helpers/passkey/verify-authentication.js @@ -100,9 +100,8 @@ module.exports = { requireUserVerification: true } - const verification = await verifyAuthenticationResponse( - verificationOptions - ) + const verification = + await verifyAuthenticationResponse(verificationOptions) if (!verification.verified) { return exits.verificationFailed() diff --git a/templates/ascent-vue/api/hooks/custom/index.js b/templates/ascent-vue/api/hooks/custom/index.js index ed36dc47..11ea95ab 100644 --- a/templates/ascent-vue/api/hooks/custom/index.js +++ b/templates/ascent-vue/api/hooks/custom/index.js @@ -54,9 +54,8 @@ module.exports = function defineCustomHook(sails) { } // Add avatar URL using helper - user.currentAvatarUrl = await sails.helpers.user.getAvatarUrl( - user - ) + user.currentAvatarUrl = + await sails.helpers.user.getAvatarUrl(user) return user }) diff --git a/templates/ascent-vue/api/models/Team.js b/templates/ascent-vue/api/models/Team.js index 9f76563d..69dfba26 100644 --- a/templates/ascent-vue/api/models/Team.js +++ b/templates/ascent-vue/api/models/Team.js @@ -68,9 +68,8 @@ module.exports = { beforeCreate: async function (valuesToSet, proceed) { // Generate invite token if not provided if (!valuesToSet.inviteToken) { - valuesToSet.inviteToken = await sails.helpers.strings.random( - 'url-friendly' - ) + valuesToSet.inviteToken = + await sails.helpers.strings.random('url-friendly') } return proceed() } diff --git a/templates/ascent-vue/assets/js/components/BackupCodesModal.vue b/templates/ascent-vue/assets/js/components/BackupCodesModal.vue index eee925e1..9c08c3c2 100644 --- a/templates/ascent-vue/assets/js/components/BackupCodesModal.vue +++ b/templates/ascent-vue/assets/js/components/BackupCodesModal.vue @@ -54,9 +54,9 @@ function handleSavedCodes() {
- +

{{ diff --git a/templates/ascent-vue/assets/js/components/Chips.vue b/templates/ascent-vue/assets/js/components/Chips.vue index 28c64c7c..ba95e0fa 100644 --- a/templates/ascent-vue/assets/js/components/Chips.vue +++ b/templates/ascent-vue/assets/js/components/Chips.vue @@ -91,7 +91,7 @@ function handleBlur() {