fixed reverse video generation issue
This commit is contained in:
parent
fd023934ad
commit
a25a67daba
5
.gitignore
vendored
5
.gitignore
vendored
@ -4,11 +4,6 @@ node_modules/
|
|||||||
*/node_modules/
|
*/node_modules/
|
||||||
**/node_modules/
|
**/node_modules/
|
||||||
*/build/
|
*/build/
|
||||||
<<<<<<< Updated upstream
|
|
||||||
package-lock.json
|
package-lock.json
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
.claude/
|
.claude/
|
||||||
=======
|
|
||||||
.claude
|
|
||||||
CLAUDE.md
|
|
||||||
>>>>>>> Stashed changes
|
|
||||||
|
|||||||
@ -9,8 +9,7 @@ RUN yarn build
|
|||||||
|
|
||||||
|
|
||||||
FROM node:20.15.1-alpine
|
FROM node:20.15.1-alpine
|
||||||
# Install FFmpeg for video processing (reversed video generation)
|
# FFmpeg is bundled via npm package ffmpeg-static
|
||||||
RUN apk add --no-cache ffmpeg
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY backend/package.json backend/yarn.lock ./
|
COPY backend/package.json backend/yarn.lock ./
|
||||||
RUN yarn install --pure-lockfile
|
RUN yarn install --pure-lockfile
|
||||||
|
|||||||
@ -15,8 +15,7 @@ RUN yarn install --pure-lockfile
|
|||||||
FROM node:20.15.1-alpine AS build
|
FROM node:20.15.1-alpine AS build
|
||||||
RUN apk add --no-cache git nginx curl
|
RUN apk add --no-cache git nginx curl
|
||||||
RUN apk add --no-cache lsof procps
|
RUN apk add --no-cache lsof procps
|
||||||
# Install FFmpeg for video processing (reversed video generation)
|
# FFmpeg is bundled via npm package ffmpeg-static
|
||||||
RUN apk add --no-cache ffmpeg
|
|
||||||
RUN yarn global add concurrently
|
RUN yarn global add concurrently
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
|
|||||||
@ -27,6 +27,8 @@
|
|||||||
"dotenv": "^16.4.0",
|
"dotenv": "^16.4.0",
|
||||||
"express": "4.18.2",
|
"express": "4.18.2",
|
||||||
"express-validator": "^7.0.0",
|
"express-validator": "^7.0.0",
|
||||||
|
"ffmpeg-static": "^5.2.0",
|
||||||
|
"ffprobe-static": "^3.1.0",
|
||||||
"fluent-ffmpeg": "^2.1.3",
|
"fluent-ffmpeg": "^2.1.3",
|
||||||
"formidable": "1.2.2",
|
"formidable": "1.2.2",
|
||||||
"helmet": "^8.0.0",
|
"helmet": "^8.0.0",
|
||||||
|
|||||||
@ -19,7 +19,8 @@ passport.use(
|
|||||||
},
|
},
|
||||||
async (req, token, done) => {
|
async (req, token, done) => {
|
||||||
try {
|
try {
|
||||||
const user = await UsersDBApi.findForAuth({ email: token.user.email });
|
// Use lightweight auth query - only loads essential fields + permissions
|
||||||
|
const user = await UsersDBApi.findByForAuth({ email: token.user.email });
|
||||||
|
|
||||||
if (user && user.disabled) {
|
if (user && user.disabled) {
|
||||||
return done(new Error(`User '${user.email}' is disabled`));
|
return done(new Error(`User '${user.email}' is disabled`));
|
||||||
|
|||||||
@ -70,6 +70,7 @@ class Asset_variantsDBApi extends GenericDBApi {
|
|||||||
static getFieldMapping(data) {
|
static getFieldMapping(data) {
|
||||||
return {
|
return {
|
||||||
id: data.id || undefined,
|
id: data.id || undefined,
|
||||||
|
assetId: data.assetId || null,
|
||||||
variant_type: data.variant_type || null,
|
variant_type: data.variant_type || null,
|
||||||
cdn_url: data.cdn_url || null,
|
cdn_url: data.cdn_url || null,
|
||||||
storage_key: data.storage_key || null,
|
storage_key: data.storage_key || null,
|
||||||
|
|||||||
@ -10,6 +10,47 @@ const Sequelize = db.Sequelize;
|
|||||||
const Op = Sequelize.Op;
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
module.exports = class UsersDBApi {
|
module.exports = class UsersDBApi {
|
||||||
|
/**
|
||||||
|
* Default includes for findBy() - minimal set for single user lookup
|
||||||
|
* Only loads avatar and app_role with permissions (needed for RBAC)
|
||||||
|
*/
|
||||||
|
static get FIND_BY_INCLUDES() {
|
||||||
|
return [
|
||||||
|
{ association: 'avatar' },
|
||||||
|
{
|
||||||
|
association: 'app_role',
|
||||||
|
include: [{ association: 'permissions' }],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal includes for findAll() - only app_role for list display
|
||||||
|
* Excludes avatar, custom_permissions (rarely needed in list views)
|
||||||
|
*/
|
||||||
|
static get FIND_ALL_INCLUDES() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
model: db.roles,
|
||||||
|
as: 'app_role',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sensitive fields that should be excluded from query results
|
||||||
|
*/
|
||||||
|
static get SENSITIVE_FIELDS() {
|
||||||
|
return [
|
||||||
|
'password',
|
||||||
|
'emailVerificationToken',
|
||||||
|
'emailVerificationTokenExpiresAt',
|
||||||
|
'passwordResetToken',
|
||||||
|
'passwordResetTokenExpiresAt',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
static async create(data, options) {
|
static async create(data, options) {
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
@ -256,24 +297,21 @@ module.exports = class UsersDBApi {
|
|||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a single user by criteria
|
||||||
|
* Uses minimal includes by default (avatar + app_role with permissions)
|
||||||
|
* @param {Object} where - Query conditions
|
||||||
|
* @param {Object} options - Options including transaction and custom includes
|
||||||
|
* @param {Array} options.include - Override default includes if needed
|
||||||
|
*/
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const include = options?.include ?? this.FIND_BY_INCLUDES;
|
||||||
|
|
||||||
const users = await db.users.findOne({
|
const users = await db.users.findOne({
|
||||||
where,
|
where,
|
||||||
transaction,
|
transaction,
|
||||||
include: [
|
include,
|
||||||
{ association: 'project_memberships_user' },
|
|
||||||
{ association: 'presigned_url_requests_user' },
|
|
||||||
{ association: 'publish_events_user' },
|
|
||||||
{ association: 'access_logs_user' },
|
|
||||||
{ association: 'avatar' },
|
|
||||||
{
|
|
||||||
association: 'app_role',
|
|
||||||
include: [{ association: 'permissions' }],
|
|
||||||
},
|
|
||||||
{ association: 'custom_permissions' },
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!users) {
|
if (!users) {
|
||||||
@ -290,28 +328,40 @@ module.exports = class UsersDBApi {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findForAuth(where, options) {
|
/**
|
||||||
|
* Lightweight user lookup for JWT authentication
|
||||||
|
* Only loads essential fields and app_role with permissions for RBAC
|
||||||
|
* Optimized for the auth flow that runs on every authenticated request
|
||||||
|
*/
|
||||||
|
static async findByForAuth(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const users = await db.users.findOne({
|
const user = await db.users.findOne({
|
||||||
where,
|
where,
|
||||||
transaction,
|
transaction,
|
||||||
|
attributes: [
|
||||||
|
'id',
|
||||||
|
'email',
|
||||||
|
'disabled',
|
||||||
|
'firstName',
|
||||||
|
'lastName',
|
||||||
|
'app_roleId',
|
||||||
|
],
|
||||||
include: [
|
include: [
|
||||||
{ association: 'avatar' },
|
|
||||||
{
|
{
|
||||||
association: 'app_role',
|
association: 'app_role',
|
||||||
include: [{ association: 'permissions' }],
|
include: [{ association: 'permissions' }],
|
||||||
},
|
},
|
||||||
{ association: 'custom_permissions' },
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!users) {
|
if (!user) {
|
||||||
return users;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = users.get({ plain: true });
|
const output = user.get({ plain: true });
|
||||||
|
|
||||||
|
// Map nested permissions from app_role for backward compatibility
|
||||||
if (output.app_role) {
|
if (output.app_role) {
|
||||||
output.app_role_permissions = output.app_role.permissions || [];
|
output.app_role_permissions = output.app_role.permissions || [];
|
||||||
}
|
}
|
||||||
@ -330,11 +380,12 @@ module.exports = class UsersDBApi {
|
|||||||
const appRoleTerms = filter.app_role ? filter.app_role.split('|') : [];
|
const appRoleTerms = filter.app_role ? filter.app_role.split('|') : [];
|
||||||
const appRoleValidUuids = Utils.filterValidUuids(appRoleTerms);
|
const appRoleValidUuids = Utils.filterValidUuids(appRoleTerms);
|
||||||
|
|
||||||
|
// Use lightweight includes for list view (only app_role, no custom_permissions or avatar)
|
||||||
let include = [
|
let include = [
|
||||||
{
|
{
|
||||||
model: db.roles,
|
model: db.roles,
|
||||||
as: 'app_role',
|
as: 'app_role',
|
||||||
|
required: false,
|
||||||
where: filter.app_role
|
where: filter.app_role
|
||||||
? {
|
? {
|
||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
@ -352,17 +403,6 @@ module.exports = class UsersDBApi {
|
|||||||
}
|
}
|
||||||
: {},
|
: {},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
model: db.permissions,
|
|
||||||
as: 'custom_permissions',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
model: db.file,
|
|
||||||
as: 'avatar',
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
@ -562,22 +602,8 @@ module.exports = class UsersDBApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
if (options?.countOnly) {
|
|
||||||
const count = await db.users.count({
|
|
||||||
where,
|
|
||||||
include: include.filter((entry) => entry.required || entry.where),
|
|
||||||
distinct: true,
|
|
||||||
transaction: options?.transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
rows: [],
|
|
||||||
count,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryOptions = {
|
const queryOptions = {
|
||||||
|
attributes: { exclude: this.SENSITIVE_FIELDS },
|
||||||
where,
|
where,
|
||||||
include,
|
include,
|
||||||
distinct: true,
|
distinct: true,
|
||||||
@ -586,15 +612,19 @@ module.exports = class UsersDBApi {
|
|||||||
? [[filter.field, filter.sort]]
|
? [[filter.field, filter.sort]]
|
||||||
: [['createdAt', 'desc']],
|
: [['createdAt', 'desc']],
|
||||||
transaction: options?.transaction,
|
transaction: options?.transaction,
|
||||||
limit: limit ? Number(limit) : undefined,
|
|
||||||
offset: offset ? Number(offset) : undefined,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!options?.countOnly) {
|
||||||
|
queryOptions.limit = limit ? Number(limit) : undefined;
|
||||||
|
queryOptions.offset = offset ? Number(offset) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
const { rows, count } = await db.users.findAndCountAll(queryOptions);
|
const { rows, count } = await db.users.findAndCountAll(queryOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rows,
|
rows: options?.countOnly ? [] : rows,
|
||||||
count,
|
count: count,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error executing query:', error);
|
console.error('Error executing query:', error);
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
|
const express = require('express');
|
||||||
const Tour_pagesService = require('../services/tour_pages');
|
const Tour_pagesService = require('../services/tour_pages');
|
||||||
const Tour_pagesDBApi = require('../db/api/tour_pages');
|
const Tour_pagesDBApi = require('../db/api/tour_pages');
|
||||||
const { createEntityRouter } = require('../factories/router.factory');
|
const { wrapAsync, commonErrorHandler, isUuidV4 } = require('../helpers');
|
||||||
|
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
@ -148,8 +151,175 @@ const { createEntityRouter } = require('../factories/router.factory');
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = createEntityRouter(
|
const router = express.Router();
|
||||||
'tour_pages',
|
|
||||||
Tour_pagesService,
|
// Apply permission checks
|
||||||
Tour_pagesDBApi,
|
router.use(checkCrudPermissions('tour_pages'));
|
||||||
|
|
||||||
|
// POST - Create
|
||||||
|
router.post(
|
||||||
|
'/',
|
||||||
|
wrapAsync(async (req, res) => {
|
||||||
|
const referer =
|
||||||
|
req.headers.referer ||
|
||||||
|
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
|
const link = new URL(referer);
|
||||||
|
const payload = await Tour_pagesService.create(
|
||||||
|
req.body.data,
|
||||||
|
req.currentUser,
|
||||||
|
true,
|
||||||
|
link.host,
|
||||||
);
|
);
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// POST - Bulk import
|
||||||
|
router.post(
|
||||||
|
'/bulk-import',
|
||||||
|
wrapAsync(async (req, res) => {
|
||||||
|
const referer =
|
||||||
|
req.headers.referer ||
|
||||||
|
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
|
const link = new URL(referer);
|
||||||
|
await Tour_pagesService.bulkImport(req, res, true, link.host);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// PUT - Update
|
||||||
|
router.put(
|
||||||
|
'/:id',
|
||||||
|
wrapAsync(async (req, res) => {
|
||||||
|
await Tour_pagesService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// DELETE - Remove
|
||||||
|
router.delete(
|
||||||
|
'/:id',
|
||||||
|
wrapAsync(async (req, res) => {
|
||||||
|
await Tour_pagesService.remove(req.params.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// POST - Delete by IDs
|
||||||
|
router.post(
|
||||||
|
'/deleteByIds',
|
||||||
|
wrapAsync(async (req, res) => {
|
||||||
|
await Tour_pagesService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// GET - List all (with reverseVideoUrl population)
|
||||||
|
router.get(
|
||||||
|
'/',
|
||||||
|
wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype;
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const runtimeContext = req.runtimeContext;
|
||||||
|
|
||||||
|
const payload = await Tour_pagesDBApi.findAll(req.query, {
|
||||||
|
currentUser,
|
||||||
|
runtimeContext,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Eagerly populate reverseVideoUrl for navigation elements
|
||||||
|
// This ensures reversed videos generated asynchronously are available immediately
|
||||||
|
if (payload.rows && payload.rows.length > 0) {
|
||||||
|
payload.rows = await Tour_pagesService.populateReverseVideoUrls(
|
||||||
|
payload.rows,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filetype === 'csv') {
|
||||||
|
const fields = Tour_pagesDBApi.CSV_FIELDS || ['id', 'createdAt'];
|
||||||
|
const opts = { fields };
|
||||||
|
try {
|
||||||
|
const csv = parse(payload.rows, opts);
|
||||||
|
res.status(200).attachment('export.csv').send(csv);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).send('CSV export error');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST - Check reverse video status
|
||||||
|
* Used by frontend to poll after save to check if reversed videos are ready.
|
||||||
|
*/
|
||||||
|
router.post(
|
||||||
|
'/reverse-video-status',
|
||||||
|
wrapAsync(async (req, res) => {
|
||||||
|
const { storageKeys } = req.body;
|
||||||
|
|
||||||
|
if (!Array.isArray(storageKeys)) {
|
||||||
|
return res.status(400).send({ error: 'storageKeys must be an array' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = await Tour_pagesService.checkReverseVideoStatus(storageKeys);
|
||||||
|
res.status(200).send(status);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// GET - Count
|
||||||
|
router.get(
|
||||||
|
'/count',
|
||||||
|
wrapAsync(async (req, res) => {
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const runtimeContext = req.runtimeContext;
|
||||||
|
const payload = await Tour_pagesDBApi.findAll(req.query, {
|
||||||
|
countOnly: true,
|
||||||
|
currentUser,
|
||||||
|
runtimeContext,
|
||||||
|
});
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// GET - Autocomplete
|
||||||
|
router.get(
|
||||||
|
'/autocomplete',
|
||||||
|
wrapAsync(async (req, res) => {
|
||||||
|
const payload = await Tour_pagesDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
);
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// GET - Single item (with reverseVideoUrl population)
|
||||||
|
router.get(
|
||||||
|
'/:id',
|
||||||
|
wrapAsync(async (req, res) => {
|
||||||
|
if (!isUuidV4(req.params.id)) {
|
||||||
|
return res.status(400).send('Invalid tour_pages id');
|
||||||
|
}
|
||||||
|
|
||||||
|
const runtimeContext = req.runtimeContext;
|
||||||
|
let payload = await Tour_pagesDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
{ runtimeContext },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Eagerly populate reverseVideoUrl for navigation elements
|
||||||
|
if (payload) {
|
||||||
|
payload = await Tour_pagesService.populateReverseVideoUrls(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
router.use('/', commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
const AssetsDBApi = require('../db/api/assets');
|
const AssetsDBApi = require('../db/api/assets');
|
||||||
const Asset_variantsDBApi = require('../db/api/asset_variants');
|
|
||||||
const { createEntityService } = require('../factories/service.factory');
|
const { createEntityService } = require('../factories/service.factory');
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
const { downloadToBuffer, uploadBuffer } = require('./file');
|
|
||||||
const videoProcessing = require('./videoProcessing');
|
// Note: Reversed video generation was moved to tour_pages.js to generate
|
||||||
const { logger } = require('../utils/logger');
|
// only when videos are actually used as transitions (not all video uploads).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Valid MIME type patterns for each asset type
|
* Valid MIME type patterns for each asset type
|
||||||
@ -91,106 +90,13 @@ class AssetsService extends BaseService {
|
|||||||
// Call parent create
|
// Call parent create
|
||||||
const asset = await super.create(data, currentUser);
|
const asset = await super.create(data, currentUser);
|
||||||
|
|
||||||
// Pre-generate reversed video for video assets (async, doesn't block response)
|
// Note: Reversed video generation is handled by tour_pages.js when the video
|
||||||
if (assetType === 'video' && data.storage_key) {
|
// is assigned to a navigation element. This avoids unnecessary processing
|
||||||
AssetsService.preGenerateReversedVideo(asset, currentUser).catch(
|
// for videos that aren't used as transitions.
|
||||||
(err) => {
|
|
||||||
logger.error(
|
|
||||||
{ err, assetId: asset.id },
|
|
||||||
'Failed to pre-generate reversed video (non-blocking)',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return asset;
|
return asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Pre-generate reversed video variant for a video asset.
|
|
||||||
* Runs asynchronously after asset creation - doesn't block the upload response.
|
|
||||||
* This ensures reversed videos are ready for instant use in transitions.
|
|
||||||
*
|
|
||||||
* @param {Object} asset - Created asset record
|
|
||||||
* @param {Object} currentUser - Current user context
|
|
||||||
*/
|
|
||||||
static async preGenerateReversedVideo(asset, currentUser) {
|
|
||||||
const log = logger.child({
|
|
||||||
assetId: asset.id,
|
|
||||||
operation: 'preGenerateReversed',
|
|
||||||
});
|
|
||||||
|
|
||||||
log.info('Starting pre-generation of reversed video');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Check if FFmpeg is available
|
|
||||||
const ffmpegAvailable = await videoProcessing.isFFmpegAvailable();
|
|
||||||
if (!ffmpegAvailable) {
|
|
||||||
log.warn('FFmpeg not available, skipping pre-generation');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if reversed variant already exists (shouldn't happen on create, but safety check)
|
|
||||||
const existingAsset = await AssetsDBApi.findBy({ id: asset.id });
|
|
||||||
const variants = existingAsset?.asset_variants_asset || [];
|
|
||||||
const existingReversed = variants.find(
|
|
||||||
(v) => v.variant_type === 'reversed',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingReversed) {
|
|
||||||
log.debug('Reversed variant already exists');
|
|
||||||
return existingReversed.storage_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download original video to buffer
|
|
||||||
const storageKey = asset.storage_key;
|
|
||||||
log.info({ storageKey }, 'Downloading original video');
|
|
||||||
|
|
||||||
const originalBuffer = await downloadToBuffer(storageKey);
|
|
||||||
|
|
||||||
// Generate reversed video
|
|
||||||
log.info('Generating reversed video with FFmpeg');
|
|
||||||
const reversedBuffer = await videoProcessing.reverseVideo(
|
|
||||||
originalBuffer,
|
|
||||||
asset.original_file_name || 'video.mp4',
|
|
||||||
);
|
|
||||||
|
|
||||||
// Upload reversed video to storage
|
|
||||||
const reversedKey = `assets/${asset.id}/reversed.mp4`;
|
|
||||||
log.info({ reversedKey }, 'Uploading reversed video');
|
|
||||||
|
|
||||||
const result = await uploadBuffer(reversedKey, reversedBuffer, {
|
|
||||||
contentType: 'video/mp4',
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create variant record
|
|
||||||
await Asset_variantsDBApi.create(
|
|
||||||
{
|
|
||||||
assetId: asset.id,
|
|
||||||
variant_type: 'reversed',
|
|
||||||
cdn_url: result.url,
|
|
||||||
storage_key: reversedKey,
|
|
||||||
size_mb: reversedBuffer.length / (1024 * 1024),
|
|
||||||
},
|
|
||||||
{ currentUser },
|
|
||||||
);
|
|
||||||
|
|
||||||
log.info(
|
|
||||||
{
|
|
||||||
reversedKey,
|
|
||||||
sizeMb: (reversedBuffer.length / (1024 * 1024)).toFixed(2),
|
|
||||||
},
|
|
||||||
'Pre-generated reversed video successfully',
|
|
||||||
);
|
|
||||||
|
|
||||||
return reversedKey;
|
|
||||||
} catch (err) {
|
|
||||||
log.error({ err }, 'Failed to pre-generate reversed video');
|
|
||||||
// Don't throw - this is a background operation
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update asset with MIME type validation
|
* Update asset with MIME type validation
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -163,8 +163,7 @@ class TourPagesService extends BaseService {
|
|||||||
const storageKey = element.transitionVideoUrl;
|
const storageKey = element.transitionVideoUrl;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fast path for request cycle: only reuse existing reversed variants.
|
// Fast path: check if reversed variant already exists
|
||||||
// Missing variants are generated asynchronously to keep save requests fast.
|
|
||||||
const reversedUrl =
|
const reversedUrl =
|
||||||
await TourPagesService.getExistingReversedVariant(storageKey);
|
await TourPagesService.getExistingReversedVariant(storageKey);
|
||||||
|
|
||||||
@ -183,6 +182,7 @@ class TourPagesService extends BaseService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enqueue async generation - doesn't block save request
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
TourPagesService.enqueueSingleReverseGeneration({
|
TourPagesService.enqueueSingleReverseGeneration({
|
||||||
projectId,
|
projectId,
|
||||||
@ -614,6 +614,146 @@ class TourPagesService extends BaseService {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate reverseVideoUrl for navigation elements from existing variants.
|
||||||
|
* This eagerly looks up reversed variants that were generated asynchronously,
|
||||||
|
* ensuring the frontend has the reversed URL even if the page wasn't re-saved
|
||||||
|
* after variant generation completed.
|
||||||
|
*
|
||||||
|
* @param {Object|Array} pages - Single page or array of pages
|
||||||
|
* @returns {Promise<Object|Array>} Pages with populated reverseVideoUrl fields
|
||||||
|
*/
|
||||||
|
static async populateReverseVideoUrls(pages) {
|
||||||
|
if (!pages) return pages;
|
||||||
|
|
||||||
|
const isArray = Array.isArray(pages);
|
||||||
|
const pageList = isArray ? pages : [pages];
|
||||||
|
|
||||||
|
// Collect all storage keys that need lookup
|
||||||
|
const storageKeysToLookup = new Set();
|
||||||
|
|
||||||
|
for (const page of pageList) {
|
||||||
|
if (!page?.ui_schema_json) continue;
|
||||||
|
|
||||||
|
const uiSchema =
|
||||||
|
typeof page.ui_schema_json === 'string'
|
||||||
|
? JSON.parse(page.ui_schema_json || '{}')
|
||||||
|
: page.ui_schema_json || {};
|
||||||
|
|
||||||
|
if (!uiSchema?.elements || !Array.isArray(uiSchema.elements)) continue;
|
||||||
|
|
||||||
|
for (const element of uiSchema.elements) {
|
||||||
|
if (!element.transitionVideoUrl) continue;
|
||||||
|
if (element.reverseVideoUrl) continue; // Already has reversed URL
|
||||||
|
|
||||||
|
// Only auto_reverse mode needs lookup; separate_video has manual URL
|
||||||
|
if (element.transitionReverseMode === 'separate_video') continue;
|
||||||
|
|
||||||
|
storageKeysToLookup.add(element.transitionVideoUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storageKeysToLookup.size === 0) {
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch lookup all reversed variants
|
||||||
|
const reversedUrlByStorageKey = new Map();
|
||||||
|
|
||||||
|
for (const storageKey of storageKeysToLookup) {
|
||||||
|
try {
|
||||||
|
const reversedUrl =
|
||||||
|
await TourPagesService.getExistingReversedVariant(storageKey);
|
||||||
|
if (reversedUrl) {
|
||||||
|
reversedUrlByStorageKey.set(storageKey, reversedUrl);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore lookup errors - element will just have no reverseVideoUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reversedUrlByStorageKey.size === 0) {
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply reversed URLs to pages
|
||||||
|
const modifiedPages = pageList.map((page) => {
|
||||||
|
if (!page?.ui_schema_json) return page;
|
||||||
|
|
||||||
|
const uiSchema =
|
||||||
|
typeof page.ui_schema_json === 'string'
|
||||||
|
? JSON.parse(page.ui_schema_json || '{}')
|
||||||
|
: page.ui_schema_json || {};
|
||||||
|
|
||||||
|
if (!uiSchema?.elements || !Array.isArray(uiSchema.elements)) return page;
|
||||||
|
|
||||||
|
let modified = false;
|
||||||
|
|
||||||
|
for (const element of uiSchema.elements) {
|
||||||
|
if (!element.transitionVideoUrl) continue;
|
||||||
|
if (element.reverseVideoUrl) continue;
|
||||||
|
if (element.transitionReverseMode === 'separate_video') continue;
|
||||||
|
|
||||||
|
const reversedUrl = reversedUrlByStorageKey.get(
|
||||||
|
element.transitionVideoUrl,
|
||||||
|
);
|
||||||
|
if (reversedUrl) {
|
||||||
|
element.reverseVideoUrl = reversedUrl;
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!modified) return page;
|
||||||
|
|
||||||
|
// Return modified page with updated ui_schema_json
|
||||||
|
const plainPage = page.get ? page.get({ plain: true }) : { ...page };
|
||||||
|
return {
|
||||||
|
...plainPage,
|
||||||
|
ui_schema_json: JSON.stringify(uiSchema),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return isArray ? modifiedPages : modifiedPages[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check reverse video generation status for given storage keys.
|
||||||
|
* Returns which keys have reversed variants ready.
|
||||||
|
*
|
||||||
|
* @param {string[]} storageKeys - Array of original video storage keys to check
|
||||||
|
* @returns {Promise<Object>} Status object with ready keys and their reversed URLs
|
||||||
|
*/
|
||||||
|
static async checkReverseVideoStatus(storageKeys) {
|
||||||
|
if (!storageKeys || !Array.isArray(storageKeys) || storageKeys.length === 0) {
|
||||||
|
return { ready: {}, pending: [], allReady: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
const ready = {};
|
||||||
|
const pending = [];
|
||||||
|
|
||||||
|
for (const storageKey of storageKeys) {
|
||||||
|
if (!storageKey) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const reversedUrl =
|
||||||
|
await TourPagesService.getExistingReversedVariant(storageKey);
|
||||||
|
if (reversedUrl) {
|
||||||
|
ready[storageKey] = reversedUrl;
|
||||||
|
} else {
|
||||||
|
pending.push(storageKey);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
pending.push(storageKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
ready,
|
||||||
|
pending,
|
||||||
|
allReady: pending.length === 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = TourPagesService;
|
module.exports = TourPagesService;
|
||||||
|
|||||||
@ -6,11 +6,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const ffmpeg = require('fluent-ffmpeg');
|
const ffmpeg = require('fluent-ffmpeg');
|
||||||
|
const ffmpegPath = require('ffmpeg-static');
|
||||||
|
const ffprobePath = require('ffprobe-static').path;
|
||||||
const fs = require('fs').promises;
|
const fs = require('fs').promises;
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const { logger } = require('../utils/logger');
|
const { logger } = require('../utils/logger');
|
||||||
|
|
||||||
|
// Configure fluent-ffmpeg to use bundled binaries
|
||||||
|
ffmpeg.setFfmpegPath(ffmpegPath);
|
||||||
|
ffmpeg.setFfprobePath(ffprobePath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverse a video using FFmpeg
|
* Reverse a video using FFmpeg
|
||||||
* @param {Buffer} inputBuffer - Input video buffer
|
* @param {Buffer} inputBuffer - Input video buffer
|
||||||
|
|||||||
@ -77,7 +77,7 @@
|
|||||||
"@smithy/util-utf8" "^2.0.0"
|
"@smithy/util-utf8" "^2.0.0"
|
||||||
tslib "^2.6.2"
|
tslib "^2.6.2"
|
||||||
|
|
||||||
"@aws-crypto/sha256-js@^5.2.0", "@aws-crypto/sha256-js@5.2.0":
|
"@aws-crypto/sha256-js@5.2.0", "@aws-crypto/sha256-js@^5.2.0":
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz"
|
resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz"
|
||||||
integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==
|
integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==
|
||||||
@ -93,7 +93,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.6.2"
|
tslib "^2.6.2"
|
||||||
|
|
||||||
"@aws-crypto/util@^5.2.0", "@aws-crypto/util@5.2.0":
|
"@aws-crypto/util@5.2.0", "@aws-crypto/util@^5.2.0":
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz"
|
resolved "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz"
|
||||||
integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==
|
integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==
|
||||||
@ -765,6 +765,16 @@
|
|||||||
jsonwebtoken "^9.0.0"
|
jsonwebtoken "^9.0.0"
|
||||||
uuid "^8.3.0"
|
uuid "^8.3.0"
|
||||||
|
|
||||||
|
"@derhuerst/http-basic@^8.2.0":
|
||||||
|
version "8.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@derhuerst/http-basic/-/http-basic-8.2.4.tgz#d021ebb8f65d54bea681ae6f4a8733ce89e7f59b"
|
||||||
|
integrity sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==
|
||||||
|
dependencies:
|
||||||
|
caseless "^0.12.0"
|
||||||
|
concat-stream "^2.0.0"
|
||||||
|
http-response-object "^3.0.1"
|
||||||
|
parse-cache-control "^1.0.1"
|
||||||
|
|
||||||
"@eslint-community/eslint-utils@^4.2.0":
|
"@eslint-community/eslint-utils@^4.2.0":
|
||||||
version "4.9.1"
|
version "4.9.1"
|
||||||
resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz"
|
resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz"
|
||||||
@ -889,7 +899,7 @@
|
|||||||
resolved "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz"
|
resolved "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz"
|
||||||
integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==
|
integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==
|
||||||
|
|
||||||
"@nodable/entities@^2.1.0", "@nodable/entities@2.1.0":
|
"@nodable/entities@2.1.0", "@nodable/entities@^2.1.0":
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz"
|
resolved "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz"
|
||||||
integrity sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==
|
integrity sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==
|
||||||
@ -1483,6 +1493,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~5.26.4"
|
undici-types "~5.26.4"
|
||||||
|
|
||||||
|
"@types/node@^10.0.3":
|
||||||
|
version "10.17.60"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b"
|
||||||
|
integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==
|
||||||
|
|
||||||
"@types/readable-stream@^4.0.0":
|
"@types/readable-stream@^4.0.0":
|
||||||
version "4.0.15"
|
version "4.0.15"
|
||||||
resolved "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz"
|
resolved "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz"
|
||||||
@ -1541,13 +1556,10 @@ acorn-jsx@^5.3.2:
|
|||||||
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
|
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
|
||||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||||
|
|
||||||
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.9.0:
|
acorn@^8.9.0:
|
||||||
version "8.15.0"
|
version "8.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a"
|
||||||
agent-base@^7.1.0, agent-base@^7.1.2:
|
integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==
|
||||||
version "7.1.4"
|
|
||||||
resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz"
|
|
||||||
integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==
|
|
||||||
|
|
||||||
agent-base@6:
|
agent-base@6:
|
||||||
version "6.0.2"
|
version "6.0.2"
|
||||||
@ -1556,8 +1568,15 @@ agent-base@6:
|
|||||||
dependencies:
|
dependencies:
|
||||||
debug "4"
|
debug "4"
|
||||||
|
|
||||||
|
agent-base@^7.1.0, agent-base@^7.1.2:
|
||||||
|
version "7.1.4"
|
||||||
|
resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz"
|
||||||
|
integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==
|
||||||
|
|
||||||
ajv@^6.12.4:
|
ajv@^6.12.4:
|
||||||
version "6.12.6"
|
version "6.15.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.15.0.tgz#07e982c74626167aa7a2495c53817892d7139492"
|
||||||
|
integrity sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-deep-equal "^3.1.1"
|
fast-deep-equal "^3.1.1"
|
||||||
fast-json-stable-stringify "^2.0.0"
|
fast-json-stable-stringify "^2.0.0"
|
||||||
@ -1841,14 +1860,7 @@ brace-expansion@^1.1.7:
|
|||||||
balanced-match "^1.0.0"
|
balanced-match "^1.0.0"
|
||||||
concat-map "0.0.1"
|
concat-map "0.0.1"
|
||||||
|
|
||||||
brace-expansion@^2.0.1:
|
brace-expansion@^2.0.1, brace-expansion@^2.0.2:
|
||||||
version "2.1.0"
|
|
||||||
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz"
|
|
||||||
integrity sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==
|
|
||||||
dependencies:
|
|
||||||
balanced-match "^1.0.0"
|
|
||||||
|
|
||||||
brace-expansion@^2.0.2:
|
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz"
|
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz"
|
||||||
integrity sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==
|
integrity sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==
|
||||||
@ -1956,6 +1968,11 @@ camelcase@^6.0.0:
|
|||||||
resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz"
|
resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz"
|
||||||
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
||||||
|
|
||||||
|
caseless@^0.12.0:
|
||||||
|
version "0.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||||
|
integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==
|
||||||
|
|
||||||
chalk@^4.0.0, chalk@^4.1.0:
|
chalk@^4.0.0, chalk@^4.1.0:
|
||||||
version "4.1.2"
|
version "4.1.2"
|
||||||
resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
|
resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
|
||||||
@ -1964,20 +1981,7 @@ chalk@^4.0.0, chalk@^4.1.0:
|
|||||||
ansi-styles "^4.1.0"
|
ansi-styles "^4.1.0"
|
||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
chokidar@^3.5.2:
|
chokidar@^3.5.2, chokidar@^3.5.3:
|
||||||
version "3.6.0"
|
|
||||||
dependencies:
|
|
||||||
anymatch "~3.1.2"
|
|
||||||
braces "~3.0.2"
|
|
||||||
glob-parent "~5.1.2"
|
|
||||||
is-binary-path "~2.1.0"
|
|
||||||
is-glob "~4.0.1"
|
|
||||||
normalize-path "~3.0.0"
|
|
||||||
readdirp "~3.6.0"
|
|
||||||
optionalDependencies:
|
|
||||||
fsevents "~2.3.2"
|
|
||||||
|
|
||||||
chokidar@^3.5.3:
|
|
||||||
version "3.6.0"
|
version "3.6.0"
|
||||||
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz"
|
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz"
|
||||||
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
|
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
|
||||||
@ -2032,6 +2036,11 @@ combined-stream@^1.0.8:
|
|||||||
dependencies:
|
dependencies:
|
||||||
delayed-stream "~1.0.0"
|
delayed-stream "~1.0.0"
|
||||||
|
|
||||||
|
commander@6.2.0:
|
||||||
|
version "6.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz"
|
||||||
|
integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==
|
||||||
|
|
||||||
commander@^10.0.0:
|
commander@^10.0.0:
|
||||||
version "10.0.1"
|
version "10.0.1"
|
||||||
resolved "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz"
|
resolved "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz"
|
||||||
@ -2042,11 +2051,6 @@ commander@^6.1.0:
|
|||||||
resolved "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz"
|
resolved "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz"
|
||||||
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
||||||
|
|
||||||
commander@6.2.0:
|
|
||||||
version "6.2.0"
|
|
||||||
resolved "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz"
|
|
||||||
integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==
|
|
||||||
|
|
||||||
concat-map@0.0.1:
|
concat-map@0.0.1:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
|
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
|
||||||
@ -2070,7 +2074,7 @@ config-chain@^1.1.13:
|
|||||||
ini "^1.3.4"
|
ini "^1.3.4"
|
||||||
proto-list "~1.2.1"
|
proto-list "~1.2.1"
|
||||||
|
|
||||||
content-disposition@^0.5.3, content-disposition@0.5.4:
|
content-disposition@0.5.4, content-disposition@^0.5.3:
|
||||||
version "0.5.4"
|
version "0.5.4"
|
||||||
resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz"
|
resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz"
|
||||||
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
|
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
|
||||||
@ -2180,6 +2184,20 @@ dateformat@^4.6.3:
|
|||||||
resolved "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz"
|
resolved "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz"
|
||||||
integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==
|
integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==
|
||||||
|
|
||||||
|
debug@2.6.9:
|
||||||
|
version "2.6.9"
|
||||||
|
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
|
||||||
|
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||||
|
dependencies:
|
||||||
|
ms "2.0.0"
|
||||||
|
|
||||||
|
debug@4, debug@^4, debug@^4.3.4, debug@^4.3.5:
|
||||||
|
version "4.3.5"
|
||||||
|
resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz"
|
||||||
|
integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
debug@^3.2.7:
|
debug@^3.2.7:
|
||||||
version "3.2.7"
|
version "3.2.7"
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz"
|
resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz"
|
||||||
@ -2187,34 +2205,13 @@ debug@^3.2.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
debug@^4, debug@^4.3.4, debug@^4.3.5, debug@4:
|
debug@^4.3.1, debug@^4.3.2:
|
||||||
version "4.3.5"
|
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz"
|
|
||||||
integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
|
|
||||||
dependencies:
|
|
||||||
ms "2.1.2"
|
|
||||||
|
|
||||||
debug@^4.3.1:
|
|
||||||
version "4.4.3"
|
version "4.4.3"
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz"
|
resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz"
|
||||||
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
|
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.3"
|
ms "^2.1.3"
|
||||||
|
|
||||||
debug@^4.3.2:
|
|
||||||
version "4.4.3"
|
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz"
|
|
||||||
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
|
|
||||||
dependencies:
|
|
||||||
ms "^2.1.3"
|
|
||||||
|
|
||||||
debug@2.6.9:
|
|
||||||
version "2.6.9"
|
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
|
|
||||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
|
||||||
dependencies:
|
|
||||||
ms "2.0.0"
|
|
||||||
|
|
||||||
decamelize@^4.0.0:
|
decamelize@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz"
|
resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz"
|
||||||
@ -2258,16 +2255,16 @@ denque@^1.4.1:
|
|||||||
resolved "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz"
|
resolved "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz"
|
||||||
integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==
|
integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==
|
||||||
|
|
||||||
depd@^1.1.0:
|
|
||||||
version "1.1.2"
|
|
||||||
resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz"
|
|
||||||
integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
|
|
||||||
|
|
||||||
depd@2.0.0:
|
depd@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz"
|
||||||
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
|
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
|
||||||
|
|
||||||
|
depd@^1.1.0:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz"
|
||||||
|
integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
|
||||||
|
|
||||||
destroy@1.2.0:
|
destroy@1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz"
|
||||||
@ -2278,6 +2275,13 @@ diff@^5.2.0:
|
|||||||
resolved "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz"
|
resolved "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz"
|
||||||
integrity sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==
|
integrity sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==
|
||||||
|
|
||||||
|
doctrine@3.0.0, doctrine@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz"
|
||||||
|
integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
|
||||||
|
dependencies:
|
||||||
|
esutils "^2.0.2"
|
||||||
|
|
||||||
doctrine@^2.1.0:
|
doctrine@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz"
|
resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz"
|
||||||
@ -2285,13 +2289,6 @@ doctrine@^2.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
esutils "^2.0.2"
|
esutils "^2.0.2"
|
||||||
|
|
||||||
doctrine@^3.0.0, doctrine@3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz"
|
|
||||||
integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
|
|
||||||
dependencies:
|
|
||||||
esutils "^2.0.2"
|
|
||||||
|
|
||||||
dotenv@^16.4.0:
|
dotenv@^16.4.0:
|
||||||
version "16.6.1"
|
version "16.6.1"
|
||||||
resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz"
|
resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz"
|
||||||
@ -2326,7 +2323,7 @@ eastasianwidth@^0.2.0:
|
|||||||
resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz"
|
resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz"
|
||||||
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
|
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
|
||||||
|
|
||||||
ecdsa-sig-formatter@^1.0.11, ecdsa-sig-formatter@1.0.11:
|
ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11:
|
||||||
version "1.0.11"
|
version "1.0.11"
|
||||||
resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz"
|
resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz"
|
||||||
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
|
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
|
||||||
@ -2370,6 +2367,11 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
once "^1.4.0"
|
once "^1.4.0"
|
||||||
|
|
||||||
|
env-paths@^2.2.0:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
|
||||||
|
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
|
||||||
|
|
||||||
es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2:
|
es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2:
|
||||||
version "1.23.3"
|
version "1.23.3"
|
||||||
resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz"
|
resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz"
|
||||||
@ -2617,7 +2619,7 @@ eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
|
|||||||
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
|
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
|
||||||
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
||||||
|
|
||||||
"eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", eslint@^8.57.0:
|
eslint@^8.57.0:
|
||||||
version "8.57.1"
|
version "8.57.1"
|
||||||
resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz"
|
resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz"
|
||||||
integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==
|
integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==
|
||||||
@ -2717,7 +2719,7 @@ express-validator@^7.0.0:
|
|||||||
lodash "^4.18.1"
|
lodash "^4.18.1"
|
||||||
validator "~13.15.23"
|
validator "~13.15.23"
|
||||||
|
|
||||||
"express@>=4.0.0 || >=5.0.0-beta", express@4.18.2:
|
express@4.18.2:
|
||||||
version "4.18.2"
|
version "4.18.2"
|
||||||
resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz"
|
resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz"
|
||||||
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
|
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
|
||||||
@ -2792,7 +2794,7 @@ fast-xml-builder@^1.1.5:
|
|||||||
path-expression-matcher "^1.5.0"
|
path-expression-matcher "^1.5.0"
|
||||||
xml-naming "^0.1.0"
|
xml-naming "^0.1.0"
|
||||||
|
|
||||||
fast-xml-parser@^5.3.4, fast-xml-parser@5.7.2:
|
fast-xml-parser@5.7.2, fast-xml-parser@^5.3.4:
|
||||||
version "5.7.2"
|
version "5.7.2"
|
||||||
resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.2.tgz"
|
resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.2.tgz"
|
||||||
integrity sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==
|
integrity sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==
|
||||||
@ -2809,6 +2811,21 @@ fastq@^1.6.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
reusify "^1.0.4"
|
reusify "^1.0.4"
|
||||||
|
|
||||||
|
ffmpeg-static@^5.2.0:
|
||||||
|
version "5.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ffmpeg-static/-/ffmpeg-static-5.3.0.tgz#3089fc01d1bb9c58cbe74631986fdacbd86d8b14"
|
||||||
|
integrity sha512-H+K6sW6TiIX6VGend0KQwthe+kaceeH/luE8dIZyOP35ik7ahYojDuqlTV1bOrtEwl01sy2HFNGQfi5IDJvotg==
|
||||||
|
dependencies:
|
||||||
|
"@derhuerst/http-basic" "^8.2.0"
|
||||||
|
env-paths "^2.2.0"
|
||||||
|
https-proxy-agent "^5.0.0"
|
||||||
|
progress "^2.0.3"
|
||||||
|
|
||||||
|
ffprobe-static@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ffprobe-static/-/ffprobe-static-3.1.0.tgz#982cfa1de111a4d6043f6fc208f64a82b53034c9"
|
||||||
|
integrity sha512-Dvpa9uhVMOYivhHKWLGDoa512J751qN1WZAIO+Xw4L/mrUSPxS4DApzSUDbCFE/LUq2+xYnznEahTd63AqBSpA==
|
||||||
|
|
||||||
file-entry-cache@^6.0.1:
|
file-entry-cache@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz"
|
resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz"
|
||||||
@ -2859,7 +2876,9 @@ flat@^5.0.2:
|
|||||||
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
|
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
|
||||||
|
|
||||||
flatted@^3.2.9:
|
flatted@^3.2.9:
|
||||||
version "3.3.3"
|
version "3.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726"
|
||||||
|
integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==
|
||||||
|
|
||||||
fluent-ffmpeg@^2.1.3:
|
fluent-ffmpeg@^2.1.3:
|
||||||
version "2.1.3"
|
version "2.1.3"
|
||||||
@ -2929,7 +2948,7 @@ forwarded@0.2.0:
|
|||||||
resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz"
|
resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz"
|
||||||
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
|
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
|
||||||
|
|
||||||
fresh@^0.5.2, fresh@0.5.2:
|
fresh@0.5.2, fresh@^0.5.2:
|
||||||
version "0.5.2"
|
version "0.5.2"
|
||||||
resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz"
|
resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz"
|
||||||
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
|
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
|
||||||
@ -2949,6 +2968,11 @@ fs.realpath@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
||||||
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
||||||
|
|
||||||
|
fsevents@~2.3.2:
|
||||||
|
version "2.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||||
|
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||||
|
|
||||||
function-bind@^1.1.2:
|
function-bind@^1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
|
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
|
||||||
@ -3018,29 +3042,7 @@ get-caller-file@^2.0.5:
|
|||||||
resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz"
|
resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz"
|
||||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||||
|
|
||||||
get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
|
get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
|
||||||
version "1.2.4"
|
|
||||||
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz"
|
|
||||||
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
|
|
||||||
dependencies:
|
|
||||||
es-errors "^1.3.0"
|
|
||||||
function-bind "^1.1.2"
|
|
||||||
has-proto "^1.0.1"
|
|
||||||
has-symbols "^1.0.3"
|
|
||||||
hasown "^2.0.0"
|
|
||||||
|
|
||||||
get-intrinsic@^1.2.1, get-intrinsic@^1.2.4:
|
|
||||||
version "1.2.4"
|
|
||||||
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz"
|
|
||||||
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
|
|
||||||
dependencies:
|
|
||||||
es-errors "^1.3.0"
|
|
||||||
function-bind "^1.1.2"
|
|
||||||
has-proto "^1.0.1"
|
|
||||||
has-symbols "^1.0.3"
|
|
||||||
hasown "^2.0.0"
|
|
||||||
|
|
||||||
get-intrinsic@^1.2.3:
|
|
||||||
version "1.2.4"
|
version "1.2.4"
|
||||||
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz"
|
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz"
|
||||||
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
|
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
|
||||||
@ -3107,6 +3109,18 @@ glob-parent@~5.1.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.1"
|
is-glob "^4.0.1"
|
||||||
|
|
||||||
|
glob@7.1.6:
|
||||||
|
version "7.1.6"
|
||||||
|
resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz"
|
||||||
|
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||||
|
dependencies:
|
||||||
|
fs.realpath "^1.0.0"
|
||||||
|
inflight "^1.0.4"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
once "^1.3.0"
|
||||||
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
glob@^10.4.2:
|
glob@^10.4.2:
|
||||||
version "10.5.0"
|
version "10.5.0"
|
||||||
resolved "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz"
|
resolved "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz"
|
||||||
@ -3142,18 +3156,6 @@ glob@^8.1.0:
|
|||||||
minimatch "^5.0.1"
|
minimatch "^5.0.1"
|
||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
|
|
||||||
glob@7.1.6:
|
|
||||||
version "7.1.6"
|
|
||||||
resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz"
|
|
||||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
|
||||||
dependencies:
|
|
||||||
fs.realpath "^1.0.0"
|
|
||||||
inflight "^1.0.4"
|
|
||||||
inherits "2"
|
|
||||||
minimatch "^3.0.4"
|
|
||||||
once "^1.3.0"
|
|
||||||
path-is-absolute "^1.0.0"
|
|
||||||
|
|
||||||
globals@^13.19.0:
|
globals@^13.19.0:
|
||||||
version "13.24.0"
|
version "13.24.0"
|
||||||
resolved "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz"
|
resolved "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz"
|
||||||
@ -3322,6 +3324,13 @@ http-proxy-agent@^7.0.0:
|
|||||||
agent-base "^7.1.0"
|
agent-base "^7.1.0"
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
|
|
||||||
|
http-response-object@^3.0.1:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/http-response-object/-/http-response-object-3.0.2.tgz#7f435bb210454e4360d074ef1f989d5ea8aa9810"
|
||||||
|
integrity sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "^10.0.3"
|
||||||
|
|
||||||
https-proxy-agent@^5.0.0:
|
https-proxy-agent@^5.0.0:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz"
|
resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz"
|
||||||
@ -3338,20 +3347,6 @@ https-proxy-agent@^7.0.0, https-proxy-agent@^7.0.1:
|
|||||||
agent-base "^7.1.2"
|
agent-base "^7.1.2"
|
||||||
debug "4"
|
debug "4"
|
||||||
|
|
||||||
iconv-lite@^0.6.2:
|
|
||||||
version "0.6.3"
|
|
||||||
resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz"
|
|
||||||
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
|
|
||||||
dependencies:
|
|
||||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
|
||||||
|
|
||||||
iconv-lite@^0.6.3:
|
|
||||||
version "0.6.3"
|
|
||||||
resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz"
|
|
||||||
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
|
|
||||||
dependencies:
|
|
||||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
|
||||||
|
|
||||||
iconv-lite@0.4.24:
|
iconv-lite@0.4.24:
|
||||||
version "0.4.24"
|
version "0.4.24"
|
||||||
resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz"
|
resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz"
|
||||||
@ -3359,6 +3354,13 @@ iconv-lite@0.4.24:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer ">= 2.1.2 < 3"
|
safer-buffer ">= 2.1.2 < 3"
|
||||||
|
|
||||||
|
iconv-lite@^0.6.2, iconv-lite@^0.6.3:
|
||||||
|
version "0.6.3"
|
||||||
|
resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz"
|
||||||
|
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
|
||||||
|
dependencies:
|
||||||
|
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||||
|
|
||||||
ieee754@^1.2.1:
|
ieee754@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz"
|
resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz"
|
||||||
@ -3400,7 +3402,7 @@ inflight@^1.0.4:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
inherits@^2.0.3, inherits@^2.0.4, inherits@2, inherits@2.0.4:
|
inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
|
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
@ -3697,16 +3699,7 @@ is-symbol@^1.0.2, is-symbol@^1.0.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-symbols "^1.0.2"
|
has-symbols "^1.0.2"
|
||||||
|
|
||||||
is-symbol@^1.0.4:
|
is-symbol@^1.0.4, is-symbol@^1.1.1:
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz"
|
|
||||||
integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==
|
|
||||||
dependencies:
|
|
||||||
call-bound "^1.0.2"
|
|
||||||
has-symbols "^1.1.0"
|
|
||||||
safe-regex-test "^1.1.0"
|
|
||||||
|
|
||||||
is-symbol@^1.1.1:
|
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz"
|
resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz"
|
||||||
integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==
|
integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==
|
||||||
@ -4038,16 +4031,16 @@ media-typer@0.3.0:
|
|||||||
resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
|
resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
|
||||||
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
|
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
|
||||||
|
|
||||||
merge-descriptors@^1.0.1:
|
|
||||||
version "1.0.3"
|
|
||||||
resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz"
|
|
||||||
integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
|
|
||||||
|
|
||||||
merge-descriptors@1.0.1:
|
merge-descriptors@1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz"
|
||||||
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
|
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
|
||||||
|
|
||||||
|
merge-descriptors@^1.0.1:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz"
|
||||||
|
integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
|
||||||
|
|
||||||
methods@^1.1.2, methods@~1.1.2:
|
methods@^1.1.2, methods@~1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz"
|
resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz"
|
||||||
@ -4065,7 +4058,7 @@ mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.24, mime-types@~2.1.34:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mime-db "1.52.0"
|
mime-db "1.52.0"
|
||||||
|
|
||||||
mime@^1.3.4, mime@1.6.0:
|
mime@1.6.0, mime@^1.3.4:
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz"
|
resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz"
|
||||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||||
@ -4096,14 +4089,7 @@ minimatch@^5.0.1, minimatch@^5.1.6:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^2.0.1"
|
brace-expansion "^2.0.1"
|
||||||
|
|
||||||
minimatch@^9.0.1:
|
minimatch@^9.0.1, minimatch@^9.0.4:
|
||||||
version "9.0.9"
|
|
||||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz"
|
|
||||||
integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==
|
|
||||||
dependencies:
|
|
||||||
brace-expansion "^2.0.2"
|
|
||||||
|
|
||||||
minimatch@^9.0.4:
|
|
||||||
version "9.0.9"
|
version "9.0.9"
|
||||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz"
|
resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz"
|
||||||
integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==
|
integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==
|
||||||
@ -4153,16 +4139,11 @@ moment-timezone@^0.5.43:
|
|||||||
dependencies:
|
dependencies:
|
||||||
moment "^2.29.4"
|
moment "^2.29.4"
|
||||||
|
|
||||||
moment@^2.29.4, moment@2.30.1:
|
moment@2.30.1, moment@^2.29.4:
|
||||||
version "2.30.1"
|
version "2.30.1"
|
||||||
resolved "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz"
|
resolved "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz"
|
||||||
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
|
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
|
||||||
|
|
||||||
ms@^2.1.1, ms@^2.1.3, ms@2.1.3:
|
|
||||||
version "2.1.3"
|
|
||||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
|
|
||||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
|
||||||
@ -4173,6 +4154,11 @@ ms@2.1.2:
|
|||||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
|
ms@2.1.3, ms@^2.1.1, ms@^2.1.3:
|
||||||
|
version "2.1.3"
|
||||||
|
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
|
||||||
|
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||||
|
|
||||||
multer@^2.0.0:
|
multer@^2.0.0:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz"
|
resolved "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz"
|
||||||
@ -4389,11 +4375,6 @@ open@^8.0.0:
|
|||||||
is-docker "^2.1.1"
|
is-docker "^2.1.1"
|
||||||
is-wsl "^2.2.0"
|
is-wsl "^2.2.0"
|
||||||
|
|
||||||
openapi-types@>=7:
|
|
||||||
version "12.1.3"
|
|
||||||
resolved "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz"
|
|
||||||
integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==
|
|
||||||
|
|
||||||
optionator@^0.9.3:
|
optionator@^0.9.3:
|
||||||
version "0.9.4"
|
version "0.9.4"
|
||||||
resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz"
|
resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz"
|
||||||
@ -4441,6 +4422,11 @@ parent-module@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
callsites "^3.0.0"
|
callsites "^3.0.0"
|
||||||
|
|
||||||
|
parse-cache-control@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e"
|
||||||
|
integrity sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==
|
||||||
|
|
||||||
parseurl@^1.3.3, parseurl@~1.3.3:
|
parseurl@^1.3.3, parseurl@~1.3.3:
|
||||||
version "1.3.3"
|
version "1.3.3"
|
||||||
resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz"
|
resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz"
|
||||||
@ -4468,7 +4454,7 @@ passport-microsoft@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
passport-oauth2 "1.8.0"
|
passport-oauth2 "1.8.0"
|
||||||
|
|
||||||
passport-oauth2@^1.1.2, passport-oauth2@1.8.0:
|
passport-oauth2@1.8.0, passport-oauth2@^1.1.2:
|
||||||
version "1.8.0"
|
version "1.8.0"
|
||||||
resolved "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.8.0.tgz"
|
resolved "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.8.0.tgz"
|
||||||
integrity sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==
|
integrity sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==
|
||||||
@ -4479,7 +4465,7 @@ passport-oauth2@^1.1.2, passport-oauth2@1.8.0:
|
|||||||
uid2 "0.0.x"
|
uid2 "0.0.x"
|
||||||
utils-merge "1.x.x"
|
utils-merge "1.x.x"
|
||||||
|
|
||||||
passport-strategy@^1.0.0, passport-strategy@1.x.x:
|
passport-strategy@1.x.x, passport-strategy@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz"
|
||||||
integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==
|
integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==
|
||||||
@ -4579,7 +4565,7 @@ pg-types@2.2.0:
|
|||||||
postgres-date "~1.0.4"
|
postgres-date "~1.0.4"
|
||||||
postgres-interval "^1.1.0"
|
postgres-interval "^1.1.0"
|
||||||
|
|
||||||
pg@^8.20.0, pg@>=8.0:
|
pg@^8.20.0:
|
||||||
version "8.20.0"
|
version "8.20.0"
|
||||||
resolved "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz"
|
resolved "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz"
|
||||||
integrity sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==
|
integrity sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==
|
||||||
@ -4700,6 +4686,11 @@ process@^0.11.10:
|
|||||||
resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz"
|
resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz"
|
||||||
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
|
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
|
||||||
|
|
||||||
|
progress@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||||
|
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||||
|
|
||||||
proto-list@~1.2.1:
|
proto-list@~1.2.1:
|
||||||
version "1.2.4"
|
version "1.2.4"
|
||||||
resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz"
|
resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz"
|
||||||
@ -4946,7 +4937,7 @@ safe-array-concat@^1.1.3:
|
|||||||
has-symbols "^1.1.0"
|
has-symbols "^1.1.0"
|
||||||
isarray "^2.0.5"
|
isarray "^2.0.5"
|
||||||
|
|
||||||
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0, safe-buffer@5.2.1:
|
safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
|
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
|
||||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||||
@ -5002,12 +4993,7 @@ semver@^6.3.1:
|
|||||||
resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
|
resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
|
||||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||||
|
|
||||||
semver@^7.5.3:
|
semver@^7.5.3, semver@^7.5.4:
|
||||||
version "7.8.0"
|
|
||||||
resolved "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz"
|
|
||||||
integrity sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==
|
|
||||||
|
|
||||||
semver@^7.5.4:
|
|
||||||
version "7.8.0"
|
version "7.8.0"
|
||||||
resolved "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz"
|
resolved "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz"
|
||||||
integrity sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==
|
integrity sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==
|
||||||
@ -5059,7 +5045,7 @@ sequelize-pool@^7.1.0:
|
|||||||
resolved "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz"
|
resolved "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz"
|
||||||
integrity sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==
|
integrity sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==
|
||||||
|
|
||||||
sequelize@^6.37.0, "sequelize@>= 4":
|
sequelize@^6.37.0:
|
||||||
version "6.37.8"
|
version "6.37.8"
|
||||||
resolved "https://registry.npmjs.org/sequelize/-/sequelize-6.37.8.tgz"
|
resolved "https://registry.npmjs.org/sequelize/-/sequelize-6.37.8.tgz"
|
||||||
integrity sha512-HJ0IQFqcTsTiqbEgiuioYFMSD00TP6Cz7zoTti+zVVBwVe9fEhev9cH6WnM3XU31+ABS356durAb99ZuOthnKw==
|
integrity sha512-HJ0IQFqcTsTiqbEgiuioYFMSD00TP6Cz7zoTti+zVVBwVe9fEhev9cH6WnM3XU31+ABS356durAb99ZuOthnKw==
|
||||||
@ -5270,13 +5256,6 @@ streamsearch@^1.1.0:
|
|||||||
resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz"
|
resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz"
|
||||||
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
|
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
|
||||||
|
|
||||||
string_decoder@^1.1.1, string_decoder@^1.3.0:
|
|
||||||
version "1.3.0"
|
|
||||||
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
|
|
||||||
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
|
||||||
dependencies:
|
|
||||||
safe-buffer "~5.2.0"
|
|
||||||
|
|
||||||
"string-width-cjs@npm:string-width@^4.2.0":
|
"string-width-cjs@npm:string-width@^4.2.0":
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
|
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
|
||||||
@ -5355,6 +5334,13 @@ string.prototype.trimstart@^1.0.8:
|
|||||||
define-properties "^1.2.1"
|
define-properties "^1.2.1"
|
||||||
es-object-atoms "^1.0.0"
|
es-object-atoms "^1.0.0"
|
||||||
|
|
||||||
|
string_decoder@^1.1.1, string_decoder@^1.3.0:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
|
||||||
|
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
||||||
|
dependencies:
|
||||||
|
safe-buffer "~5.2.0"
|
||||||
|
|
||||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
|
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
|
||||||
@ -5700,7 +5686,7 @@ universalify@^2.0.0:
|
|||||||
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz"
|
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz"
|
||||||
integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
|
integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
|
||||||
|
|
||||||
unpipe@~1.0.0, unpipe@1.0.0:
|
unpipe@1.0.0, unpipe@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
|
||||||
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
|
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
|
||||||
@ -5717,7 +5703,7 @@ util-deprecate@^1.0.1:
|
|||||||
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
|
||||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||||
|
|
||||||
utils-merge@^1.0.1, utils-merge@1.0.1, utils-merge@1.x.x:
|
utils-merge@1.0.1, utils-merge@1.x.x, utils-merge@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
|
||||||
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
|
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
|
||||||
@ -5727,12 +5713,7 @@ uuid@^8.0.0, uuid@^8.3.0, uuid@^8.3.2:
|
|||||||
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
|
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
|
||||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||||
|
|
||||||
uuid@^9.0.0:
|
uuid@^9.0.0, uuid@^9.0.1:
|
||||||
version "9.0.1"
|
|
||||||
resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz"
|
|
||||||
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
|
|
||||||
|
|
||||||
uuid@^9.0.1:
|
|
||||||
version "9.0.1"
|
version "9.0.1"
|
||||||
resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz"
|
resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz"
|
||||||
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
|
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
|
||||||
@ -5811,18 +5792,7 @@ which-collection@^1.0.2:
|
|||||||
is-weakmap "^2.0.2"
|
is-weakmap "^2.0.2"
|
||||||
is-weakset "^2.0.3"
|
is-weakset "^2.0.3"
|
||||||
|
|
||||||
which-typed-array@^1.1.14:
|
which-typed-array@^1.1.14, which-typed-array@^1.1.15:
|
||||||
version "1.1.15"
|
|
||||||
resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz"
|
|
||||||
integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==
|
|
||||||
dependencies:
|
|
||||||
available-typed-arrays "^1.0.7"
|
|
||||||
call-bind "^1.0.7"
|
|
||||||
for-each "^0.3.3"
|
|
||||||
gopd "^1.0.1"
|
|
||||||
has-tostringtag "^1.0.2"
|
|
||||||
|
|
||||||
which-typed-array@^1.1.15:
|
|
||||||
version "1.1.15"
|
version "1.1.15"
|
||||||
resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz"
|
resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz"
|
||||||
integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==
|
integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -446,11 +446,18 @@ export default function RuntimePresentation({
|
|||||||
|
|
||||||
// Check if video is already cached (use video even on slow network if cached)
|
// Check if video is already cached (use video even on slow network if cached)
|
||||||
const isTransitionCached =
|
const isTransitionCached =
|
||||||
transitionVideoUrl && preloadOrchestrator?.getReadyBlobUrl(transitionVideoUrl);
|
transitionVideoUrl &&
|
||||||
|
preloadOrchestrator?.getReadyBlobUrl(transitionVideoUrl);
|
||||||
|
|
||||||
// Use video if: has transition AND (cached OR good network)
|
// For back navigation, verify reversed video is available
|
||||||
|
// Without reversed video, back navigation should use CSS fade instead
|
||||||
|
const isBackWithoutReverse = isBack && !reverseVideoUrl;
|
||||||
|
|
||||||
|
// Use video if: has transition AND (cached OR good network) AND not back-without-reverse
|
||||||
const useVideoTransition =
|
const useVideoTransition =
|
||||||
transitionVideoUrl && (isTransitionCached || shouldUseVideoTransitions);
|
transitionVideoUrl &&
|
||||||
|
(isTransitionCached || shouldUseVideoTransitions) &&
|
||||||
|
!isBackWithoutReverse;
|
||||||
|
|
||||||
if (useVideoTransition) {
|
if (useVideoTransition) {
|
||||||
// Reset states from previous transition/navigation
|
// Reset states from previous transition/navigation
|
||||||
@ -477,8 +484,23 @@ export default function RuntimePresentation({
|
|||||||
// Mark this page as initialized to prevent redundant effect calls
|
// Mark this page as initialized to prevent redundant effect calls
|
||||||
lastInitializedPageIdRef.current = targetPageId;
|
lastInitializedPageIdRef.current = targetPageId;
|
||||||
|
|
||||||
|
// Log when skipping video due to missing reversed video (back navigation)
|
||||||
|
if (isBackWithoutReverse && transitionVideoUrl) {
|
||||||
|
logger.info(
|
||||||
|
'[NAVIGATION] Skipping video transition for back navigation - reversed video not ready, using CSS fade',
|
||||||
|
{
|
||||||
|
transitionVideoUrl: transitionVideoUrl?.slice(-60),
|
||||||
|
isBack,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Log when skipping video due to slow network
|
// Log when skipping video due to slow network
|
||||||
if (transitionVideoUrl && !shouldUseVideoTransitions) {
|
if (
|
||||||
|
transitionVideoUrl &&
|
||||||
|
!shouldUseVideoTransitions &&
|
||||||
|
!isBackWithoutReverse
|
||||||
|
) {
|
||||||
logger.info(
|
logger.info(
|
||||||
'[NAVIGATION] Skipping video transition due to slow network, downloading in background',
|
'[NAVIGATION] Skipping video transition due to slow network, downloading in background',
|
||||||
{
|
{
|
||||||
|
|||||||
@ -82,7 +82,7 @@ export function createListPage(config: ListPageConfig) {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type });
|
const blob = new Blob([response.data], { type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -76,7 +76,7 @@ export function useCSVHandling(
|
|||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
|
|
||||||
const contentType = response.headers['content-type'] || 'text/csv';
|
const contentType = (response.headers['content-type'] as string) || 'text/csv';
|
||||||
const blob = new Blob([response.data], { type: contentType });
|
const blob = new Blob([response.data], { type: contentType });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import type { CanvasElement, PageBackgroundState } from '../types/constructor';
|
|||||||
import { createLocalId } from '../lib/elementDefaults';
|
import { createLocalId } from '../lib/elementDefaults';
|
||||||
import { parseJsonObject } from '../lib/parseJson';
|
import { parseJsonObject } from '../lib/parseJson';
|
||||||
import { logger } from '../lib/logger';
|
import { logger } from '../lib/logger';
|
||||||
|
import { useReverseVideoPolling } from './useReverseVideoPolling';
|
||||||
|
|
||||||
interface TourPage {
|
interface TourPage {
|
||||||
id: string;
|
id: string;
|
||||||
@ -145,6 +146,35 @@ export function useConstructorPageActions({
|
|||||||
const [isCreatingPage, setIsCreatingPage] = useState(false);
|
const [isCreatingPage, setIsCreatingPage] = useState(false);
|
||||||
const [isCreatingTransition, setIsCreatingTransition] = useState(false);
|
const [isCreatingTransition, setIsCreatingTransition] = useState(false);
|
||||||
|
|
||||||
|
// Polling hook for reverse video generation status
|
||||||
|
const { startPolling } = useReverseVideoPolling({
|
||||||
|
onReload,
|
||||||
|
pollInterval: 2000,
|
||||||
|
maxPollDuration: 30000,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract storage keys for elements that need reverse video generation.
|
||||||
|
* These are elements with:
|
||||||
|
* - transitionVideoUrl set (has a transition video)
|
||||||
|
* - transitionReverseMode is 'auto_reverse' or not set (default)
|
||||||
|
* - reverseVideoUrl is NOT set (not yet generated)
|
||||||
|
*/
|
||||||
|
const getPendingReverseVideoKeys = useCallback(
|
||||||
|
(elementsToCheck: CanvasElement[]): string[] => {
|
||||||
|
return elementsToCheck
|
||||||
|
.filter((el) => {
|
||||||
|
const hasTransition = Boolean(el.transitionVideoUrl);
|
||||||
|
const isAutoReverse = el.transitionReverseMode !== 'separate_video'; // auto_reverse is default
|
||||||
|
const needsGeneration = !el.reverseVideoUrl;
|
||||||
|
return hasTransition && isAutoReverse && needsGeneration;
|
||||||
|
})
|
||||||
|
.map((el) => el.transitionVideoUrl as string)
|
||||||
|
.filter(Boolean);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const saveConstructor = useCallback(async () => {
|
const saveConstructor = useCallback(async () => {
|
||||||
if (!activePageId) {
|
if (!activePageId) {
|
||||||
onError?.('Select a page before saving.');
|
onError?.('Select a page before saving.');
|
||||||
@ -154,6 +184,10 @@ export function useConstructorPageActions({
|
|||||||
try {
|
try {
|
||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
|
|
||||||
|
// Capture pending reverse video keys BEFORE save
|
||||||
|
// These are elements that will trigger async reversed video generation
|
||||||
|
const pendingReverseKeys = getPendingReverseVideoKeys(elements);
|
||||||
|
|
||||||
const existingSchema = parseJsonObject<Record<string, any>>(
|
const existingSchema = parseJsonObject<Record<string, any>>(
|
||||||
activePage?.ui_schema_json,
|
activePage?.ui_schema_json,
|
||||||
{},
|
{},
|
||||||
@ -192,6 +226,15 @@ export function useConstructorPageActions({
|
|||||||
'Constructor settings saved. Element positions are stored in percentages.',
|
'Constructor settings saved. Element positions are stored in percentages.',
|
||||||
);
|
);
|
||||||
await onReload(activePageId);
|
await onReload(activePageId);
|
||||||
|
|
||||||
|
// Start polling for reverse video generation if there are pending keys
|
||||||
|
// This will automatically reload page data when all videos are ready
|
||||||
|
if (pendingReverseKeys.length > 0) {
|
||||||
|
logger.info('[SAVE] Starting reverse video polling', {
|
||||||
|
pendingCount: pendingReverseKeys.length,
|
||||||
|
});
|
||||||
|
startPolling(pendingReverseKeys, activePageId);
|
||||||
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
const axiosError = error as {
|
const axiosError = error as {
|
||||||
response?: { data?: { message?: string } };
|
response?: { data?: { message?: string } };
|
||||||
@ -224,6 +267,8 @@ export function useConstructorPageActions({
|
|||||||
onError,
|
onError,
|
||||||
onReload,
|
onReload,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
|
getPendingReverseVideoKeys,
|
||||||
|
startPolling,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const saveToStage = useCallback(async () => {
|
const saveToStage = useCallback(async () => {
|
||||||
@ -256,7 +301,8 @@ export function useConstructorPageActions({
|
|||||||
}
|
}
|
||||||
}, [projectId, saveConstructor, onError, onSuccess]);
|
}, [projectId, saveConstructor, onError, onSuccess]);
|
||||||
|
|
||||||
const createPage = useCallback(async (pageName: string, slug: string) => {
|
const createPage = useCallback(
|
||||||
|
async (pageName: string, slug: string) => {
|
||||||
if (!projectId) {
|
if (!projectId) {
|
||||||
onError?.('Project is required.');
|
onError?.('Project is required.');
|
||||||
return;
|
return;
|
||||||
@ -307,7 +353,9 @@ export function useConstructorPageActions({
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSetMenuOpen?.(true);
|
onSetMenuOpen?.(true);
|
||||||
onSuccess?.('New page created. You can now configure it in constructor.');
|
onSuccess?.(
|
||||||
|
'New page created. You can now configure it in constructor.',
|
||||||
|
);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
const axiosError = error as {
|
const axiosError = error as {
|
||||||
response?: { data?: { message?: string } };
|
response?: { data?: { message?: string } };
|
||||||
@ -324,7 +372,8 @@ export function useConstructorPageActions({
|
|||||||
} finally {
|
} finally {
|
||||||
setIsCreatingPage(false);
|
setIsCreatingPage(false);
|
||||||
}
|
}
|
||||||
}, [
|
},
|
||||||
|
[
|
||||||
activePage?.environment,
|
activePage?.environment,
|
||||||
onError,
|
onError,
|
||||||
onReload,
|
onReload,
|
||||||
@ -335,7 +384,8 @@ export function useConstructorPageActions({
|
|||||||
project?.design_width,
|
project?.design_width,
|
||||||
project?.design_height,
|
project?.design_height,
|
||||||
projectId,
|
projectId,
|
||||||
]);
|
],
|
||||||
|
);
|
||||||
|
|
||||||
const createTransition = useCallback(
|
const createTransition = useCallback(
|
||||||
async (params: {
|
async (params: {
|
||||||
|
|||||||
@ -165,9 +165,10 @@ export function usePageNavigation<TPage extends NavigablePage>(
|
|||||||
const getNavigationContext = useCallback((): NavigationContext => {
|
const getNavigationContext = useCallback((): NavigationContext => {
|
||||||
return {
|
return {
|
||||||
currentPageSlug: currentPage?.slug,
|
currentPageSlug: currentPage?.slug,
|
||||||
|
currentPageId: currentPage?.id,
|
||||||
previousPageId,
|
previousPageId,
|
||||||
};
|
};
|
||||||
}, [currentPage?.slug, previousPageId]);
|
}, [currentPage?.slug, currentPage?.id, previousPageId]);
|
||||||
|
|
||||||
// Initialize to default page
|
// Initialize to default page
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
132
frontend/src/hooks/useReverseVideoPolling.ts
Normal file
132
frontend/src/hooks/useReverseVideoPolling.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* useReverseVideoPolling Hook
|
||||||
|
*
|
||||||
|
* Polls the backend to check if reversed videos are ready after save.
|
||||||
|
* Automatically reloads page data when all pending videos are generated.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useRef, useCallback } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { logger } from '../lib/logger';
|
||||||
|
|
||||||
|
interface ReverseVideoStatusResponse {
|
||||||
|
ready: Record<string, string>; // storageKey -> reversedUrl
|
||||||
|
pending: string[];
|
||||||
|
allReady: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UseReverseVideoPollingOptions {
|
||||||
|
/** Callback to reload page data when videos are ready */
|
||||||
|
onReload: (preservePageId?: string) => Promise<void>;
|
||||||
|
/** Polling interval in ms (default: 2000) */
|
||||||
|
pollInterval?: number;
|
||||||
|
/** Maximum polling duration in ms (default: 30000) */
|
||||||
|
maxPollDuration?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UseReverseVideoPollingResult {
|
||||||
|
/** Start polling for the given storage keys */
|
||||||
|
startPolling: (storageKeys: string[], activePageId: string) => void;
|
||||||
|
/** Stop polling (called automatically when all ready or timeout) */
|
||||||
|
stopPolling: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook that polls the backend to check if reversed videos are ready.
|
||||||
|
* Used after save when elements have auto_reverse transition mode but no reverseVideoUrl yet.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { startPolling, stopPolling } = useReverseVideoPolling({
|
||||||
|
* onReload: handleReload,
|
||||||
|
* pollInterval: 2000,
|
||||||
|
* maxPollDuration: 30000,
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* // After save:
|
||||||
|
* const pendingKeys = elements
|
||||||
|
* .filter(el => el.transitionVideoUrl && !el.reverseVideoUrl)
|
||||||
|
* .map(el => el.transitionVideoUrl);
|
||||||
|
* if (pendingKeys.length > 0) {
|
||||||
|
* startPolling(pendingKeys, activePageId);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export function useReverseVideoPolling({
|
||||||
|
onReload,
|
||||||
|
pollInterval = 2000,
|
||||||
|
maxPollDuration = 30000,
|
||||||
|
}: UseReverseVideoPollingOptions): UseReverseVideoPollingResult {
|
||||||
|
const pollingRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
const startTimeRef = useRef<number>(0);
|
||||||
|
|
||||||
|
const stopPolling = useCallback(() => {
|
||||||
|
if (pollingRef.current) {
|
||||||
|
clearTimeout(pollingRef.current);
|
||||||
|
pollingRef.current = null;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const startPolling = useCallback(
|
||||||
|
(storageKeys: string[], activePageId: string) => {
|
||||||
|
if (storageKeys.length === 0) return;
|
||||||
|
|
||||||
|
stopPolling();
|
||||||
|
startTimeRef.current = Date.now();
|
||||||
|
|
||||||
|
logger.info('[REVERSE-VIDEO-POLL] Starting polling', {
|
||||||
|
storageKeys: storageKeys.map((k) => k.slice(-40)),
|
||||||
|
activePageId: activePageId.slice(-8),
|
||||||
|
});
|
||||||
|
|
||||||
|
const poll = async () => {
|
||||||
|
// Check timeout
|
||||||
|
if (Date.now() - startTimeRef.current > maxPollDuration) {
|
||||||
|
logger.warn('[REVERSE-VIDEO-POLL] Polling timed out');
|
||||||
|
stopPolling();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post<ReverseVideoStatusResponse>(
|
||||||
|
'/tour_pages/reverse-video-status',
|
||||||
|
{ storageKeys },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.data.allReady) {
|
||||||
|
logger.info(
|
||||||
|
'[REVERSE-VIDEO-POLL] All reversed videos ready, reloading',
|
||||||
|
{
|
||||||
|
readyCount: Object.keys(response.data.ready).length,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
stopPolling();
|
||||||
|
await onReload(activePageId);
|
||||||
|
} else {
|
||||||
|
logger.debug(
|
||||||
|
'[REVERSE-VIDEO-POLL] Still waiting for reversed videos',
|
||||||
|
{
|
||||||
|
pendingCount: response.data.pending.length,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// Continue polling
|
||||||
|
pollingRef.current = setTimeout(poll, pollInterval);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(
|
||||||
|
'[REVERSE-VIDEO-POLL] Status check failed:',
|
||||||
|
error instanceof Error ? error : { error },
|
||||||
|
);
|
||||||
|
// Continue polling despite errors
|
||||||
|
pollingRef.current = setTimeout(poll, pollInterval);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start first poll after interval (generation needs time)
|
||||||
|
pollingRef.current = setTimeout(poll, pollInterval);
|
||||||
|
},
|
||||||
|
[onReload, pollInterval, maxPollDuration, stopPolling],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { startPolling, stopPolling };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useReverseVideoPolling;
|
||||||
@ -320,6 +320,15 @@ export function useTransitionPlayback(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!sourceUrl) {
|
if (!sourceUrl) {
|
||||||
|
// CRITICAL: Clear video element to prevent "ghost" playback
|
||||||
|
// Without this, the forward video from previous navigation stays in the buffer
|
||||||
|
// and may replay when sourceUrl is empty (e.g., back navigation without reversed video)
|
||||||
|
const video = videoRef.current;
|
||||||
|
if (video) {
|
||||||
|
video.pause();
|
||||||
|
video.removeAttribute('src');
|
||||||
|
video.load();
|
||||||
|
}
|
||||||
void finishPlayback('missing-source');
|
void finishPlayback('missing-source');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import { parseJsonObject } from './parseJson';
|
|||||||
*/
|
*/
|
||||||
export interface NavigationContext {
|
export interface NavigationContext {
|
||||||
currentPageSlug?: string;
|
currentPageSlug?: string;
|
||||||
|
currentPageId?: string;
|
||||||
previousPageId?: string | null;
|
previousPageId?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,11 +30,13 @@ interface UiSchemaStructure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find navigation element on sourcePage that points to targetPageSlug.
|
* Find navigation element on sourcePage that points to targetPageSlug or targetPageId.
|
||||||
* Used for history-based back navigation to get the forward transition.
|
* Used for history-based back navigation to get the forward transition.
|
||||||
|
* Supports both modern (targetPageSlug) and legacy (targetPageId) navigation.
|
||||||
*
|
*
|
||||||
* @param sourcePage - The page to search for navigation elements
|
* @param sourcePage - The page to search for navigation elements
|
||||||
* @param targetPageSlug - The target page slug to find
|
* @param targetPageSlug - The target page slug to find
|
||||||
|
* @param targetPageId - Optional target page ID for legacy support
|
||||||
* @returns The navigation element pointing to target page, or null if not found
|
* @returns The navigation element pointing to target page, or null if not found
|
||||||
*/
|
*/
|
||||||
export const findIncomingNavigationElement = (
|
export const findIncomingNavigationElement = (
|
||||||
@ -42,6 +45,7 @@ export const findIncomingNavigationElement = (
|
|||||||
slug?: string;
|
slug?: string;
|
||||||
},
|
},
|
||||||
targetPageSlug: string,
|
targetPageSlug: string,
|
||||||
|
targetPageId?: string,
|
||||||
): NavigableElement | null => {
|
): NavigableElement | null => {
|
||||||
// Parse ui_schema_json using shared utility
|
// Parse ui_schema_json using shared utility
|
||||||
const uiSchema = parseJsonObject<UiSchemaStructure>(
|
const uiSchema = parseJsonObject<UiSchemaStructure>(
|
||||||
@ -53,7 +57,9 @@ export const findIncomingNavigationElement = (
|
|||||||
const candidateElements = elements.filter(
|
const candidateElements = elements.filter(
|
||||||
(el) =>
|
(el) =>
|
||||||
isNavigationType(String(el.type || '')) &&
|
isNavigationType(String(el.type || '')) &&
|
||||||
el.targetPageSlug === targetPageSlug &&
|
// Match by slug (preferred) OR legacy ID
|
||||||
|
(el.targetPageSlug === targetPageSlug ||
|
||||||
|
(targetPageId && el.targetPageId === targetPageId)) &&
|
||||||
!isBackNavigation(el as unknown as NavigableElement),
|
!isBackNavigation(el as unknown as NavigableElement),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -90,12 +96,14 @@ export const findIncomingNavigationElement = (
|
|||||||
* @param pages - Available pages
|
* @param pages - Available pages
|
||||||
* @param currentPageSlug - Current page slug
|
* @param currentPageSlug - Current page slug
|
||||||
* @param previousPageId - Previous page ID from history
|
* @param previousPageId - Previous page ID from history
|
||||||
|
* @param currentPageId - Optional current page ID for legacy navigation support
|
||||||
* @returns Navigation target or null if previous page not found
|
* @returns Navigation target or null if previous page not found
|
||||||
*/
|
*/
|
||||||
export const resolveHistoryBackTarget = (
|
export const resolveHistoryBackTarget = (
|
||||||
pages: RuntimePage[],
|
pages: RuntimePage[],
|
||||||
currentPageSlug: string,
|
currentPageSlug: string,
|
||||||
previousPageId: string | null,
|
previousPageId: string | null,
|
||||||
|
currentPageId?: string,
|
||||||
): NavigationTarget | null => {
|
): NavigationTarget | null => {
|
||||||
if (!previousPageId) return null;
|
if (!previousPageId) return null;
|
||||||
|
|
||||||
@ -103,9 +111,11 @@ export const resolveHistoryBackTarget = (
|
|||||||
if (!previousPage) return null;
|
if (!previousPage) return null;
|
||||||
|
|
||||||
// Look up the forward navigation element that brought user to current page
|
// Look up the forward navigation element that brought user to current page
|
||||||
|
// Pass both slug and ID to support both modern and legacy navigation
|
||||||
const incomingElement = findIncomingNavigationElement(
|
const incomingElement = findIncomingNavigationElement(
|
||||||
previousPage,
|
previousPage,
|
||||||
currentPageSlug,
|
currentPageSlug,
|
||||||
|
currentPageId,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -139,6 +149,7 @@ export const resolveNavigationTarget = (
|
|||||||
pages,
|
pages,
|
||||||
context?.currentPageSlug || '',
|
context?.currentPageSlug || '',
|
||||||
context?.previousPageId || null,
|
context?.previousPageId || null,
|
||||||
|
context?.currentPageId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -74,7 +74,7 @@ const Access_logsTablesPage = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type: type });
|
const blob = new Blob([response.data], { type: type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -78,7 +78,7 @@ const Asset_variantsTablesPage = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type: type });
|
const blob = new Blob([response.data], { type: type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -90,7 +90,7 @@ const AssetsTablesPage = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type: type });
|
const blob = new Blob([response.data], { type: type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -1273,6 +1273,7 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
|
|||||||
const transitionSource = isBackNavigation(element)
|
const transitionSource = isBackNavigation(element)
|
||||||
? {
|
? {
|
||||||
type: element.type,
|
type: element.type,
|
||||||
|
navType: 'back' as const, // Preserve navigation direction for downstream logic
|
||||||
transitionVideoUrl: navTarget.transitionVideoUrl,
|
transitionVideoUrl: navTarget.transitionVideoUrl,
|
||||||
transitionReverseMode: navTarget.transitionReverseMode,
|
transitionReverseMode: navTarget.transitionReverseMode,
|
||||||
reverseVideoUrl: navTarget.reverseVideoUrl,
|
reverseVideoUrl: navTarget.reverseVideoUrl,
|
||||||
@ -1311,8 +1312,23 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
|
|||||||
if (!useVideoTransition) {
|
if (!useVideoTransition) {
|
||||||
closeTransitionPreview();
|
closeTransitionPreview();
|
||||||
|
|
||||||
|
// Log when skipping video due to missing reversed video (back navigation)
|
||||||
|
if (!canPlayTransition && transitionUrl && direction === 'back') {
|
||||||
|
logger.info(
|
||||||
|
'[NAVIGATION] Skipping video transition for back navigation - reversed video not ready, using CSS fade',
|
||||||
|
{
|
||||||
|
transitionVideoUrl: transitionUrl?.slice(-60),
|
||||||
|
isBack: navTarget.isBack,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Log when skipping video due to slow network
|
// Log when skipping video due to slow network
|
||||||
if (canPlayTransition && !shouldUseVideoTransitions && transitionUrl) {
|
if (
|
||||||
|
canPlayTransition &&
|
||||||
|
!shouldUseVideoTransitions &&
|
||||||
|
transitionUrl
|
||||||
|
) {
|
||||||
logger.info(
|
logger.info(
|
||||||
'[NAVIGATION] Skipping video transition due to slow network, downloading in background',
|
'[NAVIGATION] Skipping video transition due to slow network, downloading in background',
|
||||||
{
|
{
|
||||||
|
|||||||
@ -56,7 +56,7 @@ const PermissionsTablesPage = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type: type });
|
const blob = new Blob([response.data], { type: type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -81,7 +81,7 @@ const Presigned_url_requestsTablesPage = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type: type });
|
const blob = new Blob([response.data], { type: type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -73,7 +73,7 @@ const Project_audio_tracksTablesPage = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type: type });
|
const blob = new Blob([response.data], { type: type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -71,7 +71,7 @@ const Project_membershipsTablesPage = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type: type });
|
const blob = new Blob([response.data], { type: type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -62,7 +62,7 @@ const ProjectsTablesPage = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type: type });
|
const blob = new Blob([response.data], { type: type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -88,7 +88,7 @@ const Publish_eventsTablesPage = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type: type });
|
const blob = new Blob([response.data], { type: type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -69,7 +69,7 @@ const Pwa_cachesTablesPage = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type: type });
|
const blob = new Blob([response.data], { type: type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -57,7 +57,7 @@ const RolesTablesPage = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type: type });
|
const blob = new Blob([response.data], { type: type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -72,7 +72,7 @@ const Tour_pagesTablesPage = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type: type });
|
const blob = new Blob([response.data], { type: type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
@ -62,7 +62,7 @@ const UsersTablesPage = () => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
const type = response.headers['content-type'];
|
const type = response.headers['content-type'] as string;
|
||||||
const blob = new Blob([response.data], { type: type });
|
const blob = new Blob([response.data], { type: type });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = window.URL.createObjectURL(blob);
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user