mirror of
https://github.com/civitai/civitai.git
synced 2026-09-20 22:08:18 +08:00
f835d678fd
* feat(blocks): compute the per-generation author fee, dark
The App Blocks author fee is moving from a platform-funded percentage bounty
to an ADDITIVE, AUTHOR-SET, VIEWER-PAID per-generation fee: the viewer pays it
on top of the base generation cost, the platform takes no cut and funds
nothing.
fee = max(flatBuzz, pctOfBase x base_generation_buzz)
This is slice 1 of 3 — the computation and its configuration. It is DARK: it
moves no money, writes no column and reads no row. Settlement onto the
licensing-fee rail is slice 2; the author-facing config UI and the viewer-facing
disclosure are slice 3.
What lands:
- src/server/services/blocks/author-fee.ts — the platform defaults (1 flat /
5% of base), the ceilings (flat <= 100, pct <= 100%, and NO minimum on
either), the per-generation-type table, the pure computation, and the one
gated entry point.
- app-blocks-author-fee-enabled — a dedicated global flag, fail-closed. The
flag is read FIRST, before the base is inspected and before the telemetry
module is imported, so with it off the computation is unreachable and emits
nothing at all. The flag does not exist yet, so the as-merged behaviour is
fully dark.
- Three counters and seven log fields on the path spend attribution already
uses, so the settlement slice can be sized from real traffic before anyone is
charged. Nothing is persisted.
Decisions worth flagging:
- NO MIGRATION. The defaults apply to every app including the 24 that already
exist, so absence of per-app configuration means the default applies and
there is nothing to back-fill. Per-app storage only becomes necessary when an
author can edit it, which is slice 3; a table nobody can write to yet is pure
cost on a database whose migrations are applied by hand per environment.
- ZERO BASE MINTS NOTHING, as a rule separate from the formula. A plain
max(1, 5% x 0) is 1, so the flat leg would invent a fee for a generation that
cost nothing. Zero-base generations are real (17 of 600 measured events).
- THE BASE IS WorkflowCost.base, NOT .total AND NOT the attribution row's
buzzAmount. `total` already carries the per-resource model licensing fees,
the lineage fee and the viewer's tips; the attribution row's buzzAmount is
the realized paid debit, i.e. the same gross. A percentage of either would
take a cut of another creator's licensing fee and would compound as
fee-charging resources stack. All three are plain positive Buzz integers, so
nothing downstream can tell them apart — which is why the base is hoisted off
the raw orchestrator response at each submit path and pinned by a seam test.
- The block-facing snapshot's cost stays `{ total }` only. Surfacing `base`
there would publish the cost breakdown to every third-party app for a number
no app has asked for.
- SELF-SPEND IS CHARGED, unlike attribution (which voids it) and unlike
computeSpendShare (which zeroes the share). A bounty is the platform paying
an author out of platform money, so self-spend is a wash; this fee is the
viewer paying the author, and an author using their own app is a viewer. At
settlement that becomes a transfer from an account to itself, which slice 2
must decide about explicitly rather than discover from a rejected
transaction. Called out at the call site.
- The fee STACKS on top of the lineage fee and the model licensing fee. Slice 1
computes this app's own entry only; it wires nothing into the orchestrator's
fees[] array.
- Deliberately NOT a rate card. A RateCard splits platform revenue with an
author; this is new money flowing to the author with no platform share.
* fix(blocks): seed the author-fee type table, and correct what slice 1 claims
Round-0 audit findings applied. The largest is that the platform per-type
table shipped EMPTY, so every production lookup fell to the default and the
resolver's type/coarse arms were unreachable outside their own unit tests.
- SEED `chat-completion` -> 0 flat / 0 pct. Justin's motivating example as a
PLATFORM default rather than something each author rediscovers in slice 3:
chat completion is the highest-frequency generation type an app runs, so a
1 Buzz flat floor per turn is a per-message toll, not a fee on a generation.
Seeding also makes the resolver live, which changes what is worth logging.
- REPLACE the "not a rate card" argument, which was FALSE as written. It said
a rate card splits platform revenue while this is new money with no platform
share; RATE_CARD_V4's own accounting note refutes that - its spend bounty is
"a SEPARATE platform expense paid ON TOP ... NOT a slice carved out of the
viewer's money", with no three-way conservation invariant. The conclusion
stands for two other reasons, both verified: a RateCard is an immutable,
platform-wide snapshot stamped on a row at write time while this fee is
per-app, author-set and mutable; and every card field is a percent of USD
CENTS, where the card's per-row cent flooring is exactly what made the
bounty pay $0.00 (at spendSharePct 5, computeSpendShare returns 0 cents for
every generation under 200 Buzz).
- DROP the two dynamic imports. The comment claimed the flag is read "before
the telemetry module is even imported"; that was false - buzz-attribution
statically imports ~/server/prom/client and blocks.router statically imports
app-blocks-flag, so both were already in the module cache and the .catch
fallback was unreachable. Static imports now, claim deleted not reworded.
- TRIM the Axiom fields 7 -> 4, one instrument per property. Kept
authorFeeSkipped (the only instrument for the flag-disabled denominator),
authorFeeBuzz and authorFeeBaseBuzz (the counters are labelled coarse_type
only, so the per-app / per-isSelfSpend cut lives here) and
authorFeeParamsSource (no counter carries it, and it is genuinely variable
now the table is seeded). Dropped authorFeeObserved (derivable),
authorFeeLeg (duplicates the observed counter's outcome label) and
authorFeeParamsClamped - re-derived after the seed and still a compile-time
constant false. A ledger test now fails when the set grows OR shrinks.
- REMOVE the redundant .catch at the call site. observeBlockAuthorFee is total
by contract, so it was unreachable - and had it been reachable it would file
a throw into the flag-disabled population, one of the two denominators the
slice-2 sizing read divides by.
- MOVE the seam guard to src/server/services/__tests__/no-divergent-author-fee-base.test.ts.
It is a source-text structural guard over a call-site population, which is
the class the no-*.test.ts convention-guard directory exists for. Outside it,
no-lint-rules-script-drift could not see it and test:lint-rules did not run
it, so the one genuinely red-at-base guard here only ran in the full suite.
test:lint-rules: 42 files / 593 tests -> 43 / 598.
- FIX the guard's self-contradicting header, which said the base must be
snapshot.cost?.base - the opposite of its own next paragraph and assertions.
BlockWorkflowSnapshot.cost is deliberately { total } only, so the base comes
off the raw submit response.
- RECORD that the max combinator is operator-specified verbatim ("make it a
'largest of flat or percent'"). Round 0 flagged it unattributed only because
it had not been told.
Red-before/green-after, measured by restoring byType to [] and swapping in
origin/main's router (both md5-verified back): 8 failed / 43 passed -> 51
passed. Mutation sweep 13 mutants, 12 killed each by the guard owning the
property with its own message, plus a comment-only negative control that
SURVIVED. No guard was unkillable; none deleted.
* fix(blocks): apply round-1 audit findings to the author fee
Round 1 returned no red findings; every item below is a yellow or green.
Three of them were properties the code HAD and nothing asserted — each was
found by a mutant that survived a fully green suite, so each fix is carried
here by that same mutant now dying, not by a test merely being added.
ONE REAL DEFECT — an inert constant with two spellings of one policy.
BLOCK_AUTHOR_FEE_MAX_PCT_OF_BASE had exactly two references: its declaration
and one test asserting toBe(1). No implementation read it.
clampBlockAuthorFeeParams capped the percentage leg with
BLOCK_AUTHOR_FEE_BASIS_POINTS_SCALE, which was doing double duty as scale
factor AND ceiling. Setting the constant to 0.5 and deleting the one pinning
line left every test green — i.e. the "pct <= 100%" policy had two
independent spellings that agreed only by coincidence, and slice 3 reads this
constant for author-input validation, at which point a UI bound and an
enforced bound can silently disagree.
Fixed by DERIVING: BLOCK_AUTHOR_FEE_MAX_PCT_BASIS_POINTS is
Math.round(MAX_PCT_OF_BASE * SCALE), and the clamp enforces that. Guarded by
a test asserting both halves — the RELATIONSHIP (an over-ceiling config must
land at exactly the declared fraction of the base) and the LITERAL (so the
pair cannot drift together). Proven with a mutant that un-derives the bound
and deletes both literal pins, leaving only the relationship to catch it: it
dies, naming the divergence.
THREE UNASSERTED PROPERTIES, each proven by its mutant.
- Self-spend IS charged an author fee. This DIVERGES from attribution two
lines up, where isSelfSpend voids the row and zeroes the share — a bounty is
the platform paying an author out of platform money, while the fee is the
viewer paying the author, and an author using their own app is a viewer. The
divergence was argued in ~15 lines of comment and pinned by nothing: routing
self-spend to a null base survived green. Now asserted end-to-end on the log
line (voided row, isSelfSpend true, fee 32 off a 640 base).
- The fee is observed AFTER the successful write, never before. That ordering
is the only thing stopping a P2002 re-poll from double-counting, and the
sizing number is this slice's entire purpose. Hoisting the observation above
the create survived green. Now asserted via the flag read — the one thing
every path through observeBlockAuthorFee does — as "a duplicate emits no
second observation".
- The flag-off docblock overstated its test. The comment claimed "a gate moved
below the computation would show up right here"; it does not, because
computeBlockAuthorFee is pure and a hoisted copy emits no counter either.
WIDENED rather than narrowed: a new test hands observeBlockAuthorFee an args
object whose generationType and config are GETTERS, and asserts neither is
read with the flag off — any invocation of the computation must read its
inputs, purity or not. The same test carries its own positive control (flag
ON reads both). The old comment is corrected to say exactly what the counter
assertions do and do not pin. Harmless in slice 1; load-bearing in slice 2,
where the gated code moves money.
SMALLER ITEMS.
- The comment justifying the removed .catch called the enclosing catch "loud",
which understates it by three effects: a rejection there also skips the
success Axiom line for a row that WAS written, skips the write counter, and
runs refundAppBountyAccrual against a persisted row (inert only while
appOwnerShareCents is identically 0). The unreachability argument is sound
for today's caller, so the comment is corrected rather than the .catch
reinstated — and it now records that a reinstated catch needs a THIRD skip
reason, never flag-disabled and never base-unavailable, both being live
denominators.
- The test comment claiming "the dynamic imports inside observeBlockAuthorFee
resolve to them" is corrected: both imports are static, and vi.mock hoisting
is what makes the mocks apply. The same claim was already deleted from the
source docblock.
- The basis-point quantization now FLOORS instead of rounding.
Math.round(0.049999 * 10_000) is 500 bp, i.e. a full 5%, against the module's
own promise that "a stated 5% never charges more than 5%". Unreachable in
slice 1 (only 0.05 and 0 exist), reachable the moment authors type numbers.
A naive Math.floor is NOT the fix on its own: measured, it loses a basis
point on 573 of the 10,001 exact basis-point inputs, because 0.0029 * 10_000
lands at 28.999999999999996 and an author typing 0.29% would be charged
0.28%. Normalising to 6 decimal places first makes all 10,001 exact (pinned
by an exhaustive test) while still flooring 0.049999 to 499.
- One spelling of the missing-base skip. The Prometheus outcome label said
base_unavailable while the Axiom field said base-unavailable — two spellings
of one concept across the two instruments a sizing read has to join. Both now
come from one exported constant.
MUTATION MATRIX (both covering suites, 90 tests, at this commit):
M14 self-spend routed to a null base SURVIVED -> KILLED
M15 observation hoisted above the write SURVIVED -> KILLED
M4 computation invoked before the gate SURVIVED -> KILLED
M1 ceiling moved, its one pin deleted SURVIVED -> KILLED
M1' + the relationship assertion alone SURVIVED -> KILLED
M13 comment-only (NEGATIVE CONTROL) SURVIVED (as required)
harness positive control (default flat 1->2) KILLED, 7 tests
Each kill was read from the runner's own per-test counts, not an exit code,
and each killing assertion carries its own message naming the property.
No behaviour change at any reachable slice-1 input: the derived ceiling is
10_000 exactly as before, and the only percentages production can produce are
0.05 and 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(blocks): apply round-2 audit findings to the author fee
Round 2 verified all eleven prior claims and returned no red findings. Three of
the four items here are false sentences in shipped comments — the class this PR
has been fixing for two rounds — and one is a latent code issue.
F1 — THE PR SAID THE FLIPT FLAG DOES NOT EXIST. IT NOW DOES.
app-blocks-author-fee-enabled was created in the flag store at enabled: false
after this branch's last commit, deliberately: round 1 found that an ABSENT key
makes the evaluation throw, bypass its cache and write a console.error on every
App Blocks generation submit, indefinitely.
Verified live before rewriting, rather than taken on trust — in the civitai-app
environment the flag is BOOLEAN_FLAG_TYPE, enabled: false, with empty variants /
rules / rollouts, and a global boolean evaluation returns
enabled:false, reason:DEFAULT_EVALUATION_REASON, segmentKeys:[]. Negative
control: a fabricated key 404s.
Four sites corrected, not three. The brief named author-fee.ts, app-blocks-flag.ts
and the PR body; a fourth operator note in author-fee.ts still read "create
app-blocks-author-fee-enabled", which is the same stale claim in imperative form.
The conclusion survives — as-merged behaviour is dark — but the REASON changes
and the safety property is genuinely weaker than the old wording claimed. An
absent key had to be CREATED by an operator before anyone could enable the fee;
a present base-false flag is one toggle away, with no deploy and no review. So
the comments now say the posture is flag STATE, not structure, and "cannot
regress open" is withdrawn rather than reworded.
F2 — THE LABEL UNIFICATION WAS ONE SITE SHORT, AT THE METRIC'S DECLARATION.
packages/civitai-telemetry/src/client.ts still enumerated base_unavailable
(underscore) while the emitted value is base-unavailable (hyphen). That comment
is the ONLY place in the repo enumerating the label's value set and it sits
directly above blockAuthorFeeObservedCounter, where an operator writing the
slice-2 sizing join looks: they would query outcome="base_unavailable", get an
empty series, and read it as "no generation lacked a base" — exactly the silent
empty join the unification exists to prevent.
The two other base_unavailable occurrences (author-fee.ts and the test) are
historical narration of the form "it used to say X here and Y there" and are
deliberately left alone.
F4 — THE CEILING CONSTANT ROUNDED THE OPPOSITE WAY TO THE QUANTIZATION IT GOVERNS.
BLOCK_AUTHOR_FEE_MAX_PCT_BASIS_POINTS derived with Math.round while
toBasisPoints — added in the same commit, a few lines above — deliberately
floors, because in the module's own words "every rounding goes toward the
viewer". Inert today (MAX_PCT_OF_BASE = 1 gives 10000 either way, and a guard
pins it) and live the moment slice 3 or a policy change sets a non-basis-point
ceiling: 0.123456 derives to 1235 bp = 12.35%, a ceiling ABOVE the declared
policy, the one direction the module says it never rounds.
The derivation is now blockAuthorFeeCeilingBasisPoints(maxPctOfBase), which
calls toBasisPoints. It is a named function rather than an expression inlined
into the constant for a specific reason, recorded at the site: at a declared
ceiling of 1 no assertion about the constant can tell floor from round, so
inlining it makes the defect unkillable rather than merely dormant. A comment on
toBasisPoints now names both callers, so changing its direction is visibly a
change to the enforced ceiling too.
Carried by a mutant, not by a test merely being added: reverting the derivation
to Math.round — which is the pre-change code on the same input — fails exactly
one test, with its own message, "the ceiling derivation ROUNDS - it must floor,
like the quantization it governs: expected 1235 to be 1234". The guard also pins
1235 as the mutant's answer so the case cannot quietly stop discriminating, and
asserts the property at four ceilings that each round up.
F3 — A CONTROL MESSAGE NAMED THE WRONG CAUSE ON AN OVER-READ.
The positive control asserted toBe(1) under the message "probe wired to nothing
— flag ON read nothing", which is right only for 0. Round 2 demonstrated it: an
innocuous second read past the gate produced "probe wired to nothing - flag ON
read nothing: expected 2 to be 1", sending a maintainer into the probe when the
real change is downstream of the gate. Split into a toBeGreaterThan(0) control —
the claim the zeros above actually need — and a separate exactness pin naming
the other direction. Verified by planting a second args.generationType read past
the gate: it now fails with "generationType was read more than once past the
gate - a change downstream of the flag, not a broken probe: expected 2 to be 1".
TESTING
Two covering suites: 90 passed (90) at 3a81dc7 -> 91 passed (91) here, one new
test. With the seam guard: 96 passed (96) across 3 files. The telemetry package
suite is 38 passed (38). Prettier clean on all four files, validated against a
deliberately misformatted copy that it correctly rejected.
Mutation sweep, each mutant restored and byte-compared with cmp afterwards:
M1 platform table emptied KILLED 6 failed / 85 passed
M1' ceiling un-derived, pins deleted KILLED 2 failed / 89 passed
M4 resolver exact-match arm deleted KILLED 6 failed / 85 passed
M5 dark gate deleted KILLED 3 failed / 88 passed
M14 label unification reverted KILLED 2 failed / 89 passed
M15 counter outcome label desynced KILLED 1 failed / 90 passed
F4 ceiling reverted to Math.round KILLED 1 failed / 90 passed
M13 negative control, comment-only SURVIVED 91 passed
NC2 negative control, comment-only SURVIVED 91 passed
Two negative controls rather than one, in two different files, so a green sweep
is not a claim about a runner wired to always-kill.
* fix(blocks): wire the fourth submit path, fee-free while its price is a cap
A fourth `recordSpendAttribution` call site landed on `main` mid-review —
`submitPassThroughStepWorkflow` — passing no base. The seam guard caught it and
went red, which is the guard doing its job: nothing in this branch's own commits
broke.
The new site is WIRED rather than exempted (an exemption is what the ledger
exists to make impossible), but it does NOT charge a fee while its price is a
CAP. `WorkflowCost.variable` means the quoted price is a ceiling that settles
lower: the viewer is charged the maximum up front and refunded the difference
once the provider reports the work delivered. A percentage of that is a fee on
money they did not ultimately spend. That path quotes a ceiling and refunds
`ceiling - actual` on settle, so it is the motivating case.
The cap flag is hoisted off the raw orchestrator response and threaded from ALL
FOUR submit paths, not just the new one — the predicate lives in one place
(`observeBlockAuthorFee`), and a cap-priced generation on any path is the same
question.
A cap gets its OWN counted skip reason, `price-is-cap`, checked BEFORE the base
check. It is deliberately not `base-unavailable`: that is the RECOVERABLE blind
spot, one of the two denominators the slice-2 sizing read divides by, and a
cap-priced generation would not have charged even with a base in hand. The value
is enumerated in `packages/civitai-telemetry/src/client.ts`, the only place in
the repo that enumerates that label's value set, in the same hyphenated spelling
the Axiom `authorFeeSkipped` field uses so the two instruments still join.
"No fee on a cap" is the CURRENT answer, made explicit and testable, not settled
policy — slice 2 owns what a cap-priced path should charge, and now has a counted
population to decide it against instead of an unlabelled blind spot.
Seam guard: ledger 3 -> 4, plus a second relationship over the same population
(every call site must thread the cap flag, and it must come from
`submitted.cost.variable`, never from `snapshot` — whose `cost` is `{ total }`
only, so reading one would be `undefined`, i.e. the fail-OPEN direction).
* fix(blocks): apply round-3 audit findings to the author fee
Three green findings from the final audit round.
1. The ceiling constant could be re-inlined undetected. Replacing the
initialiser of BLOCK_AUTHOR_FEE_MAX_PCT_BASIS_POINTS with
Math.round(MAX_PCT_OF_BASE * SCALE) — leaving the named derivation
function in place — SURVIVED the whole suite, because every behavioural
pin agrees while floor and round agree at the shipped policy of 1. The
"DO NOT INLINE IT BACK" comment was the only guard, and prose is not a
guard. Adds a source-text assertion, the same technique the router seam
guard already uses: the constant's initialiser must name
blockAuthorFeeCeilingBasisPoints and must perform no arithmetic of its
own. That mutant now goes red with its own message.
2. The .catch comment in buzz-attribution.service went stale. It said a
reinstated catch would need "a THIRD skip reason … never flag-disabled
and never base-unavailable"; price-is-cap has since landed, so it would
be the FOURTH and the "never" list had a hole exactly where the newest
reason sat. Restated against BlockAuthorFeeSkipReason as a whole, so the
next addition cannot re-stale it.
3. An absent WorkflowCost.variable is treated as a final price, and that is
the fail-open direction — undocumented until now. The field is
?: null | boolean, so === true merges "not a cap" with "the orchestrator
did not say", and a field that stops being sent silently resumes charging
on cap-priced generations. Documented at the txt2img hoist, at the
pass-through hoist, and in the seam guard, which pins that the flag comes
off the right OBJECT and says nothing about it being absent. Behaviour
unchanged: the tri-state policy is slice 2's to decide.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
457 lines
22 KiB
JSON
457 lines
22 KiB
JSON
{
|
|
"name": "model-share",
|
|
"version": "5.1.110",
|
|
"private": true,
|
|
"packageManager": "pnpm@10.28.1",
|
|
"engines": {
|
|
"node": ">=24.0.0 <25"
|
|
},
|
|
"scripts": {
|
|
"preinstall": "npx only-allow pnpm",
|
|
"start": "next start",
|
|
"start-debug": "NODE_OPTIONS='--inspect' next start",
|
|
"build:workers": "node scripts/build-workers.mjs",
|
|
"clean": "node -e \"fs.rmSync('.next',{recursive:true,force:true})\"",
|
|
"predev": "pnpm build:workers",
|
|
"dev": "next dev",
|
|
"dev:auth": "pnpm --filter @civitai/auth-app dev",
|
|
"dev:moderator": "pnpm --filter @civitai/moderator-app dev",
|
|
"dev:creator-studio": "pnpm --filter @civitai/creator-studio-app dev",
|
|
"dev:training-studio": "pnpm --filter @civitai/training-studio-app dev",
|
|
"dev:storage": "pnpm --filter @civitai/storage-app dev",
|
|
"dev-low": "cross-env NODE_OPTIONS=\"--max_old_space_size=6144\" next dev",
|
|
"dev-debug": "pnpm build:workers && cross-env NODE_OPTIONS=\"--max_old_space_size=8192 --inspect\" next dev",
|
|
"dev-snap": "cross-env NODE_OPTIONS=\"--max_old_space_size=8192 --heapsnapshot-near-heap-limit=3\" next dev",
|
|
"dev:daemon": "node .claude/skills/dev-server/console.mjs",
|
|
"dev:rgb": "node .claude/skills/dev-server/cli.mjs rgb start",
|
|
"dev:rgb:stop": "node .claude/skills/dev-server/cli.mjs rgb stop",
|
|
"dev:rgb:status": "node .claude/skills/dev-server/cli.mjs rgb status",
|
|
"prod": "cross-env NODE_ENV=production next dev",
|
|
"boost": "next-boost",
|
|
"release:base": "git checkout release && git pull --rebase && git rebase main && git push --force-with-lease && git checkout main",
|
|
"release:major": "git pull && npm version major && git push --follow-tags && pnpm run release:base",
|
|
"release:minor": "git pull && npm version minor && git push --follow-tags && pnpm run release:base",
|
|
"release:patch": "git pull && npm version patch && git push --follow-tags && pnpm run release:base",
|
|
"release": "pnpm run release:patch",
|
|
"release:auth": "pnpm run release:auth:patch",
|
|
"release:auth:patch": "node scripts/release-app.mjs apps/auth auth-app-v patch",
|
|
"release:auth:minor": "node scripts/release-app.mjs apps/auth auth-app-v minor",
|
|
"release:auth:major": "node scripts/release-app.mjs apps/auth auth-app-v major",
|
|
"release:notifications": "pnpm run release:notifications:patch",
|
|
"release:notifications:patch": "node scripts/release-app.mjs apps/notifications notifications-v patch",
|
|
"release:notifications:minor": "node scripts/release-app.mjs apps/notifications notifications-v minor",
|
|
"release:notifications:major": "node scripts/release-app.mjs apps/notifications notifications-v major",
|
|
"release:creator-studio": "pnpm run release:creator-studio:patch",
|
|
"release:creator-studio:patch": "node scripts/release-app.mjs apps/creator-studio creator-studio-v patch",
|
|
"release:creator-studio:minor": "node scripts/release-app.mjs apps/creator-studio creator-studio-v minor",
|
|
"release:creator-studio:major": "node scripts/release-app.mjs apps/creator-studio creator-studio-v major",
|
|
"release:storage": "pnpm run release:storage:patch",
|
|
"release:storage:patch": "node scripts/release-app.mjs apps/storage storage-v patch",
|
|
"release:storage:minor": "node scripts/release-app.mjs apps/storage storage-v minor",
|
|
"release:storage:major": "node scripts/release-app.mjs apps/storage storage-v major",
|
|
"release:event-engine": "pnpm run release:event-engine:patch",
|
|
"release:event-engine:patch": "node scripts/release-app.mjs apps/event-engine event-engine-v patch",
|
|
"release:event-engine:minor": "node scripts/release-app.mjs apps/event-engine event-engine-v minor",
|
|
"release:event-engine:major": "node scripts/release-app.mjs apps/event-engine event-engine-v major",
|
|
"release:moderator": "pnpm run release:moderator:patch",
|
|
"release:moderator:patch": "node scripts/release-app.mjs apps/moderator moderator-v patch",
|
|
"release:moderator:minor": "node scripts/release-app.mjs apps/moderator moderator-v minor",
|
|
"release:moderator:major": "node scripts/release-app.mjs apps/moderator moderator-v major",
|
|
"release:training-studio": "pnpm run release:training-studio:patch",
|
|
"release:training-studio:patch": "node scripts/release-app.mjs apps/training-studio training-studio-v patch",
|
|
"release:training-studio:minor": "node scripts/release-app.mjs apps/training-studio training-studio-v minor",
|
|
"release:training-studio:major": "node scripts/release-app.mjs apps/training-studio training-studio-v major",
|
|
"yolo": "pnpm run release",
|
|
"seed:scanner-policies": "tsx --env-file=.env scripts/seed-scanner-policies.ts",
|
|
"prebuild": "pnpm build:workers",
|
|
"build": "next build",
|
|
"build:dev": "pnpm build:workers && cross-env NODE_OPTIONS=\"--max_old_space_size=16384\" next build",
|
|
"build:analyze": "cross-env NODE_OPTIONS=\"--max_old_space_size=16384\" ANALYZE=true next build",
|
|
"size": "node scripts/bundle-budget.mjs",
|
|
"deploy": "pnpm run build && pnpm run db:deploy",
|
|
"postinstall": "pnpm run db:generate",
|
|
"typecheck": "node scripts/typecheck.mjs",
|
|
"lint": "eslint src/ --cache --cache-strategy metadata",
|
|
"lint:packages": "eslint packages --ext .ts",
|
|
"eslint": "cross-env TIMING=1 eslint src/ --quiet --cache --cache-strategy metadata",
|
|
"prettier:check": "node scripts/prettier-changed.mjs check",
|
|
"prettier:write": "node scripts/prettier-changed.mjs write",
|
|
"db:ui": "prisma studio",
|
|
"db:pull": "prisma db pull",
|
|
"db:push": "prisma db push",
|
|
"db:migrate": "node scripts/prisma-migrate-with-views-workaround.mjs",
|
|
"db:migrate:empty": "node scripts/create-empty-migration.mjs",
|
|
"db:applied": "node scripts/prisma-mark-migration-applied.mjs",
|
|
"db:deploy": "node scripts/prisma-migrate-with-views-workaround.mjs -p && npm run db:program",
|
|
"db:program": "node scripts/prisma-prepare-programmability.mjs",
|
|
"db:generate": "node scripts/generate-slim-schema.js && prisma generate --no-hints",
|
|
"db:check-generated": "pnpm run db:generate && git diff --exit-code -- packages/civitai-db-schema/src",
|
|
"db:seed": "prisma db seed",
|
|
"db:moderator:pull": "prisma db pull --schema apps/moderator/prisma/schema.prisma",
|
|
"db:moderator:generate": "prisma generate --no-hints --schema apps/moderator/prisma/schema.prisma",
|
|
"share": "ngrok http 3000",
|
|
"prepare": "husky",
|
|
"analyze": "cross-env ANALYZE=true next build",
|
|
"analyze:server": "cross-env BUNDLE_ANALYZE=server next build",
|
|
"analyze:browser": "cross-env BUNDLE_ANALYZE=browser next build",
|
|
"tsc:trace": "cross-env NODE_OPTIONS=\"--max_old_space_size=8192\" tsc --generateTrace ./trace --incremental false",
|
|
"tsc:analyze": "npx analyze-trace trace",
|
|
"test": "cross-env NODE_ENV=development npx playwright test",
|
|
"test:ui": "cross-env NODE_ENV=development npx playwright test --ui",
|
|
"test:gen": "cross-env NODE_ENV=development npx playwright codegen",
|
|
"test:reset": "make bootstrap-db",
|
|
"test:unit": "vitest --project 'unit*'",
|
|
"test:unit:run": "node scripts/test-unit-run.mjs",
|
|
"test:unit:coverage": "vitest run --project 'unit*' --coverage",
|
|
"test:packages": "vitest --project '@civitai/*'",
|
|
"test:packages:run": "vitest run --project '@civitai/*'",
|
|
"test:apps": "vitest --project 'app:*'",
|
|
"test:apps:run": "vitest run --project 'app:*'",
|
|
"test:lint-rules": "vitest run --project 'unit*' src/server/notifications/__tests__/notification-settings-polarity.test.ts src/server/schema/__tests__/track.addView.schema.test.ts src/server/services/__tests__/hub-filter-parity.test.ts src/server/services/__tests__/poi-checks-strip-benign-phrases.test.ts src/server/services/__tests__/video-leaderboard-badge-staging.test.ts src/server/services/__tests__/no-agent-ground-truth-write.test.ts src/server/services/__tests__/no-coerce-boolean-in-api.test.ts src/server/services/__tests__/no-direct-shared-module-mock.test.ts src/server/services/__tests__/no-divergent-author-fee-base.test.ts src/server/services/__tests__/no-divergent-can-generate-derivation.test.ts src/server/services/__tests__/no-divergent-model-recency-derivation.test.ts src/server/services/__tests__/no-divergent-generation-submit-payload.test.ts src/server/services/__tests__/no-divergent-paid-gate-derivation.test.ts src/server/services/__tests__/no-divergent-safetensor-rule.test.ts src/server/services/__tests__/no-doubled-free-slot-noun.test.ts src/server/services/__tests__/no-hand-typed-redis-key-constants.test.ts src/server/services/__tests__/no-io-in-transaction.test.ts src/server/services/__tests__/no-job-kind-on-remix-mint.test.ts src/server/services/__tests__/no-lint-rules-script-drift.test.ts src/server/services/__tests__/no-menu-target-tooltip-nesting.test.ts src/server/services/__tests__/no-module-scope-cache.test.ts src/server/services/__tests__/no-pk-addressed-engagement-write.test.ts src/server/services/__tests__/no-server-infra-in-app-graph.test.ts src/server/services/__tests__/no-sharp-outside-native-project.test.ts src/server/services/__tests__/no-ssr-divergent-media-query.test.ts src/server/services/__tests__/no-stale-moderator-route-probe.test.ts src/server/services/__tests__/no-static-html2canvas-import.test.ts src/server/services/__tests__/no-unbounded-paging-fake.test.ts src/server/services/__tests__/no-unbumped-draft-status-write.test.ts src/server/services/__tests__/no-unguarded-billable-submit.test.ts src/server/services/__tests__/no-unguarded-block-bridge-token.test.ts src/server/services/__tests__/no-unguarded-block-rest-token.test.ts src/server/services/__tests__/no-unguarded-user-text.test.ts src/server/services/__tests__/no-unloadable-image-fixture.test.ts src/server/services/__tests__/no-unmoderated-blob-retraction.test.ts src/server/services/__tests__/no-unmuteable-comment-processor.test.ts src/server/services/__tests__/no-unpriced-default-model.test.ts src/server/services/__tests__/no-unroled-image-resource-match.test.ts src/server/services/__tests__/no-unscoped-email-verification-exemption.test.ts src/server/services/__tests__/no-untruthy-query-gate.test.ts src/server/services/__tests__/no-unverified-provenance-write.test.ts src/server/services/__tests__/no-unwrapped-knob-rotation.test.ts src/server/services/__tests__/no-wholesale-module-mock.test.ts",
|
|
"test:component": "node scripts/test-component-run.mjs",
|
|
"test:component:watch": "vitest --project component",
|
|
"test:geometry": "vitest run --project geometry",
|
|
"test:geometry:watch": "vitest --project geometry",
|
|
"meilisearch:migrate": "NODE_ENV=development tsx scripts/oneoffs/meilisearch-migration.ts",
|
|
"tsscript": "NODE_ENV=development tsx",
|
|
"madge:orphans": "madge --orphans --image ./public/orphans-graph.svg --ts-config ./tsconfig.json --extensions ts,tsx src/",
|
|
"depcheck": "depcheck",
|
|
"generate-types": "typed-scss-modules src",
|
|
"ts-script": "NODE_ENV=development tsx",
|
|
"generate:moderator-endpoints": "node scripts/generate-moderator-endpoint-catalog.mjs"
|
|
},
|
|
"prisma": {
|
|
"schema": "packages/civitai-db-schema/prisma/schema.prisma",
|
|
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} packages/civitai-db-schema/prisma/seed.ts"
|
|
},
|
|
"dependencies": {
|
|
"@aws-sdk/client-s3": "^3.490.0",
|
|
"@aws-sdk/lib-storage": "^3.490.0",
|
|
"@aws-sdk/s3-request-presigner": "^3.490.0",
|
|
"@axiomhq/axiom-node": "^0.12.0",
|
|
"@civitai/app-sdk": "^0.14.0",
|
|
"@civitai/auth": "workspace:*",
|
|
"@civitai/buzz": "workspace:*",
|
|
"@civitai/client": "0.2.0-beta.98",
|
|
"@civitai/cybertipline-tools": "^0.1.0",
|
|
"@civitai/db-queries": "workspace:*",
|
|
"@civitai/db-schema": "workspace:*",
|
|
"@civitai/flipt": "workspace:*",
|
|
"@civitai/generation-metadata": "^0.3.0",
|
|
"@civitai/moderation": "workspace:*",
|
|
"@civitai/next-axiom": "^0.17.0",
|
|
"@civitai/orchestration-client": "0.2.0-beta.106",
|
|
"@civitai/shared": "workspace:*",
|
|
"@clavata/sdk": "^0.2.3",
|
|
"@clickhouse/client": "^0.2.2",
|
|
"@coinbase/cdp-sdk": "^1.13.0",
|
|
"@discordjs/rest": "^2.6.0",
|
|
"@dnd-kit/core": "^6.1.0",
|
|
"@dnd-kit/sortable": "^8.0.0",
|
|
"@dnd-kit/utilities": "^3.2.2",
|
|
"@emotion/react": "^11.10.4",
|
|
"@essentials/one-key-map": "^1.2.0",
|
|
"@flipt-io/flipt-client-js": "^0.2.0",
|
|
"@floating-ui/dom": "^1.6.0",
|
|
"@google-cloud/recaptcha-enterprise": "^5.1.1",
|
|
"@grafana/faro-web-sdk": "2.8.2",
|
|
"@grafana/faro-web-tracing": "2.8.2",
|
|
"@headlessui/react": "2.2",
|
|
"@hookform/resolvers": "^5.1.1",
|
|
"@mantine/core": "^7.17.7",
|
|
"@mantine/dates": "^7.17.7",
|
|
"@mantine/dropzone": "^7.17.7",
|
|
"@mantine/hooks": "^7.17.7",
|
|
"@mantine/modals": "^7.17.7",
|
|
"@mantine/notifications": "^7.17.7",
|
|
"@mantine/nprogress": "^7.17.7",
|
|
"@mantine/tiptap": "^7.17.7",
|
|
"@marsidev/react-turnstile": "^1.0.1",
|
|
"@meilisearch/instant-meilisearch": "0.13.5",
|
|
"@microsoft/signalr": "^7.0.10",
|
|
"@next/bundle-analyzer": "^16.3.0",
|
|
"@next/third-parties": "^15.0.3",
|
|
"@node-oauth/oauth2-server": "^5.3.0",
|
|
"@number-flow/react": "^0.5.7",
|
|
"@okikio/sharedworker": "^1.1.0",
|
|
"@openrouter/sdk": "^0.5.1",
|
|
"@opentelemetry/api": "^1.9.0",
|
|
"@opentelemetry/api-logs": "^0.219.0",
|
|
"@opentelemetry/core": "2.9.0",
|
|
"@opentelemetry/exporter-logs-otlp-proto": "^0.219.0",
|
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.219.0",
|
|
"@opentelemetry/instrumentation": "0.219.0",
|
|
"@opentelemetry/instrumentation-http": "^0.213.0",
|
|
"@opentelemetry/instrumentation-redis": "^0.61.0",
|
|
"@opentelemetry/resources": "2.9.0",
|
|
"@opentelemetry/sdk-logs": "^0.219.0",
|
|
"@opentelemetry/sdk-node": "^0.219.0",
|
|
"@opentelemetry/sdk-trace-node": "^2.5.0",
|
|
"@opentelemetry/sdk-trace-web": "2.9.0",
|
|
"@opentelemetry/semantic-conventions": "^1.39.0",
|
|
"@paddle/paddle-js": "^1.2.1",
|
|
"@paddle/paddle-node-sdk": "^1.4.1",
|
|
"@paypal/react-paypal-js": "^8.1.3",
|
|
"@prisma/client": "^6.3.0",
|
|
"@prisma/instrumentation": "^7.4.2",
|
|
"@pyroscope/nodejs": "0.6.2",
|
|
"@react-hook/window-size": "^3.1.1",
|
|
"@react-pdf/renderer": "^3.3.8",
|
|
"@stripe/react-stripe-js": "^2.4.0",
|
|
"@stripe/stripe-js": "^2.2.0",
|
|
"@tabler/icons-react": "^3.7.0",
|
|
"@tanstack/react-query": "^5.101.0",
|
|
"@tanstack/react-query-devtools": "^5.101.0",
|
|
"@tanstack/react-virtual": "^3.13.12",
|
|
"@tiptap/core": "3.16.0",
|
|
"@tiptap/extension-color": "3.16.0",
|
|
"@tiptap/extension-heading": "3.16.0",
|
|
"@tiptap/extension-image": "3.16.0",
|
|
"@tiptap/extension-link": "3.16.0",
|
|
"@tiptap/extension-mention": "3.16.0",
|
|
"@tiptap/extension-placeholder": "3.16.0",
|
|
"@tiptap/extension-text": "3.16.0",
|
|
"@tiptap/extension-text-style": "3.16.0",
|
|
"@tiptap/extension-underline": "3.16.0",
|
|
"@tiptap/extension-youtube": "3.16.0",
|
|
"@tiptap/extensions": "3.16.0",
|
|
"@tiptap/html": "3.16.0",
|
|
"@tiptap/pm": "3.16.0",
|
|
"@tiptap/react": "3.16.0",
|
|
"@tiptap/starter-kit": "3.16.0",
|
|
"@tiptap/static-renderer": "3.16.0",
|
|
"@tiptap/suggestion": "3.16.0",
|
|
"@trpc/client": "^11.17.0",
|
|
"@trpc/next": "^11.17.0",
|
|
"@trpc/react-query": "^11.17.0",
|
|
"@trpc/server": "^11.17.0",
|
|
"@types/stream-to-blob": "^2.0.0",
|
|
"@typescript/analyze-trace": "^0.10.1",
|
|
"algoliasearch": "^4.23.3",
|
|
"archiver": "^6.0.1",
|
|
"blurhash": "^2.0.4",
|
|
"chalk": "^5.2.0",
|
|
"chart.js": "^4.4.0",
|
|
"chartjs-adapter-dayjs-4": "^1.0.4",
|
|
"circular-dependency-plugin": "^5.2.2",
|
|
"cloudflare": "^2.9.1",
|
|
"clsx": "^2.1.1",
|
|
"compromise": "^14.14.4",
|
|
"cookies-next": "^2.1.1",
|
|
"dayjs": "^1.11.12",
|
|
"decimal.js": "^10.5.0",
|
|
"devalue": "5.8.1",
|
|
"diff": "4.0.2",
|
|
"discord-api-types": "^0.38.37",
|
|
"discord.js": "^14.7.1",
|
|
"dotenv": "^16.4.5",
|
|
"draft-js": "^0.11.7",
|
|
"embla-carousel": "^8.6.0",
|
|
"embla-carousel-autoplay": "^8.5.2",
|
|
"embla-carousel-react": "^8.5.2",
|
|
"exceljs": "^4.4.0",
|
|
"exifreader": "^4.39.0",
|
|
"fastest-levenshtein": "^1.0.16",
|
|
"file-saver": "^2.0.5",
|
|
"form-graph": "^0.4.2",
|
|
"google-auth-library": "^9.15.0",
|
|
"googleapis": "^144.0.0",
|
|
"gray-matter": "^4.0.3",
|
|
"happy-dom": "^20.0.2",
|
|
"he": "^1.2.0",
|
|
"html-to-text": "^9.0.5",
|
|
"html2canvas-pro": "2.3.8",
|
|
"htmlparser2": "8.0.2",
|
|
"idb-keyval": "^6.2.0",
|
|
"immer": "^9.0.15",
|
|
"instantsearch.js": "4.64.1",
|
|
"jose": "^6.0.11",
|
|
"js-yaml": "^4.1.1",
|
|
"jsonwebtoken": "^9.0.1",
|
|
"jssha": "^3.3.1",
|
|
"jszip": "^3.10.1",
|
|
"konva": "^10.0.12",
|
|
"linkify-react": "^4.1.3",
|
|
"linkifyjs": "^4.1.3",
|
|
"lodash-es": "^4.17.21",
|
|
"lottie-react": "^2.4.1",
|
|
"lru-cache": "^11.2.2",
|
|
"mantine-react-table": "^2.0.0-beta.9",
|
|
"masonic": "^3.7.0",
|
|
"meilisearch": "0.33.0",
|
|
"motion": "^11.11.17",
|
|
"msgpackr": "^1.10.2",
|
|
"next": "^16.3.1",
|
|
"nodemailer": "^6.8.0",
|
|
"obscenity": "^0.4.5",
|
|
"openai": "^4.73.0",
|
|
"p-limit": "^6.2.0",
|
|
"path-to-regexp": "^6.2.1",
|
|
"pg": "^8.11.3",
|
|
"prom-client": "^14.2.0",
|
|
"qrcode.react": "^4.2.0",
|
|
"query-string": "^7.1.1",
|
|
"rand-seed": "^1.0.2",
|
|
"randomstring": "^1.3.0",
|
|
"react": "^18.3.1",
|
|
"react-blurhash": "^0.2.0",
|
|
"react-chartjs-2": "^5.2.0",
|
|
"react-dom": "^18.3.1",
|
|
"react-easy-crop": "^5.4.1",
|
|
"react-highlight-within-textarea": "^3.2.1",
|
|
"react-hook-form": "^7.71.1",
|
|
"react-instantsearch": "7.12.0",
|
|
"react-instantsearch-router-nextjs": "7.12.0",
|
|
"react-intersection-observer": "^9.4.0",
|
|
"react-joyride": "^2.9.3",
|
|
"react-konva": "^18.2.14",
|
|
"react-markdown": "^9.0.1",
|
|
"react-social-media-embed": "^2.5.9",
|
|
"recheck": "^4.5.0",
|
|
"redis": "^5.8.3",
|
|
"rehype-raw": "^7.0.0",
|
|
"rehype-stringify": "^10.0.1",
|
|
"remark-breaks": "^4.0.0",
|
|
"remark-gfm": "^4.0.0",
|
|
"remark-parse": "^11.0.0",
|
|
"remark-rehype": "^11.1.1",
|
|
"request-ip": "^3.3.0",
|
|
"sanitize-html": "2.12.1",
|
|
"sass": "^1.82.0",
|
|
"semver": "^7.6.0",
|
|
"sharp": "^0.32.6",
|
|
"slate": "^0.94.1",
|
|
"slate-history": "^0.93.0",
|
|
"slate-react": "^0.95.0",
|
|
"slugify": "^1.6.5",
|
|
"socket.io-client": "^4.5.4",
|
|
"source-map": "^0.7.4",
|
|
"sqids": "^0.3.0",
|
|
"stacktrace-parser": "^0.1.10",
|
|
"stream-to-blob": "^2.0.1",
|
|
"stripe": "^11.6.0",
|
|
"superjson": "^2.2.6",
|
|
"three": "^0.180.0",
|
|
"trie-memoize": "^1.2.0",
|
|
"unfurl.js": "^6.4.0",
|
|
"unified": "^11.0.5",
|
|
"use-sound": "^5.0.0",
|
|
"uuid": "^9.0.0",
|
|
"viem": "^2.30.6",
|
|
"xml2js": "^0.6.2",
|
|
"yaml": "^2.8.1",
|
|
"zod": "^4.0.17",
|
|
"zustand": "^4.3.7"
|
|
},
|
|
"devDependencies": {
|
|
"@anthropic-ai/claude-agent-sdk": "^0.2.6",
|
|
"@electric-sql/pglite": "^0.4.6",
|
|
"@faker-js/faker": "^9.0.3",
|
|
"@ladle/react": "^5.1.1",
|
|
"@next/eslint-plugin-next": "^15.5.19",
|
|
"@playwright/test": "^1.57.0",
|
|
"@prisma/generator-helper": "^5.22.0",
|
|
"@types/archiver": "^6.0.2",
|
|
"@types/cloudflare": "^2.7.9",
|
|
"@types/diff": "4.0.2",
|
|
"@types/file-saver": "^2.0.7",
|
|
"@types/he": "^1.2.3",
|
|
"@types/html-to-text": "^9.0.4",
|
|
"@types/js-yaml": "^4.0.9",
|
|
"@types/jsonwebtoken": "^9.0.2",
|
|
"@types/lodash-es": "^4.17.7",
|
|
"@types/mailchimp__mailchimp_marketing": "^3.0.12",
|
|
"@types/marked": "^4.0.7",
|
|
"@types/node": "24.13.3",
|
|
"@types/node-os-utils": "^1.3.1",
|
|
"@types/nodemailer": "^6.4.7",
|
|
"@types/offscreencanvas": "^2019.7.3",
|
|
"@types/pg": "^8.11.0",
|
|
"@types/pg-format": "^1.0.5",
|
|
"@types/randomstring": "^1.1.8",
|
|
"@types/react": "18.0.14",
|
|
"@types/react-dom": "18.0.5",
|
|
"@types/request-ip": "^0.0.37",
|
|
"@types/sanitize-html": "^2.6.2",
|
|
"@types/semver": "^7.7.1",
|
|
"@types/sharp": "^0.31.0",
|
|
"@types/three": "^0.180.0",
|
|
"@types/uuid": "^9.0.0",
|
|
"@types/vimeo__player": "^2.18.3",
|
|
"@types/xml2js": "^0.4.14",
|
|
"@typescript-eslint/eslint-plugin": "^5.33.0",
|
|
"@typescript-eslint/parser": "^5.33.0",
|
|
"@vitest/browser": "^4.1.11",
|
|
"@vitest/browser-playwright": "4.1.11",
|
|
"@vitest/coverage-v8": "^4.1.11",
|
|
"autoprefixer": "^10.4.19",
|
|
"cross-env": "^7.0.3",
|
|
"cssnano": "^7.0.1",
|
|
"esbuild": "^0.25.5",
|
|
"eslint": "8.57.1",
|
|
"eslint-config-next": "^15.5.19",
|
|
"eslint-config-prettier": "^8.5.0",
|
|
"eslint-plugin-import": "^2.26.0",
|
|
"eslint-plugin-local-rules": "^3.0.2",
|
|
"eslint-plugin-tailwindcss": "^3.15.1",
|
|
"husky": "^9.1.1",
|
|
"jsdom": "^27.4.0",
|
|
"pg-format": "^1.0.4",
|
|
"playwright": "^1.57.0",
|
|
"postcss": "^8.5.3",
|
|
"postcss-assign-layer": "^0.4.0",
|
|
"postcss-preset-mantine": "^1.17.0",
|
|
"postcss-simple-vars": "^7.0.1",
|
|
"prettier": "^2.8.8",
|
|
"prisma": "^6.3.0",
|
|
"prisma-generator-typescript-interfaces": "^1.6.1",
|
|
"prisma-kysely": "^2.2.0",
|
|
"tailwindcss": "^3.4.3",
|
|
"ts-node": "^10.9.1",
|
|
"tsx": "^4.19.2",
|
|
"turbo": "^2.9.17",
|
|
"typed-scss-modules": "^8.1.1",
|
|
"typescript": "^5.9.2",
|
|
"vitest": "^4.1.11",
|
|
"vitest-browser-react": "^2.2.0",
|
|
"ws": "^8.19.0"
|
|
},
|
|
"ct3aMetadata": {
|
|
"initVersion": "6.2.1"
|
|
},
|
|
"//eslint-config-next": "Stay on major 15. v16 is flat-config-only (peer eslint >=9); extending it from .eslintrc.js makes @eslint/eslintrc reject it and then crash while formatting the error, so lint silently never runs. Pinned below so a `pnpm up` can't walk it forward.",
|
|
"pnpm": {
|
|
"overrides": {
|
|
"eslint-config-next": "15",
|
|
"vite": "6.4.3",
|
|
"protobufjs@7": "^7.5.6",
|
|
"fast-xml-parser": "^5.9.3",
|
|
"@aws-sdk/core>fast-xml-parser": "5.2.5",
|
|
"axios": "^1.16.0",
|
|
"undici@6": "^6.27.0",
|
|
"tar-fs@2": "^2.1.5",
|
|
"tar-fs@3": "^3.1.3",
|
|
"ws@7": "^7.5.11"
|
|
},
|
|
"onlyBuiltDependencies": [
|
|
"@parcel/watcher",
|
|
"@prisma/client",
|
|
"@prisma/engines",
|
|
"bigint-buffer",
|
|
"bufferutil",
|
|
"core-js",
|
|
"esbuild",
|
|
"exifreader",
|
|
"msgpackr-extract",
|
|
"prisma",
|
|
"protobufjs",
|
|
"sharp",
|
|
"unrs-resolver",
|
|
"utf-8-validate"
|
|
],
|
|
"patchedDependencies": {
|
|
"@mantine/hooks": "patches/@mantine__hooks.patch"
|
|
}
|
|
}
|
|
}
|