diff --git a/ghost/core/core/frontend/helpers/ghost_foot.js b/ghost/core/core/frontend/helpers/ghost_foot.js
index bb882922bbd..1c915dcfc0e 100644
--- a/ghost/core/core/frontend/helpers/ghost_foot.js
+++ b/ghost/core/core/frontend/helpers/ghost_foot.js
@@ -2,10 +2,12 @@
// Usage: `{{ghost_foot}}`
//
// Outputs scripts and other assets at the bottom of a Ghost theme
-const {settingsCache} = require('../services/proxy');
-const {SafeString} = require('../services/handlebars');
+const {settingsCache, labs} = require('../services/proxy');
+const {SafeString, templates, hbs} = require('../services/handlebars');
const _ = require('lodash');
+const createFrame = hbs.handlebars.createFrame;
+
// We use the name ghost_foot to match the helper for consistency:
module.exports = function ghost_foot(options) { // eslint-disable-line camelcase
const foot = [];
@@ -26,5 +28,15 @@ module.exports = function ghost_foot(options) { // eslint-disable-line camelcase
foot.push(tagCodeinjection);
}
+ // Reader-side gift announcement. The internal `_gift` flag is set by the
+ // gift-links controller only on the verified render path (valid token,
+ // matching slug), so the toast appears on gift reads and never on canonical
+ // post URLs. Rendered as the overridable `gift-toast` partial — themes can
+ // supply their own `partials/gift-toast.hbs`.
+ if (labs.isSet('giftLinks') && options.data._gift) {
+ const data = createFrame(options.data);
+ foot.push(templates.execute('gift-toast', this, {data}));
+ }
+
return new SafeString(foot.join(' ').trim());
};
diff --git a/ghost/core/core/frontend/helpers/tpl/gift-toast.hbs b/ghost/core/core/frontend/helpers/tpl/gift-toast.hbs
new file mode 100644
index 00000000000..6ae031c8a78
--- /dev/null
+++ b/ghost/core/core/frontend/helpers/tpl/gift-toast.hbs
@@ -0,0 +1,157 @@
+
+
+
+ {{t "{publication} unlocked this post so you can read it for free." publication=@site.title}}
+
+
+
+
+
+
+
diff --git a/ghost/core/core/frontend/services/proxy.js b/ghost/core/core/frontend/services/proxy.js
index 4e5e6518154..fbd98dab268 100644
--- a/ghost/core/core/frontend/services/proxy.js
+++ b/ghost/core/core/frontend/services/proxy.js
@@ -70,6 +70,17 @@ module.exports = {
// Labs utils for enabling/disabling helpers
labs: require('../../shared/labs'),
+ // Gift links service — the /g/ reader controller resolves gift tokens and
+ // records reads (via the read-counter) through this seam, per the
+ // frontend→server boundary rule (not a direct require).
+ giftLinks: require('../../server/services/gift-links'),
+ // Synthesize an all-paid-tiers member for gift-link reads (shared with
+ // post previews). Lazy getter so the members service is resolved at call
+ // time, avoiding any boot-time require-order coupling.
+ get synthesizePaidMember() {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ return require('../../server/services/members').synthesizePaidMember;
+ },
// URGH... Yuk (unhelpful comment :D)
urlService: require('../../server/services/url'),
urlUtils: require('../../shared/url-utils')
diff --git a/ghost/core/core/frontend/services/routing/controllers/gift-links.ts b/ghost/core/core/frontend/services/routing/controllers/gift-links.ts
new file mode 100644
index 00000000000..ee52a6ad2ed
--- /dev/null
+++ b/ghost/core/core/frontend/services/routing/controllers/gift-links.ts
@@ -0,0 +1,215 @@
+import type {Request, Response, NextFunction} from 'express';
+
+// These modules use `module.exports =` / are untyped JS, so they're loaded via
+// `require()` (matching their runtime CommonJS shape) rather than ESM imports.
+/* eslint-disable @typescript-eslint/no-require-imports */
+const debug = require('@tryghost/debug')('services:routing:controllers:gift-links');
+const _ = require('lodash');
+const labs = require('../../../../shared/labs');
+const renderer = require('../../rendering');
+const hbs = require('../../theme-engine/engine');
+const proxy = require('../../proxy');
+const urlUtils = require('../../../../shared/url-utils');
+const {GIFT_LINK_PREFIX} = require('../gift-links-router') as {GIFT_LINK_PREFIX: string};
+/* eslint-enable @typescript-eslint/no-require-imports */
+
+interface Entry {
+ slug: string;
+ url?: string;
+ [key: string]: unknown;
+}
+
+// The public content API is untyped JS; describe only the shape this controller
+// reads from it.
+interface PublicApi {
+ postsPublic: {read(query: Record): Promise<{posts?: Entry[]}>};
+ pagesPublic: {read(query: Record): Promise<{pages?: Entry[]}>};
+}
+
+// `res.routerOptions` is the frontend routing context bag — not on the stock
+// Express Response, so augment it locally.
+type GiftResponse = Response & {
+ routerOptions: {context?: string[]; [key: string]: unknown};
+};
+
+// Whether a token resolves to a post or a page decides which public read
+// controller serves it (both share the `posts` table).
+const RESOURCE_BY_TYPE = {
+ page: {controller: 'pagesPublic' as const, resource: 'pages' as const, context: ['page']},
+ post: {controller: 'postsPublic' as const, resource: 'posts' as const, context: ['post']}
+};
+
+/**
+ * True only for genuine "no such entry" errors. Anything else (DB timeout,
+ * serializer crash, etc.) must surface as a 5xx rather than be misread as
+ * not-found and 301'd to the canonical URL.
+ */
+function isNotFoundError(err: unknown): boolean {
+ return Boolean(err && (err as {errorType?: string}).errorType === 'NotFoundError');
+}
+
+/**
+ * Read a single published entry via the given public controller, treating
+ * not-found as `null` (any other error rethrows). The public API only returns
+ * published entries, so a non-null result is sufficient — you can't gift a
+ * draft, and a since-unpublished post simply resolves to null here.
+ */
+async function readPublished(
+ api: PublicApi,
+ controller: 'postsPublic' | 'pagesPublic',
+ resource: 'posts' | 'pages',
+ query: Record
+): Promise {
+ try {
+ const result = await api[controller].read(query);
+ return (result as Record)[resource]?.[0] ?? null;
+ } catch (err) {
+ if (isNotFoundError(err)) {
+ return null;
+ }
+ throw err;
+ }
+}
+
+/**
+ * Resolve the URL slug to its canonical published post/page (post first, then
+ * page). Used only on the invalid path to redirect a bad/missing token away
+ * from `/g/`.
+ */
+async function resolveCanonicalBySlug(api: PublicApi, slug: string): Promise {
+ const post = await readPublished(api, 'postsPublic', 'posts', {slug});
+ if (post) {
+ return post;
+ }
+ return readPublished(api, 'pagesPublic', 'pages', {slug});
+}
+
+/**
+ * Gift Link Controller — owns the whole `/g/:slug/?key=TOKEN` flow.
+ *
+ * Token is authoritative; the slug is cosmetic. Flow:
+ * 1. Read `?key=` (string only — an array/object query value is not a token).
+ * 2. Resolve the token to its post via the gift-links service (live links
+ * only — a revoked/reissued/unknown token resolves to null).
+ * 3. Read that post by id through the public content API, granting access via
+ * a synthetic all-paid-tiers member (the same grant `/p/` previews use).
+ * 4. If the URL slug is stale, 301 to `/g//?key=…` (keep the
+ * key — a renamed post must still open). Otherwise render the unlocked
+ * entry through the standard pipeline, flagged with the internal `_gift`
+ * template flag and `noindex` / `no-referrer` headers.
+ * 5. Invalid path (missing/invalid token, or the post is gone): 301 the URL
+ * slug to its canonical URL with the key dropped, or 404 if it doesn't
+ * resolve — or if the target is itself under `/g/` (the redirect-loop
+ * guard, since a routes.yaml collection could permalink under `/g/`).
+ *
+ * Cache headers (`no-store`) are set by [frontend-caching.js] via the `/g/`
+ * path check, covering both rendered responses and the 301s emitted here.
+ */
+async function giftLinksController(req: Request, res: GiftResponse, next: NextFunction) {
+ debug('giftLinksController');
+
+ if (!labs.isSet('giftLinks')) {
+ return next();
+ }
+
+ const api = proxy.api as PublicApi;
+ const giftLinksService = proxy.giftLinks;
+ const urlSlug = req.params.slug;
+
+ // Express's query parser turns `?key[x]=y` into an object and `?key=a&key=b`
+ // into an array — only a plain string is a real token.
+ const key = typeof req.query.key === 'string' ? req.query.key : null;
+
+ try {
+ if (key) {
+ const resolved = await giftLinksService.service.getPostByToken(key);
+
+ if (resolved) {
+ const {controller, resource, context} = resolved.type === 'page' ? RESOURCE_BY_TYPE.page : RESOURCE_BY_TYPE.post;
+
+ // Grant access the way `/p/` previews do: render as a member
+ // with every active paid tier, so the existing gating reveals
+ // member-only content unchanged. The synthetic member lands at
+ // `frame.original.context.member` for the read (and so varies
+ // the posts-public cache key by member.products), and never
+ // leaks into `@member` (themes read res.locals.member).
+ const member = await proxy.synthesizePaidMember();
+
+ const entry = await readPublished(api, controller, resource, {
+ id: resolved.id,
+ include: 'authors,tags,tiers',
+ context: {member}
+ });
+
+ if (entry) {
+ // Token authoritative, slug cosmetic: a stale slug still
+ // works — canonicalise to the current slug, keeping the key.
+ // Subdir-safe: prefix any configured subdirectory.
+ if (urlSlug !== entry.slug) {
+ const canonicalGiftPath = urlUtils.urlJoin(urlUtils.getSubdir(), GIFT_LINK_PREFIX, entry.slug, '/');
+ return res.redirect(301, `${canonicalGiftPath}?key=${encodeURIComponent(key)}`);
+ }
+
+ res.routerOptions.context = context;
+
+ // Always-on defence in depth: don't index gift URLs, and
+ // limit Referer leakage of the token to sub-resources.
+ res.set('X-Robots-Tag', 'noindex');
+ res.set('Referrer-Policy', 'no-referrer');
+
+ // Internal `_gift` flag — set only on the verified render
+ // path (valid token, matching slug). The ghost_foot helper
+ // consumes it to inject the gift toast; underscore-prefixed
+ // per the `_queryCache` precedent, so it's not a committed
+ // public theme API.
+ const localTemplateOptions = hbs.getLocalTemplateOptions(res.locals);
+ hbs.updateLocalTemplateOptions(res.locals, _.merge({}, localTemplateOptions, {
+ data: {_gift: true}
+ }));
+
+ // Count the read (bot-filtered, cookie-deduped, fire-and-
+ // forget). Only here, on the verified render path — never on
+ // redirects or 404s — so a bad slug or invalid key can't
+ // inflate the count.
+ giftLinksService.recordRead(req, res, {token: key, postId: resolved.id});
+
+ return renderer.renderEntry(req, res)(entry);
+ }
+ }
+ }
+
+ // Invalid / missing / unresolved → redirect the URL slug to its
+ // canonical URL (NOT the token's post — that would leak a renamed
+ // slug), dropping the key. 404 if it doesn't resolve, or if the target
+ // is under `/g/` (would re-enter this controller and loop forever — a
+ // routes.yaml collection can permalink under `/g/`) or is this exact
+ // request path.
+ const fallback = await resolveCanonicalBySlug(api, urlSlug);
+ if (fallback?.url) {
+ // Canonical URLs are absolute; compare on pathname only. The `/g/`
+ // and self-path checks must account for any configured
+ // subdirectory: `req.path` is mount-relative (no subdir) but the
+ // canonical pathname includes it, so prefix the subdir onto both
+ // comparison baselines.
+ let targetPath = fallback.url;
+ try {
+ targetPath = new URL(fallback.url).pathname;
+ } catch {
+ // Already a path (or unparseable) — compare as-is.
+ }
+ const giftPrefix = urlUtils.urlJoin(urlUtils.getSubdir(), GIFT_LINK_PREFIX);
+ const requestPath = urlUtils.urlJoin(urlUtils.getSubdir(), req.path);
+ if (!targetPath.startsWith(giftPrefix) && targetPath !== requestPath) {
+ return res.redirect(301, fallback.url);
+ }
+ }
+
+ return next();
+ } catch (err) {
+ return renderer.handleError(next)(err);
+ }
+}
+
+// module.exports required - using `export` causes the module to fail to register
+// when loaded via require()
+module.exports = giftLinksController;
diff --git a/ghost/core/core/frontend/services/routing/controllers/index.js b/ghost/core/core/frontend/services/routing/controllers/index.js
index a60a9c48d4a..33607f05861 100644
--- a/ghost/core/core/frontend/services/routing/controllers/index.js
+++ b/ghost/core/core/frontend/services/routing/controllers/index.js
@@ -15,6 +15,10 @@ module.exports = {
return require('./previews');
},
+ get giftLinks() {
+ return require('./gift-links');
+ },
+
get email() {
return require('./email-post');
},
diff --git a/ghost/core/core/frontend/services/routing/gift-links-router.ts b/ghost/core/core/frontend/services/routing/gift-links-router.ts
new file mode 100644
index 00000000000..2d0f445374b
--- /dev/null
+++ b/ghost/core/core/frontend/services/routing/gift-links-router.ts
@@ -0,0 +1,83 @@
+import type {Request, Response, NextFunction, Router} from 'express';
+
+// These modules use `module.exports =` / are untyped JS, so they're loaded via
+// `require()` (matching their runtime CommonJS shape) rather than ESM imports.
+/* eslint-disable @typescript-eslint/no-require-imports */
+
+// ParentRouter is an untyped JS base class; describe only the members this
+// router actually uses so the subclass type-checks without depending on the
+// wider (untyped) routing internals.
+interface ParentRouterBase {
+ route: {value: string};
+ router(): Router;
+ mountRoute(path: string, controller: unknown): void;
+}
+const ParentRouter = require('./parent-router') as new (name: string) => ParentRouterBase;
+const urlUtils = require('../../../shared/url-utils');
+const controllers = require('./controllers');
+/* eslint-enable @typescript-eslint/no-require-imports */
+
+// Single source of truth for the `/g/` route prefix. Referenced by the router
+// (route registration) and by frontend-caching (path-based cache bypass), so
+// the path lives in exactly one place.
+// @NOTE: hardcoded, not configurable. `g` is intentionally short and distinct
+// from the gift-subscriptions `/gift/` namespace.
+const GIFT_LINK_PREFIX = '/g/';
+
+/**
+ * @description Gift Link Router — mounts `/g/:slug/` for tokenised gift-link
+ * reads. Modelled on PreviewRouter: a dedicated top-level prefix outside the
+ * theme's collection space, mounted in the same privileged position so a
+ * routes.yaml collection can never intercept it.
+ *
+ * Token validation, slug canonicalisation, access grant and the
+ * redirect/404 fallback all live in the controller
+ * (`controllers/gift-links.js`) — there is no site-wide middleware.
+ */
+class GiftLinksRouter extends ParentRouter {
+ constructor() {
+ super('GiftLinksRouter');
+
+ this.route = {value: GIFT_LINK_PREFIX};
+
+ this._registerRoutes();
+ }
+
+ /**
+ * @description Register all routes of this router.
+ * @private
+ */
+ _registerRoutes(): void {
+ // REGISTER: prepare context
+ this.router().use(this._prepareContext.bind(this));
+
+ // REGISTER: gift link route — `:slug` is the post/page slug;
+ // `?key=TOKEN` is validated by the controller.
+ this.mountRoute(urlUtils.urlJoin(this.route.value, ':slug'), controllers.giftLinks);
+ }
+
+ /**
+ * @description Prepare context for further middleware/controllers. The
+ * controller overrides `routerOptions.context` once it knows whether the
+ * target is a post or a page (so template selection matches the canonical
+ * render).
+ * @param {Object} req
+ * @param {Object} res
+ * @param {Function} next
+ * @private
+ */
+ _prepareContext(req: Request, res: Response & {routerOptions?: unknown}, next: NextFunction): void {
+ res.routerOptions = {
+ type: 'entry',
+ context: ['giftLink']
+ };
+
+ next();
+ }
+}
+
+// module.exports required - using `export` causes the module to fail to register
+// when loaded via require(). Both the default (the class) and the named static
+// (GIFT_LINK_PREFIX) are preserved for existing require() callers.
+module.exports = GiftLinksRouter;
+module.exports.GIFT_LINK_PREFIX = GIFT_LINK_PREFIX;
diff --git a/ghost/core/core/frontend/services/routing/router-manager.js b/ghost/core/core/frontend/services/routing/router-manager.js
index 4fd8cd7884a..a4259f8124f 100644
--- a/ghost/core/core/frontend/services/routing/router-manager.js
+++ b/ghost/core/core/frontend/services/routing/router-manager.js
@@ -7,6 +7,7 @@ const TaxonomyRouter = require('./taxonomy-router');
const PreviewRouter = require('./preview-router');
const ParentRouter = require('./parent-router');
const EmailRouter = require('./email-router');
+const GiftLinksRouter = require('./gift-links-router');
const UnsubscribeRouter = require('./unsubscribe-router');
// This emits its own routing events
@@ -115,6 +116,15 @@ class RouterManager {
this.siteRouter.mountRouter(previewRouter.router());
this.registry.setRouter('previewRouter', previewRouter);
+ // Gift links share preview's "top-level prefix, no collection coupling"
+ // shape, mounted in the same privileged position so `/g/` always wins
+ // over a routes.yaml collection. The controller flag-gates on
+ // `labs.giftLinks` per-request, so the route no-ops when the feature is
+ // off (falling through to the collection routers below).
+ const giftLinksRouter = new GiftLinksRouter();
+ this.siteRouter.mountRouter(giftLinksRouter.router());
+ this.registry.setRouter('giftLinksRouter', giftLinksRouter);
+
_.each(routerSettings.routes, (value, key) => {
const staticRoutesRouter = new StaticRoutesRouter(key, value, this.routerCreated.bind(this));
this.siteRouter.mountRouter(staticRoutesRouter.router());
diff --git a/ghost/core/core/frontend/web/middleware/frontend-caching.js b/ghost/core/core/frontend/web/middleware/frontend-caching.js
index 12244c95b18..3c33b9542d8 100644
--- a/ghost/core/core/frontend/web/middleware/frontend-caching.js
+++ b/ghost/core/core/frontend/web/middleware/frontend-caching.js
@@ -2,9 +2,11 @@
* @file Middleware to set the appropriate cache headers on the frontend
*/
const config = require('../../../shared/config');
+const labs = require('../../../shared/labs');
const shared = require('../../../server/web/shared');
const {api} = require('../../services/proxy');
const preview = require('../../services/theme-engine/preview');
+const {GIFT_LINK_PREFIX} = require('../../services/routing/gift-links-router');
/**
* Calculate the member's active tier.
@@ -69,6 +71,15 @@ const getMiddleware = async (getFreeTier = async () => {
return shared.middleware.cacheControl('noCache')(req, res, next);
}
+ // CASE: Never cache gift-link requests. A /g//?key=TOKEN render
+ // contains unlocked gated content and carries no member cookie, so the
+ // edge would otherwise cache it and serve unlocked content to everyone.
+ // Covers both the render and the 301s emitted by the gift controller.
+ // Flag-gated so /g/ paths don't bypass cache when the feature is off.
+ if (req.path?.startsWith(GIFT_LINK_PREFIX) && labs.isSet('giftLinks')) {
+ return shared.middleware.cacheControl('noCache')(req, res, next);
+ }
+
// CASE: Cache member's content if this feature is enabled
if (req.member && shouldCacheMembersContent) {
// Set the 'cache-control' header to 'public'
diff --git a/ghost/core/core/server/api/endpoints/previews.js b/ghost/core/core/server/api/endpoints/previews.js
index 75bddcee721..cf4690fe07b 100644
--- a/ghost/core/core/server/api/endpoints/previews.js
+++ b/ghost/core/core/server/api/endpoints/previews.js
@@ -1,7 +1,7 @@
const tpl = require('@tryghost/tpl');
const errors = require('@tryghost/errors');
const models = require('../../models');
-const logging = require('@tryghost/logging');
+const membersService = require('../../services/members');
const ALLOWED_INCLUDES = ['authors', 'tags', 'tiers'];
const ALLOWED_MEMBER_STATUSES = ['anonymous', 'free', 'paid'];
@@ -31,29 +31,8 @@ const _addMemberContextToFrame = async (frame) => {
}
if (frame.options?.member_status === 'paid') {
- // For member_status=paid, add all paid tiers to the member context
- let memberProducts = [];
- try {
- const paidProducts = await models.Product.findAll({
- status: 'active',
- type: 'paid'
- });
- if (paidProducts.length > 0) {
- memberProducts = paidProducts.map((product) => {
- return {
- slug: product.get('slug')
- };
- });
- }
- } catch (error) {
- // Log error but don't fail preview - fallback to empty products array
- logging.error('Failed to fetch paid products for preview:', error);
- }
-
- frame.original.context.member = {
- status: 'paid',
- products: memberProducts
- };
+ // For member_status=paid, render as a member with all active paid tiers
+ frame.original.context.member = await membersService.synthesizePaidMember();
}
};
diff --git a/ghost/core/core/server/services/gift-links/index.ts b/ghost/core/core/server/services/gift-links/index.ts
index fe6fbe31ea3..83e61e73ed3 100644
--- a/ghost/core/core/server/services/gift-links/index.ts
+++ b/ghost/core/core/server/services/gift-links/index.ts
@@ -1,5 +1,11 @@
+import type {Request, Response} from 'express';
import {GiftLinksService} from './service';
+interface ReadContext {
+ token: string;
+ postId: string;
+}
+
// Set by init() at boot, not at import: knex only exists once the DB has connected.
export let service: GiftLinksService | undefined;
@@ -13,3 +19,19 @@ export function init(): void {
service = new GiftLinksService({knex});
}
+
+// Test seam: inject (or clear) the service singleton. The exported `service`
+// binding is read-only to importers once compiled, so tests that need a stubbed
+// service set it through here rather than by assignment.
+export function setService(stub: GiftLinksService | undefined): void {
+ service = stub;
+}
+
+// The frontend reader path (/g/) records reads through this seam. Lazily
+// required (on access, not at module load) so this module stays cheap to
+// require at boot without pulling in the read-counter's web deps (cookies, etc).
+export function recordRead(req: Request, res: Response, read: ReadContext): void {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ const counter = require('./read-counter') as (_req: Request, _res: Response, _read: ReadContext) => void;
+ counter(req, res, read);
+}
diff --git a/ghost/core/core/server/services/gift-links/is-bot-user-agent.ts b/ghost/core/core/server/services/gift-links/is-bot-user-agent.ts
new file mode 100644
index 00000000000..0a999dc7678
--- /dev/null
+++ b/ghost/core/core/server/services/gift-links/is-bot-user-agent.ts
@@ -0,0 +1,39 @@
+// Bot/crawler/scanner detection for gift-link read counting. The read count is
+// a leak-detection proxy, so we want to skip automated clients (search crawlers,
+// social preview unfurlers, email link scanners, AI/LLM fetchers, CLI tools,
+// headless browsers) without dropping real humans.
+//
+// We delegate the bulk of this to the `isbot` library, whose pattern list is
+// community-maintained and updated far more often than a hand-rolled regex
+// could be. It also matters that gift links are shared mostly through
+// social/messaging apps, where a large share of recipients read inside the
+// app's IN-APP BROWSER (a real human, UA carries a bare brand name like
+// "Twitter for iPhone", "[LinkedInApp]", "WhatsApp/…"). `isbot` is conservative
+// about these and does NOT flag them — and its tokens are word-boundary aware,
+// so a device UA like "CUBOT_X30" is not mistaken for a bot.
+//
+// One deliberate deviation from raw `isbot`: a missing/empty UA → bot.
+// `isbot('')`/`isbot(undefined)` return false, but a request with no UA is
+// almost always an automated client or scanner, and counting it would inflate
+// the proxy. We treat it as a bot (conservative).
+
+// `isbot` ships a dual CJS/ESM build; the `require` export resolves to its
+// CommonJS entry, matching this module's runtime shape. Loaded via require() to
+// match the surrounding gift-links modules (which use `module.exports =`).
+// eslint-disable-next-line @typescript-eslint/no-require-imports
+const {isbot} = require('isbot') as {isbot: (userAgent?: string | null) => boolean};
+
+/**
+ * @param {string|undefined|null} userAgent
+ * @returns {boolean} true if the request looks automated (and should not be counted)
+ */
+function isBotUserAgent(userAgent: string | undefined | null): boolean {
+ if (!userAgent || typeof userAgent !== 'string') {
+ return true;
+ }
+ return isbot(userAgent);
+}
+
+// module.exports required - using `export` causes the module to fail to register
+// when loaded via require()
+module.exports = isBotUserAgent;
diff --git a/ghost/core/core/server/services/gift-links/model.ts b/ghost/core/core/server/services/gift-links/model.ts
index 9d3ebc42eb9..7f856f4bf12 100644
--- a/ghost/core/core/server/services/gift-links/model.ts
+++ b/ghost/core/core/server/services/gift-links/model.ts
@@ -47,3 +47,9 @@ export interface Post {
id: string;
giftLinks: GiftLink[];
}
+
+/** A live token resolved to the post it unlocks: just the id and resource type. */
+export interface PostRef {
+ id: string;
+ type: string;
+}
diff --git a/ghost/core/core/server/services/gift-links/queries.ts b/ghost/core/core/server/services/gift-links/queries.ts
index ac72236716b..0901a4178bf 100644
--- a/ghost/core/core/server/services/gift-links/queries.ts
+++ b/ghost/core/core/server/services/gift-links/queries.ts
@@ -50,9 +50,18 @@ export function liveLinksForPost(postId: string) {
.select(giftLinkColumns);
}
+// The reader path (/g/) only needs which post a live token unlocks and whether
+// it's a post or page (to pick the public read controller). Anchored on
+// post_gift_links, so a revoked/reissued token (no live association) yields no
+// row — exactly the "invalid → 301-to-canonical" trigger. Redemption stats are
+// deliberately not selected here; this is a resolve, not a stats read.
export function liveLinkForToken(token: string) {
return (knex: Knex) => knex('post_gift_links')
.join('gift_links', 'gift_links.token', 'post_gift_links.gift_link_token')
+ .join('posts', 'posts.id', 'post_gift_links.post_id')
.where('gift_links.token', token)
- .first & {post_id: string}>([...giftLinkColumns, 'post_gift_links.post_id as post_id']);
+ .first<{post_id: string; post_type: string}>([
+ 'post_gift_links.post_id as post_id',
+ 'posts.type as post_type'
+ ]);
}
diff --git a/ghost/core/core/server/services/gift-links/read-counter.ts b/ghost/core/core/server/services/gift-links/read-counter.ts
new file mode 100644
index 00000000000..b0a3a8b21d3
--- /dev/null
+++ b/ghost/core/core/server/services/gift-links/read-counter.ts
@@ -0,0 +1,111 @@
+import type {Request, Response} from 'express';
+import type {GiftLinksService} from './service';
+
+// These modules use `module.exports =` / are untyped JS, so they're loaded via
+// `require()` (matching their runtime CommonJS shape) rather than ESM imports.
+/* eslint-disable @typescript-eslint/no-require-imports */
+const logging = require('@tryghost/logging');
+const Cookies = require('cookies');
+const urlUtils = require('../../../shared/url-utils');
+const isBotUserAgent = require('./is-bot-user-agent') as (userAgent: string | undefined | null) => boolean;
+/* eslint-enable @typescript-eslint/no-require-imports */
+
+// The cookie is named per-post (`ghost-gift-seen-`) so reading multiple
+// gifted posts can't collide, and its value is the token, making the count a
+// distinct-clients proxy. The `ghost-` prefix is REQUIRED, not cosmetic: the
+// Ghost(Pro) Fastly edge strips every request cookie except those matching
+// `^(ghost-|_fs_ch_)` before forwarding to origin, so a non-`ghost-` name would
+// never reach us and every view would recount (see BER-3737).
+const GIFT_SEEN_COOKIE_PREFIX = 'ghost-gift-seen-';
+
+// The dedup cookie must persist across browser sessions, otherwise the same
+// reader recounts on every fresh session and inflates `redeemed_count` (which
+// we surface as distinct uses). One year in milliseconds.
+const GIFT_SEEN_COOKIE_MAX_AGE = 365 * 24 * 60 * 60 * 1000;
+
+// The reader route prefix. The dedup cookie is scoped to this (not the specific
+// `/g//` path) so it's still sent — and the read still de-dupes — after a
+// post is renamed and the controller canonicalises to a new slug. Kept in sync
+// with GIFT_LINK_PREFIX in frontend/services/routing/gift-links-router (a server
+// module can't import the frontend constant).
+const GIFT_LINK_PREFIX = '/g/';
+
+interface ReadContext {
+ token: string;
+ postId: string;
+}
+
+/**
+ * Record a gift read once per client+post: skip bots/scanners and repeat views
+ * (de-duped via a post-scoped `ghost-gift-seen-` cookie whose value is the
+ * token). The counter write is fire-and-forget so it never blocks rendering.
+ * Call only when the request actually rendered the gift's own post.
+ *
+ * This is the single seam the reader controller calls. The counting *mechanism*
+ * (cookie dedup, bot filtering, the redemption write) is expected to change, so
+ * it all lives behind this one boundary.
+ *
+ * @param {import('express').Request} req
+ * @param {import('express').Response} res
+ * @param {{token: string, postId: string}} read
+ */
+function recordRead(req: Request, res: Response, {token, postId}: ReadContext): void {
+ try {
+ if (isBotUserAgent(req.get && req.get('user-agent'))) {
+ return;
+ }
+
+ const cookieName = `${GIFT_SEEN_COOKIE_PREFIX}${postId}`;
+ // Pass `secure` at the CONSTRUCTOR level (not as a per-`.set()` option):
+ // the cookies lib THROWS on `.set({secure: true})` when it can't see the
+ // connection as https (e.g. a TLS-terminating proxy that doesn't forward
+ // X-Forwarded-Proto), and that throw is swallowed below BEFORE
+ // `recordRedemption` runs — so the read would never be counted. Setting
+ // it on the constructor marks the cookie secure without the throw,
+ // mirroring members-ssr.
+ const cookies = new Cookies(req, res, {
+ secure: urlUtils.isSSL(urlUtils.getSiteUrl())
+ });
+ if (cookies.get(cookieName) === token) {
+ return;
+ }
+
+ cookies.set(cookieName, token, {
+ // Scope to the `/g/` prefix (subdir-aware), not the slug-specific
+ // path: the cookie name already encodes the post id, and scoping to
+ // the prefix keeps dedup working across slug renames (the render
+ // moves to a new `/g//` path but the cookie still applies).
+ path: urlUtils.urlJoin(urlUtils.getSubdir(), GIFT_LINK_PREFIX),
+ httpOnly: true,
+ sameSite: 'lax',
+ // Long-lived so the once-per-client-per-post dedup persists across
+ // browser sessions (a session-only cookie recounts every session).
+ maxAge: GIFT_SEEN_COOKIE_MAX_AGE,
+ overwrite: true
+ });
+
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ const giftLinksService = require('./') as {service: GiftLinksService};
+ // Fire-and-forget: never block rendering on the counter write. Keyed by
+ // token (the redemption counter lives on the gift_links row).
+ //
+ // The dedup cookie is committed above, before this write resolves, so a
+ // transient write failure undercounts that one client+post (the cookie
+ // already marks them seen). This is an accepted tradeoff: the count is a
+ // best-effort leak-detection proxy, not an exact ledger, and the
+ // alternatives are worse — awaiting the write would block the render (or
+ // turn a DB hiccup into a 500), and the cookie can't be set after the
+ // response is sent. A durable/retryable write is deliberately left to a
+ // future iteration of the mechanism, which lives wholly behind this seam.
+ Promise.resolve(giftLinksService.service.recordRedemption(token)).catch((err: unknown) => {
+ logging.error(err);
+ });
+ } catch (err) {
+ // Read-counting must never break rendering the post.
+ logging.error(err);
+ }
+}
+
+// module.exports required - using `export` causes the module to fail to register
+// when loaded via require()
+module.exports = recordRead;
diff --git a/ghost/core/core/server/services/gift-links/service.ts b/ghost/core/core/server/services/gift-links/service.ts
index 91b82dd32d7..db6c48891cb 100644
--- a/ghost/core/core/server/services/gift-links/service.ts
+++ b/ghost/core/core/server/services/gift-links/service.ts
@@ -2,7 +2,7 @@ import crypto from 'crypto';
import errors from '@tryghost/errors';
import {z} from 'zod';
import type {Knex} from 'knex';
-import {GiftLinkRow, GiftLinkToken, giftLinkCodec, type GiftLink, type Post} from './model';
+import {GiftLinkRow, GiftLinkToken, giftLinkCodec, type GiftLink, type Post, type PostRef} from './model';
import * as queries from './queries';
export function generateGiftLinkToken(): GiftLinkToken {
@@ -20,9 +20,12 @@ export class GiftLinksService {
return this.requirePost(postId);
}
- async getPostByToken(token: string): Promise {
+ // Resolves a live token to the post it unlocks (id + type). Live links only:
+ // a revoked/reissued/unknown token resolves to null, which the reader path
+ // treats as "invalid → 301 to canonical".
+ async getPostByToken(token: string): Promise {
const row = await this.run(queries.liveLinkForToken(token));
- return row ? {id: row.post_id, giftLinks: [z.decode(giftLinkCodec, row)]} : null;
+ return row ? {id: row.post_id, type: row.post_type} : null;
}
async issue(postId: string): Promise {
diff --git a/ghost/core/core/server/services/members/service.js b/ghost/core/core/server/services/members/service.js
index 2e9d26b604d..76820957c80 100644
--- a/ghost/core/core/server/services/members/service.js
+++ b/ghost/core/core/server/services/members/service.js
@@ -171,6 +171,10 @@ module.exports = {
},
contentGating: require('./content-gating'),
+ // Synthesize an "all active paid tiers" member for rendering gated content
+ // without a logged-in member (post previews, gift-links reader path).
+ synthesizePaidMember: require('./synthesize-member'),
+
config: membersConfig,
get api() {
diff --git a/ghost/core/core/server/services/members/synthesize-member.js b/ghost/core/core/server/services/members/synthesize-member.js
new file mode 100644
index 00000000000..a44226efc20
--- /dev/null
+++ b/ghost/core/core/server/services/members/synthesize-member.js
@@ -0,0 +1,32 @@
+const models = require('../../models');
+const logging = require('@tryghost/logging');
+
+/**
+ * Synthesize a minimal "all active paid tiers" member for content gating.
+ *
+ * This is NOT a real member — it carries only the `status` and `products` that
+ * `contentGating.checkPostAccess` / `checkGatedBlockAccess` read, so a caller
+ * can render gated content as if served to a paying subscriber without there
+ * being a logged-in member. Used by post previews (`member_status=paid`) and by
+ * the gift-links reader path (`/g/`), which is why it lives here as the single
+ * source of truth for this security-sensitive grant.
+ *
+ * @returns {Promise<{status: 'paid', products: Array<{slug: string}>}>}
+ */
+module.exports = async function synthesizePaidMember() {
+ let products = [];
+ try {
+ const paidProducts = await models.Product.findAll({status: 'active', type: 'paid'});
+ products = paidProducts.map(product => ({slug: product.get('slug')}));
+ } catch (error) {
+ // Don't fail the render on a tier lookup error — fall back to no tiers
+ // (which still grants `status:paid` / `members` content, just not
+ // tier-specific blocks).
+ logging.error('Failed to synthesize paid member tiers:', error);
+ }
+
+ return {
+ status: 'paid',
+ products
+ };
+};
diff --git a/ghost/core/package.json b/ghost/core/package.json
index 7a0c3ab52d0..06b1ecd973a 100644
--- a/ghost/core/package.json
+++ b/ghost/core/package.json
@@ -194,6 +194,7 @@
"image-size": "1.2.1",
"intl": "1.2.5",
"intl-messageformat": "5.4.3",
+ "isbot": "catalog:",
"js-yaml": "4.2.0",
"jsdom": "catalog:",
"jsonc-parser": "catalog:",
diff --git a/ghost/core/test/e2e-frontend/gift-links-toast-override.test.ts b/ghost/core/test/e2e-frontend/gift-links-toast-override.test.ts
new file mode 100644
index 00000000000..72de5386638
--- /dev/null
+++ b/ghost/core/test/e2e-frontend/gift-links-toast-override.test.ts
@@ -0,0 +1,106 @@
+import assert from 'node:assert/strict';
+import path from 'node:path';
+import sinon from 'sinon';
+import supertest from 'supertest';
+import moment from 'moment';
+import {afterAll, beforeAll} from 'vitest';
+
+const fs = require('fs-extra');
+const testUtils = require('../utils');
+const configUtils = require('../utils/config-utils');
+const settingsCache = require('../../core/shared/settings-cache');
+const themeActivator = require('../../core/server/services/themes/activate');
+
+const OVERRIDE_THEME = 'gift-toast-override-theme';
+
+// A theme's `partials/` dir is only registered (and thus able to override the
+// core `gift-toast` partial) when the theme references at least one partial:
+// gscan only reports referenced partials, and `active.partialsPath` is null
+// otherwise. So the theme references a `{{> marker}}` partial from its layout.
+const THEME_FILES: Record = {
+ 'package.json': JSON.stringify({
+ name: OVERRIDE_THEME,
+ description: 'Theme that overrides the default gift-toast partial',
+ version: '1.0.0',
+ license: 'MIT',
+ config: {posts_per_page: 25}
+ }),
+ 'index.hbs': '',
+ 'default.hbs': [
+ '',
+ '',
+ '{{ghost_head}}',
+ '',
+ ' {{> marker}}',
+ ' {{{body}}}',
+ ' {{ghost_foot}}',
+ '',
+ ''
+ ].join('\n'),
+ 'post.hbs': '{{!< default}}\n{{#post}}{{content}}
{{/post}}',
+ 'partials/marker.hbs': 'override theme ',
+ 'partials/gift-toast.hbs': 'Custom theme gift toast
'
+};
+
+// Generates the override theme on disk and activates it at runtime — kept out of
+// the shared test/utils/fixtures/themes dir so the admin themes-list fixtures
+// (and the tests that enumerate them) are unaffected.
+async function writeAndActivateOverrideTheme() {
+ const themesPath = configUtils.config.getContentPath('themes');
+ for (const [relPath, contents] of Object.entries(THEME_FILES)) {
+ const filePath = path.join(themesPath, OVERRIDE_THEME, relPath);
+ await fs.ensureDir(path.dirname(filePath));
+ await fs.writeFile(filePath, contents);
+ }
+ await themeActivator.loadAndActivate(OVERRIDE_THEME);
+}
+
+describe('Front-end gift links — theme toast override', function () {
+ let request: any;
+ let token: string;
+ const slug = 'gift-override-paid-post';
+
+ beforeAll(async function () {
+ const originalSettingsCacheGetFn = settingsCache.get;
+ sinon.stub(settingsCache, 'get').callsFake(function (key: any, options: any) {
+ if (key === 'labs') {
+ return {members: true, giftLinks: true};
+ }
+ if (key === 'active_theme') {
+ return OVERRIDE_THEME;
+ }
+ return originalSettingsCacheGetFn(key, options);
+ });
+
+ await testUtils.startGhost({copyThemes: true});
+ await writeAndActivateOverrideTheme();
+
+ const paidPost = testUtils.DataGenerator.forKnex.createPost({
+ slug,
+ visibility: 'paid',
+ status: 'published',
+ published_at: moment().toDate(),
+ mobiledoc: testUtils.DataGenerator.markdownToMobiledoc('Before paywall\n\n\n\nAfter paywall')
+ });
+ await testUtils.fixtures.insertPosts([paidPost]);
+
+ const giftLinksService = require('../../core/server/services/gift-links');
+ const post = await giftLinksService.service.issue(paidPost.id);
+ token = post.giftLinks[0].token;
+
+ request = supertest.agent(configUtils.config.get('url'));
+ });
+
+ afterAll(function () {
+ sinon.restore();
+ });
+
+ it('renders the theme partials/gift-toast.hbs instead of the core default', async function () {
+ const res = await request
+ .get(`/g/${slug}/?key=${token}`)
+ .expect(200);
+
+ assert.match(res.text, /id="custom-gift-toast"/, 'theme partial overrides the default toast');
+ assert.doesNotMatch(res.text, /id="gh-gift-toast"/, 'core default toast is not rendered when overridden');
+ });
+});
diff --git a/ghost/core/test/e2e-frontend/gift-links.test.ts b/ghost/core/test/e2e-frontend/gift-links.test.ts
new file mode 100644
index 00000000000..8083e221f8c
--- /dev/null
+++ b/ghost/core/test/e2e-frontend/gift-links.test.ts
@@ -0,0 +1,199 @@
+import assert from 'node:assert/strict';
+import sinon from 'sinon';
+import supertest from 'supertest';
+import moment from 'moment';
+// This suite runs under vitest; beforeAll/afterAll aren't in the mocha globals
+// that tsc resolves project-wide, so import them explicitly for type-checking.
+import {afterAll, beforeAll} from 'vitest';
+
+const testUtils = require('../utils');
+const configUtils = require('../utils/config-utils');
+const settingsCache = require('../../core/shared/settings-cache');
+const models = require('../../core/server/models');
+
+const HUMAN_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15';
+
+// The redemption counter lives directly on the gift_links row (no Bookshelf
+// model), and the write is fire-and-forget, so poll briefly off the table.
+async function pollRedeemedCount(token: string, atLeast: number, tries = 30): Promise {
+ const knex = models.Base.knex;
+ for (let i = 0; i < tries; i += 1) {
+ const link = await knex('gift_links').where({token}).first();
+ if (link && link.redeemed_count >= atLeast) {
+ return link.redeemed_count;
+ }
+ await new Promise((resolve) => {
+ setTimeout(resolve, 50);
+ });
+ }
+ const link = await knex('gift_links').where({token}).first();
+ return link ? link.redeemed_count : -1;
+}
+
+// Default member-test-theme renders `{{content}}`; a paid post with a
+// `` marker truncates at the paywall when the reader has no
+// access, so "After paywall" is the access tell.
+function assertUnlocked(res: any) {
+ assert.match(res.text, /Before paywall/, 'lead content should render');
+ assert.match(res.text, /After paywall/, 'gated content should render for a valid gift read');
+}
+
+function assertLocked(res: any) {
+ assert.match(res.text, /Before paywall/, 'lead content should render');
+ assert.doesNotMatch(res.text, /After paywall/, 'gated content must not render without access');
+}
+
+describe('Front-end gift links', function () {
+ let request: any;
+ let token: string;
+ const slug = 'gift-me-this-paid-post';
+
+ beforeAll(async function () {
+ const originalSettingsCacheGetFn = settingsCache.get;
+ sinon.stub(settingsCache, 'get').callsFake(function (key: any, options: any) {
+ if (key === 'labs') {
+ return {members: true, giftLinks: true};
+ }
+ if (key === 'active_theme') {
+ return 'members-test-theme';
+ }
+ return originalSettingsCacheGetFn(key, options);
+ });
+
+ await testUtils.startGhost({copyThemes: true});
+
+ const paidPost = testUtils.DataGenerator.forKnex.createPost({
+ slug,
+ visibility: 'paid',
+ status: 'published',
+ published_at: moment().toDate(),
+ mobiledoc: testUtils.DataGenerator.markdownToMobiledoc('Before paywall\n\n\n\nAfter paywall')
+ });
+ await testUtils.fixtures.insertPosts([paidPost]);
+
+ // Mint a live gift link for the paid post.
+ const giftLinksService = require('../../core/server/services/gift-links');
+ const post = await giftLinksService.service.issue(paidPost.id);
+ token = post.giftLinks[0].token;
+
+ request = supertest.agent(configUtils.config.get('url'));
+ });
+
+ afterAll(function () {
+ sinon.restore();
+ });
+
+ it('paywalls the paid post for an anonymous visitor on the canonical URL', async function () {
+ const res = await request
+ .get(`/${slug}/`)
+ .expect(200)
+ .expect(assertLocked);
+
+ // No gift → no toast on the canonical URL.
+ assert.doesNotMatch(res.text, /gh-gift-toast/, 'gift toast must not appear on the canonical URL');
+ });
+
+ it('unlocks the full post for an anonymous visitor with a valid /g/ + key', async function () {
+ const res = await request
+ .get(`/g/${slug}/?key=${token}`)
+ .expect(200)
+ .expect(assertUnlocked);
+
+ // Never cached (no member cookie, path-based bypass) and never indexed.
+ assert.match(res.headers['cache-control'], /no-store/);
+ assert.equal(res.headers['x-robots-tag'], 'noindex');
+ assert.equal(res.headers['referrer-policy'], 'no-referrer');
+
+ // The default gift toast renders on the verified gift view.
+ assert.match(res.text, /id="gh-gift-toast"/, 'default gift toast renders on a gift view');
+ assert.match(res.text, /unlocked this post/, 'toast announces the gift');
+ });
+
+ it('canonicalises a stale slug to the current slug, keeping the key', async function () {
+ // Token is authoritative; the slug is cosmetic. A wrong-but-resolving
+ // token must redirect to the token's own current slug, preserving the
+ // key so the post still opens.
+ const res = await request
+ .get(`/g/some-old-slug/?key=${token}`)
+ .redirects(0)
+ .expect(301);
+
+ assert.equal(res.headers.location, `/g/${slug}/?key=${token}`);
+ assert.match(res.headers['cache-control'], /no-store/);
+ });
+
+ it('301s to the canonical URL when the gift token is invalid, dropping the key', async function () {
+ const res = await request
+ .get(`/g/${slug}/?key=not-a-real-token`)
+ .redirects(0)
+ .expect(301);
+
+ assert.match(res.headers.location, new RegExp(`/${slug}/?$`));
+ assert.doesNotMatch(res.headers.location, /key=/);
+ // The 301 itself must not be cached, so a reset link can't poison
+ // subsequent requests via a stale browser-cached redirect.
+ assert.match(res.headers['cache-control'], /no-store/);
+ });
+
+ it('301s to the canonical URL when no key is provided', async function () {
+ const res = await request
+ .get(`/g/${slug}/`)
+ .redirects(0)
+ .expect(301);
+
+ assert.match(res.headers.location, new RegExp(`/${slug}/?$`));
+ assert.match(res.headers['cache-control'], /no-store/);
+ });
+
+ it('404s when the URL slug does not resolve and the token is invalid', async function () {
+ const res = await request
+ .get(`/g/no-such-slug/?key=not-a-real-token`)
+ .redirects(0)
+ .expect(404);
+
+ assert.doesNotMatch(res.text, /After paywall/, 'no gated content leaks on the 404 path');
+ });
+
+ it('counts a human gift read once and de-dupes repeat views via the ghost-gift-seen cookie', async function () {
+ // Baseline-relative so the assertion doesn't depend on whether earlier
+ // tests' reads were counted (UA classification of the default agent).
+ const baseline = await pollRedeemedCount(token, 0, 6);
+ // Fresh client (no prior cookie) with a real browser UA so it isn't bot-filtered.
+ const humanAgent = supertest.agent(configUtils.config.get('url'));
+
+ const res = await humanAgent
+ .get(`/g/${slug}/?key=${token}`)
+ .set('User-Agent', HUMAN_UA)
+ .expect(200);
+
+ const setCookie = ((res.headers['set-cookie'] as unknown as string[]) || []).join(';');
+ assert.match(setCookie, /ghost-gift-seen-/, 'sets the per-post de-dup cookie');
+
+ // The count is incremented out-of-band (non-blocking), so poll briefly.
+ assert.equal(await pollRedeemedCount(token, baseline + 1), baseline + 1);
+
+ // A second view from the same client must NOT double-count (cookie matches token).
+ await humanAgent
+ .get(`/g/${slug}/?key=${token}`)
+ .set('User-Agent', HUMAN_UA)
+ .expect(200);
+ assert.equal(await pollRedeemedCount(token, baseline + 1), baseline + 1, 'repeat view from same client is de-duped');
+ });
+
+ it('does not count a read from a bot user-agent', async function () {
+ const botAgent = supertest.agent(configUtils.config.get('url'));
+ // Self-contained: capture the current count, then assert a bot read
+ // doesn't bump it (rather than depending on a prior test's value).
+ const baseline = await pollRedeemedCount(token, 0, 6);
+
+ await botAgent
+ .get(`/g/${slug}/?key=${token}`)
+ .set('User-Agent', 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)')
+ .expect(200);
+
+ // Give any (erroneous) async count write a chance to land, then confirm
+ // the count is unchanged.
+ const count = await pollRedeemedCount(token, baseline + 1, 6);
+ assert.equal(count, baseline, 'bot read is not counted');
+ });
+});
diff --git a/ghost/core/test/integration/services/gift-links.test.ts b/ghost/core/test/integration/services/gift-links.test.ts
index ebc11c88ffc..a5a4a65f1e9 100644
--- a/ghost/core/test/integration/services/gift-links.test.ts
+++ b/ghost/core/test/integration/services/gift-links.test.ts
@@ -117,12 +117,13 @@ describe('GiftLinksService (integration)', function () {
});
describe('getPostByToken', function () {
- it('resolves a live token, and stops resolving once reissued', async function () {
+ it('resolves a live token to the post id and type, and stops resolving once reissued', async function () {
const original = await service.issue(postId);
const token = original.giftLinks[0]!.token;
const found = await service.getPostByToken(token);
- assert.equal(found?.giftLinks[0]?.token, token);
+ assert.equal(found?.id, postId);
+ assert.equal(found?.type, 'post');
await service.reissue(postId);
assert.equal(await service.getPostByToken(token), null);
@@ -155,9 +156,10 @@ describe('GiftLinksService (integration)', function () {
assert.equal(await service.recordRedemption(token), 1);
assert.equal(await service.recordRedemption(token), 1);
- const reloaded = await service.getPostByToken(token);
- assert.equal(reloaded?.giftLinks[0]?.redeemedCount, 2);
- assert.notEqual(reloaded?.giftLinks[0]?.lastRedeemedAt, null);
+ const reloaded = await models.Base.knex('gift_links').where({token}).first();
+ assert.ok(reloaded, 'gift_links row should exist for the issued token');
+ assert.equal(reloaded.redeemed_count, 2);
+ assert.notEqual(reloaded.last_redeemed_at, null);
});
it('counts a read against a since-reissued token (history is retained)', async function () {
diff --git a/ghost/core/test/unit/frontend/services/routing/controllers/gift-links.test.ts b/ghost/core/test/unit/frontend/services/routing/controllers/gift-links.test.ts
new file mode 100644
index 00000000000..49de4ffc61b
--- /dev/null
+++ b/ghost/core/test/unit/frontend/services/routing/controllers/gift-links.test.ts
@@ -0,0 +1,211 @@
+import sinon from 'sinon';
+
+const errors = require('@tryghost/errors');
+const labs = require('../../../../../../core/shared/labs');
+const urlUtils = require('../../../../../../core/shared/url-utils');
+const proxy = require('../../../../../../core/frontend/services/proxy');
+const renderer = require('../../../../../../core/frontend/services/rendering');
+const giftLinksController = require('../../../../../../core/frontend/services/routing/controllers/gift-links') as (_req: any, _res: any, _next: any) => Promise;
+
+describe('Unit - services/routing/controllers/gift-links', function () {
+ let req: any;
+ let res: any;
+ let next: any;
+ let postsReadStub: any;
+ let pagesReadStub: any;
+ let getPostByTokenStub: any;
+ let recordReadStub: any;
+ let renderEntryInner: any;
+ let handleErrorStub: any;
+
+ beforeEach(function () {
+ sinon.stub(labs, 'isSet').withArgs('giftLinks').returns(true);
+
+ postsReadStub = sinon.stub().resolves({posts: []});
+ pagesReadStub = sinon.stub().resolves({pages: []});
+ sinon.stub(proxy, 'api').value({
+ postsPublic: {read: postsReadStub},
+ pagesPublic: {read: pagesReadStub}
+ });
+
+ getPostByTokenStub = sinon.stub().resolves(null);
+ recordReadStub = sinon.spy();
+ sinon.stub(proxy, 'giftLinks').value({
+ service: {getPostByToken: getPostByTokenStub},
+ recordRead: recordReadStub
+ });
+
+ // `synthesizePaidMember` is a lazy getter on the proxy; stub it to return
+ // the synthesizing function.
+ sinon.stub(proxy, 'synthesizePaidMember').get(() => async () => ({status: 'paid', products: []}));
+
+ // `renderer.renderEntry` is a getter returning a curried
+ // `renderEntry(req, res)(entry)`; stub the getter to capture the entry.
+ renderEntryInner = sinon.spy();
+ sinon.stub(renderer, 'renderEntry').get(() => () => renderEntryInner);
+
+ // `renderer.handleError` getter returns a curried `handleError(next)(err)`.
+ handleErrorStub = sinon.spy();
+ sinon.stub(renderer, 'handleError').get(() => () => handleErrorStub);
+
+ req = {
+ params: {slug: 'my-post'},
+ path: '/g/my-post/',
+ query: {}
+ };
+ res = {
+ locals: {},
+ routerOptions: {},
+ set: sinon.spy(),
+ redirect: sinon.spy()
+ };
+ next = sinon.spy();
+ });
+
+ afterEach(function () {
+ sinon.restore();
+ });
+
+ it('no-ops (next) when the giftLinks flag is off', async function () {
+ labs.isSet.restore();
+ sinon.stub(labs, 'isSet').withArgs('giftLinks').returns(false);
+
+ await giftLinksController(req, res, next);
+
+ sinon.assert.calledOnce(next);
+ sinon.assert.notCalled(getPostByTokenStub);
+ });
+
+ describe('valid token', function () {
+ beforeEach(function () {
+ req.query.key = 'good-token';
+ getPostByTokenStub.resolves({id: 'post-id-1', type: 'post'});
+ });
+
+ it('renders the unlocked entry with noindex/no-referrer when the slug matches', async function () {
+ postsReadStub.resolves({posts: [{slug: 'my-post', url: 'https://example.com/my-post/'}]});
+
+ await giftLinksController(req, res, next);
+
+ // Single by-id read through the public posts API with a synthetic member.
+ sinon.assert.calledOnce(postsReadStub);
+ const readArg = postsReadStub.firstCall.args[0];
+ sinon.assert.match(readArg.id, 'post-id-1');
+ sinon.assert.match(readArg.context.member.status, 'paid');
+
+ sinon.assert.calledWith(res.set, 'X-Robots-Tag', 'noindex');
+ sinon.assert.calledWith(res.set, 'Referrer-Policy', 'no-referrer');
+ // Read counted on the verified render path with the token + post id.
+ sinon.assert.calledOnceWithExactly(recordReadStub, req, res, {token: 'good-token', postId: 'post-id-1'});
+ sinon.assert.calledOnce(renderEntryInner);
+ sinon.assert.notCalled(res.redirect);
+ sinon.assert.notCalled(next);
+ });
+
+ it('canonicalises a stale slug to the current slug, keeping the key', async function () {
+ req.params.slug = 'old-slug';
+ postsReadStub.resolves({posts: [{slug: 'my-post', url: 'https://example.com/my-post/'}]});
+
+ await giftLinksController(req, res, next);
+
+ sinon.assert.calledWith(res.redirect, 301, '/g/my-post/?key=good-token');
+ sinon.assert.notCalled(renderEntryInner);
+ // A redirect is not a read — must not count.
+ sinon.assert.notCalled(recordReadStub);
+ });
+
+ it('reads pages via pagesPublic when the token resolves to a page', async function () {
+ getPostByTokenStub.resolves({id: 'page-id-1', type: 'page'});
+ pagesReadStub.resolves({pages: [{slug: 'my-post', url: 'https://example.com/my-post/'}]});
+
+ await giftLinksController(req, res, next);
+
+ sinon.assert.calledOnce(pagesReadStub);
+ sinon.assert.notCalled(postsReadStub);
+ sinon.assert.calledOnce(renderEntryInner);
+ });
+
+ it('falls through to the canonical redirect when the post is gone (NotFound on read)', async function () {
+ // Token resolves but the post was unpublished/deleted → by-id read
+ // 404s. Fall back to resolving the URL slug; here it resolves to a
+ // normal canonical URL, so redirect with the key dropped.
+ postsReadStub.onFirstCall().rejects(new errors.NotFoundError({message: 'gone'}));
+ postsReadStub.onSecondCall().resolves({posts: [{slug: 'my-post', url: 'https://example.com/my-post/'}]});
+
+ await giftLinksController(req, res, next);
+
+ sinon.assert.notCalled(renderEntryInner);
+ sinon.assert.calledWith(res.redirect, 301, 'https://example.com/my-post/');
+ });
+ });
+
+ describe('invalid / missing token (fallback path)', function () {
+ it('redirects to the canonical URL when it is NOT under /g/', async function () {
+ postsReadStub.resolves({posts: [{slug: 'my-post', url: 'https://example.com/my-post/'}]});
+
+ await giftLinksController(req, res, next);
+
+ sinon.assert.calledWith(res.redirect, 301, 'https://example.com/my-post/');
+ sinon.assert.notCalled(next);
+ });
+
+ it('falls through to 404 instead of redirecting to a /g/ canonical URL (no self-redirect loop)', async function () {
+ postsReadStub.resolves({posts: [{slug: 'my-post', url: 'https://example.com/g/my-post/'}]});
+
+ await giftLinksController(req, res, next);
+
+ sinon.assert.notCalled(res.redirect);
+ sinon.assert.calledOnce(next);
+ });
+
+ it('falls through to 404 on a subdirectory install when the canonical URL is under /g/', async function () {
+ // Regression: the loop guard must account for the configured
+ // subdirectory. req.path is mount-relative ('/g/my-post/') but the
+ // canonical pathname includes the subdir ('/blog/g/my-post/'); a
+ // bare '/g/' prefix check would miss it and redirect into a loop.
+ sinon.stub(urlUtils, 'getSubdir').returns('/blog');
+ postsReadStub.resolves({posts: [{slug: 'my-post', url: 'https://example.com/blog/g/my-post/'}]});
+
+ await giftLinksController(req, res, next);
+
+ sinon.assert.notCalled(res.redirect);
+ sinon.assert.calledOnce(next);
+ });
+
+ it('falls through to 404 when the canonical URL equals the current request path', async function () {
+ postsReadStub.resolves({posts: [{slug: 'my-post', url: 'https://example.com/g/my-post/'}]});
+ req.path = '/g/my-post/';
+
+ await giftLinksController(req, res, next);
+
+ sinon.assert.notCalled(res.redirect);
+ sinon.assert.calledOnce(next);
+ });
+
+ it('falls through to 404 when the slug resolves to nothing', async function () {
+ await giftLinksController(req, res, next);
+
+ sinon.assert.notCalled(res.redirect);
+ sinon.assert.calledOnce(next);
+ });
+
+ it('ignores a non-string key (array/object query value)', async function () {
+ req.query.key = ['a', 'b'];
+
+ await giftLinksController(req, res, next);
+
+ sinon.assert.notCalled(getPostByTokenStub);
+ sinon.assert.calledOnce(next);
+ });
+
+ it('re-throws a non-not-found error to the outer error handler instead of 301-to-canonical', async function () {
+ const boom = new errors.InternalServerError({message: 'db timeout'});
+ postsReadStub.rejects(boom);
+
+ await giftLinksController(req, res, next);
+
+ sinon.assert.notCalled(res.redirect);
+ sinon.assert.calledOnceWithExactly(handleErrorStub, boom);
+ });
+ });
+});
diff --git a/ghost/core/test/unit/server/services/gift-links/is-bot-user-agent.test.ts b/ghost/core/test/unit/server/services/gift-links/is-bot-user-agent.test.ts
new file mode 100644
index 00000000000..70890ef36ca
--- /dev/null
+++ b/ghost/core/test/unit/server/services/gift-links/is-bot-user-agent.test.ts
@@ -0,0 +1,29 @@
+import assert from 'node:assert/strict';
+
+const isBotUserAgent = require('../../../../../core/server/services/gift-links/is-bot-user-agent') as (_ua: string | undefined | null) => boolean;
+
+describe('Unit - gift-links/is-bot-user-agent', function () {
+ it('treats a missing/empty/non-string UA as a bot (conservative)', function () {
+ assert.equal(isBotUserAgent(undefined), true);
+ assert.equal(isBotUserAgent(null), true);
+ assert.equal(isBotUserAgent(''), true);
+ assert.equal(isBotUserAgent(123 as unknown as string), true);
+ });
+
+ it('flags well-known crawlers and scanners', function () {
+ assert.equal(isBotUserAgent('Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'), true);
+ assert.equal(isBotUserAgent('facebookexternalhit/1.1'), true);
+ assert.equal(isBotUserAgent('Twitterbot/1.0'), true);
+ assert.equal(isBotUserAgent('curl/8.4.0'), true);
+ });
+
+ it('does NOT flag real humans, including social in-app browsers', function () {
+ assert.equal(isBotUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15'), false);
+ assert.equal(isBotUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 [LinkedInApp]'), false);
+ });
+
+ it('does not false-positive on device UAs that merely contain "bot" as a substring', function () {
+ // Regression guard for word-boundary matching: "CUBOT" is a phone brand.
+ assert.equal(isBotUserAgent('Mozilla/5.0 (Linux; Android 10; CUBOT_X30) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Mobile Safari/537.36'), false);
+ });
+});
diff --git a/ghost/core/test/unit/server/services/gift-links/read-counter.test.ts b/ghost/core/test/unit/server/services/gift-links/read-counter.test.ts
new file mode 100644
index 00000000000..a3802ab76ce
--- /dev/null
+++ b/ghost/core/test/unit/server/services/gift-links/read-counter.test.ts
@@ -0,0 +1,116 @@
+import assert from 'node:assert/strict';
+import sinon from 'sinon';
+
+const giftLinks = require('../../../../../core/server/services/gift-links');
+const recordRead = require('../../../../../core/server/services/gift-links/read-counter') as (_req: any, _res: any, _read: {token: string; postId: string}) => void;
+const urlUtils = require('../../../../../core/shared/url-utils');
+
+const HUMAN_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15';
+const BOT_UA = 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)';
+
+const POST_ID = '6123456789abcdef01234567';
+const TOKEN = 'live-token-abc';
+const COOKIE_NAME = `ghost-gift-seen-${POST_ID}`;
+
+function makeReq({ua, cookie}: {ua?: string; cookie?: string} = {}): any {
+ return {
+ get: (header: string) => (header.toLowerCase() === 'user-agent' ? ua : undefined),
+ headers: cookie ? {cookie} : {},
+ originalUrl: '/g/my-post/?key=' + TOKEN
+ };
+}
+
+function makeRes(): any {
+ const headers: Record = {};
+ return {
+ getHeader: (k: string) => headers[k],
+ setHeader: (k: string, v: unknown) => {
+ headers[k] = v;
+ },
+ _headers: headers
+ };
+}
+
+function setCookieHeader(res: any): string {
+ const raw = res.getHeader('Set-Cookie') || [];
+ return (Array.isArray(raw) ? raw : [raw]).join('\n');
+}
+
+describe('Unit - gift-links/read-counter', function () {
+ let recordRedemption: sinon.SinonStub;
+
+ beforeEach(function () {
+ recordRedemption = sinon.stub().resolves(1);
+ giftLinks.setService({recordRedemption});
+ });
+
+ afterEach(function () {
+ giftLinks.setService(undefined);
+ sinon.restore();
+ });
+
+ it('does not count bot/scanner reads', function () {
+ const res = makeRes();
+ recordRead(makeReq({ua: BOT_UA}), res, {token: TOKEN, postId: POST_ID});
+
+ sinon.assert.notCalled(recordRedemption);
+ assert.equal(res.getHeader('Set-Cookie'), undefined, 'must not set the dedup cookie for bots');
+ });
+
+ it('counts a human read once and sets a long-lived per-post dedup cookie', function () {
+ const res = makeRes();
+ recordRead(makeReq({ua: HUMAN_UA}), res, {token: TOKEN, postId: POST_ID});
+
+ sinon.assert.calledOnceWithExactly(recordRedemption, TOKEN);
+
+ const setCookie = setCookieHeader(res);
+ assert.match(setCookie, new RegExp(`${COOKIE_NAME}=${TOKEN}`), 'cookie name is post-scoped, value is the token');
+ assert.match(setCookie, /httponly/i);
+ assert.match(setCookie, /samesite=lax/i);
+ // Scoped to the /g/ prefix, not the slug path, so dedup survives a slug
+ // rename (the cookie name already encodes the post id).
+ assert.match(setCookie, /path=\/g\//i);
+ assert.doesNotMatch(setCookie, /path=\/g\/my-post\//i);
+ // Long-lived (≈1 year) so dedup survives across browser sessions.
+ const expiresMatch = setCookie.match(/expires=([^;]+)/i);
+ assert.ok(expiresMatch, 'cookie carries an explicit expiry (not a session cookie)');
+ const expiresAt = new Date(expiresMatch![1]);
+ assert.ok(expiresAt.getTime() > Date.now() + 300 * 24 * 60 * 60 * 1000, 'expiry is far in the future');
+ });
+
+ it('de-dupes a repeat view from the same client (cookie already matches the token)', function () {
+ const res = makeRes();
+ recordRead(makeReq({ua: HUMAN_UA, cookie: `${COOKIE_NAME}=${TOKEN}`}), res, {token: TOKEN, postId: POST_ID});
+
+ sinon.assert.notCalled(recordRedemption);
+ });
+
+ it('still counts when the existing cookie is for a different token (re-issued link)', function () {
+ const res = makeRes();
+ recordRead(makeReq({ua: HUMAN_UA, cookie: `${COOKIE_NAME}=an-older-token`}), res, {token: TOKEN, postId: POST_ID});
+
+ sinon.assert.calledOnceWithExactly(recordRedemption, TOKEN);
+ });
+
+ it('marks the cookie Secure behind a TLS-terminating proxy without throwing (constructor-level secure)', function () {
+ // Site is https but the proxy hands origin a plain http request: passing
+ // `secure` per-`.set()` would throw and skip the count. Constructor-level
+ // secure (what the counter uses) must set Secure and still count.
+ // `urlUtils.isSSL` is a getter that returns the isSSL function, so stub
+ // the getter to hand back a function that reports the site as https.
+ sinon.stub(urlUtils, 'isSSL').get(() => () => true);
+ const res = makeRes();
+
+ recordRead(makeReq({ua: HUMAN_UA}), res, {token: TOKEN, postId: POST_ID});
+
+ sinon.assert.calledOnceWithExactly(recordRedemption, TOKEN);
+ assert.match(setCookieHeader(res), /secure/i, 'cookie is marked Secure');
+ });
+
+ it('never throws out of recordRead even if the redemption write rejects', function () {
+ recordRedemption.rejects(new Error('db down'));
+ const res = makeRes();
+
+ assert.doesNotThrow(() => recordRead(makeReq({ua: HUMAN_UA}), res, {token: TOKEN, postId: POST_ID}));
+ });
+});
diff --git a/ghost/core/test/unit/server/services/members/synthesize-member.test.ts b/ghost/core/test/unit/server/services/members/synthesize-member.test.ts
new file mode 100644
index 00000000000..e107efffa22
--- /dev/null
+++ b/ghost/core/test/unit/server/services/members/synthesize-member.test.ts
@@ -0,0 +1,36 @@
+import assert from 'node:assert/strict';
+import sinon from 'sinon';
+
+const models = require('../../../../../core/server/models');
+const logging = require('@tryghost/logging');
+const synthesizePaidMember = require('../../../../../core/server/services/members/synthesize-member') as () => Promise<{status: string; products: Array<{slug: string}>}>;
+
+describe('Unit - members/synthesize-member', function () {
+ afterEach(function () {
+ sinon.restore();
+ });
+
+ it('returns a paid member with every active paid tier', async function () {
+ sinon.stub(models.Product, 'findAll').resolves([
+ {get: (key: string) => (key === 'slug' ? 'silver' : undefined)},
+ {get: (key: string) => (key === 'slug' ? 'gold' : undefined)}
+ ]);
+
+ const member = await synthesizePaidMember();
+
+ assert.deepEqual(member, {status: 'paid', products: [{slug: 'silver'}, {slug: 'gold'}]});
+ });
+
+ it('falls back to a paid member with no tiers (never throws) when the tier lookup fails', async function () {
+ const logError = sinon.stub(logging, 'error');
+ sinon.stub(models.Product, 'findAll').rejects(new Error('db down'));
+
+ const member = await synthesizePaidMember();
+
+ // Security-sensitive: must still resolve to a safe member shape rather
+ // than throwing — granting status:paid/members content but no
+ // tier-specific blocks.
+ assert.deepEqual(member, {status: 'paid', products: []});
+ sinon.assert.calledOnce(logError);
+ });
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b6bdcbea1d2..cb5158d7ed9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -198,6 +198,9 @@ catalogs:
glob:
specifier: 13.0.6
version: 13.0.6
+ isbot:
+ specifier: ^5.1.42
+ version: 5.1.43
jsdom:
specifier: 29.1.1
version: 29.1.1
@@ -369,19 +372,19 @@ importers:
version: 1.60.0
'@secretlint/secretlint-rule-pattern':
specifier: 13.0.2
- version: 13.0.2
+ version: 13.0.2(supports-color@5.5.0)
'@secretlint/secretlint-rule-preset-recommend':
specifier: 13.0.2
version: 13.0.2
eslint:
specifier: 'catalog:'
- version: 8.57.1
+ version: 8.57.1(supports-color@5.5.0)
eslint-plugin-ghost:
specifier: 3.5.0
- version: 3.5.0(@babel/core@7.29.7)(eslint@8.57.1)
+ version: 3.5.0(@babel/core@7.29.7)(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)
eslint-plugin-react:
specifier: 7.37.5
- version: 7.37.5(eslint@8.57.1)
+ version: 7.37.5(eslint@8.57.1(supports-color@5.5.0))
husky:
specifier: 9.1.7
version: 9.1.7
@@ -405,7 +408,7 @@ importers:
version: 6.1.3
secretlint:
specifier: 13.0.2
- version: 13.0.2
+ version: 13.0.2(supports-color@5.5.0)
semver:
specifier: 7.8.4
version: 7.8.4
@@ -481,10 +484,10 @@ importers:
version: 18.3.7(@types/react@18.3.31)
'@typescript-eslint/eslint-plugin':
specifier: 'catalog:'
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
+ version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3))(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/parser':
specifier: 'catalog:'
- version: 8.49.0(eslint@8.57.1)(typescript@5.9.3)
+ version: 8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
eslint:
specifier: 'catalog:'
version: 8.57.1
@@ -581,16 +584,16 @@ importers:
version: 4.1.0(@swc/helpers@0.5.21)(vite@7.3.2(@types/node@22.19.21)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
eslint:
specifier: catalog:eslint9
- version: 9.39.4(jiti@2.7.0)
+ version: 9.39.4(jiti@2.7.0)(supports-color@5.5.0)
eslint-plugin-no-relative-import-paths:
specifier: 1.6.1
version: 1.6.1
eslint-plugin-react-hooks:
specifier: 'catalog:'
- version: 5.2.0(eslint@9.39.4(jiti@2.7.0))
+ version: 5.2.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))
eslint-plugin-react-refresh:
specifier: 'catalog:'
- version: 0.5.2(eslint@9.39.4(jiti@2.7.0))
+ version: 0.5.2(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))
eslint-plugin-tailwindcss:
specifier: 'catalog:'
version: 4.0.0-beta.0(tailwindcss@4.2.2)
@@ -617,13 +620,13 @@ importers:
version: 5.9.3
typescript-eslint:
specifier: 8.58.0
- version: 8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
+ version: 8.58.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)
vite:
specifier: 'catalog:'
version: 7.3.2(@types/node@22.19.21)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)
vite-tsconfig-paths:
specifier: 6.1.1
- version: 6.1.1(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.21)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 6.1.1(supports-color@5.5.0)(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.21)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
vitest:
specifier: 'catalog:'
version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@22.19.21)(@vitest/coverage-v8@4.1.8)(jsdom@29.1.1(@noble/hashes@1.8.0))(msw@2.14.6(@types/node@22.19.21)(typescript@5.9.3))(vite@7.3.2(@types/node@22.19.21)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
@@ -760,7 +763,7 @@ importers:
version: 13.15.10
'@typescript-eslint/parser':
specifier: 'catalog:'
- version: 8.49.0(eslint@8.57.1)(typescript@5.9.3)
+ version: 8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@vitejs/plugin-react':
specifier: 'catalog:'
version: 4.7.0(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
@@ -986,7 +989,7 @@ importers:
version: 1.5.6
'@tryghost/nql':
specifier: 'catalog:'
- version: 0.13.1
+ version: 0.13.1(supports-color@5.5.0)
'@tryghost/string':
specifier: 'catalog:'
version: 0.3.5
@@ -1062,10 +1065,10 @@ importers:
version: 13.15.10
'@typescript-eslint/eslint-plugin':
specifier: 'catalog:'
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
+ version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3))(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/parser':
specifier: 'catalog:'
- version: 8.49.0(eslint@8.57.1)(typescript@5.9.3)
+ version: 8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@vitest/coverage-v8':
specifier: 'catalog:'
version: 4.1.8(vitest@4.1.8)
@@ -1183,7 +1186,7 @@ importers:
version: 2.26.3(@tiptap/core@2.26.3(@tiptap/pm@2.26.3))(@tiptap/pm@2.26.3)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
'@tryghost/debug':
specifier: 'catalog:'
- version: 2.2.3
+ version: 2.2.3(supports-color@5.5.0)
react:
specifier: catalog:react17
version: 17.0.2
@@ -1208,7 +1211,7 @@ importers:
version: link:../../ghost/i18n
'@tryghost/nql':
specifier: 'catalog:'
- version: 0.13.1
+ version: 0.13.1(supports-color@5.5.0)
'@vitejs/plugin-react':
specifier: 'catalog:'
version: 4.7.0(vite@7.3.2(@types/node@25.9.1)(jiti@1.21.7)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
@@ -1265,7 +1268,7 @@ importers:
dependencies:
'@tryghost/debug':
specifier: 'catalog:'
- version: 2.2.3
+ version: 2.2.3(supports-color@5.5.0)
devDependencies:
'@doist/react-interpolate':
specifier: 2.2.2
@@ -1287,10 +1290,10 @@ importers:
version: link:../../ghost/i18n
'@typescript-eslint/eslint-plugin':
specifier: 8.49.0
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
+ version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3))(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/parser':
specifier: 'catalog:'
- version: 8.49.0(eslint@8.57.1)(typescript@5.9.3)
+ version: 8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@vitejs/plugin-react':
specifier: 'catalog:'
version: 4.7.0(vite@7.3.2(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
@@ -1604,10 +1607,10 @@ importers:
version: 1.6.0
'@typescript-eslint/eslint-plugin':
specifier: 'catalog:'
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
+ version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3))(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/parser':
specifier: 'catalog:'
- version: 8.49.0(eslint@8.57.1)(typescript@5.9.3)
+ version: 8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@vitejs/plugin-react':
specifier: 'catalog:'
version: 4.7.0(vite@7.3.2(@types/node@22.19.21)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
@@ -1628,7 +1631,7 @@ importers:
version: 0.5.2(eslint@8.57.1)
eslint-plugin-storybook:
specifier: 10.4.4
- version: 10.4.4(eslint@8.57.1)(storybook@10.4.4(@testing-library/dom@9.3.4)(@types/react@18.3.31)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.3)
+ version: 10.4.4(eslint@8.57.1)(storybook@10.4.4(@testing-library/dom@9.3.4)(@types/react@18.3.31)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(supports-color@5.5.0)(typescript@5.9.3)
eslint-plugin-tailwindcss:
specifier: 'catalog:'
version: 4.0.0-beta.0(tailwindcss@4.2.2)
@@ -1643,7 +1646,7 @@ importers:
version: 8.5.15
remark-gfm:
specifier: 4.0.1
- version: 4.0.1
+ version: 4.0.1(supports-color@5.5.0)
storybook:
specifier: 'catalog:'
version: 10.4.4(@testing-library/dom@9.3.4)(@types/react@18.3.31)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -1676,7 +1679,7 @@ importers:
dependencies:
'@tryghost/debug':
specifier: 'catalog:'
- version: 2.2.3
+ version: 2.2.3(supports-color@5.5.0)
react:
specifier: 'catalog:'
version: 18.3.1
@@ -1752,7 +1755,7 @@ importers:
dependencies:
'@tryghost/debug':
specifier: 'catalog:'
- version: 2.2.3
+ version: 2.2.3(supports-color@5.5.0)
'@tryghost/i18n':
specifier: workspace:*
version: link:../../ghost/i18n
@@ -1871,10 +1874,10 @@ importers:
version: 2.1.4
'@typescript-eslint/eslint-plugin':
specifier: 'catalog:'
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
+ version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3))(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/parser':
specifier: 'catalog:'
- version: 8.49.0(eslint@8.57.1)(typescript@5.9.3)
+ version: 8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@vitest/coverage-v8':
specifier: 'catalog:'
version: 4.1.8(vitest@4.1.8)
@@ -1922,10 +1925,10 @@ importers:
version: 1.60.0
'@tryghost/debug':
specifier: 'catalog:'
- version: 2.2.3
+ version: 2.2.3(supports-color@5.5.0)
'@tryghost/logging':
specifier: 4.2.1
- version: 4.2.1
+ version: 4.2.1(supports-color@5.5.0)
'@types/dockerode':
specifier: 4.0.1
version: 4.0.1
@@ -1937,7 +1940,7 @@ importers:
version: 1.6.0
dockerode:
specifier: 4.0.12
- version: 4.0.12
+ version: 4.0.12(supports-color@5.5.0)
dotenv:
specifier: 'catalog:'
version: 17.4.2
@@ -1952,7 +1955,7 @@ importers:
version: 2.10.4(eslint@8.57.1)
express:
specifier: 4.22.2
- version: 4.22.2
+ version: 4.22.2(supports-color@1.2.0)
knex:
specifier: 3.1.0
version: 3.1.0(mysql2@3.22.5(@types/node@25.9.1))
@@ -2013,13 +2016,13 @@ importers:
version: 10.4.0
'@glimmer/component':
specifier: 1.1.2
- version: 1.1.2(@babel/core@7.29.7)
+ version: 1.1.2(@babel/core@7.29.7)(supports-color@5.5.0)
'@html-next/vertical-collection':
specifier: 3.0.0
version: 3.0.0(@babel/core@7.29.7)
'@sentry/ember':
specifier: 7.120.3
- version: 7.120.3(webpack@5.105.4(@swc/core@1.15.40(@swc/helpers@0.5.21))(cssnano@4.1.10)(lightningcss@1.32.0)(postcss@8.5.15))
+ version: 7.120.3(supports-color@5.5.0)(webpack@5.105.4(@swc/core@1.15.40(@swc/helpers@0.5.21))(cssnano@4.1.10)(lightningcss@1.32.0)(postcss@8.5.15))
'@sentry/integrations':
specifier: 7.114.0
version: 7.114.0
@@ -2052,7 +2055,7 @@ importers:
version: 1.5.6
'@tryghost/nql':
specifier: 'catalog:'
- version: 0.13.1
+ version: 0.13.1(supports-color@5.5.0)
'@tryghost/string':
specifier: 'catalog:'
version: 0.3.5
@@ -2085,7 +2088,7 @@ importers:
version: 4.2.0
broccoli-terser-sourcemap:
specifier: 4.1.1
- version: 4.1.1
+ version: 4.1.1(supports-color@5.5.0)
chai:
specifier: 'catalog:'
version: 4.5.0
@@ -2118,7 +2121,7 @@ importers:
version: 3.0.1
ember-cli:
specifier: 3.24.0
- version: 3.24.0(encoding@0.1.13)(handlebars@4.7.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(underscore@1.13.8)
+ version: 3.24.0(encoding@0.1.13)(handlebars@4.7.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@1.2.0)(underscore@1.13.8)
ember-cli-app-version:
specifier: 5.0.0
version: 5.0.0
@@ -2133,7 +2136,7 @@ importers:
version: 1.0.3
ember-cli-dependency-checker:
specifier: 3.3.2
- version: 3.3.2(ember-cli@3.24.0(encoding@0.1.13)(handlebars@4.7.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(underscore@1.13.8))
+ version: 3.3.2(ember-cli@3.24.0(encoding@0.1.13)(handlebars@4.7.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@1.2.0)(underscore@1.13.8))
ember-cli-deprecation-workflow:
specifier: 2.2.0
version: 2.2.0
@@ -2166,7 +2169,7 @@ importers:
version: 3.1.0
ember-composable-helpers:
specifier: 5.0.0
- version: 5.0.0
+ version: 5.0.0(supports-color@1.2.0)
ember-concurrency:
specifier: 2.3.7
version: 2.3.7(@babel/core@7.29.7)
@@ -2187,13 +2190,13 @@ importers:
version: 0.4.8(@babel/core@7.29.7)
ember-exam:
specifier: 6.0.1
- version: 6.0.1(ember-mocha@0.16.2(@babel/core@7.29.7))
+ version: 6.0.1(ember-mocha@0.16.2(@babel/core@7.29.7))(supports-color@5.5.0)
ember-export-application-global:
specifier: 2.0.1
version: 2.0.1
ember-fetch:
specifier: 8.1.2
- version: 8.1.2(encoding@0.1.13)
+ version: 8.1.2(encoding@0.1.13)(supports-color@5.5.0)
ember-in-viewport:
specifier: 4.1.0
version: 4.1.0(@babel/core@7.29.7)(ember-source@3.24.0(@babel/core@7.29.7))(webpack@5.105.4(@swc/core@1.15.40(@swc/helpers@0.5.21))(cssnano@4.1.10)(lightningcss@1.32.0)(postcss@8.5.15))
@@ -2208,7 +2211,7 @@ importers:
version: 0.0.17
ember-load-initializers:
specifier: 2.1.2
- version: 2.1.2(@babel/core@7.29.7)
+ version: 2.1.2(@babel/core@7.29.7)(supports-color@5.5.0)
ember-mocha:
specifier: 0.16.2
version: 0.16.2(@babel/core@7.29.7)
@@ -2232,7 +2235,7 @@ importers:
version: 8.1.0(@babel/core@7.29.7)
ember-simple-auth:
specifier: 5.0.0
- version: 5.0.0(ember-fetch@8.1.2(encoding@0.1.13))
+ version: 5.0.0(ember-fetch@8.1.2(encoding@0.1.13)(supports-color@5.5.0))
ember-sinon:
specifier: 5.0.0
version: 5.0.0
@@ -2250,7 +2253,7 @@ importers:
version: 6.0.0
ember-tooltips:
specifier: 3.6.0
- version: 3.6.0
+ version: 3.6.0(supports-color@5.5.0)
ember-truth-helpers:
specifier: 3.1.1
version: 3.1.1
@@ -2394,7 +2397,7 @@ importers:
version: 0.3.35
'@tryghost/debug':
specifier: 'catalog:'
- version: 2.2.3
+ version: 2.2.3(supports-color@5.5.0)
'@tryghost/domain-events':
specifier: 'catalog:'
version: 3.2.5
@@ -2418,7 +2421,7 @@ importers:
version: 1.4.16
'@tryghost/job-manager':
specifier: 1.0.9
- version: 1.0.9
+ version: 1.0.9(supports-color@5.5.0)
'@tryghost/kg-card-factory':
specifier: 5.2.2
version: 5.2.2
@@ -2454,13 +2457,13 @@ importers:
version: 1.5.6
'@tryghost/logging':
specifier: 4.2.1
- version: 4.2.1
+ version: 4.2.1(supports-color@5.5.0)
'@tryghost/members-csv':
specifier: 2.0.7
version: 2.0.7
'@tryghost/metrics':
specifier: 1.0.43
- version: 1.0.43
+ version: 1.0.43(supports-color@5.5.0)
'@tryghost/mongo-utils':
specifier: 0.6.5
version: 0.6.5
@@ -2475,7 +2478,7 @@ importers:
version: 2.3.0(babel-core@6.26.3)(handlebars@4.7.9)(lodash@4.18.1)(underscore@1.13.8)
'@tryghost/nql':
specifier: 'catalog:'
- version: 0.13.1
+ version: 0.13.1(supports-color@5.5.0)
'@tryghost/nql-lang':
specifier: 'catalog:'
version: 0.6.6
@@ -2487,7 +2490,7 @@ importers:
version: 3.2.3
'@tryghost/prometheus-metrics':
specifier: 1.0.8
- version: 1.0.8
+ version: 1.0.8(supports-color@1.2.0)
'@tryghost/promise':
specifier: 2.2.3
version: 2.2.3
@@ -2523,7 +2526,7 @@ importers:
version: 2.2.3
'@tryghost/zip':
specifier: 3.3.4
- version: 3.3.4
+ version: 3.3.4(supports-color@5.5.0)
body-parser:
specifier: 1.20.5
version: 1.20.5
@@ -2535,7 +2538,7 @@ importers:
version: 2.8.0(bookshelf@1.2.0(knex@2.4.2(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7)))
brute-knex:
specifier: 4.0.1
- version: 4.0.1(express@4.22.2)(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7)
+ version: 4.0.1(express@4.22.2)(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7)(supports-color@5.5.0)
bson-objectid:
specifier: 'catalog:'
version: 2.0.4
@@ -2544,7 +2547,7 @@ importers:
version: 4.1.0
cache-manager-ioredis:
specifier: 2.1.0
- version: 2.1.0
+ version: 2.1.0(supports-color@5.5.0)
chalk:
specifier: 4.1.2
version: 4.1.2
@@ -2601,7 +2604,7 @@ importers:
version: 4.5.0
express:
specifier: 4.22.2
- version: 4.22.2
+ version: 4.22.2(supports-color@1.2.0)
express-brute:
specifier: 1.0.1
version: 1.0.1(express@4.22.2)
@@ -2619,13 +2622,13 @@ importers:
version: 2.0.0
express-queue:
specifier: 0.0.13
- version: 0.0.13
+ version: 0.0.13(supports-color@1.2.0)
express-session:
specifier: 1.19.0
- version: 1.19.0
+ version: 1.19.0(supports-color@1.2.0)
file-type:
specifier: 21.3.4
- version: 21.3.4
+ version: 21.3.4(supports-color@5.5.0)
form-data:
specifier: 4.0.6
version: 4.0.6
@@ -2643,7 +2646,7 @@ importers:
version: 13.0.0
gscan:
specifier: 6.3.0
- version: 6.3.0
+ version: 6.3.0(supports-color@5.5.0)
handlebars:
specifier: 4.7.9
version: 4.7.9
@@ -2671,6 +2674,9 @@ importers:
intl-messageformat:
specifier: 5.4.3
version: 5.4.3
+ isbot:
+ specifier: 'catalog:'
+ version: 5.1.43
js-yaml:
specifier: 4.2.0
version: 4.2.0
@@ -2787,7 +2793,7 @@ importers:
version: 0.9.1
probe-image-size:
specifier: 7.3.0
- version: 7.3.0
+ version: 7.3.0(supports-color@1.2.0)
rss:
specifier: 1.2.2
version: 1.2.2
@@ -2811,7 +2817,7 @@ importers:
version: 8.222.0
superagent:
specifier: 5.3.1
- version: 5.3.1
+ version: 5.3.1(supports-color@5.5.0)
superagent-throttle:
specifier: 1.0.1
version: 1.0.1
@@ -2908,7 +2914,7 @@ importers:
version: 6.0.3
'@typescript-eslint/parser':
specifier: 'catalog:'
- version: 8.49.0(eslint@8.57.1)(typescript@5.9.3)
+ version: 8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@vitest/coverage-v8':
specifier: 'catalog:'
version: 4.1.8(vitest@4.1.8)
@@ -2950,7 +2956,7 @@ importers:
version: 2.0.7
jwks-rsa:
specifier: 3.2.2
- version: 3.2.2
+ version: 3.2.2(supports-color@5.5.0)
lodash-es:
specifier: 4.18.1
version: 4.18.1
@@ -2974,7 +2980,7 @@ importers:
version: 22.0.0
supertest:
specifier: 'catalog:'
- version: 7.2.2
+ version: 7.2.2(supports-color@5.5.0)
tmp:
specifier: 0.2.7
version: 0.2.7
@@ -3002,7 +3008,7 @@ importers:
dependencies:
'@tryghost/debug':
specifier: 'catalog:'
- version: 2.2.3
+ version: 2.2.3(supports-color@5.5.0)
i18next:
specifier: 23.16.8
version: 23.16.8
@@ -3037,7 +3043,7 @@ importers:
version: 22.19.21
'@typescript-eslint/parser':
specifier: 'catalog:'
- version: 8.49.0(eslint@8.57.1)(typescript@5.9.3)
+ version: 8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@vitest/coverage-v8':
specifier: 'catalog:'
version: 4.1.8(vitest@4.1.8)
@@ -15717,6 +15723,10 @@ packages:
resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==}
engines: {node: '>= 8.0.0'}
+ isbot@5.1.43:
+ resolution: {integrity: sha512-drJhFmibra4LO6Wd7D3Oi6UICRK9244vSZkmxzhlZP0TTdwCA2ueK4PEkUkzPYeuqug9+cqqdWPgihjk5+83Cg==}
+ engines: {node: '>=18'}
+
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
@@ -22612,6 +22622,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/eslint-parser@7.28.6(@babel/core@7.29.7)(eslint@8.57.1(supports-color@5.5.0))':
+ dependencies:
+ '@babel/core': 7.29.7
+ '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1
+ eslint: 8.57.1(supports-color@5.5.0)
+ eslint-visitor-keys: 2.1.0
+ semver: 6.3.1
+
'@babel/eslint-parser@7.28.6(@babel/core@7.29.7)(eslint@8.57.1)':
dependencies:
'@babel/core': 7.29.7
@@ -23749,23 +23767,23 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- '@elastic/elasticsearch@8.13.1':
+ '@elastic/elasticsearch@8.13.1(supports-color@5.5.0)':
dependencies:
- '@elastic/transport': 8.4.1
+ '@elastic/transport': 8.4.1(supports-color@5.5.0)
tslib: 2.8.1
transitivePeerDependencies:
- supports-color
- '@elastic/elasticsearch@8.19.1':
+ '@elastic/elasticsearch@8.19.1(supports-color@5.5.0)':
dependencies:
- '@elastic/transport': 8.10.1
+ '@elastic/transport': 8.10.1(supports-color@5.5.0)
apache-arrow: 21.1.0
tslib: 2.8.1
transitivePeerDependencies:
- '@75lb/nature'
- supports-color
- '@elastic/transport@8.10.1':
+ '@elastic/transport@8.10.1(supports-color@5.5.0)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1)
@@ -23778,7 +23796,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@elastic/transport@8.4.1':
+ '@elastic/transport@8.4.1(supports-color@5.5.0)':
dependencies:
debug: 4.4.3(supports-color@5.5.0)
hpagent: 1.2.0
@@ -24544,11 +24562,21 @@ snapshots:
'@esbuild/win32-x64@0.28.0':
optional: true
+ '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1(supports-color@5.5.0))':
+ dependencies:
+ eslint: 8.57.1(supports-color@5.5.0)
+ eslint-visitor-keys: 3.4.3
+
'@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)':
dependencies:
eslint: 8.57.1
eslint-visitor-keys: 3.4.3
+ '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))':
+ dependencies:
+ eslint: 9.39.4(jiti@2.7.0)(supports-color@5.5.0)
+ eslint-visitor-keys: 3.4.3
+
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0))':
dependencies:
eslint: 9.39.4(jiti@2.7.0)
@@ -24556,7 +24584,7 @@ snapshots:
'@eslint-community/regexpp@4.12.2': {}
- '@eslint/config-array@0.21.2':
+ '@eslint/config-array@0.21.2(supports-color@5.5.0)':
dependencies:
'@eslint/object-schema': 2.1.7
debug: 4.4.3(supports-color@5.5.0)
@@ -24572,7 +24600,7 @@ snapshots:
dependencies:
'@types/json-schema': 7.0.15
- '@eslint/eslintrc@2.1.4':
+ '@eslint/eslintrc@2.1.4(supports-color@5.5.0)':
dependencies:
ajv: 6.15.0
debug: 4.4.3(supports-color@5.5.0)
@@ -24586,7 +24614,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@eslint/eslintrc@3.3.5':
+ '@eslint/eslintrc@3.3.5(supports-color@5.5.0)':
dependencies:
ajv: 6.15.0
debug: 4.4.3(supports-color@5.5.0)
@@ -24643,7 +24671,7 @@ snapshots:
'@gar/promisify@1.1.3':
optional: true
- '@glimmer/component@1.1.2(@babel/core@7.29.7)':
+ '@glimmer/component@1.1.2(@babel/core@7.29.7)(supports-color@5.5.0)':
dependencies:
'@glimmer/di': 0.1.11
'@glimmer/env': 0.1.7
@@ -24656,7 +24684,7 @@ snapshots:
ember-cli-normalize-entity-name: 1.0.0
ember-cli-path-utils: 1.0.0
ember-cli-string-utils: 1.1.0
- ember-cli-typescript: 3.0.0(@babel/core@7.29.7)
+ ember-cli-typescript: 3.0.0(@babel/core@7.29.7)(supports-color@5.5.0)
ember-cli-version-checker: 3.1.3
ember-compatibility-helpers: 1.2.7(@babel/core@7.29.7)
transitivePeerDependencies:
@@ -24847,7 +24875,7 @@ snapshots:
'@humanfs/types@0.15.0': {}
- '@humanwhocodes/config-array@0.13.0':
+ '@humanwhocodes/config-array@0.13.0(supports-color@5.5.0)':
dependencies:
'@humanwhocodes/object-schema': 2.0.3
debug: 4.4.3(supports-color@5.5.0)
@@ -25708,12 +25736,12 @@ snapshots:
'@opentelemetry/api': 1.9.1
'@opentelemetry/semantic-conventions': 1.41.1
- '@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1)':
+ '@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1)(supports-color@5.5.0)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/api-logs': 0.214.0
import-in-the-middle: 3.0.1
- require-in-the-middle: 8.0.1
+ require-in-the-middle: 8.0.1(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -26673,18 +26701,18 @@ snapshots:
dependencies:
'@secretlint/types': 13.0.2
- '@secretlint/config-loader@13.0.2':
+ '@secretlint/config-loader@13.0.2(supports-color@5.5.0)':
dependencies:
'@secretlint/profiler': 13.0.2
'@secretlint/resolver': 13.0.2
'@secretlint/types': 13.0.2
ajv: 8.20.0
debug: 4.4.3(supports-color@5.5.0)
- rc-config-loader: 4.1.4
+ rc-config-loader: 4.1.4(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
- '@secretlint/core@13.0.2':
+ '@secretlint/core@13.0.2(supports-color@5.5.0)':
dependencies:
'@secretlint/profiler': 13.0.2
'@secretlint/types': 13.0.2
@@ -26709,10 +26737,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@secretlint/node@13.0.2':
+ '@secretlint/node@13.0.2(supports-color@5.5.0)':
dependencies:
- '@secretlint/config-loader': 13.0.2
- '@secretlint/core': 13.0.2
+ '@secretlint/config-loader': 13.0.2(supports-color@5.5.0)
+ '@secretlint/core': 13.0.2(supports-color@5.5.0)
'@secretlint/formatter': 13.0.2
'@secretlint/profiler': 13.0.2
'@secretlint/source-creator': 13.0.2
@@ -26726,9 +26754,9 @@ snapshots:
'@secretlint/resolver@13.0.2': {}
- '@secretlint/secretlint-rule-pattern@13.0.2':
+ '@secretlint/secretlint-rule-pattern@13.0.2(supports-color@5.5.0)':
dependencies:
- '@secretlint/tester': 13.0.2
+ '@secretlint/tester': 13.0.2(supports-color@5.5.0)
'@secretlint/types': 13.0.2
'@textlint/regexp-string-matcher': 2.0.2
micromatch: 4.0.8
@@ -26742,10 +26770,10 @@ snapshots:
'@secretlint/types': 13.0.2
istextorbinary: 9.5.0
- '@secretlint/tester@13.0.2':
+ '@secretlint/tester@13.0.2(supports-color@5.5.0)':
dependencies:
- '@secretlint/config-loader': 13.0.2
- '@secretlint/core': 13.0.2
+ '@secretlint/config-loader': 13.0.2(supports-color@5.5.0)
+ '@secretlint/core': 13.0.2(supports-color@5.5.0)
'@secretlint/source-creator': 13.0.2
'@secretlint/types': 13.0.2
transitivePeerDependencies:
@@ -26855,7 +26883,7 @@ snapshots:
'@sentry/types': 7.120.4
'@sentry/utils': 7.120.4
- '@sentry/ember@7.120.3(webpack@5.105.4(@swc/core@1.15.40(@swc/helpers@0.5.21))(cssnano@4.1.10)(lightningcss@1.32.0)(postcss@8.5.15))':
+ '@sentry/ember@7.120.3(supports-color@5.5.0)(webpack@5.105.4(@swc/core@1.15.40(@swc/helpers@0.5.21))(cssnano@4.1.10)(lightningcss@1.32.0)(postcss@8.5.15))':
dependencies:
'@embroider/macros': 1.16.13
'@sentry/browser': 7.120.3
@@ -26865,7 +26893,7 @@ snapshots:
ember-auto-import: 2.10.0(webpack@5.105.4(@swc/core@1.15.40(@swc/helpers@0.5.21))(cssnano@4.1.10)(lightningcss@1.32.0)(postcss@8.5.15))
ember-cli-babel: 7.26.11
ember-cli-htmlbars: 6.3.0
- ember-cli-typescript: 5.3.0
+ ember-cli-typescript: 5.3.0(supports-color@5.5.0)
transitivePeerDependencies:
- '@glint/template'
- supports-color
@@ -26892,7 +26920,7 @@ snapshots:
'@sentry/utils': 7.120.4
localforage: 1.10.0
- '@sentry/node-core@10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)':
+ '@sentry/node-core@10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1)(supports-color@5.5.0))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)':
dependencies:
'@sentry/core': 10.56.0
'@sentry/opentelemetry': 10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)
@@ -26900,20 +26928,20 @@ snapshots:
optionalDependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1)
- '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1)
+ '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1)(supports-color@5.5.0)
'@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1)
'@opentelemetry/semantic-conventions': 1.41.1
- '@sentry/node@10.56.0':
+ '@sentry/node@10.56.0(supports-color@5.5.0)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1)
- '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1)
+ '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1)(supports-color@5.5.0)
'@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1)
'@opentelemetry/semantic-conventions': 1.41.1
'@sentry-internal/server-utils': 10.56.0
'@sentry/core': 10.56.0
- '@sentry/node-core': 10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)
+ '@sentry/node-core': 10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1)(supports-color@5.5.0))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)
'@sentry/opentelemetry': 10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)
import-in-the-middle: 3.0.1
transitivePeerDependencies:
@@ -28585,7 +28613,7 @@ snapshots:
react-dom: 17.0.2(react@17.0.2)
use-sync-external-store: 1.6.0(react@17.0.2)
- '@tokenizer/inflate@0.4.1':
+ '@tokenizer/inflate@0.4.1(supports-color@5.5.0)':
dependencies:
debug: 4.4.3(supports-color@5.5.0)
token-types: 6.1.2
@@ -28609,7 +28637,7 @@ snapshots:
'@tryghost/api-framework@3.2.4':
dependencies:
- '@tryghost/debug': 2.2.3
+ '@tryghost/debug': 2.2.3(supports-color@5.5.0)
'@tryghost/errors': 1.3.13
'@tryghost/promise': 2.2.3
'@tryghost/tpl': 2.2.3
@@ -28630,14 +28658,14 @@ snapshots:
'@tryghost/bookshelf-eager-load@2.2.3':
dependencies:
- '@tryghost/debug': 2.2.3
+ '@tryghost/debug': 2.2.3(supports-color@5.5.0)
lodash: 4.18.1
transitivePeerDependencies:
- supports-color
'@tryghost/bookshelf-filter@2.2.4':
dependencies:
- '@tryghost/debug': 2.2.3
+ '@tryghost/debug': 2.2.3(supports-color@5.5.0)
'@tryghost/errors': 1.3.13
'@tryghost/nql': 0.13.0
'@tryghost/tpl': 2.2.3
@@ -28646,14 +28674,14 @@ snapshots:
'@tryghost/bookshelf-has-posts@2.3.3':
dependencies:
- '@tryghost/debug': 2.2.3
+ '@tryghost/debug': 2.2.3(supports-color@5.5.0)
lodash: 4.18.1
transitivePeerDependencies:
- supports-color
'@tryghost/bookshelf-include-count@2.2.3':
dependencies:
- '@tryghost/debug': 2.2.3
+ '@tryghost/debug': 2.2.3(supports-color@5.5.0)
lodash: 4.18.1
transitivePeerDependencies:
- supports-color
@@ -28718,21 +28746,21 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tryghost/debug@2.2.0':
+ '@tryghost/debug@2.2.0(supports-color@5.5.0)':
dependencies:
'@tryghost/root-utils': 2.2.0
debug: 4.4.3(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
- '@tryghost/debug@2.2.2':
+ '@tryghost/debug@2.2.2(supports-color@5.5.0)':
dependencies:
'@tryghost/root-utils': 2.2.2
debug: 4.4.3(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
- '@tryghost/debug@2.2.3':
+ '@tryghost/debug@2.2.3(supports-color@5.5.0)':
dependencies:
'@tryghost/root-utils': 2.2.3
debug: 4.4.3(supports-color@5.5.0)
@@ -28741,18 +28769,18 @@ snapshots:
'@tryghost/domain-events@3.2.5': {}
- '@tryghost/elasticsearch@3.0.29':
+ '@tryghost/elasticsearch@3.0.29(supports-color@5.5.0)':
dependencies:
- '@elastic/elasticsearch': 8.13.1
+ '@elastic/elasticsearch': 8.13.1(supports-color@5.5.0)
'@tryghost/debug': 0.1.40
split2: 4.2.0
transitivePeerDependencies:
- supports-color
- '@tryghost/elasticsearch@5.2.0':
+ '@tryghost/elasticsearch@5.2.0(supports-color@5.5.0)':
dependencies:
- '@elastic/elasticsearch': 8.19.1
- '@tryghost/debug': 2.2.0
+ '@elastic/elasticsearch': 8.19.1(supports-color@5.5.0)
+ '@tryghost/debug': 2.2.0(supports-color@5.5.0)
split2: 4.2.0
transitivePeerDependencies:
- '@75lb/nature'
@@ -28792,7 +28820,7 @@ snapshots:
dependencies:
'@tryghost/jest-snapshot': 2.1.0
cookiejar: 2.1.4
- express: 4.22.2
+ express: 4.22.2(supports-color@1.2.0)
form-data: 4.0.5
mime-types: 3.0.2
transitivePeerDependencies:
@@ -28844,12 +28872,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tryghost/job-manager@1.0.9':
+ '@tryghost/job-manager@1.0.9(supports-color@5.5.0)':
dependencies:
'@breejs/later': 4.2.0
'@tryghost/errors': 1.3.13
- '@tryghost/logging': 4.2.1
- bree: 6.5.0
+ '@tryghost/logging': 4.2.1(supports-color@5.5.0)
+ bree: 6.5.0(supports-color@5.5.0)
cron-validate: 1.4.5
fastq: 1.20.1
p-wait-for: 3.2.0
@@ -28989,10 +29017,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tryghost/logging@4.2.1':
+ '@tryghost/logging@4.2.1(supports-color@5.5.0)':
dependencies:
'@tryghost/bunyan-rotating-filestream': 0.0.7
- '@tryghost/elasticsearch': 5.2.0
+ '@tryghost/elasticsearch': 5.2.0(supports-color@5.5.0)
'@tryghost/http-stream': 2.2.1
'@tryghost/pretty-stream': 2.2.0
'@tryghost/root-utils': 2.2.0
@@ -29013,9 +29041,9 @@ snapshots:
papaparse: 5.3.2
pump: 3.0.2
- '@tryghost/metrics@1.0.43':
+ '@tryghost/metrics@1.0.43(supports-color@5.5.0)':
dependencies:
- '@tryghost/elasticsearch': 3.0.29
+ '@tryghost/elasticsearch': 3.0.29(supports-color@5.5.0)
'@tryghost/pretty-stream': 0.2.5
'@tryghost/root-utils': 0.3.38
json-stringify-safe: 5.0.1
@@ -29028,14 +29056,14 @@ snapshots:
mobiledoc-text-renderer: 0.4.0
optional: true
- '@tryghost/mongo-knex@0.10.1':
+ '@tryghost/mongo-knex@0.10.1(supports-color@5.5.0)':
dependencies:
debug: 4.4.3(supports-color@5.5.0)
lodash: 4.18.1
transitivePeerDependencies:
- supports-color
- '@tryghost/mongo-knex@0.9.5':
+ '@tryghost/mongo-knex@0.9.5(supports-color@5.5.0)':
dependencies:
debug: 4.4.3(supports-color@5.5.0)
lodash: 4.18.1
@@ -29128,9 +29156,9 @@ snapshots:
dependencies:
date-fns: 2.30.0
- '@tryghost/nql@0.12.11':
+ '@tryghost/nql@0.12.11(supports-color@5.5.0)':
dependencies:
- '@tryghost/mongo-knex': 0.9.5
+ '@tryghost/mongo-knex': 0.9.5(supports-color@5.5.0)
'@tryghost/mongo-utils': 0.6.5
'@tryghost/nql-lang': 0.6.6
lodash: 4.18.1
@@ -29140,7 +29168,7 @@ snapshots:
'@tryghost/nql@0.13.0':
dependencies:
- '@tryghost/mongo-knex': 0.10.1
+ '@tryghost/mongo-knex': 0.10.1(supports-color@5.5.0)
'@tryghost/mongo-utils': 0.6.5
'@tryghost/nql-lang': 0.6.6
lodash: 4.18.1
@@ -29148,9 +29176,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tryghost/nql@0.13.1':
+ '@tryghost/nql@0.13.1(supports-color@5.5.0)':
dependencies:
- '@tryghost/mongo-knex': 0.10.1
+ '@tryghost/mongo-knex': 0.10.1(supports-color@5.5.0)
'@tryghost/mongo-utils': 0.6.5
'@tryghost/nql-lang': 0.6.6
lodash: 4.18.1
@@ -29180,10 +29208,10 @@ snapshots:
lodash: 4.18.1
prettyjson: 1.2.5
- '@tryghost/prometheus-metrics@1.0.8':
+ '@tryghost/prometheus-metrics@1.0.8(supports-color@1.2.0)':
dependencies:
- '@tryghost/logging': 4.2.1
- express: 4.22.1
+ '@tryghost/logging': 4.2.1(supports-color@5.5.0)
+ express: 4.22.1(supports-color@1.2.0)
prom-client: 15.1.3
stoppable: 1.1.0
transitivePeerDependencies:
@@ -29245,8 +29273,8 @@ snapshots:
'@tryghost/server@3.0.2':
dependencies:
- '@tryghost/debug': 2.2.2
- '@tryghost/logging': 4.2.1
+ '@tryghost/debug': 2.2.2(supports-color@5.5.0)
+ '@tryghost/logging': 4.2.1(supports-color@5.5.0)
transitivePeerDependencies:
- '@75lb/nature'
- supports-color
@@ -29350,18 +29378,18 @@ snapshots:
dependencies:
'@tryghost/errors': 1.3.13
archiver: 7.0.1
- extract-zip: 2.0.1
+ extract-zip: 2.0.1(supports-color@5.5.0)
transitivePeerDependencies:
- bare-abort-controller
- bare-buffer
- react-native-b4a
- supports-color
- '@tryghost/zip@3.3.4':
+ '@tryghost/zip@3.3.4(supports-color@5.5.0)':
dependencies:
'@tryghost/errors': 1.3.13
archiver: 8.0.0
- extract-zip: 2.0.1
+ extract-zip: 2.0.1(supports-color@5.5.0)
transitivePeerDependencies:
- bare-abort-controller
- bare-buffer
@@ -29851,12 +29879,12 @@ snapshots:
'@types/node': 25.9.1
optional: true
- '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)':
+ '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3))(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.49.0(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.49.0
- '@typescript-eslint/type-utils': 8.49.0(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/type-utils': 8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/utils': 8.49.0(eslint@8.57.1)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.49.0
eslint: 8.57.1
@@ -29867,15 +29895,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)':
+ '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@8.57.1(supports-color@5.5.0))(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.49.0
- '@typescript-eslint/type-utils': 8.49.0(eslint@8.57.1)(typescript@5.9.3)
- '@typescript-eslint/utils': 8.49.0(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/type-utils': 8.49.0(eslint@8.57.1(supports-color@5.5.0))(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.49.0(eslint@8.57.1(supports-color@5.5.0))(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.49.0
- eslint: 8.57.1
+ eslint: 8.57.1(supports-color@5.5.0)
ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@5.9.3)
@@ -29883,10 +29911,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)':
+ '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.49.0
'@typescript-eslint/type-utils': 8.49.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
'@typescript-eslint/utils': 8.49.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
@@ -29915,15 +29943,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/eslint-plugin@8.58.0(@typescript-eslint/parser@8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)':
+ '@typescript-eslint/eslint-plugin@8.58.0(@typescript-eslint/parser@8.58.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.58.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.58.0
- '@typescript-eslint/type-utils': 8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
- '@typescript-eslint/utils': 8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
+ '@typescript-eslint/type-utils': 8.58.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.58.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.58.0
- eslint: 9.39.4(jiti@2.7.0)
+ eslint: 9.39.4(jiti@2.7.0)(supports-color@5.5.0)
ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@5.9.3)
@@ -29931,7 +29959,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.49.0(eslint@8.57.1)(typescript@5.9.3)':
+ '@typescript-eslint/parser@8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.49.0
'@typescript-eslint/types': 8.49.0
@@ -29943,14 +29971,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3)':
+ '@typescript-eslint/parser@8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.56.1
'@typescript-eslint/types': 8.56.1
- '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3)
+ '@typescript-eslint/typescript-estree': 8.56.1(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.56.1
debug: 4.4.3(supports-color@5.5.0)
- eslint: 8.57.1
+ eslint: 8.57.1(supports-color@5.5.0)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -29959,7 +29987,7 @@ snapshots:
dependencies:
'@typescript-eslint/scope-manager': 8.56.1
'@typescript-eslint/types': 8.56.1
- '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3)
+ '@typescript-eslint/typescript-estree': 8.56.1(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.56.1
debug: 4.4.3(supports-color@5.5.0)
eslint: 9.39.4(jiti@2.7.0)
@@ -29979,14 +30007,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)':
+ '@typescript-eslint/parser@8.58.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.58.0
'@typescript-eslint/types': 8.58.0
'@typescript-eslint/typescript-estree': 8.58.0(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.58.0
debug: 4.4.3(supports-color@5.5.0)
- eslint: 9.39.4(jiti@2.7.0)
+ eslint: 9.39.4(jiti@2.7.0)(supports-color@5.5.0)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -30000,7 +30028,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/project-service@8.56.1(typescript@5.9.3)':
+ '@typescript-eslint/project-service@8.56.1(supports-color@5.5.0)(typescript@5.9.3)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.9.3)
'@typescript-eslint/types': 8.60.0
@@ -30018,7 +30046,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/project-service@8.60.0(typescript@5.9.3)':
+ '@typescript-eslint/project-service@8.60.0(supports-color@5.5.0)(typescript@5.9.3)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.9.3)
'@typescript-eslint/types': 8.60.0
@@ -30063,7 +30091,19 @@ snapshots:
dependencies:
typescript: 5.9.3
- '@typescript-eslint/type-utils@8.49.0(eslint@8.57.1)(typescript@5.9.3)':
+ '@typescript-eslint/type-utils@8.49.0(eslint@8.57.1(supports-color@5.5.0))(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/types': 8.49.0
+ '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.49.0(eslint@8.57.1(supports-color@5.5.0))(typescript@5.9.3)
+ debug: 4.4.3(supports-color@5.5.0)
+ eslint: 8.57.1(supports-color@5.5.0)
+ ts-api-utils: 2.5.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/type-utils@8.49.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)':
dependencies:
'@typescript-eslint/types': 8.49.0
'@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
@@ -30099,13 +30139,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/type-utils@8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)':
+ '@typescript-eslint/type-utils@8.58.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)':
dependencies:
'@typescript-eslint/types': 8.58.0
'@typescript-eslint/typescript-estree': 8.58.0(typescript@5.9.3)
- '@typescript-eslint/utils': 8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.58.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(typescript@5.9.3)
debug: 4.4.3(supports-color@5.5.0)
- eslint: 9.39.4(jiti@2.7.0)
+ eslint: 9.39.4(jiti@2.7.0)(supports-color@5.5.0)
ts-api-utils: 2.5.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
@@ -30134,9 +30174,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/typescript-estree@8.56.1(typescript@5.9.3)':
+ '@typescript-eslint/typescript-estree@8.56.1(supports-color@5.5.0)(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/project-service': 8.56.1(typescript@5.9.3)
+ '@typescript-eslint/project-service': 8.56.1(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.3)
'@typescript-eslint/types': 8.56.1
'@typescript-eslint/visitor-keys': 8.56.1
@@ -30164,9 +30204,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/typescript-estree@8.60.0(typescript@5.9.3)':
+ '@typescript-eslint/typescript-estree@8.60.0(supports-color@5.5.0)(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/project-service': 8.60.0(typescript@5.9.3)
+ '@typescript-eslint/project-service': 8.60.0(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.9.3)
'@typescript-eslint/types': 8.60.0
'@typescript-eslint/visitor-keys': 8.60.0
@@ -30179,6 +30219,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@typescript-eslint/utils@8.49.0(eslint@8.57.1(supports-color@5.5.0))(typescript@5.9.3)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1(supports-color@5.5.0))
+ '@typescript-eslint/scope-manager': 8.49.0
+ '@typescript-eslint/types': 8.49.0
+ '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
+ eslint: 8.57.1(supports-color@5.5.0)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
'@typescript-eslint/utils@8.49.0(eslint@8.57.1)(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1)
@@ -30212,23 +30263,23 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)':
+ '@typescript-eslint/utils@8.58.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(typescript@5.9.3)':
dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0))
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))
'@typescript-eslint/scope-manager': 8.58.0
'@typescript-eslint/types': 8.58.0
'@typescript-eslint/typescript-estree': 8.58.0(typescript@5.9.3)
- eslint: 9.39.4(jiti@2.7.0)
+ eslint: 9.39.4(jiti@2.7.0)(supports-color@5.5.0)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.60.0(eslint@8.57.1)(typescript@5.9.3)':
+ '@typescript-eslint/utils@8.60.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1)
'@typescript-eslint/scope-manager': 8.60.0
'@typescript-eslint/types': 8.60.0
- '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.9.3)
+ '@typescript-eslint/typescript-estree': 8.60.0(supports-color@5.5.0)(typescript@5.9.3)
eslint: 8.57.1
typescript: 5.9.3
transitivePeerDependencies:
@@ -31902,7 +31953,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- body-parser@2.2.2:
+ body-parser@2.2.2(supports-color@5.5.0):
dependencies:
bytes: 3.1.2
content-type: 1.0.5
@@ -31977,7 +32028,7 @@ snapshots:
dependencies:
fill-range: 7.1.1
- bree@6.5.0:
+ bree@6.5.0(supports-color@5.5.0):
dependencies:
'@babel/runtime': 7.29.7
'@breejs/later': 4.2.0
@@ -32113,7 +32164,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- broccoli-config-replace@1.1.3:
+ broccoli-config-replace@1.1.3(supports-color@1.2.0):
dependencies:
broccoli-plugin: 1.3.1
debug: 2.6.9(supports-color@1.2.0)
@@ -32177,7 +32228,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- broccoli-funnel@2.0.1:
+ broccoli-funnel@2.0.1(supports-color@1.2.0):
dependencies:
array-equal: 1.0.2
blank-object: 1.0.2
@@ -32448,7 +32499,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- broccoli-stew@3.0.0:
+ broccoli-stew@3.0.0(supports-color@5.5.0):
dependencies:
broccoli-debug: 0.6.5
broccoli-funnel: 2.0.2
@@ -32492,7 +32543,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- broccoli-terser-sourcemap@4.1.1:
+ broccoli-terser-sourcemap@4.1.1(supports-color@5.5.0):
dependencies:
async-promise-queue: 1.0.5
broccoli-plugin: 4.0.7
@@ -32507,7 +32558,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- broccoli@3.5.2:
+ broccoli@3.5.2(supports-color@1.2.0):
dependencies:
'@types/chai': 4.3.20
'@types/chai-as-promised': 7.1.8
@@ -32517,7 +32568,7 @@ snapshots:
broccoli-slow-trees: 3.1.0
broccoli-source: 3.0.1
commander: 4.1.1
- connect: 3.7.0
+ connect: 3.7.0(supports-color@1.2.0)
console-ui: 3.1.2
esm: 3.2.25
findup-sync: 4.0.0
@@ -32597,10 +32648,10 @@ snapshots:
node-releases: 2.0.46
update-browserslist-db: 1.2.3(browserslist@4.28.2)
- brute-knex@4.0.1(express@4.22.2)(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7):
+ brute-knex@4.0.1(express@4.22.2)(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7)(supports-color@5.5.0):
dependencies:
express-brute: 1.0.1(express@4.22.2)
- knex: 0.20.15(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7)
+ knex: 0.20.15(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7)(supports-color@5.5.0)
transitivePeerDependencies:
- express
- mssql
@@ -32739,9 +32790,9 @@ snapshots:
- bluebird
optional: true
- cache-manager-ioredis@2.1.0:
+ cache-manager-ioredis@2.1.0(supports-color@5.5.0):
dependencies:
- ioredis: 4.31.0
+ ioredis: 4.31.0(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -33385,10 +33436,10 @@ snapshots:
connect-slashes@1.4.0: {}
- connect@3.7.0:
+ connect@3.7.0(supports-color@1.2.0):
dependencies:
debug: 2.6.9(supports-color@1.2.0)
- finalhandler: 1.1.2
+ finalhandler: 1.1.2(supports-color@1.2.0)
parseurl: 1.3.3
utils-merge: 1.0.1
transitivePeerDependencies:
@@ -34215,7 +34266,7 @@ snapshots:
dlv@1.1.3: {}
- docker-modem@5.0.7:
+ docker-modem@5.0.7(supports-color@5.5.0):
dependencies:
debug: 4.4.3(supports-color@5.5.0)
readable-stream: 3.6.2
@@ -34224,12 +34275,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
- dockerode@4.0.12:
+ dockerode@4.0.12(supports-color@5.5.0):
dependencies:
'@balena/dockerignore': 1.0.2
'@grpc/grpc-js': 1.14.4
'@grpc/proto-loader': 0.7.15
- docker-modem: 5.0.7
+ docker-modem: 5.0.7(supports-color@5.5.0)
protobufjs: 7.6.1
tar-fs: 2.1.4
uuid: 10.0.0
@@ -34547,11 +34598,11 @@ snapshots:
'@ember/render-modifiers': 2.1.0(@babel/core@7.29.7)(ember-source@3.24.0(@babel/core@7.29.7))
'@embroider/macros': 1.16.13
'@embroider/util': 1.13.5(ember-source@3.24.0(@babel/core@7.29.7))
- '@glimmer/component': 1.1.2(@babel/core@7.29.7)
+ '@glimmer/component': 1.1.2(@babel/core@7.29.7)(supports-color@5.5.0)
'@glimmer/tracking': 1.1.2
ember-cli-babel: 7.26.11
ember-cli-htmlbars: 6.3.0
- ember-cli-typescript: 4.2.1
+ ember-cli-typescript: 4.2.1(supports-color@5.5.0)
ember-element-helper: 0.6.1(ember-source@3.24.0(@babel/core@7.29.7))
ember-get-config: 2.1.1
ember-maybe-in-element: 2.1.0
@@ -34695,10 +34746,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- ember-cli-dependency-checker@3.3.2(ember-cli@3.24.0(encoding@0.1.13)(handlebars@4.7.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(underscore@1.13.8)):
+ ember-cli-dependency-checker@3.3.2(ember-cli@3.24.0(encoding@0.1.13)(handlebars@4.7.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@1.2.0)(underscore@1.13.8)):
dependencies:
chalk: 2.4.2
- ember-cli: 3.24.0(encoding@0.1.13)(handlebars@4.7.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(underscore@1.13.8)
+ ember-cli: 3.24.0(encoding@0.1.13)(handlebars@4.7.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@1.2.0)(underscore@1.13.8)
find-yarn-workspace-root: 1.2.1
is-git-url: 1.0.0
resolve: 1.22.12
@@ -34899,7 +34950,7 @@ snapshots:
ember-cli-terser@4.0.1:
dependencies:
- broccoli-terser-sourcemap: 4.1.1
+ broccoli-terser-sourcemap: 4.1.1(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -34920,7 +34971,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- ember-cli-typescript@2.0.2(@babel/core@7.29.7):
+ ember-cli-typescript@2.0.2(@babel/core@7.29.7)(supports-color@5.5.0):
dependencies:
'@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.29.7)
'@babel/plugin-transform-typescript': 7.4.5(@babel/core@7.29.7)
@@ -34938,7 +34989,7 @@ snapshots:
- '@babel/core'
- supports-color
- ember-cli-typescript@3.0.0(@babel/core@7.29.7):
+ ember-cli-typescript@3.0.0(@babel/core@7.29.7)(supports-color@5.5.0):
dependencies:
'@babel/plugin-transform-typescript': 7.5.5(@babel/core@7.29.7)
ansi-to-html: 0.6.15
@@ -34961,7 +35012,7 @@ snapshots:
'@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.29.7)
'@babel/plugin-transform-typescript': 7.8.7(@babel/core@7.29.7)
ansi-to-html: 0.6.15
- broccoli-stew: 3.0.0
+ broccoli-stew: 3.0.0(supports-color@5.5.0)
debug: 4.4.3(supports-color@5.5.0)
ember-cli-babel-plugin-helpers: 1.1.1
execa: 3.4.0
@@ -34975,10 +35026,10 @@ snapshots:
- '@babel/core'
- supports-color
- ember-cli-typescript@4.2.1:
+ ember-cli-typescript@4.2.1(supports-color@5.5.0):
dependencies:
ansi-to-html: 0.6.15
- broccoli-stew: 3.0.0
+ broccoli-stew: 3.0.0(supports-color@5.5.0)
debug: 4.4.3(supports-color@5.5.0)
execa: 4.1.0
fs-extra: 9.1.0
@@ -34990,10 +35041,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- ember-cli-typescript@5.3.0:
+ ember-cli-typescript@5.3.0(supports-color@5.5.0):
dependencies:
ansi-to-html: 0.6.15
- broccoli-stew: 3.0.0
+ broccoli-stew: 3.0.0(supports-color@5.5.0)
debug: 4.4.3(supports-color@5.5.0)
execa: 4.1.0
fs-extra: 9.1.0
@@ -35031,7 +35082,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- ember-cli@3.24.0(encoding@0.1.13)(handlebars@4.7.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(underscore@1.13.8):
+ ember-cli@3.24.0(encoding@0.1.13)(handlebars@4.7.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@1.2.0)(underscore@1.13.8):
dependencies:
'@babel/core': 7.29.7
'@babel/plugin-transform-modules-amd': 7.29.7(@babel/core@7.29.7)
@@ -35039,13 +35090,13 @@ snapshots:
babel-plugin-module-resolver: 4.1.0
bower-config: 1.4.3
bower-endpoint-parser: 0.2.2
- broccoli: 3.5.2
+ broccoli: 3.5.2(supports-color@1.2.0)
broccoli-amd-funnel: 2.0.1
broccoli-babel-transpiler: 7.8.1
broccoli-builder: 0.18.14
broccoli-concat: 4.2.7
broccoli-config-loader: 1.0.1
- broccoli-config-replace: 1.1.3
+ broccoli-config-replace: 1.1.3(supports-color@1.2.0)
broccoli-debug: 0.6.5
broccoli-funnel: 2.0.2
broccoli-funnel-reducer: 1.0.0
@@ -35053,7 +35104,7 @@ snapshots:
broccoli-middleware: 2.1.2
broccoli-slow-trees: 3.1.0
broccoli-source: 3.0.1
- broccoli-stew: 3.0.0
+ broccoli-stew: 3.0.0(supports-color@5.5.0)
calculate-cache-key-for-tree: 2.0.0
capture-exit: 2.0.0
chalk: 4.1.2
@@ -35074,7 +35125,7 @@ snapshots:
ensure-posix-path: 1.1.1
execa: 4.1.0
exit: 0.1.2
- express: 4.22.2
+ express: 4.22.2(supports-color@1.2.0)
filesize: 6.4.0
find-up: 5.0.0
find-yarn-workspace-root: 2.0.0
@@ -35095,16 +35146,16 @@ snapshots:
isbinaryfile: 4.0.10
js-yaml: 3.14.2
json-stable-stringify: 1.3.0
- leek: 0.0.24
+ leek: 0.0.24(supports-color@1.2.0)
lodash.template: 4.5.0
markdown-it: 12.3.2
markdown-it-terminal: 0.2.1
minimatch: 3.1.5
- morgan: 1.10.1
+ morgan: 1.10.1(supports-color@1.2.0)
nopt: 3.0.6
npm-package-arg: 8.1.5
p-defer: 3.0.0
- portfinder: 1.0.38
+ portfinder: 1.0.38(supports-color@5.5.0)
promise-map-series: 0.3.0
promise.hash.helper: 1.0.8
quick-temp: 0.1.9
@@ -35187,10 +35238,10 @@ snapshots:
- '@babel/core'
- supports-color
- ember-composable-helpers@5.0.0:
+ ember-composable-helpers@5.0.0(supports-color@1.2.0):
dependencies:
'@babel/core': 7.29.7
- broccoli-funnel: 2.0.1
+ broccoli-funnel: 2.0.1(supports-color@1.2.0)
ember-cli-babel: 7.26.11
resolve: 1.22.12
transitivePeerDependencies:
@@ -35317,10 +35368,10 @@ snapshots:
- '@glint/template'
- supports-color
- ember-eslint-parser@0.5.13(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3):
+ ember-eslint-parser@0.5.13(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@8.57.1(supports-color@5.5.0))(typescript@5.9.3):
dependencies:
'@babel/core': 7.29.7
- '@babel/eslint-parser': 7.28.6(@babel/core@7.29.7)(eslint@8.57.1)
+ '@babel/eslint-parser': 7.28.6(@babel/core@7.29.7)(eslint@8.57.1(supports-color@5.5.0))
'@glimmer/syntax': 0.95.0
'@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.9.3)
content-tag: 2.0.3
@@ -35329,12 +35380,12 @@ snapshots:
mathml-tag-names: 2.1.3
svg-tags: 1.0.0
optionalDependencies:
- '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)
transitivePeerDependencies:
- eslint
- typescript
- ember-eslint-parser@0.5.13(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3):
+ ember-eslint-parser@0.5.13(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3):
dependencies:
'@babel/core': 7.29.7
'@babel/eslint-parser': 7.28.6(@babel/core@7.29.7)(eslint@9.39.4(jiti@2.7.0))
@@ -35346,12 +35397,12 @@ snapshots:
mathml-tag-names: 2.1.3
svg-tags: 1.0.0
optionalDependencies:
- '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)
transitivePeerDependencies:
- eslint
- typescript
- ember-exam@6.0.1(ember-mocha@0.16.2(@babel/core@7.29.7)):
+ ember-exam@6.0.1(ember-mocha@0.16.2(@babel/core@7.29.7))(supports-color@5.5.0):
dependencies:
'@embroider/macros': 0.29.0
chalk: 4.1.2
@@ -35379,19 +35430,19 @@ snapshots:
dependencies:
ember-cli-version-checker: 2.2.0
- ember-fetch@8.1.2(encoding@0.1.13):
+ ember-fetch@8.1.2(encoding@0.1.13)(supports-color@5.5.0):
dependencies:
abortcontroller-polyfill: 1.7.8
broccoli-concat: 4.2.7
broccoli-debug: 0.6.5
broccoli-merge-trees: 4.2.0
broccoli-rollup: 2.1.1
- broccoli-stew: 3.0.0
+ broccoli-stew: 3.0.0(supports-color@5.5.0)
broccoli-templater: 2.0.2
calculate-cache-key-for-tree: 2.0.0
caniuse-api: 3.0.0
ember-cli-babel: 7.26.11
- ember-cli-typescript: 4.2.1
+ ember-cli-typescript: 4.2.1(supports-color@5.5.0)
ember-cli-version-checker: 5.1.2
node-fetch: 2.7.0(encoding@0.1.13)
whatwg-fetch: 3.6.20
@@ -35420,7 +35471,7 @@ snapshots:
ember-cli-version-checker: 2.2.0
ember-factory-for-polyfill: 1.3.1
- ember-in-element-polyfill@1.0.1:
+ ember-in-element-polyfill@1.0.1(supports-color@5.5.0):
dependencies:
debug: 4.4.3(supports-color@5.5.0)
ember-cli-babel: 7.26.11
@@ -35486,10 +35537,10 @@ snapshots:
- ember-source
- supports-color
- ember-load-initializers@2.1.2(@babel/core@7.29.7):
+ ember-load-initializers@2.1.2(@babel/core@7.29.7)(supports-color@5.5.0):
dependencies:
ember-cli-babel: 7.26.11
- ember-cli-typescript: 2.0.2(@babel/core@7.29.7)
+ ember-cli-typescript: 2.0.2(@babel/core@7.29.7)(supports-color@5.5.0)
transitivePeerDependencies:
- '@babel/core'
- supports-color
@@ -35547,7 +35598,7 @@ snapshots:
ember-cli-babel: 7.26.11
ember-cli-normalize-entity-name: 1.0.0
ember-cli-string-utils: 1.1.0
- ember-cli-typescript: 5.3.0
+ ember-cli-typescript: 5.3.0(supports-color@5.5.0)
ember-compatibility-helpers: 1.2.7(@babel/core@7.29.7)
transitivePeerDependencies:
- '@babel/core'
@@ -35612,13 +35663,13 @@ snapshots:
ember-power-select@6.0.1(@babel/core@7.29.7)(ember-source@3.24.0(@babel/core@7.29.7)):
dependencies:
'@embroider/util': 1.13.5(ember-source@3.24.0(@babel/core@7.29.7))
- '@glimmer/component': 1.1.2(@babel/core@7.29.7)
+ '@glimmer/component': 1.1.2(@babel/core@7.29.7)(supports-color@5.5.0)
'@glimmer/tracking': 1.1.2
ember-assign-helper: 0.4.0
ember-basic-dropdown: 6.0.2(@babel/core@7.29.7)(ember-source@3.24.0(@babel/core@7.29.7))
ember-cli-babel: 7.26.11
ember-cli-htmlbars: 6.3.0
- ember-cli-typescript: 4.2.1
+ ember-cli-typescript: 4.2.1(supports-color@5.5.0)
ember-concurrency: 2.3.7(@babel/core@7.29.7)
ember-concurrency-decorators: 2.0.3(@babel/core@7.29.7)
ember-text-measurer: 0.6.0
@@ -35658,12 +35709,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
- ember-simple-auth@5.0.0(ember-fetch@8.1.2(encoding@0.1.13)):
+ ember-simple-auth@5.0.0(ember-fetch@8.1.2(encoding@0.1.13)(supports-color@5.5.0)):
dependencies:
ember-cli-babel: 7.26.11
ember-cli-is-package-missing: 1.0.0
ember-cookies: 0.5.2
- ember-fetch: 8.1.2(encoding@0.1.13)
+ ember-fetch: 8.1.2(encoding@0.1.13)(supports-color@5.5.0)
silent-error: 1.1.1
transitivePeerDependencies:
- supports-color
@@ -35745,7 +35796,7 @@ snapshots:
ember-template-imports@3.4.2:
dependencies:
babel-import-util: 0.2.0
- broccoli-stew: 3.0.0
+ broccoli-stew: 3.0.0(supports-color@5.5.0)
ember-cli-babel-plugin-helpers: 1.1.1
ember-cli-version-checker: 5.1.2
line-column: 1.0.2
@@ -35817,7 +35868,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- ember-tooltips@3.6.0:
+ ember-tooltips@3.6.0(supports-color@5.5.0):
dependencies:
'@embroider/macros': 1.16.13
broccoli-file-creator: 2.1.1
@@ -35825,7 +35876,7 @@ snapshots:
ember-auto-import: 1.12.2
ember-cli-babel: 7.26.11
ember-cli-htmlbars: 5.7.2
- ember-in-element-polyfill: 1.0.1
+ ember-in-element-polyfill: 1.0.1(supports-color@5.5.0)
popper.js: 1.16.1
resolve: 1.22.12
tooltip.js: 1.3.3
@@ -36187,9 +36238,9 @@ snapshots:
optionalDependencies:
source-map: 0.6.1
- eslint-compat-utils@0.5.1(eslint@8.57.1):
+ eslint-compat-utils@0.5.1(eslint@8.57.1(supports-color@5.5.0)):
dependencies:
- eslint: 8.57.1
+ eslint: 8.57.1(supports-color@5.5.0)
semver: 7.8.4
eslint-compat-utils@0.5.1(eslint@9.39.4(jiti@2.7.0)):
@@ -36204,30 +36255,30 @@ snapshots:
eslint: 8.57.1
eslint-rule-composer: 0.3.0
- eslint-plugin-ember@12.7.5(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3):
+ eslint-plugin-ember@12.7.5(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@8.57.1(supports-color@5.5.0))(typescript@5.9.3):
dependencies:
'@ember-data/rfc395-data': 0.0.4
css-tree: 3.2.1
- ember-eslint-parser: 0.5.13(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
+ ember-eslint-parser: 0.5.13(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@8.57.1(supports-color@5.5.0))(typescript@5.9.3)
ember-rfc176-data: 0.3.18
- eslint: 8.57.1
- eslint-utils: 3.0.0(eslint@8.57.1)
+ eslint: 8.57.1(supports-color@5.5.0)
+ eslint-utils: 3.0.0(eslint@8.57.1(supports-color@5.5.0))
estraverse: 5.3.0
lodash.camelcase: 4.3.0
lodash.kebabcase: 4.1.1
requireindex: 1.2.0
snake-case: 3.0.4
optionalDependencies:
- '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)
transitivePeerDependencies:
- '@babel/core'
- typescript
- eslint-plugin-ember@12.7.5(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3):
+ eslint-plugin-ember@12.7.5(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3):
dependencies:
'@ember-data/rfc395-data': 0.0.4
css-tree: 3.2.1
- ember-eslint-parser: 0.5.13(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
+ ember-eslint-parser: 0.5.13(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
ember-rfc176-data: 0.3.18
eslint: 9.39.4(jiti@2.7.0)
eslint-utils: 3.0.0(eslint@9.39.4(jiti@2.7.0))
@@ -36237,17 +36288,17 @@ snapshots:
requireindex: 1.2.0
snake-case: 3.0.4
optionalDependencies:
- '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)
transitivePeerDependencies:
- '@babel/core'
- typescript
- eslint-plugin-es-x@7.8.0(eslint@8.57.1):
+ eslint-plugin-es-x@7.8.0(eslint@8.57.1(supports-color@5.5.0)):
dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1)
+ '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1(supports-color@5.5.0))
'@eslint-community/regexpp': 4.12.2
- eslint: 8.57.1
- eslint-compat-utils: 0.5.1(eslint@8.57.1)
+ eslint: 8.57.1(supports-color@5.5.0)
+ eslint-compat-utils: 0.5.1(eslint@8.57.1(supports-color@5.5.0))
eslint-plugin-es-x@7.8.0(eslint@9.39.4(jiti@2.7.0)):
dependencies:
@@ -36256,9 +36307,9 @@ snapshots:
eslint: 9.39.4(jiti@2.7.0)
eslint-compat-utils: 0.5.1(eslint@9.39.4(jiti@2.7.0))
- eslint-plugin-filenames-ts@1.3.2(eslint@8.57.1):
+ eslint-plugin-filenames-ts@1.3.2(eslint@8.57.1(supports-color@5.5.0)):
dependencies:
- eslint: 8.57.1
+ eslint: 8.57.1(supports-color@5.5.0)
lodash.camelcase: 4.3.0
lodash.kebabcase: 4.1.1
lodash.snakecase: 4.1.1
@@ -36272,18 +36323,18 @@ snapshots:
lodash.snakecase: 4.1.1
lodash.upperfirst: 4.3.1
- eslint-plugin-ghost@3.5.0(@babel/core@7.29.7)(eslint@8.57.1):
+ eslint-plugin-ghost@3.5.0(@babel/core@7.29.7)(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0):
dependencies:
'@kapouer/eslint-plugin-no-return-in-loop': 1.0.0
- '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
- '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3)
- eslint: 8.57.1
- eslint-plugin-ember: 12.7.5(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
- eslint-plugin-filenames-ts: 1.3.2(eslint@8.57.1)
- eslint-plugin-mocha: 7.0.1(eslint@8.57.1)
- eslint-plugin-n: 17.24.0(eslint@8.57.1)(typescript@5.9.3)
- eslint-plugin-sort-imports-es6-autofix: 0.6.0(eslint@8.57.1)
- eslint-plugin-unicorn: 42.0.0(eslint@8.57.1)
+ '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@8.57.1(supports-color@5.5.0))(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)
+ eslint: 8.57.1(supports-color@5.5.0)
+ eslint-plugin-ember: 12.7.5(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@8.57.1(supports-color@5.5.0))(typescript@5.9.3)
+ eslint-plugin-filenames-ts: 1.3.2(eslint@8.57.1(supports-color@5.5.0))
+ eslint-plugin-mocha: 7.0.1(eslint@8.57.1(supports-color@5.5.0))
+ eslint-plugin-n: 17.24.0(eslint@8.57.1(supports-color@5.5.0))(typescript@5.9.3)
+ eslint-plugin-sort-imports-es6-autofix: 0.6.0(eslint@8.57.1(supports-color@5.5.0))
+ eslint-plugin-unicorn: 42.0.0(eslint@8.57.1(supports-color@5.5.0))
typescript: 5.9.3
transitivePeerDependencies:
- '@babel/core'
@@ -36292,10 +36343,10 @@ snapshots:
eslint-plugin-ghost@3.5.0(@babel/core@7.29.7)(eslint@9.39.4(jiti@2.7.0)):
dependencies:
'@kapouer/eslint-plugin-no-return-in-loop': 1.0.0
- '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
+ '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
'@typescript-eslint/parser': 8.56.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
eslint: 9.39.4(jiti@2.7.0)
- eslint-plugin-ember: 12.7.5(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
+ eslint-plugin-ember: 12.7.5(@babel/core@7.29.7)(@typescript-eslint/parser@8.56.1(eslint@8.57.1(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
eslint-plugin-filenames-ts: 1.3.2(eslint@9.39.4(jiti@2.7.0))
eslint-plugin-mocha: 7.0.1(eslint@9.39.4(jiti@2.7.0))
eslint-plugin-n: 17.24.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
@@ -36311,9 +36362,9 @@ snapshots:
lodash: 4.18.1
requireindex: 1.1.0
- eslint-plugin-mocha@7.0.1(eslint@8.57.1):
+ eslint-plugin-mocha@7.0.1(eslint@8.57.1(supports-color@5.5.0)):
dependencies:
- eslint: 8.57.1
+ eslint: 8.57.1(supports-color@5.5.0)
eslint-utils: 2.1.0
ramda: 0.27.2
@@ -36323,12 +36374,12 @@ snapshots:
eslint-utils: 2.1.0
ramda: 0.27.2
- eslint-plugin-n@17.24.0(eslint@8.57.1)(typescript@5.9.3):
+ eslint-plugin-n@17.24.0(eslint@8.57.1(supports-color@5.5.0))(typescript@5.9.3):
dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1)
+ '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1(supports-color@5.5.0))
enhanced-resolve: 5.22.0
- eslint: 8.57.1
- eslint-plugin-es-x: 7.8.0(eslint@8.57.1)
+ eslint: 8.57.1(supports-color@5.5.0)
+ eslint-plugin-es-x: 7.8.0(eslint@8.57.1(supports-color@5.5.0))
get-tsconfig: 4.14.0
globals: 15.15.0
globrex: 0.1.2
@@ -36364,19 +36415,19 @@ snapshots:
dependencies:
eslint: 8.57.1
- eslint-plugin-react-hooks@5.2.0(eslint@9.39.4(jiti@2.7.0)):
+ eslint-plugin-react-hooks@5.2.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0)):
dependencies:
- eslint: 9.39.4(jiti@2.7.0)
+ eslint: 9.39.4(jiti@2.7.0)(supports-color@5.5.0)
eslint-plugin-react-refresh@0.5.2(eslint@8.57.1):
dependencies:
eslint: 8.57.1
- eslint-plugin-react-refresh@0.5.2(eslint@9.39.4(jiti@2.7.0)):
+ eslint-plugin-react-refresh@0.5.2(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0)):
dependencies:
- eslint: 9.39.4(jiti@2.7.0)
+ eslint: 9.39.4(jiti@2.7.0)(supports-color@5.5.0)
- eslint-plugin-react@7.37.5(eslint@8.57.1):
+ eslint-plugin-react@7.37.5(eslint@8.57.1(supports-color@5.5.0)):
dependencies:
array-includes: 3.1.9
array.prototype.findlast: 1.2.5
@@ -36384,7 +36435,7 @@ snapshots:
array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
es-iterator-helpers: 1.3.2
- eslint: 8.57.1
+ eslint: 8.57.1(supports-color@5.5.0)
estraverse: 5.3.0
hasown: 2.0.4
jsx-ast-utils: 3.3.5
@@ -36420,17 +36471,17 @@ snapshots:
string.prototype.matchall: 4.0.12
string.prototype.repeat: 1.0.0
- eslint-plugin-sort-imports-es6-autofix@0.6.0(eslint@8.57.1):
+ eslint-plugin-sort-imports-es6-autofix@0.6.0(eslint@8.57.1(supports-color@5.5.0)):
dependencies:
- eslint: 8.57.1
+ eslint: 8.57.1(supports-color@5.5.0)
eslint-plugin-sort-imports-es6-autofix@0.6.0(eslint@9.39.4(jiti@2.7.0)):
dependencies:
eslint: 9.39.4(jiti@2.7.0)
- eslint-plugin-storybook@10.4.4(eslint@8.57.1)(storybook@10.4.4(@testing-library/dom@9.3.4)(@types/react@18.3.31)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.3):
+ eslint-plugin-storybook@10.4.4(eslint@8.57.1)(storybook@10.4.4(@testing-library/dom@9.3.4)(@types/react@18.3.31)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(supports-color@5.5.0)(typescript@5.9.3):
dependencies:
- '@typescript-eslint/utils': 8.60.0(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.60.0(eslint@8.57.1)(supports-color@5.5.0)(typescript@5.9.3)
eslint: 8.57.1
storybook: 10.4.4(@testing-library/dom@9.3.4)(@types/react@18.3.31)(prettier@3.8.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
transitivePeerDependencies:
@@ -36453,13 +36504,13 @@ snapshots:
tailwind-api-utils: 1.0.3(tailwindcss@4.2.2)
tailwindcss: 4.2.2
- eslint-plugin-unicorn@42.0.0(eslint@8.57.1):
+ eslint-plugin-unicorn@42.0.0(eslint@8.57.1(supports-color@5.5.0)):
dependencies:
'@babel/helper-validator-identifier': 7.29.7
ci-info: 3.9.0
clean-regexp: 1.0.0
- eslint: 8.57.1
- eslint-utils: 3.0.0(eslint@8.57.1)
+ eslint: 8.57.1(supports-color@5.5.0)
+ eslint-utils: 3.0.0(eslint@8.57.1(supports-color@5.5.0))
esquery: 1.7.0
indent-string: 4.0.0
is-builtin-module: 3.2.1
@@ -36515,9 +36566,9 @@ snapshots:
dependencies:
eslint-visitor-keys: 1.3.0
- eslint-utils@3.0.0(eslint@8.57.1):
+ eslint-utils@3.0.0(eslint@8.57.1(supports-color@5.5.0)):
dependencies:
- eslint: 8.57.1
+ eslint: 8.57.1(supports-color@5.5.0)
eslint-visitor-keys: 2.1.0
eslint-utils@3.0.0(eslint@9.39.4(jiti@2.7.0)):
@@ -36539,9 +36590,52 @@ snapshots:
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1)
'@eslint-community/regexpp': 4.12.2
- '@eslint/eslintrc': 2.1.4
+ '@eslint/eslintrc': 2.1.4(supports-color@5.5.0)
'@eslint/js': 8.57.1
- '@humanwhocodes/config-array': 0.13.0
+ '@humanwhocodes/config-array': 0.13.0(supports-color@5.5.0)
+ '@humanwhocodes/module-importer': 1.0.1
+ '@nodelib/fs.walk': 1.2.8
+ '@ungap/structured-clone': 1.3.1
+ ajv: 6.15.0
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.4.3(supports-color@5.5.0)
+ doctrine: 3.0.0
+ escape-string-regexp: 4.0.0
+ eslint-scope: 7.2.2
+ eslint-visitor-keys: 3.4.3
+ espree: 9.6.1
+ esquery: 1.7.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 6.0.1
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ globals: 13.24.0
+ graphemer: 1.4.0
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ is-path-inside: 3.0.3
+ js-yaml: 4.2.0
+ json-stable-stringify-without-jsonify: 1.0.1
+ levn: 0.4.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.5
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ strip-ansi: 6.0.1
+ text-table: 0.2.0
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint@8.57.1(supports-color@5.5.0):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1(supports-color@5.5.0))
+ '@eslint-community/regexpp': 4.12.2
+ '@eslint/eslintrc': 2.1.4(supports-color@5.5.0)
+ '@eslint/js': 8.57.1
+ '@humanwhocodes/config-array': 0.13.0(supports-color@5.5.0)
'@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8
'@ungap/structured-clone': 1.3.1
@@ -36582,10 +36676,51 @@ snapshots:
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0))
'@eslint-community/regexpp': 4.12.2
- '@eslint/config-array': 0.21.2
+ '@eslint/config-array': 0.21.2(supports-color@5.5.0)
+ '@eslint/config-helpers': 0.4.2
+ '@eslint/core': 0.17.0
+ '@eslint/eslintrc': 3.3.5(supports-color@5.5.0)
+ '@eslint/js': 9.39.4
+ '@eslint/plugin-kit': 0.4.1
+ '@humanfs/node': 0.16.8
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.9
+ ajv: 6.15.0
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.4.3(supports-color@5.5.0)
+ escape-string-regexp: 4.0.0
+ eslint-scope: 8.4.0
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
+ esquery: 1.7.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.5
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ optionalDependencies:
+ jiti: 2.7.0
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))
+ '@eslint-community/regexpp': 4.12.2
+ '@eslint/config-array': 0.21.2(supports-color@5.5.0)
'@eslint/config-helpers': 0.4.2
'@eslint/core': 0.17.0
- '@eslint/eslintrc': 3.3.5
+ '@eslint/eslintrc': 3.3.5(supports-color@5.5.0)
'@eslint/js': 9.39.4
'@eslint/plugin-kit': 0.4.1
'@humanfs/node': 0.16.8
@@ -36794,11 +36929,11 @@ snapshots:
express-brute@1.0.1(express@4.22.2):
dependencies:
- express: 4.22.2
+ express: 4.22.2(supports-color@1.2.0)
long-timeout: 0.1.1
underscore: 1.13.8
- express-end@0.0.8:
+ express-end@0.0.8(supports-color@1.2.0):
dependencies:
debug: 2.6.9(supports-color@1.2.0)
transitivePeerDependencies:
@@ -36826,19 +36961,19 @@ snapshots:
express-lazy-router@1.0.6(express@4.22.2):
dependencies:
- express: 4.22.2
+ express: 4.22.2(supports-color@1.2.0)
express-query-boolean@2.0.0: {}
- express-queue@0.0.13:
+ express-queue@0.0.13(supports-color@1.2.0):
dependencies:
debug: 4.4.3(supports-color@5.5.0)
- express-end: 0.0.8
+ express-end: 0.0.8(supports-color@1.2.0)
mini-queue: 0.0.14
transitivePeerDependencies:
- supports-color
- express-session@1.19.0:
+ express-session@1.19.0(supports-color@1.2.0):
dependencies:
cookie: 0.7.2
cookie-signature: 1.0.7
@@ -36853,7 +36988,7 @@ snapshots:
express-unless@2.1.3: {}
- express@4.22.1:
+ express@4.22.1(supports-color@1.2.0):
dependencies:
accepts: 1.3.8
array-flatten: 1.1.1
@@ -36867,7 +37002,7 @@ snapshots:
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
- finalhandler: 1.3.2
+ finalhandler: 1.3.2(supports-color@1.2.0)
fresh: 0.5.2
http-errors: 2.0.1
merge-descriptors: 1.0.3
@@ -36879,7 +37014,7 @@ snapshots:
qs: 6.15.2
range-parser: 1.2.1
safe-buffer: 5.2.1
- send: 0.19.2
+ send: 0.19.2(supports-color@1.2.0)
serve-static: 1.16.3
setprototypeof: 1.2.0
statuses: 2.0.2
@@ -36889,7 +37024,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- express@4.22.2:
+ express@4.22.2(supports-color@1.2.0):
dependencies:
accepts: 1.3.8
array-flatten: 1.1.1
@@ -36903,7 +37038,7 @@ snapshots:
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
- finalhandler: 1.3.2
+ finalhandler: 1.3.2(supports-color@1.2.0)
fresh: 0.5.2
http-errors: 2.0.1
merge-descriptors: 1.0.3
@@ -36915,7 +37050,7 @@ snapshots:
qs: 6.15.2
range-parser: 1.2.1
safe-buffer: 5.2.1
- send: 0.19.2
+ send: 0.19.2(supports-color@1.2.0)
serve-static: 1.16.3
setprototypeof: 1.2.0
statuses: 2.0.2
@@ -36925,10 +37060,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- express@5.2.1:
+ express@5.2.1(supports-color@5.5.0):
dependencies:
accepts: 2.0.0
- body-parser: 2.2.2
+ body-parser: 2.2.2(supports-color@5.5.0)
content-disposition: 1.1.0
content-type: 1.0.5
cookie: 0.7.2
@@ -36938,7 +37073,7 @@ snapshots:
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
- finalhandler: 2.1.1
+ finalhandler: 2.1.1(supports-color@5.5.0)
fresh: 2.0.0
http-errors: 2.0.1
merge-descriptors: 2.0.0
@@ -36949,8 +37084,8 @@ snapshots:
proxy-addr: 2.0.7
qs: 6.15.2
range-parser: 1.2.1
- router: 2.2.0
- send: 1.2.1
+ router: 2.2.0(supports-color@5.5.0)
+ send: 1.2.1(supports-color@5.5.0)
serve-static: 2.2.1
statuses: 2.0.2
type-is: 2.1.0
@@ -36970,7 +37105,7 @@ snapshots:
extract-stack@2.0.0: {}
- extract-zip@2.0.1:
+ extract-zip@2.0.1(supports-color@5.5.0):
dependencies:
debug: 4.4.3(supports-color@5.5.0)
get-stream: 5.2.0
@@ -37118,9 +37253,9 @@ snapshots:
dependencies:
tslib: 2.8.1
- file-type@21.3.4:
+ file-type@21.3.4(supports-color@5.5.0):
dependencies:
- '@tokenizer/inflate': 0.4.1
+ '@tokenizer/inflate': 0.4.1(supports-color@5.5.0)
strtok3: 10.3.5
token-types: 6.1.2
uint8array-extras: 1.5.0
@@ -37138,7 +37273,7 @@ snapshots:
dependencies:
to-regex-range: 5.0.1
- finalhandler@1.1.2:
+ finalhandler@1.1.2(supports-color@1.2.0):
dependencies:
debug: 2.6.9(supports-color@1.2.0)
encodeurl: 1.0.2
@@ -37150,7 +37285,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- finalhandler@1.3.2:
+ finalhandler@1.3.2(supports-color@1.2.0):
dependencies:
debug: 2.6.9(supports-color@1.2.0)
encodeurl: 2.0.0
@@ -37162,7 +37297,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- finalhandler@2.1.1:
+ finalhandler@2.1.1(supports-color@5.5.0):
dependencies:
debug: 4.4.3(supports-color@5.5.0)
encodeurl: 2.0.0
@@ -37886,19 +38021,19 @@ snapshots:
growly@1.3.0: {}
- gscan@6.3.0:
+ gscan@6.3.0(supports-color@5.5.0):
dependencies:
- '@sentry/node': 10.56.0
+ '@sentry/node': 10.56.0(supports-color@5.5.0)
'@tryghost/config': 2.2.2
- '@tryghost/debug': 2.2.2
+ '@tryghost/debug': 2.2.2(supports-color@5.5.0)
'@tryghost/errors': 1.3.13
- '@tryghost/logging': 4.2.1
- '@tryghost/nql': 0.12.11
+ '@tryghost/logging': 4.2.1(supports-color@5.5.0)
+ '@tryghost/nql': 0.12.11(supports-color@5.5.0)
'@tryghost/pretty-cli': 3.2.2
'@tryghost/server': 3.0.2
'@tryghost/zip': 3.3.3
chalk: 5.6.2
- express: 5.2.1
+ express: 5.2.1(supports-color@5.5.0)
express-handlebars: 8.0.1
glob: 13.0.6
handlebars: 4.7.9
@@ -38497,7 +38632,7 @@ snapshots:
dependencies:
loose-envify: 1.4.0
- ioredis@4.31.0:
+ ioredis@4.31.0(supports-color@5.5.0):
dependencies:
'@ioredis/commands': 1.10.0
cluster-key-slot: 1.1.2
@@ -38838,6 +38973,8 @@ snapshots:
isbinaryfile@4.0.10: {}
+ isbot@5.1.43: {}
+
isexe@2.0.0: {}
isexe@4.0.0: {}
@@ -39687,7 +39824,7 @@ snapshots:
elliptic: 6.6.1
safe-buffer: 5.2.1
- jwks-rsa@3.2.2:
+ jwks-rsa@3.2.2(supports-color@5.5.0):
dependencies:
'@types/jsonwebtoken': 9.0.10
debug: 4.4.3(supports-color@5.5.0)
@@ -39733,7 +39870,7 @@ snapshots:
dependencies:
'@tryghost/database-info': 0.3.22
'@tryghost/errors': 1.3.13
- '@tryghost/logging': 4.2.1
+ '@tryghost/logging': 4.2.1(supports-color@5.5.0)
'@tryghost/promise': 0.3.8
commander: 5.1.0
compare-ver: 2.0.2
@@ -39756,7 +39893,7 @@ snapshots:
- supports-color
- tedious
- knex@0.20.15(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7):
+ knex@0.20.15(mysql2@3.22.5(@types/node@22.19.21))(sqlite3@5.1.7)(supports-color@5.5.0):
dependencies:
colorette: 1.1.0
commander: 4.1.1
@@ -39903,7 +40040,7 @@ snapshots:
ee-log: 3.0.9
ee-types: 2.2.1
- leek@0.0.24:
+ leek@0.0.24(supports-color@1.2.0):
dependencies:
debug: 2.6.9(supports-color@1.2.0)
lodash.assign: 3.2.0
@@ -40031,7 +40168,7 @@ snapshots:
'@embroider/macros': 1.16.13
broccoli-funnel: 3.0.8
broccoli-merge-trees: 4.2.0
- broccoli-stew: 3.0.0
+ broccoli-stew: 3.0.0(supports-color@5.5.0)
broccoli-string-replace: 0.1.2
ember-cli-babel: 7.26.11
ember-cli-htmlbars: 6.3.0
@@ -40048,7 +40185,7 @@ snapshots:
'@ember/jquery': 2.0.0
'@ember/render-modifiers': 2.1.0(@babel/core@7.29.7)(ember-source@3.24.0(@babel/core@7.29.7))
'@embroider/util': 1.13.5(ember-source@3.24.0(@babel/core@7.29.7))
- '@glimmer/component': 1.1.2(@babel/core@7.29.7)
+ '@glimmer/component': 1.1.2(@babel/core@7.29.7)(supports-color@5.5.0)
'@glimmer/tracking': 1.1.2
ember-cli-babel: 7.26.11
ember-cli-htmlbars: 6.3.0
@@ -40528,14 +40665,14 @@ snapshots:
unist-util-is: 6.0.1
unist-util-visit-parents: 6.0.2
- mdast-util-from-markdown@2.0.3:
+ mdast-util-from-markdown@2.0.3(supports-color@5.5.0):
dependencies:
'@types/mdast': 4.0.4
'@types/unist': 3.0.3
decode-named-character-reference: 1.3.0
devlop: 1.1.0
mdast-util-to-string: 4.0.0
- micromark: 4.0.2
+ micromark: 4.0.2(supports-color@5.5.0)
micromark-util-decode-numeric-character-reference: 2.0.2
micromark-util-decode-string: 2.0.1
micromark-util-normalize-identifier: 2.0.1
@@ -40557,7 +40694,7 @@ snapshots:
dependencies:
'@types/mdast': 4.0.4
devlop: 1.1.0
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@5.5.0)
mdast-util-to-markdown: 2.1.2
micromark-util-normalize-identifier: 2.0.1
transitivePeerDependencies:
@@ -40566,7 +40703,7 @@ snapshots:
mdast-util-gfm-strikethrough@2.0.0:
dependencies:
'@types/mdast': 4.0.4
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@5.5.0)
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
@@ -40576,7 +40713,7 @@ snapshots:
'@types/mdast': 4.0.4
devlop: 1.1.0
markdown-table: 3.0.4
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@5.5.0)
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
@@ -40585,14 +40722,14 @@ snapshots:
dependencies:
'@types/mdast': 4.0.4
devlop: 1.1.0
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@5.5.0)
mdast-util-to-markdown: 2.1.2
transitivePeerDependencies:
- supports-color
- mdast-util-gfm@3.1.0:
+ mdast-util-gfm@3.1.0(supports-color@5.5.0):
dependencies:
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@5.5.0)
mdast-util-gfm-autolink-literal: 2.0.1
mdast-util-gfm-footnote: 2.1.0
mdast-util-gfm-strikethrough: 2.0.0
@@ -40969,7 +41106,7 @@ snapshots:
micromark-util-types@2.0.2: {}
- micromark@4.0.2:
+ micromark@4.0.2(supports-color@5.5.0):
dependencies:
'@types/debug': 4.1.13
debug: 4.4.3(supports-color@5.5.0)
@@ -41216,7 +41353,7 @@ snapshots:
moo@0.5.3: {}
- morgan@1.10.1:
+ morgan@1.10.1(supports-color@1.2.0):
dependencies:
basic-auth: 2.0.1
debug: 2.6.9(supports-color@1.2.0)
@@ -42495,7 +42632,7 @@ snapshots:
popper.js@1.16.1: {}
- portfinder@1.0.38:
+ portfinder@1.0.38(supports-color@5.5.0):
dependencies:
async: 3.2.6
debug: 4.4.3(supports-color@5.5.0)
@@ -43241,11 +43378,11 @@ snapshots:
dependencies:
crypto: 0.0.3
- probe-image-size@7.3.0:
+ probe-image-size@7.3.0(supports-color@1.2.0):
dependencies:
lodash.merge: 4.6.2
needle: 2.9.1
- stream-parser: 0.3.1
+ stream-parser: 0.3.1(supports-color@1.2.0)
transitivePeerDependencies:
- supports-color
@@ -43552,7 +43689,7 @@ snapshots:
iconv-lite: 0.7.2
unpipe: 1.0.0
- rc-config-loader@4.1.4:
+ rc-config-loader@4.1.4(supports-color@5.5.0):
dependencies:
debug: 4.4.3(supports-color@5.5.0)
js-yaml: 4.2.0
@@ -43956,10 +44093,10 @@ snapshots:
remark-footnotes@1.0.0: {}
- remark-gfm@4.0.1:
+ remark-gfm@4.0.1(supports-color@5.5.0):
dependencies:
'@types/mdast': 4.0.4
- mdast-util-gfm: 3.1.0
+ mdast-util-gfm: 3.1.0(supports-color@5.5.0)
micromark-extension-gfm: 3.0.0
remark-parse: 11.0.0
remark-stringify: 11.0.0
@@ -43970,7 +44107,7 @@ snapshots:
remark-parse@11.0.0:
dependencies:
'@types/mdast': 4.0.4
- mdast-util-from-markdown: 2.0.3
+ mdast-util-from-markdown: 2.0.3(supports-color@5.5.0)
micromark-util-types: 2.0.2
unified: 11.0.5
transitivePeerDependencies:
@@ -44062,7 +44199,7 @@ snapshots:
require-from-string@2.0.2: {}
- require-in-the-middle@8.0.1:
+ require-in-the-middle@8.0.1(supports-color@5.5.0):
dependencies:
debug: 4.4.3(supports-color@5.5.0)
module-details-from-path: 1.0.4
@@ -44283,7 +44420,7 @@ snapshots:
route-recognizer@0.3.4: {}
- router@2.2.0:
+ router@2.2.0(supports-color@5.5.0):
dependencies:
debug: 4.4.3(supports-color@5.5.0)
depd: 2.0.0
@@ -44430,11 +44567,11 @@ snapshots:
ajv-formats: 2.1.1(ajv@8.20.0)
ajv-keywords: 5.1.0(ajv@8.20.0)
- secretlint@13.0.2:
+ secretlint@13.0.2(supports-color@5.5.0):
dependencies:
'@secretlint/config-creator': 13.0.2
'@secretlint/formatter': 13.0.2
- '@secretlint/node': 13.0.2
+ '@secretlint/node': 13.0.2(supports-color@5.5.0)
'@secretlint/profiler': 13.0.2
'@secretlint/resolver': 13.0.2
'@secretlint/walker': 13.0.2
@@ -44475,7 +44612,7 @@ snapshots:
semver@7.8.4: {}
- send@0.19.2:
+ send@0.19.2(supports-color@1.2.0):
dependencies:
debug: 2.6.9(supports-color@1.2.0)
depd: 2.0.0
@@ -44493,7 +44630,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- send@1.2.1:
+ send@1.2.1(supports-color@5.5.0):
dependencies:
debug: 4.4.3(supports-color@5.5.0)
encodeurl: 2.0.0
@@ -44512,7 +44649,7 @@ snapshots:
sentry-testkit@6.4.1:
dependencies:
body-parser: 1.20.5
- express: 4.22.2
+ express: 4.22.2(supports-color@1.2.0)
transitivePeerDependencies:
- supports-color
@@ -44527,7 +44664,7 @@ snapshots:
encodeurl: 2.0.0
escape-html: 1.0.3
parseurl: 1.3.3
- send: 0.19.2
+ send: 0.19.2(supports-color@1.2.0)
transitivePeerDependencies:
- supports-color
@@ -44536,7 +44673,7 @@ snapshots:
encodeurl: 2.0.0
escape-html: 1.0.3
parseurl: 1.3.3
- send: 1.2.1
+ send: 1.2.1(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -45054,7 +45191,7 @@ snapshots:
to-arraybuffer: 1.0.1
xtend: 4.0.2
- stream-parser@0.3.1:
+ stream-parser@0.3.1(supports-color@1.2.0):
dependencies:
debug: 2.6.9(supports-color@1.2.0)
transitivePeerDependencies:
@@ -45272,7 +45409,7 @@ snapshots:
superagent-throttle@1.0.1: {}
- superagent@10.3.0:
+ superagent@10.3.0(supports-color@5.5.0):
dependencies:
component-emitter: 1.3.1
cookiejar: 2.1.4
@@ -45286,7 +45423,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- superagent@5.3.1:
+ superagent@5.3.1(supports-color@5.5.0):
dependencies:
component-emitter: 1.3.1
cookiejar: 2.1.4
@@ -45302,11 +45439,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- supertest@7.2.2:
+ supertest@7.2.2(supports-color@5.5.0):
dependencies:
cookie-signature: 1.2.2
methods: 1.1.2
- superagent: 10.3.0
+ superagent: 10.3.0(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -45632,7 +45769,7 @@ snapshots:
compression: 1.8.1
consolidate: 1.0.4(@babel/core@7.29.7)(handlebars@4.7.9)(lodash@4.18.1)(mustache@4.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(underscore@1.13.8)
execa: 9.6.1
- express: 4.22.2
+ express: 4.22.2(supports-color@1.2.0)
fireworm: 0.7.2
glob: 13.0.6
http-proxy: 1.18.1
@@ -46051,13 +46188,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- typescript-eslint@8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3):
+ typescript-eslint@8.58.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3):
dependencies:
- '@typescript-eslint/eslint-plugin': 8.58.0(@typescript-eslint/parser@8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
- '@typescript-eslint/parser': 8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
+ '@typescript-eslint/eslint-plugin': 8.58.0(@typescript-eslint/parser@8.58.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.58.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(supports-color@5.5.0)(typescript@5.9.3)
'@typescript-eslint/typescript-estree': 8.58.0(typescript@5.9.3)
- '@typescript-eslint/utils': 8.58.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
- eslint: 9.39.4(jiti@2.7.0)
+ '@typescript-eslint/utils': 8.58.0(eslint@9.39.4(jiti@2.7.0)(supports-color@5.5.0))(typescript@5.9.3)
+ eslint: 9.39.4(jiti@2.7.0)(supports-color@5.5.0)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -46538,7 +46675,7 @@ snapshots:
- supports-color
- typescript
- vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.21)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vite-tsconfig-paths@6.1.1(supports-color@5.5.0)(typescript@5.9.3)(vite@7.3.2(@types/node@22.19.21)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
debug: 4.4.3(supports-color@5.5.0)
globrex: 0.1.2
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 3c2af167ecf..eb5076ac8c8 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -90,6 +90,7 @@ catalog:
eslint-plugin-tailwindcss: 4.0.0-beta.0
fs-extra: 11.3.5
glob: 13.0.6
+ isbot: ^5.1.42
jsdom: 29.1.1
jsonc-parser: 3.3.1
lodash: 4.18.1