Commit fc62d6d7a693f621909a9c7489122a937001708d
1 parent
db8d7b51
feat(web+distribution): R1 Web SPA bootstrap + demo seed
First runnable end-to-end demo: open the browser, log in, click
through every PBC, and walk a sales order DRAFT → CONFIRMED →
SHIPPED. Stock balances drop, the SALES_SHIPMENT row appears in the
ledger, and the AR journal entry settles — all visible in the SPA
without touching curl.
Bumps version to 0.29.0-SNAPSHOT.
What landed
-----------
* New `:web` Gradle subproject — Vite + React 18 + TypeScript +
Tailwind 3.4. The Gradle wrapper is two `Exec` tasks
(`npmInstall`, `npmBuild`) with proper inputs/outputs declared
for incremental builds. Deliberately no node-gradle plugin —
one less moving piece.
* SPA architecture: hand-written typed REST client over `fetch`
(auth header injection + 401 handler), AuthContext that decodes
the JWT for display, ProtectedRoute, AppLayout with sidebar
grouped by PBC, 16 page components covering the full v1 surface
(Items, UoMs, Partners, Locations, Stock Balances + Movements,
Sales Orders + detail w/ confirm/ship/cancel, Purchase Orders +
detail w/ confirm/receive/cancel, Work Orders + detail w/
start/complete, Shop-Floor dashboard with 5s polling, Journal
Entries). 211 KB JS / 21 KB CSS gzipped.
* Sales-order detail page: confirm/ship/cancel verbs each refresh
the order, the (SO-filtered) movements list, and the
(SO-filtered) journal entries — so an operator watches the
ledger row appear and the AR row settle in real time after a
single click. Same pattern on the purchase-order detail page
for the AP/RECEIPT side.
* Shop-floor dashboard polls /api/v1/production/work-orders/shop-
floor every 5s and renders one card per IN_PROGRESS WO with
current operation, planned vs actual minutes (progress bar),
and operations-completed.
* `:distribution` consumes the SPA dist via a normal Gradle
outgoing/incoming configuration: `:web` exposes
`webStaticBundle`, `:distribution`'s `bundleWebStatic` Sync
task copies it into `${buildDir}/web-static/static/`, and
that parent directory is added to the main resources source
set so Spring Boot serves the SPA from `classpath:/static/`
out of the same fat-jar. Single artifact, no nginx, no CORS.
* New `SpaController` in platform-bootstrap forwards every
known SPA route prefix to `/index.html` so React Router's
HTML5 history mode works on hard refresh / deep-link entry.
Explicit list (12 prefixes) rather than catch-all so typoed
API URLs still get an honest 404 instead of the SPA shell.
* SecurityConfiguration restructured: keeps the public allowlist
for /api/v1/auth + /api/v1/_meta + /v3/api-docs + /swagger-ui,
then `/api/**` is `.authenticated()`, then SPA static assets
+ every SPA route prefix are `.permitAll()`. The order is
load-bearing — putting `.authenticated()` for /api/** BEFORE
the SPA permitAll preserves the framework's "API is always
authenticated" invariant even with the SPA bundled in the
same fat-jar. The SPA bundle itself is just HTML+CSS+JS so
permitting it is correct; secrets are gated by /api/**.
* New `DemoSeedRunner` in `:distribution` (gated behind
`vibeerp.demo.seed=true`, set in application-dev.yaml only).
Idempotent — the runner short-circuits if its sentinel item
(DEMO-PAPER-A4) already exists. Seeds 5 items, 2 warehouses,
4 partners, opening stock for every item, one open
DEMO-SO-0001 (50× business cards + 20× brochures, $720), one
open DEMO-PO-0001 (10000× paper, $400). Every row carries
the DEMO- prefix so it's trivially distinguishable from
hand-created data; a future "delete demo data" command has
an obvious filter. Production deploys never set the property,
so the @ConditionalOnProperty bean stays absent from the
context.
How to run
----------
docker compose up -d db
./gradlew :distribution:bootRun
open http://localhost:8080
# Read the bootstrap admin password from the boot log,
# log in as admin, and walk DEMO-SO-0001 through the
# confirm + ship flow to see the buy-sell loop in the UI.
What was caught by the smoke test
---------------------------------
* TypeScript strict mode + `error: unknown` in React state →
`{error && <X/>}` evaluates to `unknown` and JSX rejects it.
Fixed by typing the state as `Error | null` and converting
in catches with `e instanceof Error ? e : new Error(String(e))`.
Affected 16 page files; the conversion is now uniform.
* DataTable's `T extends Record<string, unknown>` constraint was
too restrictive for typed row interfaces; relaxed to
unconstrained `T` with `(row as unknown as Record<…>)[key]`
for the unkeyed cell read fallback.
* `vite.config.ts` needs `@types/node` for `node:path` +
`__dirname`; added to devDependencies and tsconfig.node.json
declares `"types": ["node"]`.
* KDoc nested-comment trap (4th time): SpaController's KDoc
had `/api/v1/...` in backticks; the `/*` inside backticks
starts a nested block comment and breaks Kotlin compilation
with "Unclosed comment". Rephrased to "the api-v1 prefix".
* SecurityConfiguration order: a draft version that put the
SPA permit-all rules BEFORE `/api/**` authenticated() let
unauthenticated requests reach API endpoints. Caught by an
explicit smoke test (curl /api/v1/some-bogus-endpoint should
return 401, not 404 from a missing static file). Reordered
so /api/** authentication runs first.
End-to-end smoke (real Postgres, fresh DB)
------------------------------------------
- bootRun starts in 7.6s
- DemoSeedRunner reports "populating starter dataset… done"
- GET / returns the SPA HTML
- GET /sales-orders, /sales-orders/<uuid>, /journal-entries
all return 200 (SPA shell — React Router takes over)
- GET /assets/index-*.js / /assets/index-*.css both 200
- GET /api/v1/some-bogus-endpoint → 401 (Spring Security
rejects before any controller mapping)
- admin login via /api/v1/auth/login → 200 + JWT
- GET /catalog/items → 5 DEMO-* rows
- GET /partners/partners → 4 DEMO-* rows
- GET /inventory/locations → 2 DEMO-* warehouses
- GET /inventory/balances → 5 starting balances
- POST /orders/sales-orders/<id>/confirm → CONFIRMED;
GET /finance/journal-entries shows AR POSTED 720 USD
- POST /orders/sales-orders/<id>/ship {"shippingLocationCode":
"DEMO-WH-FG"} → SHIPPED; balances drop to 150 + 80;
journal entry flips to SETTLED
- POST /orders/purchase-orders/<id>/confirm → CONFIRMED;
AP POSTED 400 USD appears
- POST /orders/purchase-orders/<id>/receive → RECEIVED;
PAPER balance grows from 5000 to 15000; AP row SETTLED
- 8 stock_movement rows in the ledger total
Showing
46 changed files
with
6282 additions
and
8 deletions
Too many changes to show.
To preserve performance only 33 of 46 files are displayed.
distribution/build.gradle.kts
| ... | ... | @@ -4,6 +4,16 @@ |
| 4 | 4 | // together into a bootable Spring Boot application. It is referenced by |
| 5 | 5 | // the Dockerfile (stage 1) which produces the shipping image documented |
| 6 | 6 | // in the architecture spec, sections 10 and 11. |
| 7 | +// | |
| 8 | +// **Web SPA bundling.** The `:web` subproject is a Gradle wrapper around | |
| 9 | +// the Vite/React build. We declare a `webStaticBundle` consumer | |
| 10 | +// configuration against it; the `bundleWebStatic` Sync task copies | |
| 11 | +// the dist/ directory into `${buildDir}/web-static/static/` and that | |
| 12 | +// parent directory is added to the main resources source set so Spring | |
| 13 | +// Boot's `classpath:/static/**` resource resolver picks the SPA up at | |
| 14 | +// runtime. The result is a single fat-jar containing both the JVM | |
| 15 | +// backend and the SPA — no nginx, no separate static-file server, no | |
| 16 | +// CORS. | |
| 7 | 17 | |
| 8 | 18 | plugins { |
| 9 | 19 | alias(libs.plugins.kotlin.jvm) |
| ... | ... | @@ -89,4 +99,45 @@ tasks.bootRun { |
| 89 | 99 | // <repo>/plugins-dev/, not <repo>/distribution/plugins-dev/. |
| 90 | 100 | workingDir = rootProject.layout.projectDirectory.asFile |
| 91 | 101 | dependsOn(":reference-customer:plugin-printing-shop:installToDev") |
| 102 | + // Make sure the SPA dist/ is staged into the classpath before | |
| 103 | + // Spring Boot starts; otherwise an in-IDE bootRun with no prior | |
| 104 | + // build serves a 404 at the root path. processResources already | |
| 105 | + // depends on bundleWebStatic, but listing it explicitly here | |
| 106 | + // makes the relationship visible to anyone reading bootRun. | |
| 107 | + dependsOn("bundleWebStatic") | |
| 108 | +} | |
| 109 | + | |
| 110 | +// ─── Web SPA bundling ──────────────────────────────────────────────── | |
| 111 | +// | |
| 112 | +// Resolvable configuration that pulls the `webStaticBundle` artifact | |
| 113 | +// from `:web`. The `:web` subproject's outgoing configuration declares | |
| 114 | +// the dist/ directory as its single artifact, built by the npmBuild | |
| 115 | +// Exec task. We don't need attribute matchers because there's only | |
| 116 | +// one artifact in the configuration. | |
| 117 | +val webStaticBundle: Configuration by configurations.creating { | |
| 118 | + isCanBeConsumed = false | |
| 119 | + isCanBeResolved = true | |
| 120 | +} | |
| 121 | + | |
| 122 | +dependencies { | |
| 123 | + webStaticBundle(project(mapOf("path" to ":web", "configuration" to "webStaticBundle"))) | |
| 124 | +} | |
| 125 | + | |
| 126 | +// Stage the SPA dist into ${buildDir}/web-static/static/ so the | |
| 127 | +// directory layout matches Spring Boot's classpath:/static/ convention. | |
| 128 | +// Adding the parent dir as a resource source set is what then makes | |
| 129 | +// the JAR's classpath include those files at the right path. | |
| 130 | +val bundleWebStatic by tasks.registering(Sync::class) { | |
| 131 | + group = "build" | |
| 132 | + description = "Stage the :web SPA bundle into the distribution resources tree." | |
| 133 | + from({ webStaticBundle }) | |
| 134 | + into(layout.buildDirectory.dir("web-static/static")) | |
| 135 | +} | |
| 136 | + | |
| 137 | +sourceSets.named("main") { | |
| 138 | + resources.srcDir(layout.buildDirectory.dir("web-static")) | |
| 139 | +} | |
| 140 | + | |
| 141 | +tasks.named("processResources") { | |
| 142 | + dependsOn(bundleWebStatic) | |
| 92 | 143 | } | ... | ... |
distribution/src/main/kotlin/org/vibeerp/demo/DemoSeedRunner.kt
0 → 100644
| 1 | +package org.vibeerp.demo | |
| 2 | + | |
| 3 | +import org.slf4j.LoggerFactory | |
| 4 | +import org.springframework.boot.CommandLineRunner | |
| 5 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty | |
| 6 | +import org.springframework.stereotype.Component | |
| 7 | +import org.springframework.transaction.annotation.Transactional | |
| 8 | +import org.vibeerp.pbc.catalog.application.CreateItemCommand | |
| 9 | +import org.vibeerp.pbc.catalog.application.ItemService | |
| 10 | +import org.vibeerp.pbc.catalog.domain.ItemType | |
| 11 | +import org.vibeerp.pbc.inventory.application.CreateLocationCommand | |
| 12 | +import org.vibeerp.pbc.inventory.application.LocationService | |
| 13 | +import org.vibeerp.pbc.inventory.application.StockBalanceService | |
| 14 | +import org.vibeerp.pbc.inventory.domain.LocationType | |
| 15 | +import org.vibeerp.pbc.orders.purchase.application.CreatePurchaseOrderCommand | |
| 16 | +import org.vibeerp.pbc.orders.purchase.application.PurchaseOrderLineCommand | |
| 17 | +import org.vibeerp.pbc.orders.purchase.application.PurchaseOrderService | |
| 18 | +import org.vibeerp.pbc.orders.sales.application.CreateSalesOrderCommand | |
| 19 | +import org.vibeerp.pbc.orders.sales.application.SalesOrderLineCommand | |
| 20 | +import org.vibeerp.pbc.orders.sales.application.SalesOrderService | |
| 21 | +import org.vibeerp.pbc.partners.application.CreatePartnerCommand | |
| 22 | +import org.vibeerp.pbc.partners.application.PartnerService | |
| 23 | +import org.vibeerp.pbc.partners.domain.PartnerType | |
| 24 | +import org.vibeerp.platform.persistence.security.PrincipalContext | |
| 25 | +import java.math.BigDecimal | |
| 26 | +import java.time.LocalDate | |
| 27 | + | |
| 28 | +/** | |
| 29 | + * One-shot demo data seeder, gated behind `vibeerp.demo.seed=true`. | |
| 30 | + * | |
| 31 | + * **Why this exists.** Out-of-the-box, vibe_erp boots against an | |
| 32 | + * empty Postgres and the SPA dashboard shows zeros for every PBC. | |
| 33 | + * Onboarding a new operator (or running a tomorrow-morning demo) | |
| 34 | + * needs a couple of minutes of clicking to create items, | |
| 35 | + * locations, partners, and a starting inventory before the | |
| 36 | + * interesting screens become useful. This runner stages a tiny | |
| 37 | + * but representative dataset on first boot so the moment the | |
| 38 | + * bootstrap admin lands on `/`, every page already has rows. | |
| 39 | + * | |
| 40 | + * **Opt-in by property.** `@ConditionalOnProperty` keeps this | |
| 41 | + * bean entirely absent from production deployments — only the | |
| 42 | + * dev profile (`application-dev.yaml` sets `vibeerp.demo.seed: | |
| 43 | + * true`) opts in. A future release can ship a `--demo` CLI flag | |
| 44 | + * or a one-time admin "Load demo data" button that flips the | |
| 45 | + * same property at runtime; for v1 the dev profile is enough. | |
| 46 | + * | |
| 47 | + * **Idempotent.** The runner checks for one of its own seeded | |
| 48 | + * item codes and short-circuits if already present. Restarting | |
| 49 | + * the dev server is a no-op; deleting the demo data has to | |
| 50 | + * happen via SQL or by dropping the DB. Idempotency on the | |
| 51 | + * sentinel item is intentional (vs. on every entity it creates): | |
| 52 | + * a half-seeded DB from a crashed first run will *not* recover | |
| 53 | + * cleanly, but that case is exotic and we can clear and retry | |
| 54 | + * in dev. | |
| 55 | + * | |
| 56 | + * **All seeded data shares the `DEMO-` prefix.** Items, partners, | |
| 57 | + * locations, and order codes all start with `DEMO-`. This makes | |
| 58 | + * the seeded data trivially distinguishable from anything an | |
| 59 | + * operator creates by hand later — and gives a future | |
| 60 | + * "delete demo data" command an obvious filter. | |
| 61 | + * | |
| 62 | + * **System principal.** Audit columns need a non-blank | |
| 63 | + * `created_by`; `PrincipalContext.runAs("__demo_seed__")` wraps | |
| 64 | + * the entire seed so every row carries that sentinel. The | |
| 65 | + * authorization aspect (`@RequirePermission`) lives on | |
| 66 | + * controllers, not services — calling services directly bypasses | |
| 67 | + * it cleanly, which is correct for system-level seeders. | |
| 68 | + * | |
| 69 | + * **Why CommandLineRunner.** Equivalent to `ApplicationRunner` | |
| 70 | + * here — there are no command-line args this seeder cares about. | |
| 71 | + * Spring runs every CommandLineRunner once, after the application | |
| 72 | + * context is fully initialized but before serving traffic, which | |
| 73 | + * is exactly the right window: services are wired, the schema is | |
| 74 | + * applied, but the first HTTP request hasn't arrived yet. | |
| 75 | + * | |
| 76 | + * **Lives in distribution.** This is the only module that | |
| 77 | + * already depends on every PBC, which is what the seeder needs | |
| 78 | + * to compose. It's gated behind a property the production | |
| 79 | + * application.yaml never sets, so its presence in the fat-jar | |
| 80 | + * is dormant unless explicitly opted in. | |
| 81 | + */ | |
| 82 | +@Component | |
| 83 | +@ConditionalOnProperty(prefix = "vibeerp.demo", name = ["seed"], havingValue = "true") | |
| 84 | +class DemoSeedRunner( | |
| 85 | + private val itemService: ItemService, | |
| 86 | + private val locationService: LocationService, | |
| 87 | + private val stockBalanceService: StockBalanceService, | |
| 88 | + private val partnerService: PartnerService, | |
| 89 | + private val salesOrderService: SalesOrderService, | |
| 90 | + private val purchaseOrderService: PurchaseOrderService, | |
| 91 | +) : CommandLineRunner { | |
| 92 | + | |
| 93 | + private val log = LoggerFactory.getLogger(DemoSeedRunner::class.java) | |
| 94 | + | |
| 95 | + @Transactional | |
| 96 | + override fun run(vararg args: String?) { | |
| 97 | + if (itemService.findByCode(SENTINEL_ITEM_CODE) != null) { | |
| 98 | + log.info("Demo seed: data already present (sentinel item {} found); skipping", SENTINEL_ITEM_CODE) | |
| 99 | + return | |
| 100 | + } | |
| 101 | + | |
| 102 | + log.info("Demo seed: populating starter dataset…") | |
| 103 | + PrincipalContext.runAs("__demo_seed__") { | |
| 104 | + seedItems() | |
| 105 | + seedLocations() | |
| 106 | + seedPartners() | |
| 107 | + seedStock() | |
| 108 | + seedSalesOrder() | |
| 109 | + seedPurchaseOrder() | |
| 110 | + } | |
| 111 | + log.info("Demo seed: done") | |
| 112 | + } | |
| 113 | + | |
| 114 | + // ─── Items ─────────────────────────────────────────────────────── | |
| 115 | + | |
| 116 | + private fun seedItems() { | |
| 117 | + item(SENTINEL_ITEM_CODE, "A4 paper, 80gsm, white", ItemType.GOOD, "sheet") | |
| 118 | + item("DEMO-INK-CYAN", "Cyan offset ink", ItemType.GOOD, "kg") | |
| 119 | + item("DEMO-INK-MAGENTA", "Magenta offset ink", ItemType.GOOD, "kg") | |
| 120 | + item("DEMO-CARD-BIZ", "Business cards, 100/pack", ItemType.GOOD, "pack") | |
| 121 | + item("DEMO-BROCHURE-A5", "Folded A5 brochure", ItemType.GOOD, "ea") | |
| 122 | + } | |
| 123 | + | |
| 124 | + private fun item(code: String, name: String, type: ItemType, baseUomCode: String) { | |
| 125 | + itemService.create( | |
| 126 | + CreateItemCommand( | |
| 127 | + code = code, | |
| 128 | + name = name, | |
| 129 | + description = null, | |
| 130 | + itemType = type, | |
| 131 | + baseUomCode = baseUomCode, | |
| 132 | + active = true, | |
| 133 | + ), | |
| 134 | + ) | |
| 135 | + } | |
| 136 | + | |
| 137 | + // ─── Locations ─────────────────────────────────────────────────── | |
| 138 | + | |
| 139 | + private fun seedLocations() { | |
| 140 | + location("DEMO-WH-RAW", "Raw materials warehouse") | |
| 141 | + location("DEMO-WH-FG", "Finished goods warehouse") | |
| 142 | + } | |
| 143 | + | |
| 144 | + private fun location(code: String, name: String) { | |
| 145 | + locationService.create( | |
| 146 | + CreateLocationCommand( | |
| 147 | + code = code, | |
| 148 | + name = name, | |
| 149 | + type = LocationType.WAREHOUSE, | |
| 150 | + active = true, | |
| 151 | + ), | |
| 152 | + ) | |
| 153 | + } | |
| 154 | + | |
| 155 | + // ─── Partners ──────────────────────────────────────────────────── | |
| 156 | + | |
| 157 | + private fun seedPartners() { | |
| 158 | + partner("DEMO-CUST-ACME", "Acme Print Co.", PartnerType.CUSTOMER, "ap@acme.example") | |
| 159 | + partner("DEMO-CUST-GLOBE", "Globe Marketing", PartnerType.CUSTOMER, "ops@globe.example") | |
| 160 | + partner("DEMO-SUPP-PAPERWORLD", "Paper World Ltd.", PartnerType.SUPPLIER, "sales@paperworld.example") | |
| 161 | + partner("DEMO-SUPP-INKCO", "InkCo Industries", PartnerType.SUPPLIER, "orders@inkco.example") | |
| 162 | + } | |
| 163 | + | |
| 164 | + private fun partner(code: String, name: String, type: PartnerType, email: String) { | |
| 165 | + partnerService.create( | |
| 166 | + CreatePartnerCommand( | |
| 167 | + code = code, | |
| 168 | + name = name, | |
| 169 | + type = type, | |
| 170 | + email = email, | |
| 171 | + active = true, | |
| 172 | + ), | |
| 173 | + ) | |
| 174 | + } | |
| 175 | + | |
| 176 | + // ─── Initial stock ─────────────────────────────────────────────── | |
| 177 | + | |
| 178 | + private fun seedStock() { | |
| 179 | + val rawWh = locationService.findByCode("DEMO-WH-RAW")!! | |
| 180 | + val fgWh = locationService.findByCode("DEMO-WH-FG")!! | |
| 181 | + | |
| 182 | + stockBalanceService.adjust(SENTINEL_ITEM_CODE, rawWh.id, BigDecimal("5000")) | |
| 183 | + stockBalanceService.adjust("DEMO-INK-CYAN", rawWh.id, BigDecimal("50")) | |
| 184 | + stockBalanceService.adjust("DEMO-INK-MAGENTA", rawWh.id, BigDecimal("50")) | |
| 185 | + | |
| 186 | + stockBalanceService.adjust("DEMO-CARD-BIZ", fgWh.id, BigDecimal("200")) | |
| 187 | + stockBalanceService.adjust("DEMO-BROCHURE-A5", fgWh.id, BigDecimal("100")) | |
| 188 | + } | |
| 189 | + | |
| 190 | + // ─── Open sales order (DRAFT — ready to confirm + ship) ────────── | |
| 191 | + | |
| 192 | + private fun seedSalesOrder() { | |
| 193 | + salesOrderService.create( | |
| 194 | + CreateSalesOrderCommand( | |
| 195 | + code = "DEMO-SO-0001", | |
| 196 | + partnerCode = "DEMO-CUST-ACME", | |
| 197 | + orderDate = LocalDate.now(), | |
| 198 | + currencyCode = "USD", | |
| 199 | + lines = listOf( | |
| 200 | + SalesOrderLineCommand( | |
| 201 | + lineNo = 1, | |
| 202 | + itemCode = "DEMO-CARD-BIZ", | |
| 203 | + quantity = BigDecimal("50"), | |
| 204 | + unitPrice = BigDecimal("12.50"), | |
| 205 | + currencyCode = "USD", | |
| 206 | + ), | |
| 207 | + SalesOrderLineCommand( | |
| 208 | + lineNo = 2, | |
| 209 | + itemCode = "DEMO-BROCHURE-A5", | |
| 210 | + quantity = BigDecimal("20"), | |
| 211 | + unitPrice = BigDecimal("4.75"), | |
| 212 | + currencyCode = "USD", | |
| 213 | + ), | |
| 214 | + ), | |
| 215 | + ), | |
| 216 | + ) | |
| 217 | + } | |
| 218 | + | |
| 219 | + // ─── Open purchase order (DRAFT — ready to confirm + receive) ──── | |
| 220 | + | |
| 221 | + private fun seedPurchaseOrder() { | |
| 222 | + purchaseOrderService.create( | |
| 223 | + CreatePurchaseOrderCommand( | |
| 224 | + code = "DEMO-PO-0001", | |
| 225 | + partnerCode = "DEMO-SUPP-PAPERWORLD", | |
| 226 | + orderDate = LocalDate.now(), | |
| 227 | + expectedDate = LocalDate.now().plusDays(7), | |
| 228 | + currencyCode = "USD", | |
| 229 | + lines = listOf( | |
| 230 | + PurchaseOrderLineCommand( | |
| 231 | + lineNo = 1, | |
| 232 | + itemCode = SENTINEL_ITEM_CODE, | |
| 233 | + quantity = BigDecimal("10000"), | |
| 234 | + unitPrice = BigDecimal("0.04"), | |
| 235 | + currencyCode = "USD", | |
| 236 | + ), | |
| 237 | + ), | |
| 238 | + ), | |
| 239 | + ) | |
| 240 | + } | |
| 241 | + | |
| 242 | + companion object { | |
| 243 | + /** | |
| 244 | + * The seeder uses the presence of this item as the | |
| 245 | + * idempotency marker — re-running the seeder against a | |
| 246 | + * Postgres that already contains it short-circuits. The | |
| 247 | + * choice of "the very first item the seeder creates" is | |
| 248 | + * deliberate: if the seed transaction commits at all, this | |
| 249 | + * row is in the DB; if it doesn't, nothing is. | |
| 250 | + */ | |
| 251 | + const val SENTINEL_ITEM_CODE: String = "DEMO-PAPER-A4" | |
| 252 | + } | |
| 253 | +} | ... | ... |
distribution/src/main/resources/application-dev.yaml
| ... | ... | @@ -21,6 +21,14 @@ vibeerp: |
| 21 | 21 | directory: ./plugins-dev |
| 22 | 22 | files: |
| 23 | 23 | local-path: ./files-dev |
| 24 | + demo: | |
| 25 | + # Opt in to the one-shot DemoSeedRunner (in distribution/.../demo/) | |
| 26 | + # which stages a tiny but representative dataset on first boot. | |
| 27 | + # Idempotent — the runner checks for its own sentinel item before | |
| 28 | + # creating anything, so restarting the dev server is a no-op once | |
| 29 | + # the seed has been applied. Production deployments leave this | |
| 30 | + # property unset and the @ConditionalOnProperty bean never loads. | |
| 31 | + seed: true | |
| 24 | 32 | |
| 25 | 33 | logging: |
| 26 | 34 | level: | ... | ... |
gradle.properties
| ... | ... | @@ -19,6 +19,6 @@ kotlin.incremental=true |
| 19 | 19 | # api/api-v1/build.gradle.kts. |
| 20 | 20 | # Bump this line in lockstep with PROGRESS.md's "Latest version" |
| 21 | 21 | # row when shipping a new working surface. |
| 22 | -vibeerp.version=0.28.0-SNAPSHOT | |
| 22 | +vibeerp.version=0.29.0-SNAPSHOT | |
| 23 | 23 | vibeerp.api.version=1.0.0-SNAPSHOT |
| 24 | 24 | vibeerp.group=org.vibeerp | ... | ... |
platform/platform-bootstrap/src/main/kotlin/org/vibeerp/platform/bootstrap/web/SpaController.kt
0 → 100644
| 1 | +package org.vibeerp.platform.bootstrap.web | |
| 2 | + | |
| 3 | +import org.springframework.stereotype.Controller | |
| 4 | +import org.springframework.web.bind.annotation.GetMapping | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * Static SPA fallback controller. | |
| 8 | + * | |
| 9 | + * **The problem.** The vibe_erp Web SPA (the `:web` Gradle subproject) | |
| 10 | + * uses React Router's HTML5 history mode for client-side routing. | |
| 11 | + * When a user hits `http://host/sales-orders` directly — by typing | |
| 12 | + * the URL, refreshing the page, or clicking a deep link — Spring | |
| 13 | + * Boot's static resource handler tries to serve a file at | |
| 14 | + * `static/sales-orders` and returns 404 because no such file | |
| 15 | + * exists. The actual routing for `/sales-orders` lives inside the | |
| 16 | + * SPA's compiled JavaScript, so we need the server to return | |
| 17 | + * `static/index.html` for every SPA route and let React Router | |
| 18 | + * take over once the bundle has loaded. | |
| 19 | + * | |
| 20 | + * **Why an explicit list, not a catch-all.** Spring Security needs | |
| 21 | + * to permit the same paths the SPA uses, and a catch-all | |
| 22 | + * `@GetMapping` with a wildcard path matcher would also intercept | |
| 23 | + * legitimate 404s for typoed API URLs under the api-v1 prefix | |
| 24 | + * (turning them into "here's the SPA's 404 page" instead of an | |
| 25 | + * honest server-side 404). Listing the SPA's known top-level routes | |
| 26 | + * here keeps the seam explicit: adding a new sidebar item to | |
| 27 | + * AppLayout.tsx requires adding the route here. The list is small | |
| 28 | + * (12 entries) so the maintenance cost is trivial; if it ever | |
| 29 | + * passes ~25 entries we can revisit with a single regex matcher. | |
| 30 | + * | |
| 31 | + * **Forward, not redirect.** `forward:/index.html` is an internal | |
| 32 | + * RequestDispatcher.forward — the URL bar still shows | |
| 33 | + * `/sales-orders` and Spring Security still sees the original | |
| 34 | + * request path. Redirecting (`redirect:/index.html`) would lose | |
| 35 | + * the deep link entirely and break the back button. | |
| 36 | + * | |
| 37 | + * **The "/" entry.** Without it, hitting the root would also fall | |
| 38 | + * through to the static resource handler, which DOES correctly | |
| 39 | + * serve `static/index.html` for `/`. We list it explicitly anyway | |
| 40 | + * so the route table is self-explanatory. | |
| 41 | + * | |
| 42 | + * **API paths are NOT in this list.** Anything under the api-v1 | |
| 43 | + * prefix matches a `@RestController` mapping registered higher up | |
| 44 | + * in the request-mapping chain, so the SPA fallback is never | |
| 45 | + * reached for an authentic API request. For an UNKNOWN API path, | |
| 46 | + * the absence of a controller match returns a real 404, which is | |
| 47 | + * what we want. | |
| 48 | + * | |
| 49 | + * **Where this controller is in the build.** Lives in | |
| 50 | + * `platform-bootstrap` rather than `:distribution` because it is | |
| 51 | + * a `@Controller` that needs to be picked up by Spring's component | |
| 52 | + * scan, and `platform-bootstrap` is the only module with the | |
| 53 | + * `@SpringBootApplication`. Putting it in `:distribution` would | |
| 54 | + * require adding a Spring scan to a module that has zero Kotlin | |
| 55 | + * sources of its own. | |
| 56 | + */ | |
| 57 | +@Controller | |
| 58 | +class SpaController { | |
| 59 | + | |
| 60 | + @GetMapping( | |
| 61 | + value = [ | |
| 62 | + // Root + auth | |
| 63 | + "/", | |
| 64 | + "/login", | |
| 65 | + | |
| 66 | + // Catalog & partners | |
| 67 | + "/items", "/items/", "/items/**", | |
| 68 | + "/uoms", "/uoms/", "/uoms/**", | |
| 69 | + "/partners", "/partners/", "/partners/**", | |
| 70 | + | |
| 71 | + // Inventory | |
| 72 | + "/locations", "/locations/", "/locations/**", | |
| 73 | + "/balances", "/balances/", "/balances/**", | |
| 74 | + "/movements", "/movements/", "/movements/**", | |
| 75 | + | |
| 76 | + // Orders | |
| 77 | + "/sales-orders", "/sales-orders/", "/sales-orders/**", | |
| 78 | + "/purchase-orders", "/purchase-orders/", "/purchase-orders/**", | |
| 79 | + | |
| 80 | + // Production | |
| 81 | + "/work-orders", "/work-orders/", "/work-orders/**", | |
| 82 | + "/shop-floor", "/shop-floor/", "/shop-floor/**", | |
| 83 | + | |
| 84 | + // Finance | |
| 85 | + "/journal-entries", "/journal-entries/", "/journal-entries/**", | |
| 86 | + ], | |
| 87 | + ) | |
| 88 | + fun spa(): String = "forward:/index.html" | |
| 89 | +} | ... | ... |
platform/platform-security/src/main/kotlin/org/vibeerp/platform/security/SecurityConfiguration.kt
| ... | ... | @@ -37,6 +37,15 @@ class SecurityConfiguration { |
| 37 | 37 | .cors { } // default; tighten in v0.4 once the React SPA exists |
| 38 | 38 | .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) } |
| 39 | 39 | .authorizeHttpRequests { auth -> |
| 40 | + // ─── Public API endpoints ─────────────────────────── | |
| 41 | + // | |
| 42 | + // The framework's API surface is split into a tiny | |
| 43 | + // public allowlist and a much larger authenticated | |
| 44 | + // surface. Listing the public bits explicitly first | |
| 45 | + // (and authenticating /api/** as a whole afterwards) | |
| 46 | + // keeps the matcher precedence honest: a typo in a | |
| 47 | + // public path can never accidentally permit an | |
| 48 | + // authenticated endpoint. | |
| 40 | 49 | auth.requestMatchers( |
| 41 | 50 | // Public actuator probes |
| 42 | 51 | "/actuator/health", |
| ... | ... | @@ -46,17 +55,65 @@ class SecurityConfiguration { |
| 46 | 55 | // Auth endpoints themselves cannot require auth |
| 47 | 56 | "/api/v1/auth/login", |
| 48 | 57 | "/api/v1/auth/refresh", |
| 49 | - // OpenAPI / Swagger UI — public because it describes the | |
| 50 | - // API surface, not the data. The *data* still requires a | |
| 51 | - // valid JWT: an unauthenticated "Try it out" call from | |
| 52 | - // the Swagger UI against a pbc endpoint returns 401 just | |
| 53 | - // like a curl does. Exposing the spec is the first step | |
| 54 | - // toward both the future R1 web SPA (OpenAPI codegen) | |
| 55 | - // and the A1 MCP server (discoverable tool catalog). | |
| 58 | + // OpenAPI / Swagger UI — public because it describes | |
| 59 | + // the API surface, not the data. The *data* still | |
| 60 | + // requires a valid JWT: an unauthenticated "Try it | |
| 61 | + // out" call against a pbc endpoint returns 401. | |
| 56 | 62 | "/v3/api-docs/**", |
| 57 | 63 | "/swagger-ui/**", |
| 58 | 64 | "/swagger-ui.html", |
| 59 | 65 | ).permitAll() |
| 66 | + | |
| 67 | + // Every other /api/** call requires a Bearer JWT. | |
| 68 | + // This MUST come before the SPA permitAll below; the | |
| 69 | + // permit-all rule for "/" + static assets uses | |
| 70 | + // anyRequest() and would otherwise swallow API calls | |
| 71 | + // too. The order preserves the framework's "API is | |
| 72 | + // always authenticated" invariant even as the SPA | |
| 73 | + // moves into the same fat-jar. | |
| 74 | + auth.requestMatchers("/api/**").authenticated() | |
| 75 | + | |
| 76 | + // ─── Web SPA (the :web React+TS bundle) ───────────── | |
| 77 | + // | |
| 78 | + // Static asset paths the Vite build produces. | |
| 79 | + // Permitted because the SPA itself is just HTML+CSS+JS | |
| 80 | + // — every secret is fetched from /api/** which is | |
| 81 | + // already gated above. A user without a valid JWT | |
| 82 | + // sees the login page instead of a forbidden screen, | |
| 83 | + // which is the correct UX. | |
| 84 | + auth.requestMatchers( | |
| 85 | + "/", | |
| 86 | + "/index.html", | |
| 87 | + "/favicon.svg", | |
| 88 | + "/favicon.ico", | |
| 89 | + "/manifest.json", | |
| 90 | + "/assets/**", | |
| 91 | + "/static/**", | |
| 92 | + ).permitAll() | |
| 93 | + | |
| 94 | + // SPA route paths handled by the SpaController in | |
| 95 | + // platform-bootstrap (which forwards them to | |
| 96 | + // /index.html so React Router can take over). The | |
| 97 | + // controller and this list MUST stay in sync — adding | |
| 98 | + // a sidebar entry to AppLayout.tsx implies updating | |
| 99 | + // both. See SpaController for the rationale. | |
| 100 | + auth.requestMatchers( | |
| 101 | + "/login", | |
| 102 | + "/items", "/items/**", | |
| 103 | + "/uoms", "/uoms/**", | |
| 104 | + "/partners", "/partners/**", | |
| 105 | + "/locations", "/locations/**", | |
| 106 | + "/balances", "/balances/**", | |
| 107 | + "/movements", "/movements/**", | |
| 108 | + "/sales-orders", "/sales-orders/**", | |
| 109 | + "/purchase-orders", "/purchase-orders/**", | |
| 110 | + "/work-orders", "/work-orders/**", | |
| 111 | + "/shop-floor", "/shop-floor/**", | |
| 112 | + "/journal-entries", "/journal-entries/**", | |
| 113 | + ).permitAll() | |
| 114 | + | |
| 115 | + // Anything else — return 401 so a typoed deep link | |
| 116 | + // doesn't accidentally serve the SPA shell. | |
| 60 | 117 | auth.anyRequest().authenticated() |
| 61 | 118 | } |
| 62 | 119 | .oauth2ResourceServer { rs -> | ... | ... |
settings.gradle.kts
| ... | ... | @@ -89,6 +89,16 @@ project(":pbc:pbc-production").projectDir = file("pbc/pbc-production") |
| 89 | 89 | include(":reference-customer:plugin-printing-shop") |
| 90 | 90 | project(":reference-customer:plugin-printing-shop").projectDir = file("reference-customer/plugin-printing-shop") |
| 91 | 91 | |
| 92 | +// ─── Web SPA ──────────────────────────────────────────────────────── | |
| 93 | +// | |
| 94 | +// `:web` is a Gradle wrapper around an npm build. It produces a | |
| 95 | +// `dist/` directory of static assets that `:distribution` consumes | |
| 96 | +// at processResources time. See `web/build.gradle.kts` for the | |
| 97 | +// rationale (no Kotlin/JVM source set, no node-gradle plugin — | |
| 98 | +// just Exec tasks against system npm). | |
| 99 | +include(":web") | |
| 100 | +project(":web").projectDir = file("web") | |
| 101 | + | |
| 92 | 102 | // ─── Distribution (assembles the runnable image) ──────────────────── |
| 93 | 103 | include(":distribution") |
| 94 | 104 | project(":distribution").projectDir = file("distribution") | ... | ... |
web/.gitignore
0 → 100644
web/build.gradle.kts
0 → 100644
| 1 | +// vibe_erp web SPA — Gradle wrapper around the npm build. | |
| 2 | +// | |
| 3 | +// **Why no Kotlin/JVM source set.** This subproject has zero JVM | |
| 4 | +// code: every artifact under src/ is TypeScript/React. The Gradle | |
| 5 | +// presence is purely so `./gradlew build` runs the npm build as | |
| 6 | +// part of the standard build pipeline, and so :distribution can | |
| 7 | +// declare a normal project dependency on the produced static | |
| 8 | +// bundle instead of a fragile path reference. | |
| 9 | +// | |
| 10 | +// **Tasks.** | |
| 11 | +// - `npmInstall` → runs `npm install` in web/. Inputs: | |
| 12 | +// package.json + package-lock.json. Output: node_modules/. | |
| 13 | +// - `npmBuild` → runs `npm run build` in web/. Inputs: | |
| 14 | +// src/**, index.html, vite.config.ts, tsconfig*.json, | |
| 15 | +// tailwind.config.js, postcss.config.js. Output: dist/. | |
| 16 | +// - `assemble` → wired to depend on npmBuild so a normal | |
| 17 | +// `./gradlew :web:build` produces dist/. | |
| 18 | +// | |
| 19 | +// **No npm/node Gradle plugin.** I deliberately avoided | |
| 20 | +// `com.github.node-gradle.node` to keep the dep surface minimal — | |
| 21 | +// every plugin is one more thing to keep current with Gradle and | |
| 22 | +// JDK upgrades. Exec tasks are stable and the inputs/outputs | |
| 23 | +// declarations give Gradle the same up-to-date checking the plugin | |
| 24 | +// would. | |
| 25 | +// | |
| 26 | +// **Where the bundle goes.** Read by `:distribution` as a normal | |
| 27 | +// project artifact via the `webStaticBundle` configuration below; | |
| 28 | +// the consuming module copies dist/ into the Spring Boot | |
| 29 | +// classpath under `static/` at processResources time so the | |
| 30 | +// bootJar contains the entire SPA. | |
| 31 | + | |
| 32 | +import org.gradle.api.tasks.Exec | |
| 33 | + | |
| 34 | +plugins { | |
| 35 | + base | |
| 36 | +} | |
| 37 | + | |
| 38 | +description = "vibe_erp web SPA — Vite + React + TS, served by Spring Boot at the root path." | |
| 39 | + | |
| 40 | +// Resolve npm against the system PATH. Same approach as the | |
| 41 | +// reference plug-in's installToDev task — keeps developer setup | |
| 42 | +// simple ("you must have node + npm on PATH"). | |
| 43 | +val npmCommand: String = if (org.gradle.internal.os.OperatingSystem.current().isWindows) "npm.cmd" else "npm" | |
| 44 | + | |
| 45 | +val npmInstall by tasks.registering(Exec::class) { | |
| 46 | + group = "build" | |
| 47 | + description = "Run `npm install` in web/." | |
| 48 | + workingDir = projectDir | |
| 49 | + commandLine(npmCommand, "install", "--no-audit", "--no-fund", "--silent") | |
| 50 | + | |
| 51 | + inputs.file("package.json") | |
| 52 | + inputs.file("package-lock.json").optional() | |
| 53 | + outputs.dir("node_modules") | |
| 54 | +} | |
| 55 | + | |
| 56 | +val npmBuild by tasks.registering(Exec::class) { | |
| 57 | + group = "build" | |
| 58 | + description = "Run `npm run build` in web/. Produces dist/." | |
| 59 | + workingDir = projectDir | |
| 60 | + commandLine(npmCommand, "run", "build") | |
| 61 | + | |
| 62 | + dependsOn(npmInstall) | |
| 63 | + | |
| 64 | + inputs.dir("src") | |
| 65 | + inputs.file("index.html") | |
| 66 | + inputs.file("vite.config.ts") | |
| 67 | + inputs.file("tsconfig.json") | |
| 68 | + inputs.file("tsconfig.node.json") | |
| 69 | + inputs.file("tailwind.config.js") | |
| 70 | + inputs.file("postcss.config.js") | |
| 71 | + inputs.file("package.json") | |
| 72 | + | |
| 73 | + outputs.dir("dist") | |
| 74 | +} | |
| 75 | + | |
| 76 | +tasks.named("assemble") { | |
| 77 | + dependsOn(npmBuild) | |
| 78 | +} | |
| 79 | + | |
| 80 | +// Outgoing configuration so :distribution can consume the dist | |
| 81 | +// directory as a normal Gradle artifact. Using a single-element | |
| 82 | +// `artifacts` block keeps the consumer side trivial. | |
| 83 | +val webStaticBundle: Configuration by configurations.creating { | |
| 84 | + isCanBeConsumed = true | |
| 85 | + isCanBeResolved = false | |
| 86 | +} | |
| 87 | + | |
| 88 | +artifacts { | |
| 89 | + add(webStaticBundle.name, file("dist")) { | |
| 90 | + builtBy(npmBuild) | |
| 91 | + } | |
| 92 | +} | ... | ... |
web/index.html
0 → 100644
| 1 | +<!doctype html> | |
| 2 | +<html lang="en"> | |
| 3 | + <head> | |
| 4 | + <meta charset="UTF-8" /> | |
| 5 | + <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> | |
| 6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| 7 | + <meta name="description" content="vibe_erp — composable ERP framework for the printing industry" /> | |
| 8 | + <title>vibe_erp</title> | |
| 9 | + </head> | |
| 10 | + <body class="bg-slate-50"> | |
| 11 | + <div id="root"></div> | |
| 12 | + <script type="module" src="/src/main.tsx"></script> | |
| 13 | + </body> | |
| 14 | +</html> | ... | ... |
web/package-lock.json
0 → 100644
| 1 | +{ | |
| 2 | + "name": "vibe-erp-web", | |
| 3 | + "version": "0.0.0", | |
| 4 | + "lockfileVersion": 3, | |
| 5 | + "requires": true, | |
| 6 | + "packages": { | |
| 7 | + "": { | |
| 8 | + "name": "vibe-erp-web", | |
| 9 | + "version": "0.0.0", | |
| 10 | + "dependencies": { | |
| 11 | + "react": "^18.3.1", | |
| 12 | + "react-dom": "^18.3.1", | |
| 13 | + "react-router-dom": "^6.28.0" | |
| 14 | + }, | |
| 15 | + "devDependencies": { | |
| 16 | + "@types/node": "^25.5.2", | |
| 17 | + "@types/react": "^18.3.12", | |
| 18 | + "@types/react-dom": "^18.3.1", | |
| 19 | + "@vitejs/plugin-react": "^4.3.3", | |
| 20 | + "autoprefixer": "^10.4.20", | |
| 21 | + "postcss": "^8.4.49", | |
| 22 | + "tailwindcss": "^3.4.15", | |
| 23 | + "typescript": "^5.6.3", | |
| 24 | + "vite": "^5.4.11" | |
| 25 | + } | |
| 26 | + }, | |
| 27 | + "node_modules/@alloc/quick-lru": { | |
| 28 | + "version": "5.2.0", | |
| 29 | + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", | |
| 30 | + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", | |
| 31 | + "dev": true, | |
| 32 | + "license": "MIT", | |
| 33 | + "engines": { | |
| 34 | + "node": ">=10" | |
| 35 | + }, | |
| 36 | + "funding": { | |
| 37 | + "url": "https://github.com/sponsors/sindresorhus" | |
| 38 | + } | |
| 39 | + }, | |
| 40 | + "node_modules/@babel/code-frame": { | |
| 41 | + "version": "7.29.0", | |
| 42 | + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", | |
| 43 | + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", | |
| 44 | + "dev": true, | |
| 45 | + "license": "MIT", | |
| 46 | + "dependencies": { | |
| 47 | + "@babel/helper-validator-identifier": "^7.28.5", | |
| 48 | + "js-tokens": "^4.0.0", | |
| 49 | + "picocolors": "^1.1.1" | |
| 50 | + }, | |
| 51 | + "engines": { | |
| 52 | + "node": ">=6.9.0" | |
| 53 | + } | |
| 54 | + }, | |
| 55 | + "node_modules/@babel/compat-data": { | |
| 56 | + "version": "7.29.0", | |
| 57 | + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", | |
| 58 | + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", | |
| 59 | + "dev": true, | |
| 60 | + "license": "MIT", | |
| 61 | + "engines": { | |
| 62 | + "node": ">=6.9.0" | |
| 63 | + } | |
| 64 | + }, | |
| 65 | + "node_modules/@babel/core": { | |
| 66 | + "version": "7.29.0", | |
| 67 | + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", | |
| 68 | + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", | |
| 69 | + "dev": true, | |
| 70 | + "license": "MIT", | |
| 71 | + "dependencies": { | |
| 72 | + "@babel/code-frame": "^7.29.0", | |
| 73 | + "@babel/generator": "^7.29.0", | |
| 74 | + "@babel/helper-compilation-targets": "^7.28.6", | |
| 75 | + "@babel/helper-module-transforms": "^7.28.6", | |
| 76 | + "@babel/helpers": "^7.28.6", | |
| 77 | + "@babel/parser": "^7.29.0", | |
| 78 | + "@babel/template": "^7.28.6", | |
| 79 | + "@babel/traverse": "^7.29.0", | |
| 80 | + "@babel/types": "^7.29.0", | |
| 81 | + "@jridgewell/remapping": "^2.3.5", | |
| 82 | + "convert-source-map": "^2.0.0", | |
| 83 | + "debug": "^4.1.0", | |
| 84 | + "gensync": "^1.0.0-beta.2", | |
| 85 | + "json5": "^2.2.3", | |
| 86 | + "semver": "^6.3.1" | |
| 87 | + }, | |
| 88 | + "engines": { | |
| 89 | + "node": ">=6.9.0" | |
| 90 | + }, | |
| 91 | + "funding": { | |
| 92 | + "type": "opencollective", | |
| 93 | + "url": "https://opencollective.com/babel" | |
| 94 | + } | |
| 95 | + }, | |
| 96 | + "node_modules/@babel/generator": { | |
| 97 | + "version": "7.29.1", | |
| 98 | + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", | |
| 99 | + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", | |
| 100 | + "dev": true, | |
| 101 | + "license": "MIT", | |
| 102 | + "dependencies": { | |
| 103 | + "@babel/parser": "^7.29.0", | |
| 104 | + "@babel/types": "^7.29.0", | |
| 105 | + "@jridgewell/gen-mapping": "^0.3.12", | |
| 106 | + "@jridgewell/trace-mapping": "^0.3.28", | |
| 107 | + "jsesc": "^3.0.2" | |
| 108 | + }, | |
| 109 | + "engines": { | |
| 110 | + "node": ">=6.9.0" | |
| 111 | + } | |
| 112 | + }, | |
| 113 | + "node_modules/@babel/helper-compilation-targets": { | |
| 114 | + "version": "7.28.6", | |
| 115 | + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", | |
| 116 | + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", | |
| 117 | + "dev": true, | |
| 118 | + "license": "MIT", | |
| 119 | + "dependencies": { | |
| 120 | + "@babel/compat-data": "^7.28.6", | |
| 121 | + "@babel/helper-validator-option": "^7.27.1", | |
| 122 | + "browserslist": "^4.24.0", | |
| 123 | + "lru-cache": "^5.1.1", | |
| 124 | + "semver": "^6.3.1" | |
| 125 | + }, | |
| 126 | + "engines": { | |
| 127 | + "node": ">=6.9.0" | |
| 128 | + } | |
| 129 | + }, | |
| 130 | + "node_modules/@babel/helper-globals": { | |
| 131 | + "version": "7.28.0", | |
| 132 | + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", | |
| 133 | + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", | |
| 134 | + "dev": true, | |
| 135 | + "license": "MIT", | |
| 136 | + "engines": { | |
| 137 | + "node": ">=6.9.0" | |
| 138 | + } | |
| 139 | + }, | |
| 140 | + "node_modules/@babel/helper-module-imports": { | |
| 141 | + "version": "7.28.6", | |
| 142 | + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", | |
| 143 | + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", | |
| 144 | + "dev": true, | |
| 145 | + "license": "MIT", | |
| 146 | + "dependencies": { | |
| 147 | + "@babel/traverse": "^7.28.6", | |
| 148 | + "@babel/types": "^7.28.6" | |
| 149 | + }, | |
| 150 | + "engines": { | |
| 151 | + "node": ">=6.9.0" | |
| 152 | + } | |
| 153 | + }, | |
| 154 | + "node_modules/@babel/helper-module-transforms": { | |
| 155 | + "version": "7.28.6", | |
| 156 | + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", | |
| 157 | + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", | |
| 158 | + "dev": true, | |
| 159 | + "license": "MIT", | |
| 160 | + "dependencies": { | |
| 161 | + "@babel/helper-module-imports": "^7.28.6", | |
| 162 | + "@babel/helper-validator-identifier": "^7.28.5", | |
| 163 | + "@babel/traverse": "^7.28.6" | |
| 164 | + }, | |
| 165 | + "engines": { | |
| 166 | + "node": ">=6.9.0" | |
| 167 | + }, | |
| 168 | + "peerDependencies": { | |
| 169 | + "@babel/core": "^7.0.0" | |
| 170 | + } | |
| 171 | + }, | |
| 172 | + "node_modules/@babel/helper-plugin-utils": { | |
| 173 | + "version": "7.28.6", | |
| 174 | + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", | |
| 175 | + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", | |
| 176 | + "dev": true, | |
| 177 | + "license": "MIT", | |
| 178 | + "engines": { | |
| 179 | + "node": ">=6.9.0" | |
| 180 | + } | |
| 181 | + }, | |
| 182 | + "node_modules/@babel/helper-string-parser": { | |
| 183 | + "version": "7.27.1", | |
| 184 | + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", | |
| 185 | + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", | |
| 186 | + "dev": true, | |
| 187 | + "license": "MIT", | |
| 188 | + "engines": { | |
| 189 | + "node": ">=6.9.0" | |
| 190 | + } | |
| 191 | + }, | |
| 192 | + "node_modules/@babel/helper-validator-identifier": { | |
| 193 | + "version": "7.28.5", | |
| 194 | + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", | |
| 195 | + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", | |
| 196 | + "dev": true, | |
| 197 | + "license": "MIT", | |
| 198 | + "engines": { | |
| 199 | + "node": ">=6.9.0" | |
| 200 | + } | |
| 201 | + }, | |
| 202 | + "node_modules/@babel/helper-validator-option": { | |
| 203 | + "version": "7.27.1", | |
| 204 | + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", | |
| 205 | + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", | |
| 206 | + "dev": true, | |
| 207 | + "license": "MIT", | |
| 208 | + "engines": { | |
| 209 | + "node": ">=6.9.0" | |
| 210 | + } | |
| 211 | + }, | |
| 212 | + "node_modules/@babel/helpers": { | |
| 213 | + "version": "7.29.2", | |
| 214 | + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", | |
| 215 | + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", | |
| 216 | + "dev": true, | |
| 217 | + "license": "MIT", | |
| 218 | + "dependencies": { | |
| 219 | + "@babel/template": "^7.28.6", | |
| 220 | + "@babel/types": "^7.29.0" | |
| 221 | + }, | |
| 222 | + "engines": { | |
| 223 | + "node": ">=6.9.0" | |
| 224 | + } | |
| 225 | + }, | |
| 226 | + "node_modules/@babel/parser": { | |
| 227 | + "version": "7.29.2", | |
| 228 | + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", | |
| 229 | + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", | |
| 230 | + "dev": true, | |
| 231 | + "license": "MIT", | |
| 232 | + "dependencies": { | |
| 233 | + "@babel/types": "^7.29.0" | |
| 234 | + }, | |
| 235 | + "bin": { | |
| 236 | + "parser": "bin/babel-parser.js" | |
| 237 | + }, | |
| 238 | + "engines": { | |
| 239 | + "node": ">=6.0.0" | |
| 240 | + } | |
| 241 | + }, | |
| 242 | + "node_modules/@babel/plugin-transform-react-jsx-self": { | |
| 243 | + "version": "7.27.1", | |
| 244 | + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", | |
| 245 | + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", | |
| 246 | + "dev": true, | |
| 247 | + "license": "MIT", | |
| 248 | + "dependencies": { | |
| 249 | + "@babel/helper-plugin-utils": "^7.27.1" | |
| 250 | + }, | |
| 251 | + "engines": { | |
| 252 | + "node": ">=6.9.0" | |
| 253 | + }, | |
| 254 | + "peerDependencies": { | |
| 255 | + "@babel/core": "^7.0.0-0" | |
| 256 | + } | |
| 257 | + }, | |
| 258 | + "node_modules/@babel/plugin-transform-react-jsx-source": { | |
| 259 | + "version": "7.27.1", | |
| 260 | + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", | |
| 261 | + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", | |
| 262 | + "dev": true, | |
| 263 | + "license": "MIT", | |
| 264 | + "dependencies": { | |
| 265 | + "@babel/helper-plugin-utils": "^7.27.1" | |
| 266 | + }, | |
| 267 | + "engines": { | |
| 268 | + "node": ">=6.9.0" | |
| 269 | + }, | |
| 270 | + "peerDependencies": { | |
| 271 | + "@babel/core": "^7.0.0-0" | |
| 272 | + } | |
| 273 | + }, | |
| 274 | + "node_modules/@babel/template": { | |
| 275 | + "version": "7.28.6", | |
| 276 | + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", | |
| 277 | + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", | |
| 278 | + "dev": true, | |
| 279 | + "license": "MIT", | |
| 280 | + "dependencies": { | |
| 281 | + "@babel/code-frame": "^7.28.6", | |
| 282 | + "@babel/parser": "^7.28.6", | |
| 283 | + "@babel/types": "^7.28.6" | |
| 284 | + }, | |
| 285 | + "engines": { | |
| 286 | + "node": ">=6.9.0" | |
| 287 | + } | |
| 288 | + }, | |
| 289 | + "node_modules/@babel/traverse": { | |
| 290 | + "version": "7.29.0", | |
| 291 | + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", | |
| 292 | + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", | |
| 293 | + "dev": true, | |
| 294 | + "license": "MIT", | |
| 295 | + "dependencies": { | |
| 296 | + "@babel/code-frame": "^7.29.0", | |
| 297 | + "@babel/generator": "^7.29.0", | |
| 298 | + "@babel/helper-globals": "^7.28.0", | |
| 299 | + "@babel/parser": "^7.29.0", | |
| 300 | + "@babel/template": "^7.28.6", | |
| 301 | + "@babel/types": "^7.29.0", | |
| 302 | + "debug": "^4.3.1" | |
| 303 | + }, | |
| 304 | + "engines": { | |
| 305 | + "node": ">=6.9.0" | |
| 306 | + } | |
| 307 | + }, | |
| 308 | + "node_modules/@babel/types": { | |
| 309 | + "version": "7.29.0", | |
| 310 | + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", | |
| 311 | + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", | |
| 312 | + "dev": true, | |
| 313 | + "license": "MIT", | |
| 314 | + "dependencies": { | |
| 315 | + "@babel/helper-string-parser": "^7.27.1", | |
| 316 | + "@babel/helper-validator-identifier": "^7.28.5" | |
| 317 | + }, | |
| 318 | + "engines": { | |
| 319 | + "node": ">=6.9.0" | |
| 320 | + } | |
| 321 | + }, | |
| 322 | + "node_modules/@esbuild/aix-ppc64": { | |
| 323 | + "version": "0.21.5", | |
| 324 | + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", | |
| 325 | + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", | |
| 326 | + "cpu": [ | |
| 327 | + "ppc64" | |
| 328 | + ], | |
| 329 | + "dev": true, | |
| 330 | + "license": "MIT", | |
| 331 | + "optional": true, | |
| 332 | + "os": [ | |
| 333 | + "aix" | |
| 334 | + ], | |
| 335 | + "engines": { | |
| 336 | + "node": ">=12" | |
| 337 | + } | |
| 338 | + }, | |
| 339 | + "node_modules/@esbuild/android-arm": { | |
| 340 | + "version": "0.21.5", | |
| 341 | + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", | |
| 342 | + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", | |
| 343 | + "cpu": [ | |
| 344 | + "arm" | |
| 345 | + ], | |
| 346 | + "dev": true, | |
| 347 | + "license": "MIT", | |
| 348 | + "optional": true, | |
| 349 | + "os": [ | |
| 350 | + "android" | |
| 351 | + ], | |
| 352 | + "engines": { | |
| 353 | + "node": ">=12" | |
| 354 | + } | |
| 355 | + }, | |
| 356 | + "node_modules/@esbuild/android-arm64": { | |
| 357 | + "version": "0.21.5", | |
| 358 | + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", | |
| 359 | + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", | |
| 360 | + "cpu": [ | |
| 361 | + "arm64" | |
| 362 | + ], | |
| 363 | + "dev": true, | |
| 364 | + "license": "MIT", | |
| 365 | + "optional": true, | |
| 366 | + "os": [ | |
| 367 | + "android" | |
| 368 | + ], | |
| 369 | + "engines": { | |
| 370 | + "node": ">=12" | |
| 371 | + } | |
| 372 | + }, | |
| 373 | + "node_modules/@esbuild/android-x64": { | |
| 374 | + "version": "0.21.5", | |
| 375 | + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", | |
| 376 | + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", | |
| 377 | + "cpu": [ | |
| 378 | + "x64" | |
| 379 | + ], | |
| 380 | + "dev": true, | |
| 381 | + "license": "MIT", | |
| 382 | + "optional": true, | |
| 383 | + "os": [ | |
| 384 | + "android" | |
| 385 | + ], | |
| 386 | + "engines": { | |
| 387 | + "node": ">=12" | |
| 388 | + } | |
| 389 | + }, | |
| 390 | + "node_modules/@esbuild/darwin-arm64": { | |
| 391 | + "version": "0.21.5", | |
| 392 | + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", | |
| 393 | + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", | |
| 394 | + "cpu": [ | |
| 395 | + "arm64" | |
| 396 | + ], | |
| 397 | + "dev": true, | |
| 398 | + "license": "MIT", | |
| 399 | + "optional": true, | |
| 400 | + "os": [ | |
| 401 | + "darwin" | |
| 402 | + ], | |
| 403 | + "engines": { | |
| 404 | + "node": ">=12" | |
| 405 | + } | |
| 406 | + }, | |
| 407 | + "node_modules/@esbuild/darwin-x64": { | |
| 408 | + "version": "0.21.5", | |
| 409 | + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", | |
| 410 | + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", | |
| 411 | + "cpu": [ | |
| 412 | + "x64" | |
| 413 | + ], | |
| 414 | + "dev": true, | |
| 415 | + "license": "MIT", | |
| 416 | + "optional": true, | |
| 417 | + "os": [ | |
| 418 | + "darwin" | |
| 419 | + ], | |
| 420 | + "engines": { | |
| 421 | + "node": ">=12" | |
| 422 | + } | |
| 423 | + }, | |
| 424 | + "node_modules/@esbuild/freebsd-arm64": { | |
| 425 | + "version": "0.21.5", | |
| 426 | + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", | |
| 427 | + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", | |
| 428 | + "cpu": [ | |
| 429 | + "arm64" | |
| 430 | + ], | |
| 431 | + "dev": true, | |
| 432 | + "license": "MIT", | |
| 433 | + "optional": true, | |
| 434 | + "os": [ | |
| 435 | + "freebsd" | |
| 436 | + ], | |
| 437 | + "engines": { | |
| 438 | + "node": ">=12" | |
| 439 | + } | |
| 440 | + }, | |
| 441 | + "node_modules/@esbuild/freebsd-x64": { | |
| 442 | + "version": "0.21.5", | |
| 443 | + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", | |
| 444 | + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", | |
| 445 | + "cpu": [ | |
| 446 | + "x64" | |
| 447 | + ], | |
| 448 | + "dev": true, | |
| 449 | + "license": "MIT", | |
| 450 | + "optional": true, | |
| 451 | + "os": [ | |
| 452 | + "freebsd" | |
| 453 | + ], | |
| 454 | + "engines": { | |
| 455 | + "node": ">=12" | |
| 456 | + } | |
| 457 | + }, | |
| 458 | + "node_modules/@esbuild/linux-arm": { | |
| 459 | + "version": "0.21.5", | |
| 460 | + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", | |
| 461 | + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", | |
| 462 | + "cpu": [ | |
| 463 | + "arm" | |
| 464 | + ], | |
| 465 | + "dev": true, | |
| 466 | + "license": "MIT", | |
| 467 | + "optional": true, | |
| 468 | + "os": [ | |
| 469 | + "linux" | |
| 470 | + ], | |
| 471 | + "engines": { | |
| 472 | + "node": ">=12" | |
| 473 | + } | |
| 474 | + }, | |
| 475 | + "node_modules/@esbuild/linux-arm64": { | |
| 476 | + "version": "0.21.5", | |
| 477 | + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", | |
| 478 | + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", | |
| 479 | + "cpu": [ | |
| 480 | + "arm64" | |
| 481 | + ], | |
| 482 | + "dev": true, | |
| 483 | + "license": "MIT", | |
| 484 | + "optional": true, | |
| 485 | + "os": [ | |
| 486 | + "linux" | |
| 487 | + ], | |
| 488 | + "engines": { | |
| 489 | + "node": ">=12" | |
| 490 | + } | |
| 491 | + }, | |
| 492 | + "node_modules/@esbuild/linux-ia32": { | |
| 493 | + "version": "0.21.5", | |
| 494 | + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", | |
| 495 | + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", | |
| 496 | + "cpu": [ | |
| 497 | + "ia32" | |
| 498 | + ], | |
| 499 | + "dev": true, | |
| 500 | + "license": "MIT", | |
| 501 | + "optional": true, | |
| 502 | + "os": [ | |
| 503 | + "linux" | |
| 504 | + ], | |
| 505 | + "engines": { | |
| 506 | + "node": ">=12" | |
| 507 | + } | |
| 508 | + }, | |
| 509 | + "node_modules/@esbuild/linux-loong64": { | |
| 510 | + "version": "0.21.5", | |
| 511 | + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", | |
| 512 | + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", | |
| 513 | + "cpu": [ | |
| 514 | + "loong64" | |
| 515 | + ], | |
| 516 | + "dev": true, | |
| 517 | + "license": "MIT", | |
| 518 | + "optional": true, | |
| 519 | + "os": [ | |
| 520 | + "linux" | |
| 521 | + ], | |
| 522 | + "engines": { | |
| 523 | + "node": ">=12" | |
| 524 | + } | |
| 525 | + }, | |
| 526 | + "node_modules/@esbuild/linux-mips64el": { | |
| 527 | + "version": "0.21.5", | |
| 528 | + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", | |
| 529 | + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", | |
| 530 | + "cpu": [ | |
| 531 | + "mips64el" | |
| 532 | + ], | |
| 533 | + "dev": true, | |
| 534 | + "license": "MIT", | |
| 535 | + "optional": true, | |
| 536 | + "os": [ | |
| 537 | + "linux" | |
| 538 | + ], | |
| 539 | + "engines": { | |
| 540 | + "node": ">=12" | |
| 541 | + } | |
| 542 | + }, | |
| 543 | + "node_modules/@esbuild/linux-ppc64": { | |
| 544 | + "version": "0.21.5", | |
| 545 | + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", | |
| 546 | + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", | |
| 547 | + "cpu": [ | |
| 548 | + "ppc64" | |
| 549 | + ], | |
| 550 | + "dev": true, | |
| 551 | + "license": "MIT", | |
| 552 | + "optional": true, | |
| 553 | + "os": [ | |
| 554 | + "linux" | |
| 555 | + ], | |
| 556 | + "engines": { | |
| 557 | + "node": ">=12" | |
| 558 | + } | |
| 559 | + }, | |
| 560 | + "node_modules/@esbuild/linux-riscv64": { | |
| 561 | + "version": "0.21.5", | |
| 562 | + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", | |
| 563 | + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", | |
| 564 | + "cpu": [ | |
| 565 | + "riscv64" | |
| 566 | + ], | |
| 567 | + "dev": true, | |
| 568 | + "license": "MIT", | |
| 569 | + "optional": true, | |
| 570 | + "os": [ | |
| 571 | + "linux" | |
| 572 | + ], | |
| 573 | + "engines": { | |
| 574 | + "node": ">=12" | |
| 575 | + } | |
| 576 | + }, | |
| 577 | + "node_modules/@esbuild/linux-s390x": { | |
| 578 | + "version": "0.21.5", | |
| 579 | + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", | |
| 580 | + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", | |
| 581 | + "cpu": [ | |
| 582 | + "s390x" | |
| 583 | + ], | |
| 584 | + "dev": true, | |
| 585 | + "license": "MIT", | |
| 586 | + "optional": true, | |
| 587 | + "os": [ | |
| 588 | + "linux" | |
| 589 | + ], | |
| 590 | + "engines": { | |
| 591 | + "node": ">=12" | |
| 592 | + } | |
| 593 | + }, | |
| 594 | + "node_modules/@esbuild/linux-x64": { | |
| 595 | + "version": "0.21.5", | |
| 596 | + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", | |
| 597 | + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", | |
| 598 | + "cpu": [ | |
| 599 | + "x64" | |
| 600 | + ], | |
| 601 | + "dev": true, | |
| 602 | + "license": "MIT", | |
| 603 | + "optional": true, | |
| 604 | + "os": [ | |
| 605 | + "linux" | |
| 606 | + ], | |
| 607 | + "engines": { | |
| 608 | + "node": ">=12" | |
| 609 | + } | |
| 610 | + }, | |
| 611 | + "node_modules/@esbuild/netbsd-x64": { | |
| 612 | + "version": "0.21.5", | |
| 613 | + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", | |
| 614 | + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", | |
| 615 | + "cpu": [ | |
| 616 | + "x64" | |
| 617 | + ], | |
| 618 | + "dev": true, | |
| 619 | + "license": "MIT", | |
| 620 | + "optional": true, | |
| 621 | + "os": [ | |
| 622 | + "netbsd" | |
| 623 | + ], | |
| 624 | + "engines": { | |
| 625 | + "node": ">=12" | |
| 626 | + } | |
| 627 | + }, | |
| 628 | + "node_modules/@esbuild/openbsd-x64": { | |
| 629 | + "version": "0.21.5", | |
| 630 | + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", | |
| 631 | + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", | |
| 632 | + "cpu": [ | |
| 633 | + "x64" | |
| 634 | + ], | |
| 635 | + "dev": true, | |
| 636 | + "license": "MIT", | |
| 637 | + "optional": true, | |
| 638 | + "os": [ | |
| 639 | + "openbsd" | |
| 640 | + ], | |
| 641 | + "engines": { | |
| 642 | + "node": ">=12" | |
| 643 | + } | |
| 644 | + }, | |
| 645 | + "node_modules/@esbuild/sunos-x64": { | |
| 646 | + "version": "0.21.5", | |
| 647 | + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", | |
| 648 | + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", | |
| 649 | + "cpu": [ | |
| 650 | + "x64" | |
| 651 | + ], | |
| 652 | + "dev": true, | |
| 653 | + "license": "MIT", | |
| 654 | + "optional": true, | |
| 655 | + "os": [ | |
| 656 | + "sunos" | |
| 657 | + ], | |
| 658 | + "engines": { | |
| 659 | + "node": ">=12" | |
| 660 | + } | |
| 661 | + }, | |
| 662 | + "node_modules/@esbuild/win32-arm64": { | |
| 663 | + "version": "0.21.5", | |
| 664 | + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", | |
| 665 | + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", | |
| 666 | + "cpu": [ | |
| 667 | + "arm64" | |
| 668 | + ], | |
| 669 | + "dev": true, | |
| 670 | + "license": "MIT", | |
| 671 | + "optional": true, | |
| 672 | + "os": [ | |
| 673 | + "win32" | |
| 674 | + ], | |
| 675 | + "engines": { | |
| 676 | + "node": ">=12" | |
| 677 | + } | |
| 678 | + }, | |
| 679 | + "node_modules/@esbuild/win32-ia32": { | |
| 680 | + "version": "0.21.5", | |
| 681 | + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", | |
| 682 | + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", | |
| 683 | + "cpu": [ | |
| 684 | + "ia32" | |
| 685 | + ], | |
| 686 | + "dev": true, | |
| 687 | + "license": "MIT", | |
| 688 | + "optional": true, | |
| 689 | + "os": [ | |
| 690 | + "win32" | |
| 691 | + ], | |
| 692 | + "engines": { | |
| 693 | + "node": ">=12" | |
| 694 | + } | |
| 695 | + }, | |
| 696 | + "node_modules/@esbuild/win32-x64": { | |
| 697 | + "version": "0.21.5", | |
| 698 | + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", | |
| 699 | + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", | |
| 700 | + "cpu": [ | |
| 701 | + "x64" | |
| 702 | + ], | |
| 703 | + "dev": true, | |
| 704 | + "license": "MIT", | |
| 705 | + "optional": true, | |
| 706 | + "os": [ | |
| 707 | + "win32" | |
| 708 | + ], | |
| 709 | + "engines": { | |
| 710 | + "node": ">=12" | |
| 711 | + } | |
| 712 | + }, | |
| 713 | + "node_modules/@jridgewell/gen-mapping": { | |
| 714 | + "version": "0.3.13", | |
| 715 | + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", | |
| 716 | + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", | |
| 717 | + "dev": true, | |
| 718 | + "license": "MIT", | |
| 719 | + "dependencies": { | |
| 720 | + "@jridgewell/sourcemap-codec": "^1.5.0", | |
| 721 | + "@jridgewell/trace-mapping": "^0.3.24" | |
| 722 | + } | |
| 723 | + }, | |
| 724 | + "node_modules/@jridgewell/remapping": { | |
| 725 | + "version": "2.3.5", | |
| 726 | + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", | |
| 727 | + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", | |
| 728 | + "dev": true, | |
| 729 | + "license": "MIT", | |
| 730 | + "dependencies": { | |
| 731 | + "@jridgewell/gen-mapping": "^0.3.5", | |
| 732 | + "@jridgewell/trace-mapping": "^0.3.24" | |
| 733 | + } | |
| 734 | + }, | |
| 735 | + "node_modules/@jridgewell/resolve-uri": { | |
| 736 | + "version": "3.1.2", | |
| 737 | + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", | |
| 738 | + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", | |
| 739 | + "dev": true, | |
| 740 | + "license": "MIT", | |
| 741 | + "engines": { | |
| 742 | + "node": ">=6.0.0" | |
| 743 | + } | |
| 744 | + }, | |
| 745 | + "node_modules/@jridgewell/sourcemap-codec": { | |
| 746 | + "version": "1.5.5", | |
| 747 | + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", | |
| 748 | + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", | |
| 749 | + "dev": true, | |
| 750 | + "license": "MIT" | |
| 751 | + }, | |
| 752 | + "node_modules/@jridgewell/trace-mapping": { | |
| 753 | + "version": "0.3.31", | |
| 754 | + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", | |
| 755 | + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", | |
| 756 | + "dev": true, | |
| 757 | + "license": "MIT", | |
| 758 | + "dependencies": { | |
| 759 | + "@jridgewell/resolve-uri": "^3.1.0", | |
| 760 | + "@jridgewell/sourcemap-codec": "^1.4.14" | |
| 761 | + } | |
| 762 | + }, | |
| 763 | + "node_modules/@nodelib/fs.scandir": { | |
| 764 | + "version": "2.1.5", | |
| 765 | + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", | |
| 766 | + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", | |
| 767 | + "dev": true, | |
| 768 | + "license": "MIT", | |
| 769 | + "dependencies": { | |
| 770 | + "@nodelib/fs.stat": "2.0.5", | |
| 771 | + "run-parallel": "^1.1.9" | |
| 772 | + }, | |
| 773 | + "engines": { | |
| 774 | + "node": ">= 8" | |
| 775 | + } | |
| 776 | + }, | |
| 777 | + "node_modules/@nodelib/fs.stat": { | |
| 778 | + "version": "2.0.5", | |
| 779 | + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", | |
| 780 | + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", | |
| 781 | + "dev": true, | |
| 782 | + "license": "MIT", | |
| 783 | + "engines": { | |
| 784 | + "node": ">= 8" | |
| 785 | + } | |
| 786 | + }, | |
| 787 | + "node_modules/@nodelib/fs.walk": { | |
| 788 | + "version": "1.2.8", | |
| 789 | + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", | |
| 790 | + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", | |
| 791 | + "dev": true, | |
| 792 | + "license": "MIT", | |
| 793 | + "dependencies": { | |
| 794 | + "@nodelib/fs.scandir": "2.1.5", | |
| 795 | + "fastq": "^1.6.0" | |
| 796 | + }, | |
| 797 | + "engines": { | |
| 798 | + "node": ">= 8" | |
| 799 | + } | |
| 800 | + }, | |
| 801 | + "node_modules/@remix-run/router": { | |
| 802 | + "version": "1.23.2", | |
| 803 | + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", | |
| 804 | + "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", | |
| 805 | + "license": "MIT", | |
| 806 | + "engines": { | |
| 807 | + "node": ">=14.0.0" | |
| 808 | + } | |
| 809 | + }, | |
| 810 | + "node_modules/@rolldown/pluginutils": { | |
| 811 | + "version": "1.0.0-beta.27", | |
| 812 | + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", | |
| 813 | + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", | |
| 814 | + "dev": true, | |
| 815 | + "license": "MIT" | |
| 816 | + }, | |
| 817 | + "node_modules/@rollup/rollup-android-arm-eabi": { | |
| 818 | + "version": "4.60.1", | |
| 819 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", | |
| 820 | + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", | |
| 821 | + "cpu": [ | |
| 822 | + "arm" | |
| 823 | + ], | |
| 824 | + "dev": true, | |
| 825 | + "license": "MIT", | |
| 826 | + "optional": true, | |
| 827 | + "os": [ | |
| 828 | + "android" | |
| 829 | + ] | |
| 830 | + }, | |
| 831 | + "node_modules/@rollup/rollup-android-arm64": { | |
| 832 | + "version": "4.60.1", | |
| 833 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", | |
| 834 | + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", | |
| 835 | + "cpu": [ | |
| 836 | + "arm64" | |
| 837 | + ], | |
| 838 | + "dev": true, | |
| 839 | + "license": "MIT", | |
| 840 | + "optional": true, | |
| 841 | + "os": [ | |
| 842 | + "android" | |
| 843 | + ] | |
| 844 | + }, | |
| 845 | + "node_modules/@rollup/rollup-darwin-arm64": { | |
| 846 | + "version": "4.60.1", | |
| 847 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", | |
| 848 | + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", | |
| 849 | + "cpu": [ | |
| 850 | + "arm64" | |
| 851 | + ], | |
| 852 | + "dev": true, | |
| 853 | + "license": "MIT", | |
| 854 | + "optional": true, | |
| 855 | + "os": [ | |
| 856 | + "darwin" | |
| 857 | + ] | |
| 858 | + }, | |
| 859 | + "node_modules/@rollup/rollup-darwin-x64": { | |
| 860 | + "version": "4.60.1", | |
| 861 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", | |
| 862 | + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", | |
| 863 | + "cpu": [ | |
| 864 | + "x64" | |
| 865 | + ], | |
| 866 | + "dev": true, | |
| 867 | + "license": "MIT", | |
| 868 | + "optional": true, | |
| 869 | + "os": [ | |
| 870 | + "darwin" | |
| 871 | + ] | |
| 872 | + }, | |
| 873 | + "node_modules/@rollup/rollup-freebsd-arm64": { | |
| 874 | + "version": "4.60.1", | |
| 875 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", | |
| 876 | + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", | |
| 877 | + "cpu": [ | |
| 878 | + "arm64" | |
| 879 | + ], | |
| 880 | + "dev": true, | |
| 881 | + "license": "MIT", | |
| 882 | + "optional": true, | |
| 883 | + "os": [ | |
| 884 | + "freebsd" | |
| 885 | + ] | |
| 886 | + }, | |
| 887 | + "node_modules/@rollup/rollup-freebsd-x64": { | |
| 888 | + "version": "4.60.1", | |
| 889 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", | |
| 890 | + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", | |
| 891 | + "cpu": [ | |
| 892 | + "x64" | |
| 893 | + ], | |
| 894 | + "dev": true, | |
| 895 | + "license": "MIT", | |
| 896 | + "optional": true, | |
| 897 | + "os": [ | |
| 898 | + "freebsd" | |
| 899 | + ] | |
| 900 | + }, | |
| 901 | + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { | |
| 902 | + "version": "4.60.1", | |
| 903 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", | |
| 904 | + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", | |
| 905 | + "cpu": [ | |
| 906 | + "arm" | |
| 907 | + ], | |
| 908 | + "dev": true, | |
| 909 | + "libc": [ | |
| 910 | + "glibc" | |
| 911 | + ], | |
| 912 | + "license": "MIT", | |
| 913 | + "optional": true, | |
| 914 | + "os": [ | |
| 915 | + "linux" | |
| 916 | + ] | |
| 917 | + }, | |
| 918 | + "node_modules/@rollup/rollup-linux-arm-musleabihf": { | |
| 919 | + "version": "4.60.1", | |
| 920 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", | |
| 921 | + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", | |
| 922 | + "cpu": [ | |
| 923 | + "arm" | |
| 924 | + ], | |
| 925 | + "dev": true, | |
| 926 | + "libc": [ | |
| 927 | + "musl" | |
| 928 | + ], | |
| 929 | + "license": "MIT", | |
| 930 | + "optional": true, | |
| 931 | + "os": [ | |
| 932 | + "linux" | |
| 933 | + ] | |
| 934 | + }, | |
| 935 | + "node_modules/@rollup/rollup-linux-arm64-gnu": { | |
| 936 | + "version": "4.60.1", | |
| 937 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", | |
| 938 | + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", | |
| 939 | + "cpu": [ | |
| 940 | + "arm64" | |
| 941 | + ], | |
| 942 | + "dev": true, | |
| 943 | + "libc": [ | |
| 944 | + "glibc" | |
| 945 | + ], | |
| 946 | + "license": "MIT", | |
| 947 | + "optional": true, | |
| 948 | + "os": [ | |
| 949 | + "linux" | |
| 950 | + ] | |
| 951 | + }, | |
| 952 | + "node_modules/@rollup/rollup-linux-arm64-musl": { | |
| 953 | + "version": "4.60.1", | |
| 954 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", | |
| 955 | + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", | |
| 956 | + "cpu": [ | |
| 957 | + "arm64" | |
| 958 | + ], | |
| 959 | + "dev": true, | |
| 960 | + "libc": [ | |
| 961 | + "musl" | |
| 962 | + ], | |
| 963 | + "license": "MIT", | |
| 964 | + "optional": true, | |
| 965 | + "os": [ | |
| 966 | + "linux" | |
| 967 | + ] | |
| 968 | + }, | |
| 969 | + "node_modules/@rollup/rollup-linux-loong64-gnu": { | |
| 970 | + "version": "4.60.1", | |
| 971 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", | |
| 972 | + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", | |
| 973 | + "cpu": [ | |
| 974 | + "loong64" | |
| 975 | + ], | |
| 976 | + "dev": true, | |
| 977 | + "libc": [ | |
| 978 | + "glibc" | |
| 979 | + ], | |
| 980 | + "license": "MIT", | |
| 981 | + "optional": true, | |
| 982 | + "os": [ | |
| 983 | + "linux" | |
| 984 | + ] | |
| 985 | + }, | |
| 986 | + "node_modules/@rollup/rollup-linux-loong64-musl": { | |
| 987 | + "version": "4.60.1", | |
| 988 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", | |
| 989 | + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", | |
| 990 | + "cpu": [ | |
| 991 | + "loong64" | |
| 992 | + ], | |
| 993 | + "dev": true, | |
| 994 | + "libc": [ | |
| 995 | + "musl" | |
| 996 | + ], | |
| 997 | + "license": "MIT", | |
| 998 | + "optional": true, | |
| 999 | + "os": [ | |
| 1000 | + "linux" | |
| 1001 | + ] | |
| 1002 | + }, | |
| 1003 | + "node_modules/@rollup/rollup-linux-ppc64-gnu": { | |
| 1004 | + "version": "4.60.1", | |
| 1005 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", | |
| 1006 | + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", | |
| 1007 | + "cpu": [ | |
| 1008 | + "ppc64" | |
| 1009 | + ], | |
| 1010 | + "dev": true, | |
| 1011 | + "libc": [ | |
| 1012 | + "glibc" | |
| 1013 | + ], | |
| 1014 | + "license": "MIT", | |
| 1015 | + "optional": true, | |
| 1016 | + "os": [ | |
| 1017 | + "linux" | |
| 1018 | + ] | |
| 1019 | + }, | |
| 1020 | + "node_modules/@rollup/rollup-linux-ppc64-musl": { | |
| 1021 | + "version": "4.60.1", | |
| 1022 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", | |
| 1023 | + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", | |
| 1024 | + "cpu": [ | |
| 1025 | + "ppc64" | |
| 1026 | + ], | |
| 1027 | + "dev": true, | |
| 1028 | + "libc": [ | |
| 1029 | + "musl" | |
| 1030 | + ], | |
| 1031 | + "license": "MIT", | |
| 1032 | + "optional": true, | |
| 1033 | + "os": [ | |
| 1034 | + "linux" | |
| 1035 | + ] | |
| 1036 | + }, | |
| 1037 | + "node_modules/@rollup/rollup-linux-riscv64-gnu": { | |
| 1038 | + "version": "4.60.1", | |
| 1039 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", | |
| 1040 | + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", | |
| 1041 | + "cpu": [ | |
| 1042 | + "riscv64" | |
| 1043 | + ], | |
| 1044 | + "dev": true, | |
| 1045 | + "libc": [ | |
| 1046 | + "glibc" | |
| 1047 | + ], | |
| 1048 | + "license": "MIT", | |
| 1049 | + "optional": true, | |
| 1050 | + "os": [ | |
| 1051 | + "linux" | |
| 1052 | + ] | |
| 1053 | + }, | |
| 1054 | + "node_modules/@rollup/rollup-linux-riscv64-musl": { | |
| 1055 | + "version": "4.60.1", | |
| 1056 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", | |
| 1057 | + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", | |
| 1058 | + "cpu": [ | |
| 1059 | + "riscv64" | |
| 1060 | + ], | |
| 1061 | + "dev": true, | |
| 1062 | + "libc": [ | |
| 1063 | + "musl" | |
| 1064 | + ], | |
| 1065 | + "license": "MIT", | |
| 1066 | + "optional": true, | |
| 1067 | + "os": [ | |
| 1068 | + "linux" | |
| 1069 | + ] | |
| 1070 | + }, | |
| 1071 | + "node_modules/@rollup/rollup-linux-s390x-gnu": { | |
| 1072 | + "version": "4.60.1", | |
| 1073 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", | |
| 1074 | + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", | |
| 1075 | + "cpu": [ | |
| 1076 | + "s390x" | |
| 1077 | + ], | |
| 1078 | + "dev": true, | |
| 1079 | + "libc": [ | |
| 1080 | + "glibc" | |
| 1081 | + ], | |
| 1082 | + "license": "MIT", | |
| 1083 | + "optional": true, | |
| 1084 | + "os": [ | |
| 1085 | + "linux" | |
| 1086 | + ] | |
| 1087 | + }, | |
| 1088 | + "node_modules/@rollup/rollup-linux-x64-gnu": { | |
| 1089 | + "version": "4.60.1", | |
| 1090 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", | |
| 1091 | + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", | |
| 1092 | + "cpu": [ | |
| 1093 | + "x64" | |
| 1094 | + ], | |
| 1095 | + "dev": true, | |
| 1096 | + "libc": [ | |
| 1097 | + "glibc" | |
| 1098 | + ], | |
| 1099 | + "license": "MIT", | |
| 1100 | + "optional": true, | |
| 1101 | + "os": [ | |
| 1102 | + "linux" | |
| 1103 | + ] | |
| 1104 | + }, | |
| 1105 | + "node_modules/@rollup/rollup-linux-x64-musl": { | |
| 1106 | + "version": "4.60.1", | |
| 1107 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", | |
| 1108 | + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", | |
| 1109 | + "cpu": [ | |
| 1110 | + "x64" | |
| 1111 | + ], | |
| 1112 | + "dev": true, | |
| 1113 | + "libc": [ | |
| 1114 | + "musl" | |
| 1115 | + ], | |
| 1116 | + "license": "MIT", | |
| 1117 | + "optional": true, | |
| 1118 | + "os": [ | |
| 1119 | + "linux" | |
| 1120 | + ] | |
| 1121 | + }, | |
| 1122 | + "node_modules/@rollup/rollup-openbsd-x64": { | |
| 1123 | + "version": "4.60.1", | |
| 1124 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", | |
| 1125 | + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", | |
| 1126 | + "cpu": [ | |
| 1127 | + "x64" | |
| 1128 | + ], | |
| 1129 | + "dev": true, | |
| 1130 | + "license": "MIT", | |
| 1131 | + "optional": true, | |
| 1132 | + "os": [ | |
| 1133 | + "openbsd" | |
| 1134 | + ] | |
| 1135 | + }, | |
| 1136 | + "node_modules/@rollup/rollup-openharmony-arm64": { | |
| 1137 | + "version": "4.60.1", | |
| 1138 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", | |
| 1139 | + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", | |
| 1140 | + "cpu": [ | |
| 1141 | + "arm64" | |
| 1142 | + ], | |
| 1143 | + "dev": true, | |
| 1144 | + "license": "MIT", | |
| 1145 | + "optional": true, | |
| 1146 | + "os": [ | |
| 1147 | + "openharmony" | |
| 1148 | + ] | |
| 1149 | + }, | |
| 1150 | + "node_modules/@rollup/rollup-win32-arm64-msvc": { | |
| 1151 | + "version": "4.60.1", | |
| 1152 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", | |
| 1153 | + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", | |
| 1154 | + "cpu": [ | |
| 1155 | + "arm64" | |
| 1156 | + ], | |
| 1157 | + "dev": true, | |
| 1158 | + "license": "MIT", | |
| 1159 | + "optional": true, | |
| 1160 | + "os": [ | |
| 1161 | + "win32" | |
| 1162 | + ] | |
| 1163 | + }, | |
| 1164 | + "node_modules/@rollup/rollup-win32-ia32-msvc": { | |
| 1165 | + "version": "4.60.1", | |
| 1166 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", | |
| 1167 | + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", | |
| 1168 | + "cpu": [ | |
| 1169 | + "ia32" | |
| 1170 | + ], | |
| 1171 | + "dev": true, | |
| 1172 | + "license": "MIT", | |
| 1173 | + "optional": true, | |
| 1174 | + "os": [ | |
| 1175 | + "win32" | |
| 1176 | + ] | |
| 1177 | + }, | |
| 1178 | + "node_modules/@rollup/rollup-win32-x64-gnu": { | |
| 1179 | + "version": "4.60.1", | |
| 1180 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", | |
| 1181 | + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", | |
| 1182 | + "cpu": [ | |
| 1183 | + "x64" | |
| 1184 | + ], | |
| 1185 | + "dev": true, | |
| 1186 | + "license": "MIT", | |
| 1187 | + "optional": true, | |
| 1188 | + "os": [ | |
| 1189 | + "win32" | |
| 1190 | + ] | |
| 1191 | + }, | |
| 1192 | + "node_modules/@rollup/rollup-win32-x64-msvc": { | |
| 1193 | + "version": "4.60.1", | |
| 1194 | + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", | |
| 1195 | + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", | |
| 1196 | + "cpu": [ | |
| 1197 | + "x64" | |
| 1198 | + ], | |
| 1199 | + "dev": true, | |
| 1200 | + "license": "MIT", | |
| 1201 | + "optional": true, | |
| 1202 | + "os": [ | |
| 1203 | + "win32" | |
| 1204 | + ] | |
| 1205 | + }, | |
| 1206 | + "node_modules/@types/babel__core": { | |
| 1207 | + "version": "7.20.5", | |
| 1208 | + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", | |
| 1209 | + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", | |
| 1210 | + "dev": true, | |
| 1211 | + "license": "MIT", | |
| 1212 | + "dependencies": { | |
| 1213 | + "@babel/parser": "^7.20.7", | |
| 1214 | + "@babel/types": "^7.20.7", | |
| 1215 | + "@types/babel__generator": "*", | |
| 1216 | + "@types/babel__template": "*", | |
| 1217 | + "@types/babel__traverse": "*" | |
| 1218 | + } | |
| 1219 | + }, | |
| 1220 | + "node_modules/@types/babel__generator": { | |
| 1221 | + "version": "7.27.0", | |
| 1222 | + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", | |
| 1223 | + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", | |
| 1224 | + "dev": true, | |
| 1225 | + "license": "MIT", | |
| 1226 | + "dependencies": { | |
| 1227 | + "@babel/types": "^7.0.0" | |
| 1228 | + } | |
| 1229 | + }, | |
| 1230 | + "node_modules/@types/babel__template": { | |
| 1231 | + "version": "7.4.4", | |
| 1232 | + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", | |
| 1233 | + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", | |
| 1234 | + "dev": true, | |
| 1235 | + "license": "MIT", | |
| 1236 | + "dependencies": { | |
| 1237 | + "@babel/parser": "^7.1.0", | |
| 1238 | + "@babel/types": "^7.0.0" | |
| 1239 | + } | |
| 1240 | + }, | |
| 1241 | + "node_modules/@types/babel__traverse": { | |
| 1242 | + "version": "7.28.0", | |
| 1243 | + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", | |
| 1244 | + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", | |
| 1245 | + "dev": true, | |
| 1246 | + "license": "MIT", | |
| 1247 | + "dependencies": { | |
| 1248 | + "@babel/types": "^7.28.2" | |
| 1249 | + } | |
| 1250 | + }, | |
| 1251 | + "node_modules/@types/estree": { | |
| 1252 | + "version": "1.0.8", | |
| 1253 | + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", | |
| 1254 | + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", | |
| 1255 | + "dev": true, | |
| 1256 | + "license": "MIT" | |
| 1257 | + }, | |
| 1258 | + "node_modules/@types/node": { | |
| 1259 | + "version": "25.5.2", | |
| 1260 | + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", | |
| 1261 | + "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", | |
| 1262 | + "dev": true, | |
| 1263 | + "license": "MIT", | |
| 1264 | + "dependencies": { | |
| 1265 | + "undici-types": "~7.18.0" | |
| 1266 | + } | |
| 1267 | + }, | |
| 1268 | + "node_modules/@types/prop-types": { | |
| 1269 | + "version": "15.7.15", | |
| 1270 | + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", | |
| 1271 | + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", | |
| 1272 | + "dev": true, | |
| 1273 | + "license": "MIT" | |
| 1274 | + }, | |
| 1275 | + "node_modules/@types/react": { | |
| 1276 | + "version": "18.3.28", | |
| 1277 | + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", | |
| 1278 | + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", | |
| 1279 | + "dev": true, | |
| 1280 | + "license": "MIT", | |
| 1281 | + "dependencies": { | |
| 1282 | + "@types/prop-types": "*", | |
| 1283 | + "csstype": "^3.2.2" | |
| 1284 | + } | |
| 1285 | + }, | |
| 1286 | + "node_modules/@types/react-dom": { | |
| 1287 | + "version": "18.3.7", | |
| 1288 | + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", | |
| 1289 | + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", | |
| 1290 | + "dev": true, | |
| 1291 | + "license": "MIT", | |
| 1292 | + "peerDependencies": { | |
| 1293 | + "@types/react": "^18.0.0" | |
| 1294 | + } | |
| 1295 | + }, | |
| 1296 | + "node_modules/@vitejs/plugin-react": { | |
| 1297 | + "version": "4.7.0", | |
| 1298 | + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", | |
| 1299 | + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", | |
| 1300 | + "dev": true, | |
| 1301 | + "license": "MIT", | |
| 1302 | + "dependencies": { | |
| 1303 | + "@babel/core": "^7.28.0", | |
| 1304 | + "@babel/plugin-transform-react-jsx-self": "^7.27.1", | |
| 1305 | + "@babel/plugin-transform-react-jsx-source": "^7.27.1", | |
| 1306 | + "@rolldown/pluginutils": "1.0.0-beta.27", | |
| 1307 | + "@types/babel__core": "^7.20.5", | |
| 1308 | + "react-refresh": "^0.17.0" | |
| 1309 | + }, | |
| 1310 | + "engines": { | |
| 1311 | + "node": "^14.18.0 || >=16.0.0" | |
| 1312 | + }, | |
| 1313 | + "peerDependencies": { | |
| 1314 | + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" | |
| 1315 | + } | |
| 1316 | + }, | |
| 1317 | + "node_modules/any-promise": { | |
| 1318 | + "version": "1.3.0", | |
| 1319 | + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", | |
| 1320 | + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", | |
| 1321 | + "dev": true, | |
| 1322 | + "license": "MIT" | |
| 1323 | + }, | |
| 1324 | + "node_modules/anymatch": { | |
| 1325 | + "version": "3.1.3", | |
| 1326 | + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", | |
| 1327 | + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", | |
| 1328 | + "dev": true, | |
| 1329 | + "license": "ISC", | |
| 1330 | + "dependencies": { | |
| 1331 | + "normalize-path": "^3.0.0", | |
| 1332 | + "picomatch": "^2.0.4" | |
| 1333 | + }, | |
| 1334 | + "engines": { | |
| 1335 | + "node": ">= 8" | |
| 1336 | + } | |
| 1337 | + }, | |
| 1338 | + "node_modules/arg": { | |
| 1339 | + "version": "5.0.2", | |
| 1340 | + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", | |
| 1341 | + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", | |
| 1342 | + "dev": true, | |
| 1343 | + "license": "MIT" | |
| 1344 | + }, | |
| 1345 | + "node_modules/autoprefixer": { | |
| 1346 | + "version": "10.4.27", | |
| 1347 | + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", | |
| 1348 | + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", | |
| 1349 | + "dev": true, | |
| 1350 | + "funding": [ | |
| 1351 | + { | |
| 1352 | + "type": "opencollective", | |
| 1353 | + "url": "https://opencollective.com/postcss/" | |
| 1354 | + }, | |
| 1355 | + { | |
| 1356 | + "type": "tidelift", | |
| 1357 | + "url": "https://tidelift.com/funding/github/npm/autoprefixer" | |
| 1358 | + }, | |
| 1359 | + { | |
| 1360 | + "type": "github", | |
| 1361 | + "url": "https://github.com/sponsors/ai" | |
| 1362 | + } | |
| 1363 | + ], | |
| 1364 | + "license": "MIT", | |
| 1365 | + "dependencies": { | |
| 1366 | + "browserslist": "^4.28.1", | |
| 1367 | + "caniuse-lite": "^1.0.30001774", | |
| 1368 | + "fraction.js": "^5.3.4", | |
| 1369 | + "picocolors": "^1.1.1", | |
| 1370 | + "postcss-value-parser": "^4.2.0" | |
| 1371 | + }, | |
| 1372 | + "bin": { | |
| 1373 | + "autoprefixer": "bin/autoprefixer" | |
| 1374 | + }, | |
| 1375 | + "engines": { | |
| 1376 | + "node": "^10 || ^12 || >=14" | |
| 1377 | + }, | |
| 1378 | + "peerDependencies": { | |
| 1379 | + "postcss": "^8.1.0" | |
| 1380 | + } | |
| 1381 | + }, | |
| 1382 | + "node_modules/baseline-browser-mapping": { | |
| 1383 | + "version": "2.10.16", | |
| 1384 | + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.16.tgz", | |
| 1385 | + "integrity": "sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==", | |
| 1386 | + "dev": true, | |
| 1387 | + "license": "Apache-2.0", | |
| 1388 | + "bin": { | |
| 1389 | + "baseline-browser-mapping": "dist/cli.cjs" | |
| 1390 | + }, | |
| 1391 | + "engines": { | |
| 1392 | + "node": ">=6.0.0" | |
| 1393 | + } | |
| 1394 | + }, | |
| 1395 | + "node_modules/binary-extensions": { | |
| 1396 | + "version": "2.3.0", | |
| 1397 | + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", | |
| 1398 | + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", | |
| 1399 | + "dev": true, | |
| 1400 | + "license": "MIT", | |
| 1401 | + "engines": { | |
| 1402 | + "node": ">=8" | |
| 1403 | + }, | |
| 1404 | + "funding": { | |
| 1405 | + "url": "https://github.com/sponsors/sindresorhus" | |
| 1406 | + } | |
| 1407 | + }, | |
| 1408 | + "node_modules/braces": { | |
| 1409 | + "version": "3.0.3", | |
| 1410 | + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", | |
| 1411 | + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", | |
| 1412 | + "dev": true, | |
| 1413 | + "license": "MIT", | |
| 1414 | + "dependencies": { | |
| 1415 | + "fill-range": "^7.1.1" | |
| 1416 | + }, | |
| 1417 | + "engines": { | |
| 1418 | + "node": ">=8" | |
| 1419 | + } | |
| 1420 | + }, | |
| 1421 | + "node_modules/browserslist": { | |
| 1422 | + "version": "4.28.2", | |
| 1423 | + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", | |
| 1424 | + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", | |
| 1425 | + "dev": true, | |
| 1426 | + "funding": [ | |
| 1427 | + { | |
| 1428 | + "type": "opencollective", | |
| 1429 | + "url": "https://opencollective.com/browserslist" | |
| 1430 | + }, | |
| 1431 | + { | |
| 1432 | + "type": "tidelift", | |
| 1433 | + "url": "https://tidelift.com/funding/github/npm/browserslist" | |
| 1434 | + }, | |
| 1435 | + { | |
| 1436 | + "type": "github", | |
| 1437 | + "url": "https://github.com/sponsors/ai" | |
| 1438 | + } | |
| 1439 | + ], | |
| 1440 | + "license": "MIT", | |
| 1441 | + "dependencies": { | |
| 1442 | + "baseline-browser-mapping": "^2.10.12", | |
| 1443 | + "caniuse-lite": "^1.0.30001782", | |
| 1444 | + "electron-to-chromium": "^1.5.328", | |
| 1445 | + "node-releases": "^2.0.36", | |
| 1446 | + "update-browserslist-db": "^1.2.3" | |
| 1447 | + }, | |
| 1448 | + "bin": { | |
| 1449 | + "browserslist": "cli.js" | |
| 1450 | + }, | |
| 1451 | + "engines": { | |
| 1452 | + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" | |
| 1453 | + } | |
| 1454 | + }, | |
| 1455 | + "node_modules/camelcase-css": { | |
| 1456 | + "version": "2.0.1", | |
| 1457 | + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", | |
| 1458 | + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", | |
| 1459 | + "dev": true, | |
| 1460 | + "license": "MIT", | |
| 1461 | + "engines": { | |
| 1462 | + "node": ">= 6" | |
| 1463 | + } | |
| 1464 | + }, | |
| 1465 | + "node_modules/caniuse-lite": { | |
| 1466 | + "version": "1.0.30001787", | |
| 1467 | + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz", | |
| 1468 | + "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==", | |
| 1469 | + "dev": true, | |
| 1470 | + "funding": [ | |
| 1471 | + { | |
| 1472 | + "type": "opencollective", | |
| 1473 | + "url": "https://opencollective.com/browserslist" | |
| 1474 | + }, | |
| 1475 | + { | |
| 1476 | + "type": "tidelift", | |
| 1477 | + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" | |
| 1478 | + }, | |
| 1479 | + { | |
| 1480 | + "type": "github", | |
| 1481 | + "url": "https://github.com/sponsors/ai" | |
| 1482 | + } | |
| 1483 | + ], | |
| 1484 | + "license": "CC-BY-4.0" | |
| 1485 | + }, | |
| 1486 | + "node_modules/chokidar": { | |
| 1487 | + "version": "3.6.0", | |
| 1488 | + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", | |
| 1489 | + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", | |
| 1490 | + "dev": true, | |
| 1491 | + "license": "MIT", | |
| 1492 | + "dependencies": { | |
| 1493 | + "anymatch": "~3.1.2", | |
| 1494 | + "braces": "~3.0.2", | |
| 1495 | + "glob-parent": "~5.1.2", | |
| 1496 | + "is-binary-path": "~2.1.0", | |
| 1497 | + "is-glob": "~4.0.1", | |
| 1498 | + "normalize-path": "~3.0.0", | |
| 1499 | + "readdirp": "~3.6.0" | |
| 1500 | + }, | |
| 1501 | + "engines": { | |
| 1502 | + "node": ">= 8.10.0" | |
| 1503 | + }, | |
| 1504 | + "funding": { | |
| 1505 | + "url": "https://paulmillr.com/funding/" | |
| 1506 | + }, | |
| 1507 | + "optionalDependencies": { | |
| 1508 | + "fsevents": "~2.3.2" | |
| 1509 | + } | |
| 1510 | + }, | |
| 1511 | + "node_modules/chokidar/node_modules/glob-parent": { | |
| 1512 | + "version": "5.1.2", | |
| 1513 | + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", | |
| 1514 | + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", | |
| 1515 | + "dev": true, | |
| 1516 | + "license": "ISC", | |
| 1517 | + "dependencies": { | |
| 1518 | + "is-glob": "^4.0.1" | |
| 1519 | + }, | |
| 1520 | + "engines": { | |
| 1521 | + "node": ">= 6" | |
| 1522 | + } | |
| 1523 | + }, | |
| 1524 | + "node_modules/commander": { | |
| 1525 | + "version": "4.1.1", | |
| 1526 | + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", | |
| 1527 | + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", | |
| 1528 | + "dev": true, | |
| 1529 | + "license": "MIT", | |
| 1530 | + "engines": { | |
| 1531 | + "node": ">= 6" | |
| 1532 | + } | |
| 1533 | + }, | |
| 1534 | + "node_modules/convert-source-map": { | |
| 1535 | + "version": "2.0.0", | |
| 1536 | + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", | |
| 1537 | + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", | |
| 1538 | + "dev": true, | |
| 1539 | + "license": "MIT" | |
| 1540 | + }, | |
| 1541 | + "node_modules/cssesc": { | |
| 1542 | + "version": "3.0.0", | |
| 1543 | + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", | |
| 1544 | + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", | |
| 1545 | + "dev": true, | |
| 1546 | + "license": "MIT", | |
| 1547 | + "bin": { | |
| 1548 | + "cssesc": "bin/cssesc" | |
| 1549 | + }, | |
| 1550 | + "engines": { | |
| 1551 | + "node": ">=4" | |
| 1552 | + } | |
| 1553 | + }, | |
| 1554 | + "node_modules/csstype": { | |
| 1555 | + "version": "3.2.3", | |
| 1556 | + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", | |
| 1557 | + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", | |
| 1558 | + "dev": true, | |
| 1559 | + "license": "MIT" | |
| 1560 | + }, | |
| 1561 | + "node_modules/debug": { | |
| 1562 | + "version": "4.4.3", | |
| 1563 | + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", | |
| 1564 | + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", | |
| 1565 | + "dev": true, | |
| 1566 | + "license": "MIT", | |
| 1567 | + "dependencies": { | |
| 1568 | + "ms": "^2.1.3" | |
| 1569 | + }, | |
| 1570 | + "engines": { | |
| 1571 | + "node": ">=6.0" | |
| 1572 | + }, | |
| 1573 | + "peerDependenciesMeta": { | |
| 1574 | + "supports-color": { | |
| 1575 | + "optional": true | |
| 1576 | + } | |
| 1577 | + } | |
| 1578 | + }, | |
| 1579 | + "node_modules/didyoumean": { | |
| 1580 | + "version": "1.2.2", | |
| 1581 | + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", | |
| 1582 | + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", | |
| 1583 | + "dev": true, | |
| 1584 | + "license": "Apache-2.0" | |
| 1585 | + }, | |
| 1586 | + "node_modules/dlv": { | |
| 1587 | + "version": "1.1.3", | |
| 1588 | + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", | |
| 1589 | + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", | |
| 1590 | + "dev": true, | |
| 1591 | + "license": "MIT" | |
| 1592 | + }, | |
| 1593 | + "node_modules/electron-to-chromium": { | |
| 1594 | + "version": "1.5.334", | |
| 1595 | + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.334.tgz", | |
| 1596 | + "integrity": "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog==", | |
| 1597 | + "dev": true, | |
| 1598 | + "license": "ISC" | |
| 1599 | + }, | |
| 1600 | + "node_modules/esbuild": { | |
| 1601 | + "version": "0.21.5", | |
| 1602 | + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", | |
| 1603 | + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", | |
| 1604 | + "dev": true, | |
| 1605 | + "hasInstallScript": true, | |
| 1606 | + "license": "MIT", | |
| 1607 | + "bin": { | |
| 1608 | + "esbuild": "bin/esbuild" | |
| 1609 | + }, | |
| 1610 | + "engines": { | |
| 1611 | + "node": ">=12" | |
| 1612 | + }, | |
| 1613 | + "optionalDependencies": { | |
| 1614 | + "@esbuild/aix-ppc64": "0.21.5", | |
| 1615 | + "@esbuild/android-arm": "0.21.5", | |
| 1616 | + "@esbuild/android-arm64": "0.21.5", | |
| 1617 | + "@esbuild/android-x64": "0.21.5", | |
| 1618 | + "@esbuild/darwin-arm64": "0.21.5", | |
| 1619 | + "@esbuild/darwin-x64": "0.21.5", | |
| 1620 | + "@esbuild/freebsd-arm64": "0.21.5", | |
| 1621 | + "@esbuild/freebsd-x64": "0.21.5", | |
| 1622 | + "@esbuild/linux-arm": "0.21.5", | |
| 1623 | + "@esbuild/linux-arm64": "0.21.5", | |
| 1624 | + "@esbuild/linux-ia32": "0.21.5", | |
| 1625 | + "@esbuild/linux-loong64": "0.21.5", | |
| 1626 | + "@esbuild/linux-mips64el": "0.21.5", | |
| 1627 | + "@esbuild/linux-ppc64": "0.21.5", | |
| 1628 | + "@esbuild/linux-riscv64": "0.21.5", | |
| 1629 | + "@esbuild/linux-s390x": "0.21.5", | |
| 1630 | + "@esbuild/linux-x64": "0.21.5", | |
| 1631 | + "@esbuild/netbsd-x64": "0.21.5", | |
| 1632 | + "@esbuild/openbsd-x64": "0.21.5", | |
| 1633 | + "@esbuild/sunos-x64": "0.21.5", | |
| 1634 | + "@esbuild/win32-arm64": "0.21.5", | |
| 1635 | + "@esbuild/win32-ia32": "0.21.5", | |
| 1636 | + "@esbuild/win32-x64": "0.21.5" | |
| 1637 | + } | |
| 1638 | + }, | |
| 1639 | + "node_modules/escalade": { | |
| 1640 | + "version": "3.2.0", | |
| 1641 | + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", | |
| 1642 | + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", | |
| 1643 | + "dev": true, | |
| 1644 | + "license": "MIT", | |
| 1645 | + "engines": { | |
| 1646 | + "node": ">=6" | |
| 1647 | + } | |
| 1648 | + }, | |
| 1649 | + "node_modules/fast-glob": { | |
| 1650 | + "version": "3.3.3", | |
| 1651 | + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", | |
| 1652 | + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", | |
| 1653 | + "dev": true, | |
| 1654 | + "license": "MIT", | |
| 1655 | + "dependencies": { | |
| 1656 | + "@nodelib/fs.stat": "^2.0.2", | |
| 1657 | + "@nodelib/fs.walk": "^1.2.3", | |
| 1658 | + "glob-parent": "^5.1.2", | |
| 1659 | + "merge2": "^1.3.0", | |
| 1660 | + "micromatch": "^4.0.8" | |
| 1661 | + }, | |
| 1662 | + "engines": { | |
| 1663 | + "node": ">=8.6.0" | |
| 1664 | + } | |
| 1665 | + }, | |
| 1666 | + "node_modules/fast-glob/node_modules/glob-parent": { | |
| 1667 | + "version": "5.1.2", | |
| 1668 | + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", | |
| 1669 | + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", | |
| 1670 | + "dev": true, | |
| 1671 | + "license": "ISC", | |
| 1672 | + "dependencies": { | |
| 1673 | + "is-glob": "^4.0.1" | |
| 1674 | + }, | |
| 1675 | + "engines": { | |
| 1676 | + "node": ">= 6" | |
| 1677 | + } | |
| 1678 | + }, | |
| 1679 | + "node_modules/fastq": { | |
| 1680 | + "version": "1.20.1", | |
| 1681 | + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", | |
| 1682 | + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", | |
| 1683 | + "dev": true, | |
| 1684 | + "license": "ISC", | |
| 1685 | + "dependencies": { | |
| 1686 | + "reusify": "^1.0.4" | |
| 1687 | + } | |
| 1688 | + }, | |
| 1689 | + "node_modules/fill-range": { | |
| 1690 | + "version": "7.1.1", | |
| 1691 | + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", | |
| 1692 | + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", | |
| 1693 | + "dev": true, | |
| 1694 | + "license": "MIT", | |
| 1695 | + "dependencies": { | |
| 1696 | + "to-regex-range": "^5.0.1" | |
| 1697 | + }, | |
| 1698 | + "engines": { | |
| 1699 | + "node": ">=8" | |
| 1700 | + } | |
| 1701 | + }, | |
| 1702 | + "node_modules/fraction.js": { | |
| 1703 | + "version": "5.3.4", | |
| 1704 | + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", | |
| 1705 | + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", | |
| 1706 | + "dev": true, | |
| 1707 | + "license": "MIT", | |
| 1708 | + "engines": { | |
| 1709 | + "node": "*" | |
| 1710 | + }, | |
| 1711 | + "funding": { | |
| 1712 | + "type": "github", | |
| 1713 | + "url": "https://github.com/sponsors/rawify" | |
| 1714 | + } | |
| 1715 | + }, | |
| 1716 | + "node_modules/fsevents": { | |
| 1717 | + "version": "2.3.3", | |
| 1718 | + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", | |
| 1719 | + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", | |
| 1720 | + "dev": true, | |
| 1721 | + "hasInstallScript": true, | |
| 1722 | + "license": "MIT", | |
| 1723 | + "optional": true, | |
| 1724 | + "os": [ | |
| 1725 | + "darwin" | |
| 1726 | + ], | |
| 1727 | + "engines": { | |
| 1728 | + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" | |
| 1729 | + } | |
| 1730 | + }, | |
| 1731 | + "node_modules/function-bind": { | |
| 1732 | + "version": "1.1.2", | |
| 1733 | + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", | |
| 1734 | + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", | |
| 1735 | + "dev": true, | |
| 1736 | + "license": "MIT", | |
| 1737 | + "funding": { | |
| 1738 | + "url": "https://github.com/sponsors/ljharb" | |
| 1739 | + } | |
| 1740 | + }, | |
| 1741 | + "node_modules/gensync": { | |
| 1742 | + "version": "1.0.0-beta.2", | |
| 1743 | + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", | |
| 1744 | + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", | |
| 1745 | + "dev": true, | |
| 1746 | + "license": "MIT", | |
| 1747 | + "engines": { | |
| 1748 | + "node": ">=6.9.0" | |
| 1749 | + } | |
| 1750 | + }, | |
| 1751 | + "node_modules/glob-parent": { | |
| 1752 | + "version": "6.0.2", | |
| 1753 | + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", | |
| 1754 | + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", | |
| 1755 | + "dev": true, | |
| 1756 | + "license": "ISC", | |
| 1757 | + "dependencies": { | |
| 1758 | + "is-glob": "^4.0.3" | |
| 1759 | + }, | |
| 1760 | + "engines": { | |
| 1761 | + "node": ">=10.13.0" | |
| 1762 | + } | |
| 1763 | + }, | |
| 1764 | + "node_modules/hasown": { | |
| 1765 | + "version": "2.0.2", | |
| 1766 | + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", | |
| 1767 | + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", | |
| 1768 | + "dev": true, | |
| 1769 | + "license": "MIT", | |
| 1770 | + "dependencies": { | |
| 1771 | + "function-bind": "^1.1.2" | |
| 1772 | + }, | |
| 1773 | + "engines": { | |
| 1774 | + "node": ">= 0.4" | |
| 1775 | + } | |
| 1776 | + }, | |
| 1777 | + "node_modules/is-binary-path": { | |
| 1778 | + "version": "2.1.0", | |
| 1779 | + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", | |
| 1780 | + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", | |
| 1781 | + "dev": true, | |
| 1782 | + "license": "MIT", | |
| 1783 | + "dependencies": { | |
| 1784 | + "binary-extensions": "^2.0.0" | |
| 1785 | + }, | |
| 1786 | + "engines": { | |
| 1787 | + "node": ">=8" | |
| 1788 | + } | |
| 1789 | + }, | |
| 1790 | + "node_modules/is-core-module": { | |
| 1791 | + "version": "2.16.1", | |
| 1792 | + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", | |
| 1793 | + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", | |
| 1794 | + "dev": true, | |
| 1795 | + "license": "MIT", | |
| 1796 | + "dependencies": { | |
| 1797 | + "hasown": "^2.0.2" | |
| 1798 | + }, | |
| 1799 | + "engines": { | |
| 1800 | + "node": ">= 0.4" | |
| 1801 | + }, | |
| 1802 | + "funding": { | |
| 1803 | + "url": "https://github.com/sponsors/ljharb" | |
| 1804 | + } | |
| 1805 | + }, | |
| 1806 | + "node_modules/is-extglob": { | |
| 1807 | + "version": "2.1.1", | |
| 1808 | + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", | |
| 1809 | + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", | |
| 1810 | + "dev": true, | |
| 1811 | + "license": "MIT", | |
| 1812 | + "engines": { | |
| 1813 | + "node": ">=0.10.0" | |
| 1814 | + } | |
| 1815 | + }, | |
| 1816 | + "node_modules/is-glob": { | |
| 1817 | + "version": "4.0.3", | |
| 1818 | + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", | |
| 1819 | + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", | |
| 1820 | + "dev": true, | |
| 1821 | + "license": "MIT", | |
| 1822 | + "dependencies": { | |
| 1823 | + "is-extglob": "^2.1.1" | |
| 1824 | + }, | |
| 1825 | + "engines": { | |
| 1826 | + "node": ">=0.10.0" | |
| 1827 | + } | |
| 1828 | + }, | |
| 1829 | + "node_modules/is-number": { | |
| 1830 | + "version": "7.0.0", | |
| 1831 | + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", | |
| 1832 | + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", | |
| 1833 | + "dev": true, | |
| 1834 | + "license": "MIT", | |
| 1835 | + "engines": { | |
| 1836 | + "node": ">=0.12.0" | |
| 1837 | + } | |
| 1838 | + }, | |
| 1839 | + "node_modules/jiti": { | |
| 1840 | + "version": "1.21.7", | |
| 1841 | + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", | |
| 1842 | + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", | |
| 1843 | + "dev": true, | |
| 1844 | + "license": "MIT", | |
| 1845 | + "bin": { | |
| 1846 | + "jiti": "bin/jiti.js" | |
| 1847 | + } | |
| 1848 | + }, | |
| 1849 | + "node_modules/js-tokens": { | |
| 1850 | + "version": "4.0.0", | |
| 1851 | + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", | |
| 1852 | + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", | |
| 1853 | + "license": "MIT" | |
| 1854 | + }, | |
| 1855 | + "node_modules/jsesc": { | |
| 1856 | + "version": "3.1.0", | |
| 1857 | + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", | |
| 1858 | + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", | |
| 1859 | + "dev": true, | |
| 1860 | + "license": "MIT", | |
| 1861 | + "bin": { | |
| 1862 | + "jsesc": "bin/jsesc" | |
| 1863 | + }, | |
| 1864 | + "engines": { | |
| 1865 | + "node": ">=6" | |
| 1866 | + } | |
| 1867 | + }, | |
| 1868 | + "node_modules/json5": { | |
| 1869 | + "version": "2.2.3", | |
| 1870 | + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", | |
| 1871 | + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", | |
| 1872 | + "dev": true, | |
| 1873 | + "license": "MIT", | |
| 1874 | + "bin": { | |
| 1875 | + "json5": "lib/cli.js" | |
| 1876 | + }, | |
| 1877 | + "engines": { | |
| 1878 | + "node": ">=6" | |
| 1879 | + } | |
| 1880 | + }, | |
| 1881 | + "node_modules/lilconfig": { | |
| 1882 | + "version": "3.1.3", | |
| 1883 | + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", | |
| 1884 | + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", | |
| 1885 | + "dev": true, | |
| 1886 | + "license": "MIT", | |
| 1887 | + "engines": { | |
| 1888 | + "node": ">=14" | |
| 1889 | + }, | |
| 1890 | + "funding": { | |
| 1891 | + "url": "https://github.com/sponsors/antonk52" | |
| 1892 | + } | |
| 1893 | + }, | |
| 1894 | + "node_modules/lines-and-columns": { | |
| 1895 | + "version": "1.2.4", | |
| 1896 | + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", | |
| 1897 | + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", | |
| 1898 | + "dev": true, | |
| 1899 | + "license": "MIT" | |
| 1900 | + }, | |
| 1901 | + "node_modules/loose-envify": { | |
| 1902 | + "version": "1.4.0", | |
| 1903 | + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", | |
| 1904 | + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", | |
| 1905 | + "license": "MIT", | |
| 1906 | + "dependencies": { | |
| 1907 | + "js-tokens": "^3.0.0 || ^4.0.0" | |
| 1908 | + }, | |
| 1909 | + "bin": { | |
| 1910 | + "loose-envify": "cli.js" | |
| 1911 | + } | |
| 1912 | + }, | |
| 1913 | + "node_modules/lru-cache": { | |
| 1914 | + "version": "5.1.1", | |
| 1915 | + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", | |
| 1916 | + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", | |
| 1917 | + "dev": true, | |
| 1918 | + "license": "ISC", | |
| 1919 | + "dependencies": { | |
| 1920 | + "yallist": "^3.0.2" | |
| 1921 | + } | |
| 1922 | + }, | |
| 1923 | + "node_modules/merge2": { | |
| 1924 | + "version": "1.4.1", | |
| 1925 | + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", | |
| 1926 | + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", | |
| 1927 | + "dev": true, | |
| 1928 | + "license": "MIT", | |
| 1929 | + "engines": { | |
| 1930 | + "node": ">= 8" | |
| 1931 | + } | |
| 1932 | + }, | |
| 1933 | + "node_modules/micromatch": { | |
| 1934 | + "version": "4.0.8", | |
| 1935 | + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", | |
| 1936 | + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", | |
| 1937 | + "dev": true, | |
| 1938 | + "license": "MIT", | |
| 1939 | + "dependencies": { | |
| 1940 | + "braces": "^3.0.3", | |
| 1941 | + "picomatch": "^2.3.1" | |
| 1942 | + }, | |
| 1943 | + "engines": { | |
| 1944 | + "node": ">=8.6" | |
| 1945 | + } | |
| 1946 | + }, | |
| 1947 | + "node_modules/ms": { | |
| 1948 | + "version": "2.1.3", | |
| 1949 | + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", | |
| 1950 | + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", | |
| 1951 | + "dev": true, | |
| 1952 | + "license": "MIT" | |
| 1953 | + }, | |
| 1954 | + "node_modules/mz": { | |
| 1955 | + "version": "2.7.0", | |
| 1956 | + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", | |
| 1957 | + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", | |
| 1958 | + "dev": true, | |
| 1959 | + "license": "MIT", | |
| 1960 | + "dependencies": { | |
| 1961 | + "any-promise": "^1.0.0", | |
| 1962 | + "object-assign": "^4.0.1", | |
| 1963 | + "thenify-all": "^1.0.0" | |
| 1964 | + } | |
| 1965 | + }, | |
| 1966 | + "node_modules/nanoid": { | |
| 1967 | + "version": "3.3.11", | |
| 1968 | + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", | |
| 1969 | + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", | |
| 1970 | + "dev": true, | |
| 1971 | + "funding": [ | |
| 1972 | + { | |
| 1973 | + "type": "github", | |
| 1974 | + "url": "https://github.com/sponsors/ai" | |
| 1975 | + } | |
| 1976 | + ], | |
| 1977 | + "license": "MIT", | |
| 1978 | + "bin": { | |
| 1979 | + "nanoid": "bin/nanoid.cjs" | |
| 1980 | + }, | |
| 1981 | + "engines": { | |
| 1982 | + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" | |
| 1983 | + } | |
| 1984 | + }, | |
| 1985 | + "node_modules/node-releases": { | |
| 1986 | + "version": "2.0.37", | |
| 1987 | + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", | |
| 1988 | + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", | |
| 1989 | + "dev": true, | |
| 1990 | + "license": "MIT" | |
| 1991 | + }, | |
| 1992 | + "node_modules/normalize-path": { | |
| 1993 | + "version": "3.0.0", | |
| 1994 | + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", | |
| 1995 | + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", | |
| 1996 | + "dev": true, | |
| 1997 | + "license": "MIT", | |
| 1998 | + "engines": { | |
| 1999 | + "node": ">=0.10.0" | |
| 2000 | + } | |
| 2001 | + }, | |
| 2002 | + "node_modules/object-assign": { | |
| 2003 | + "version": "4.1.1", | |
| 2004 | + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", | |
| 2005 | + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", | |
| 2006 | + "dev": true, | |
| 2007 | + "license": "MIT", | |
| 2008 | + "engines": { | |
| 2009 | + "node": ">=0.10.0" | |
| 2010 | + } | |
| 2011 | + }, | |
| 2012 | + "node_modules/object-hash": { | |
| 2013 | + "version": "3.0.0", | |
| 2014 | + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", | |
| 2015 | + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", | |
| 2016 | + "dev": true, | |
| 2017 | + "license": "MIT", | |
| 2018 | + "engines": { | |
| 2019 | + "node": ">= 6" | |
| 2020 | + } | |
| 2021 | + }, | |
| 2022 | + "node_modules/path-parse": { | |
| 2023 | + "version": "1.0.7", | |
| 2024 | + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", | |
| 2025 | + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", | |
| 2026 | + "dev": true, | |
| 2027 | + "license": "MIT" | |
| 2028 | + }, | |
| 2029 | + "node_modules/picocolors": { | |
| 2030 | + "version": "1.1.1", | |
| 2031 | + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", | |
| 2032 | + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", | |
| 2033 | + "dev": true, | |
| 2034 | + "license": "ISC" | |
| 2035 | + }, | |
| 2036 | + "node_modules/picomatch": { | |
| 2037 | + "version": "2.3.2", | |
| 2038 | + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", | |
| 2039 | + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", | |
| 2040 | + "dev": true, | |
| 2041 | + "license": "MIT", | |
| 2042 | + "engines": { | |
| 2043 | + "node": ">=8.6" | |
| 2044 | + }, | |
| 2045 | + "funding": { | |
| 2046 | + "url": "https://github.com/sponsors/jonschlinkert" | |
| 2047 | + } | |
| 2048 | + }, | |
| 2049 | + "node_modules/pify": { | |
| 2050 | + "version": "2.3.0", | |
| 2051 | + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", | |
| 2052 | + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", | |
| 2053 | + "dev": true, | |
| 2054 | + "license": "MIT", | |
| 2055 | + "engines": { | |
| 2056 | + "node": ">=0.10.0" | |
| 2057 | + } | |
| 2058 | + }, | |
| 2059 | + "node_modules/pirates": { | |
| 2060 | + "version": "4.0.7", | |
| 2061 | + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", | |
| 2062 | + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", | |
| 2063 | + "dev": true, | |
| 2064 | + "license": "MIT", | |
| 2065 | + "engines": { | |
| 2066 | + "node": ">= 6" | |
| 2067 | + } | |
| 2068 | + }, | |
| 2069 | + "node_modules/postcss": { | |
| 2070 | + "version": "8.5.9", | |
| 2071 | + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", | |
| 2072 | + "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", | |
| 2073 | + "dev": true, | |
| 2074 | + "funding": [ | |
| 2075 | + { | |
| 2076 | + "type": "opencollective", | |
| 2077 | + "url": "https://opencollective.com/postcss/" | |
| 2078 | + }, | |
| 2079 | + { | |
| 2080 | + "type": "tidelift", | |
| 2081 | + "url": "https://tidelift.com/funding/github/npm/postcss" | |
| 2082 | + }, | |
| 2083 | + { | |
| 2084 | + "type": "github", | |
| 2085 | + "url": "https://github.com/sponsors/ai" | |
| 2086 | + } | |
| 2087 | + ], | |
| 2088 | + "license": "MIT", | |
| 2089 | + "dependencies": { | |
| 2090 | + "nanoid": "^3.3.11", | |
| 2091 | + "picocolors": "^1.1.1", | |
| 2092 | + "source-map-js": "^1.2.1" | |
| 2093 | + }, | |
| 2094 | + "engines": { | |
| 2095 | + "node": "^10 || ^12 || >=14" | |
| 2096 | + } | |
| 2097 | + }, | |
| 2098 | + "node_modules/postcss-import": { | |
| 2099 | + "version": "15.1.0", | |
| 2100 | + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", | |
| 2101 | + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", | |
| 2102 | + "dev": true, | |
| 2103 | + "license": "MIT", | |
| 2104 | + "dependencies": { | |
| 2105 | + "postcss-value-parser": "^4.0.0", | |
| 2106 | + "read-cache": "^1.0.0", | |
| 2107 | + "resolve": "^1.1.7" | |
| 2108 | + }, | |
| 2109 | + "engines": { | |
| 2110 | + "node": ">=14.0.0" | |
| 2111 | + }, | |
| 2112 | + "peerDependencies": { | |
| 2113 | + "postcss": "^8.0.0" | |
| 2114 | + } | |
| 2115 | + }, | |
| 2116 | + "node_modules/postcss-js": { | |
| 2117 | + "version": "4.1.0", | |
| 2118 | + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", | |
| 2119 | + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", | |
| 2120 | + "dev": true, | |
| 2121 | + "funding": [ | |
| 2122 | + { | |
| 2123 | + "type": "opencollective", | |
| 2124 | + "url": "https://opencollective.com/postcss/" | |
| 2125 | + }, | |
| 2126 | + { | |
| 2127 | + "type": "github", | |
| 2128 | + "url": "https://github.com/sponsors/ai" | |
| 2129 | + } | |
| 2130 | + ], | |
| 2131 | + "license": "MIT", | |
| 2132 | + "dependencies": { | |
| 2133 | + "camelcase-css": "^2.0.1" | |
| 2134 | + }, | |
| 2135 | + "engines": { | |
| 2136 | + "node": "^12 || ^14 || >= 16" | |
| 2137 | + }, | |
| 2138 | + "peerDependencies": { | |
| 2139 | + "postcss": "^8.4.21" | |
| 2140 | + } | |
| 2141 | + }, | |
| 2142 | + "node_modules/postcss-load-config": { | |
| 2143 | + "version": "6.0.1", | |
| 2144 | + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", | |
| 2145 | + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", | |
| 2146 | + "dev": true, | |
| 2147 | + "funding": [ | |
| 2148 | + { | |
| 2149 | + "type": "opencollective", | |
| 2150 | + "url": "https://opencollective.com/postcss/" | |
| 2151 | + }, | |
| 2152 | + { | |
| 2153 | + "type": "github", | |
| 2154 | + "url": "https://github.com/sponsors/ai" | |
| 2155 | + } | |
| 2156 | + ], | |
| 2157 | + "license": "MIT", | |
| 2158 | + "dependencies": { | |
| 2159 | + "lilconfig": "^3.1.1" | |
| 2160 | + }, | |
| 2161 | + "engines": { | |
| 2162 | + "node": ">= 18" | |
| 2163 | + }, | |
| 2164 | + "peerDependencies": { | |
| 2165 | + "jiti": ">=1.21.0", | |
| 2166 | + "postcss": ">=8.0.9", | |
| 2167 | + "tsx": "^4.8.1", | |
| 2168 | + "yaml": "^2.4.2" | |
| 2169 | + }, | |
| 2170 | + "peerDependenciesMeta": { | |
| 2171 | + "jiti": { | |
| 2172 | + "optional": true | |
| 2173 | + }, | |
| 2174 | + "postcss": { | |
| 2175 | + "optional": true | |
| 2176 | + }, | |
| 2177 | + "tsx": { | |
| 2178 | + "optional": true | |
| 2179 | + }, | |
| 2180 | + "yaml": { | |
| 2181 | + "optional": true | |
| 2182 | + } | |
| 2183 | + } | |
| 2184 | + }, | |
| 2185 | + "node_modules/postcss-nested": { | |
| 2186 | + "version": "6.2.0", | |
| 2187 | + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", | |
| 2188 | + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", | |
| 2189 | + "dev": true, | |
| 2190 | + "funding": [ | |
| 2191 | + { | |
| 2192 | + "type": "opencollective", | |
| 2193 | + "url": "https://opencollective.com/postcss/" | |
| 2194 | + }, | |
| 2195 | + { | |
| 2196 | + "type": "github", | |
| 2197 | + "url": "https://github.com/sponsors/ai" | |
| 2198 | + } | |
| 2199 | + ], | |
| 2200 | + "license": "MIT", | |
| 2201 | + "dependencies": { | |
| 2202 | + "postcss-selector-parser": "^6.1.1" | |
| 2203 | + }, | |
| 2204 | + "engines": { | |
| 2205 | + "node": ">=12.0" | |
| 2206 | + }, | |
| 2207 | + "peerDependencies": { | |
| 2208 | + "postcss": "^8.2.14" | |
| 2209 | + } | |
| 2210 | + }, | |
| 2211 | + "node_modules/postcss-selector-parser": { | |
| 2212 | + "version": "6.1.2", | |
| 2213 | + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", | |
| 2214 | + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", | |
| 2215 | + "dev": true, | |
| 2216 | + "license": "MIT", | |
| 2217 | + "dependencies": { | |
| 2218 | + "cssesc": "^3.0.0", | |
| 2219 | + "util-deprecate": "^1.0.2" | |
| 2220 | + }, | |
| 2221 | + "engines": { | |
| 2222 | + "node": ">=4" | |
| 2223 | + } | |
| 2224 | + }, | |
| 2225 | + "node_modules/postcss-value-parser": { | |
| 2226 | + "version": "4.2.0", | |
| 2227 | + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", | |
| 2228 | + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", | |
| 2229 | + "dev": true, | |
| 2230 | + "license": "MIT" | |
| 2231 | + }, | |
| 2232 | + "node_modules/queue-microtask": { | |
| 2233 | + "version": "1.2.3", | |
| 2234 | + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", | |
| 2235 | + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", | |
| 2236 | + "dev": true, | |
| 2237 | + "funding": [ | |
| 2238 | + { | |
| 2239 | + "type": "github", | |
| 2240 | + "url": "https://github.com/sponsors/feross" | |
| 2241 | + }, | |
| 2242 | + { | |
| 2243 | + "type": "patreon", | |
| 2244 | + "url": "https://www.patreon.com/feross" | |
| 2245 | + }, | |
| 2246 | + { | |
| 2247 | + "type": "consulting", | |
| 2248 | + "url": "https://feross.org/support" | |
| 2249 | + } | |
| 2250 | + ], | |
| 2251 | + "license": "MIT" | |
| 2252 | + }, | |
| 2253 | + "node_modules/react": { | |
| 2254 | + "version": "18.3.1", | |
| 2255 | + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", | |
| 2256 | + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", | |
| 2257 | + "license": "MIT", | |
| 2258 | + "dependencies": { | |
| 2259 | + "loose-envify": "^1.1.0" | |
| 2260 | + }, | |
| 2261 | + "engines": { | |
| 2262 | + "node": ">=0.10.0" | |
| 2263 | + } | |
| 2264 | + }, | |
| 2265 | + "node_modules/react-dom": { | |
| 2266 | + "version": "18.3.1", | |
| 2267 | + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", | |
| 2268 | + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", | |
| 2269 | + "license": "MIT", | |
| 2270 | + "dependencies": { | |
| 2271 | + "loose-envify": "^1.1.0", | |
| 2272 | + "scheduler": "^0.23.2" | |
| 2273 | + }, | |
| 2274 | + "peerDependencies": { | |
| 2275 | + "react": "^18.3.1" | |
| 2276 | + } | |
| 2277 | + }, | |
| 2278 | + "node_modules/react-refresh": { | |
| 2279 | + "version": "0.17.0", | |
| 2280 | + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", | |
| 2281 | + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", | |
| 2282 | + "dev": true, | |
| 2283 | + "license": "MIT", | |
| 2284 | + "engines": { | |
| 2285 | + "node": ">=0.10.0" | |
| 2286 | + } | |
| 2287 | + }, | |
| 2288 | + "node_modules/react-router": { | |
| 2289 | + "version": "6.30.3", | |
| 2290 | + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", | |
| 2291 | + "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", | |
| 2292 | + "license": "MIT", | |
| 2293 | + "dependencies": { | |
| 2294 | + "@remix-run/router": "1.23.2" | |
| 2295 | + }, | |
| 2296 | + "engines": { | |
| 2297 | + "node": ">=14.0.0" | |
| 2298 | + }, | |
| 2299 | + "peerDependencies": { | |
| 2300 | + "react": ">=16.8" | |
| 2301 | + } | |
| 2302 | + }, | |
| 2303 | + "node_modules/react-router-dom": { | |
| 2304 | + "version": "6.30.3", | |
| 2305 | + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", | |
| 2306 | + "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", | |
| 2307 | + "license": "MIT", | |
| 2308 | + "dependencies": { | |
| 2309 | + "@remix-run/router": "1.23.2", | |
| 2310 | + "react-router": "6.30.3" | |
| 2311 | + }, | |
| 2312 | + "engines": { | |
| 2313 | + "node": ">=14.0.0" | |
| 2314 | + }, | |
| 2315 | + "peerDependencies": { | |
| 2316 | + "react": ">=16.8", | |
| 2317 | + "react-dom": ">=16.8" | |
| 2318 | + } | |
| 2319 | + }, | |
| 2320 | + "node_modules/read-cache": { | |
| 2321 | + "version": "1.0.0", | |
| 2322 | + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", | |
| 2323 | + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", | |
| 2324 | + "dev": true, | |
| 2325 | + "license": "MIT", | |
| 2326 | + "dependencies": { | |
| 2327 | + "pify": "^2.3.0" | |
| 2328 | + } | |
| 2329 | + }, | |
| 2330 | + "node_modules/readdirp": { | |
| 2331 | + "version": "3.6.0", | |
| 2332 | + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", | |
| 2333 | + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", | |
| 2334 | + "dev": true, | |
| 2335 | + "license": "MIT", | |
| 2336 | + "dependencies": { | |
| 2337 | + "picomatch": "^2.2.1" | |
| 2338 | + }, | |
| 2339 | + "engines": { | |
| 2340 | + "node": ">=8.10.0" | |
| 2341 | + } | |
| 2342 | + }, | |
| 2343 | + "node_modules/resolve": { | |
| 2344 | + "version": "1.22.11", | |
| 2345 | + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", | |
| 2346 | + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", | |
| 2347 | + "dev": true, | |
| 2348 | + "license": "MIT", | |
| 2349 | + "dependencies": { | |
| 2350 | + "is-core-module": "^2.16.1", | |
| 2351 | + "path-parse": "^1.0.7", | |
| 2352 | + "supports-preserve-symlinks-flag": "^1.0.0" | |
| 2353 | + }, | |
| 2354 | + "bin": { | |
| 2355 | + "resolve": "bin/resolve" | |
| 2356 | + }, | |
| 2357 | + "engines": { | |
| 2358 | + "node": ">= 0.4" | |
| 2359 | + }, | |
| 2360 | + "funding": { | |
| 2361 | + "url": "https://github.com/sponsors/ljharb" | |
| 2362 | + } | |
| 2363 | + }, | |
| 2364 | + "node_modules/reusify": { | |
| 2365 | + "version": "1.1.0", | |
| 2366 | + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", | |
| 2367 | + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", | |
| 2368 | + "dev": true, | |
| 2369 | + "license": "MIT", | |
| 2370 | + "engines": { | |
| 2371 | + "iojs": ">=1.0.0", | |
| 2372 | + "node": ">=0.10.0" | |
| 2373 | + } | |
| 2374 | + }, | |
| 2375 | + "node_modules/rollup": { | |
| 2376 | + "version": "4.60.1", | |
| 2377 | + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", | |
| 2378 | + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", | |
| 2379 | + "dev": true, | |
| 2380 | + "license": "MIT", | |
| 2381 | + "dependencies": { | |
| 2382 | + "@types/estree": "1.0.8" | |
| 2383 | + }, | |
| 2384 | + "bin": { | |
| 2385 | + "rollup": "dist/bin/rollup" | |
| 2386 | + }, | |
| 2387 | + "engines": { | |
| 2388 | + "node": ">=18.0.0", | |
| 2389 | + "npm": ">=8.0.0" | |
| 2390 | + }, | |
| 2391 | + "optionalDependencies": { | |
| 2392 | + "@rollup/rollup-android-arm-eabi": "4.60.1", | |
| 2393 | + "@rollup/rollup-android-arm64": "4.60.1", | |
| 2394 | + "@rollup/rollup-darwin-arm64": "4.60.1", | |
| 2395 | + "@rollup/rollup-darwin-x64": "4.60.1", | |
| 2396 | + "@rollup/rollup-freebsd-arm64": "4.60.1", | |
| 2397 | + "@rollup/rollup-freebsd-x64": "4.60.1", | |
| 2398 | + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", | |
| 2399 | + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", | |
| 2400 | + "@rollup/rollup-linux-arm64-gnu": "4.60.1", | |
| 2401 | + "@rollup/rollup-linux-arm64-musl": "4.60.1", | |
| 2402 | + "@rollup/rollup-linux-loong64-gnu": "4.60.1", | |
| 2403 | + "@rollup/rollup-linux-loong64-musl": "4.60.1", | |
| 2404 | + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", | |
| 2405 | + "@rollup/rollup-linux-ppc64-musl": "4.60.1", | |
| 2406 | + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", | |
| 2407 | + "@rollup/rollup-linux-riscv64-musl": "4.60.1", | |
| 2408 | + "@rollup/rollup-linux-s390x-gnu": "4.60.1", | |
| 2409 | + "@rollup/rollup-linux-x64-gnu": "4.60.1", | |
| 2410 | + "@rollup/rollup-linux-x64-musl": "4.60.1", | |
| 2411 | + "@rollup/rollup-openbsd-x64": "4.60.1", | |
| 2412 | + "@rollup/rollup-openharmony-arm64": "4.60.1", | |
| 2413 | + "@rollup/rollup-win32-arm64-msvc": "4.60.1", | |
| 2414 | + "@rollup/rollup-win32-ia32-msvc": "4.60.1", | |
| 2415 | + "@rollup/rollup-win32-x64-gnu": "4.60.1", | |
| 2416 | + "@rollup/rollup-win32-x64-msvc": "4.60.1", | |
| 2417 | + "fsevents": "~2.3.2" | |
| 2418 | + } | |
| 2419 | + }, | |
| 2420 | + "node_modules/run-parallel": { | |
| 2421 | + "version": "1.2.0", | |
| 2422 | + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", | |
| 2423 | + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", | |
| 2424 | + "dev": true, | |
| 2425 | + "funding": [ | |
| 2426 | + { | |
| 2427 | + "type": "github", | |
| 2428 | + "url": "https://github.com/sponsors/feross" | |
| 2429 | + }, | |
| 2430 | + { | |
| 2431 | + "type": "patreon", | |
| 2432 | + "url": "https://www.patreon.com/feross" | |
| 2433 | + }, | |
| 2434 | + { | |
| 2435 | + "type": "consulting", | |
| 2436 | + "url": "https://feross.org/support" | |
| 2437 | + } | |
| 2438 | + ], | |
| 2439 | + "license": "MIT", | |
| 2440 | + "dependencies": { | |
| 2441 | + "queue-microtask": "^1.2.2" | |
| 2442 | + } | |
| 2443 | + }, | |
| 2444 | + "node_modules/scheduler": { | |
| 2445 | + "version": "0.23.2", | |
| 2446 | + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", | |
| 2447 | + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", | |
| 2448 | + "license": "MIT", | |
| 2449 | + "dependencies": { | |
| 2450 | + "loose-envify": "^1.1.0" | |
| 2451 | + } | |
| 2452 | + }, | |
| 2453 | + "node_modules/semver": { | |
| 2454 | + "version": "6.3.1", | |
| 2455 | + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", | |
| 2456 | + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", | |
| 2457 | + "dev": true, | |
| 2458 | + "license": "ISC", | |
| 2459 | + "bin": { | |
| 2460 | + "semver": "bin/semver.js" | |
| 2461 | + } | |
| 2462 | + }, | |
| 2463 | + "node_modules/source-map-js": { | |
| 2464 | + "version": "1.2.1", | |
| 2465 | + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", | |
| 2466 | + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", | |
| 2467 | + "dev": true, | |
| 2468 | + "license": "BSD-3-Clause", | |
| 2469 | + "engines": { | |
| 2470 | + "node": ">=0.10.0" | |
| 2471 | + } | |
| 2472 | + }, | |
| 2473 | + "node_modules/sucrase": { | |
| 2474 | + "version": "3.35.1", | |
| 2475 | + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", | |
| 2476 | + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", | |
| 2477 | + "dev": true, | |
| 2478 | + "license": "MIT", | |
| 2479 | + "dependencies": { | |
| 2480 | + "@jridgewell/gen-mapping": "^0.3.2", | |
| 2481 | + "commander": "^4.0.0", | |
| 2482 | + "lines-and-columns": "^1.1.6", | |
| 2483 | + "mz": "^2.7.0", | |
| 2484 | + "pirates": "^4.0.1", | |
| 2485 | + "tinyglobby": "^0.2.11", | |
| 2486 | + "ts-interface-checker": "^0.1.9" | |
| 2487 | + }, | |
| 2488 | + "bin": { | |
| 2489 | + "sucrase": "bin/sucrase", | |
| 2490 | + "sucrase-node": "bin/sucrase-node" | |
| 2491 | + }, | |
| 2492 | + "engines": { | |
| 2493 | + "node": ">=16 || 14 >=14.17" | |
| 2494 | + } | |
| 2495 | + }, | |
| 2496 | + "node_modules/supports-preserve-symlinks-flag": { | |
| 2497 | + "version": "1.0.0", | |
| 2498 | + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", | |
| 2499 | + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", | |
| 2500 | + "dev": true, | |
| 2501 | + "license": "MIT", | |
| 2502 | + "engines": { | |
| 2503 | + "node": ">= 0.4" | |
| 2504 | + }, | |
| 2505 | + "funding": { | |
| 2506 | + "url": "https://github.com/sponsors/ljharb" | |
| 2507 | + } | |
| 2508 | + }, | |
| 2509 | + "node_modules/tailwindcss": { | |
| 2510 | + "version": "3.4.19", | |
| 2511 | + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", | |
| 2512 | + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", | |
| 2513 | + "dev": true, | |
| 2514 | + "license": "MIT", | |
| 2515 | + "dependencies": { | |
| 2516 | + "@alloc/quick-lru": "^5.2.0", | |
| 2517 | + "arg": "^5.0.2", | |
| 2518 | + "chokidar": "^3.6.0", | |
| 2519 | + "didyoumean": "^1.2.2", | |
| 2520 | + "dlv": "^1.1.3", | |
| 2521 | + "fast-glob": "^3.3.2", | |
| 2522 | + "glob-parent": "^6.0.2", | |
| 2523 | + "is-glob": "^4.0.3", | |
| 2524 | + "jiti": "^1.21.7", | |
| 2525 | + "lilconfig": "^3.1.3", | |
| 2526 | + "micromatch": "^4.0.8", | |
| 2527 | + "normalize-path": "^3.0.0", | |
| 2528 | + "object-hash": "^3.0.0", | |
| 2529 | + "picocolors": "^1.1.1", | |
| 2530 | + "postcss": "^8.4.47", | |
| 2531 | + "postcss-import": "^15.1.0", | |
| 2532 | + "postcss-js": "^4.0.1", | |
| 2533 | + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", | |
| 2534 | + "postcss-nested": "^6.2.0", | |
| 2535 | + "postcss-selector-parser": "^6.1.2", | |
| 2536 | + "resolve": "^1.22.8", | |
| 2537 | + "sucrase": "^3.35.0" | |
| 2538 | + }, | |
| 2539 | + "bin": { | |
| 2540 | + "tailwind": "lib/cli.js", | |
| 2541 | + "tailwindcss": "lib/cli.js" | |
| 2542 | + }, | |
| 2543 | + "engines": { | |
| 2544 | + "node": ">=14.0.0" | |
| 2545 | + } | |
| 2546 | + }, | |
| 2547 | + "node_modules/thenify": { | |
| 2548 | + "version": "3.3.1", | |
| 2549 | + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", | |
| 2550 | + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", | |
| 2551 | + "dev": true, | |
| 2552 | + "license": "MIT", | |
| 2553 | + "dependencies": { | |
| 2554 | + "any-promise": "^1.0.0" | |
| 2555 | + } | |
| 2556 | + }, | |
| 2557 | + "node_modules/thenify-all": { | |
| 2558 | + "version": "1.6.0", | |
| 2559 | + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", | |
| 2560 | + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", | |
| 2561 | + "dev": true, | |
| 2562 | + "license": "MIT", | |
| 2563 | + "dependencies": { | |
| 2564 | + "thenify": ">= 3.1.0 < 4" | |
| 2565 | + }, | |
| 2566 | + "engines": { | |
| 2567 | + "node": ">=0.8" | |
| 2568 | + } | |
| 2569 | + }, | |
| 2570 | + "node_modules/tinyglobby": { | |
| 2571 | + "version": "0.2.16", | |
| 2572 | + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", | |
| 2573 | + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", | |
| 2574 | + "dev": true, | |
| 2575 | + "license": "MIT", | |
| 2576 | + "dependencies": { | |
| 2577 | + "fdir": "^6.5.0", | |
| 2578 | + "picomatch": "^4.0.4" | |
| 2579 | + }, | |
| 2580 | + "engines": { | |
| 2581 | + "node": ">=12.0.0" | |
| 2582 | + }, | |
| 2583 | + "funding": { | |
| 2584 | + "url": "https://github.com/sponsors/SuperchupuDev" | |
| 2585 | + } | |
| 2586 | + }, | |
| 2587 | + "node_modules/tinyglobby/node_modules/fdir": { | |
| 2588 | + "version": "6.5.0", | |
| 2589 | + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", | |
| 2590 | + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", | |
| 2591 | + "dev": true, | |
| 2592 | + "license": "MIT", | |
| 2593 | + "engines": { | |
| 2594 | + "node": ">=12.0.0" | |
| 2595 | + }, | |
| 2596 | + "peerDependencies": { | |
| 2597 | + "picomatch": "^3 || ^4" | |
| 2598 | + }, | |
| 2599 | + "peerDependenciesMeta": { | |
| 2600 | + "picomatch": { | |
| 2601 | + "optional": true | |
| 2602 | + } | |
| 2603 | + } | |
| 2604 | + }, | |
| 2605 | + "node_modules/tinyglobby/node_modules/picomatch": { | |
| 2606 | + "version": "4.0.4", | |
| 2607 | + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", | |
| 2608 | + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", | |
| 2609 | + "dev": true, | |
| 2610 | + "license": "MIT", | |
| 2611 | + "engines": { | |
| 2612 | + "node": ">=12" | |
| 2613 | + }, | |
| 2614 | + "funding": { | |
| 2615 | + "url": "https://github.com/sponsors/jonschlinkert" | |
| 2616 | + } | |
| 2617 | + }, | |
| 2618 | + "node_modules/to-regex-range": { | |
| 2619 | + "version": "5.0.1", | |
| 2620 | + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", | |
| 2621 | + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", | |
| 2622 | + "dev": true, | |
| 2623 | + "license": "MIT", | |
| 2624 | + "dependencies": { | |
| 2625 | + "is-number": "^7.0.0" | |
| 2626 | + }, | |
| 2627 | + "engines": { | |
| 2628 | + "node": ">=8.0" | |
| 2629 | + } | |
| 2630 | + }, | |
| 2631 | + "node_modules/ts-interface-checker": { | |
| 2632 | + "version": "0.1.13", | |
| 2633 | + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", | |
| 2634 | + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", | |
| 2635 | + "dev": true, | |
| 2636 | + "license": "Apache-2.0" | |
| 2637 | + }, | |
| 2638 | + "node_modules/typescript": { | |
| 2639 | + "version": "5.9.3", | |
| 2640 | + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", | |
| 2641 | + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", | |
| 2642 | + "dev": true, | |
| 2643 | + "license": "Apache-2.0", | |
| 2644 | + "bin": { | |
| 2645 | + "tsc": "bin/tsc", | |
| 2646 | + "tsserver": "bin/tsserver" | |
| 2647 | + }, | |
| 2648 | + "engines": { | |
| 2649 | + "node": ">=14.17" | |
| 2650 | + } | |
| 2651 | + }, | |
| 2652 | + "node_modules/undici-types": { | |
| 2653 | + "version": "7.18.2", | |
| 2654 | + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", | |
| 2655 | + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", | |
| 2656 | + "dev": true, | |
| 2657 | + "license": "MIT" | |
| 2658 | + }, | |
| 2659 | + "node_modules/update-browserslist-db": { | |
| 2660 | + "version": "1.2.3", | |
| 2661 | + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", | |
| 2662 | + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", | |
| 2663 | + "dev": true, | |
| 2664 | + "funding": [ | |
| 2665 | + { | |
| 2666 | + "type": "opencollective", | |
| 2667 | + "url": "https://opencollective.com/browserslist" | |
| 2668 | + }, | |
| 2669 | + { | |
| 2670 | + "type": "tidelift", | |
| 2671 | + "url": "https://tidelift.com/funding/github/npm/browserslist" | |
| 2672 | + }, | |
| 2673 | + { | |
| 2674 | + "type": "github", | |
| 2675 | + "url": "https://github.com/sponsors/ai" | |
| 2676 | + } | |
| 2677 | + ], | |
| 2678 | + "license": "MIT", | |
| 2679 | + "dependencies": { | |
| 2680 | + "escalade": "^3.2.0", | |
| 2681 | + "picocolors": "^1.1.1" | |
| 2682 | + }, | |
| 2683 | + "bin": { | |
| 2684 | + "update-browserslist-db": "cli.js" | |
| 2685 | + }, | |
| 2686 | + "peerDependencies": { | |
| 2687 | + "browserslist": ">= 4.21.0" | |
| 2688 | + } | |
| 2689 | + }, | |
| 2690 | + "node_modules/util-deprecate": { | |
| 2691 | + "version": "1.0.2", | |
| 2692 | + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", | |
| 2693 | + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", | |
| 2694 | + "dev": true, | |
| 2695 | + "license": "MIT" | |
| 2696 | + }, | |
| 2697 | + "node_modules/vite": { | |
| 2698 | + "version": "5.4.21", | |
| 2699 | + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", | |
| 2700 | + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", | |
| 2701 | + "dev": true, | |
| 2702 | + "license": "MIT", | |
| 2703 | + "dependencies": { | |
| 2704 | + "esbuild": "^0.21.3", | |
| 2705 | + "postcss": "^8.4.43", | |
| 2706 | + "rollup": "^4.20.0" | |
| 2707 | + }, | |
| 2708 | + "bin": { | |
| 2709 | + "vite": "bin/vite.js" | |
| 2710 | + }, | |
| 2711 | + "engines": { | |
| 2712 | + "node": "^18.0.0 || >=20.0.0" | |
| 2713 | + }, | |
| 2714 | + "funding": { | |
| 2715 | + "url": "https://github.com/vitejs/vite?sponsor=1" | |
| 2716 | + }, | |
| 2717 | + "optionalDependencies": { | |
| 2718 | + "fsevents": "~2.3.3" | |
| 2719 | + }, | |
| 2720 | + "peerDependencies": { | |
| 2721 | + "@types/node": "^18.0.0 || >=20.0.0", | |
| 2722 | + "less": "*", | |
| 2723 | + "lightningcss": "^1.21.0", | |
| 2724 | + "sass": "*", | |
| 2725 | + "sass-embedded": "*", | |
| 2726 | + "stylus": "*", | |
| 2727 | + "sugarss": "*", | |
| 2728 | + "terser": "^5.4.0" | |
| 2729 | + }, | |
| 2730 | + "peerDependenciesMeta": { | |
| 2731 | + "@types/node": { | |
| 2732 | + "optional": true | |
| 2733 | + }, | |
| 2734 | + "less": { | |
| 2735 | + "optional": true | |
| 2736 | + }, | |
| 2737 | + "lightningcss": { | |
| 2738 | + "optional": true | |
| 2739 | + }, | |
| 2740 | + "sass": { | |
| 2741 | + "optional": true | |
| 2742 | + }, | |
| 2743 | + "sass-embedded": { | |
| 2744 | + "optional": true | |
| 2745 | + }, | |
| 2746 | + "stylus": { | |
| 2747 | + "optional": true | |
| 2748 | + }, | |
| 2749 | + "sugarss": { | |
| 2750 | + "optional": true | |
| 2751 | + }, | |
| 2752 | + "terser": { | |
| 2753 | + "optional": true | |
| 2754 | + } | |
| 2755 | + } | |
| 2756 | + }, | |
| 2757 | + "node_modules/yallist": { | |
| 2758 | + "version": "3.1.1", | |
| 2759 | + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", | |
| 2760 | + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", | |
| 2761 | + "dev": true, | |
| 2762 | + "license": "ISC" | |
| 2763 | + } | |
| 2764 | + } | |
| 2765 | +} | ... | ... |
web/package.json
0 → 100644
| 1 | +{ | |
| 2 | + "name": "vibe-erp-web", | |
| 3 | + "private": true, | |
| 4 | + "version": "0.0.0", | |
| 5 | + "type": "module", | |
| 6 | + "scripts": { | |
| 7 | + "dev": "vite", | |
| 8 | + "build": "tsc -b && vite build", | |
| 9 | + "preview": "vite preview" | |
| 10 | + }, | |
| 11 | + "dependencies": { | |
| 12 | + "react": "^18.3.1", | |
| 13 | + "react-dom": "^18.3.1", | |
| 14 | + "react-router-dom": "^6.28.0" | |
| 15 | + }, | |
| 16 | + "devDependencies": { | |
| 17 | + "@types/node": "^25.5.2", | |
| 18 | + "@types/react": "^18.3.12", | |
| 19 | + "@types/react-dom": "^18.3.1", | |
| 20 | + "@vitejs/plugin-react": "^4.3.3", | |
| 21 | + "autoprefixer": "^10.4.20", | |
| 22 | + "postcss": "^8.4.49", | |
| 23 | + "tailwindcss": "^3.4.15", | |
| 24 | + "typescript": "^5.6.3", | |
| 25 | + "vite": "^5.4.11" | |
| 26 | + } | |
| 27 | +} | ... | ... |
web/postcss.config.js
0 → 100644
web/src/App.tsx
0 → 100644
| 1 | +// vibe_erp web SPA route table. | |
| 2 | +// | |
| 3 | +// One <Routes> for the whole SPA: /login is open, everything else | |
| 4 | +// nests under <ProtectedRoute><AppLayout/>. The layout component | |
| 5 | +// renders <Outlet/> for the active child route. | |
| 6 | +// | |
| 7 | +// **Why a flat route table.** v1 has 12 screens; nested routing | |
| 8 | +// would just be ceremony. Future chunks can introduce nested | |
| 9 | +// routes when, e.g., the Settings area gets sub-tabs. | |
| 10 | + | |
| 11 | +import { Navigate, Route, Routes } from 'react-router-dom' | |
| 12 | +import { AppLayout } from '@/layout/AppLayout' | |
| 13 | +import { ProtectedRoute } from '@/components/ProtectedRoute' | |
| 14 | +import { LoginPage } from '@/pages/LoginPage' | |
| 15 | +import { DashboardPage } from '@/pages/DashboardPage' | |
| 16 | +import { ItemsPage } from '@/pages/ItemsPage' | |
| 17 | +import { UomsPage } from '@/pages/UomsPage' | |
| 18 | +import { PartnersPage } from '@/pages/PartnersPage' | |
| 19 | +import { LocationsPage } from '@/pages/LocationsPage' | |
| 20 | +import { BalancesPage } from '@/pages/BalancesPage' | |
| 21 | +import { MovementsPage } from '@/pages/MovementsPage' | |
| 22 | +import { SalesOrdersPage } from '@/pages/SalesOrdersPage' | |
| 23 | +import { SalesOrderDetailPage } from '@/pages/SalesOrderDetailPage' | |
| 24 | +import { PurchaseOrdersPage } from '@/pages/PurchaseOrdersPage' | |
| 25 | +import { PurchaseOrderDetailPage } from '@/pages/PurchaseOrderDetailPage' | |
| 26 | +import { WorkOrdersPage } from '@/pages/WorkOrdersPage' | |
| 27 | +import { WorkOrderDetailPage } from '@/pages/WorkOrderDetailPage' | |
| 28 | +import { ShopFloorPage } from '@/pages/ShopFloorPage' | |
| 29 | +import { JournalEntriesPage } from '@/pages/JournalEntriesPage' | |
| 30 | + | |
| 31 | +export default function App() { | |
| 32 | + return ( | |
| 33 | + <Routes> | |
| 34 | + <Route path="/login" element={<LoginPage />} /> | |
| 35 | + <Route | |
| 36 | + path="/" | |
| 37 | + element={ | |
| 38 | + <ProtectedRoute> | |
| 39 | + <AppLayout /> | |
| 40 | + </ProtectedRoute> | |
| 41 | + } | |
| 42 | + > | |
| 43 | + <Route index element={<DashboardPage />} /> | |
| 44 | + <Route path="items" element={<ItemsPage />} /> | |
| 45 | + <Route path="uoms" element={<UomsPage />} /> | |
| 46 | + <Route path="partners" element={<PartnersPage />} /> | |
| 47 | + <Route path="locations" element={<LocationsPage />} /> | |
| 48 | + <Route path="balances" element={<BalancesPage />} /> | |
| 49 | + <Route path="movements" element={<MovementsPage />} /> | |
| 50 | + <Route path="sales-orders" element={<SalesOrdersPage />} /> | |
| 51 | + <Route path="sales-orders/:id" element={<SalesOrderDetailPage />} /> | |
| 52 | + <Route path="purchase-orders" element={<PurchaseOrdersPage />} /> | |
| 53 | + <Route path="purchase-orders/:id" element={<PurchaseOrderDetailPage />} /> | |
| 54 | + <Route path="work-orders" element={<WorkOrdersPage />} /> | |
| 55 | + <Route path="work-orders/:id" element={<WorkOrderDetailPage />} /> | |
| 56 | + <Route path="shop-floor" element={<ShopFloorPage />} /> | |
| 57 | + <Route path="journal-entries" element={<JournalEntriesPage />} /> | |
| 58 | + </Route> | |
| 59 | + <Route path="*" element={<Navigate to="/" replace />} /> | |
| 60 | + </Routes> | |
| 61 | + ) | |
| 62 | +} | ... | ... |
web/src/api/client.ts
0 → 100644
| 1 | +// vibe_erp typed REST client. | |
| 2 | +// | |
| 3 | +// **Why a thin wrapper instead of a generated client.** The framework | |
| 4 | +// already serves a real OpenAPI spec at /v3/api-docs and could feed | |
| 5 | +// openapi-typescript or openapi-generator-cli — but every code | |
| 6 | +// generator pulls a chain of npm/Java toolchain steps into the build | |
| 7 | +// and generates ~thousands of lines of churn for every backend tweak. | |
| 8 | +// A hand-written client over `fetch` keeps the SPA bundle small, | |
| 9 | +// auditable, and dependency-free, and v1 of the SPA only needs the | |
| 10 | +// few dozen calls listed below. Codegen can return when (a) the | |
| 11 | +// API surface is too large to maintain by hand or (b) external | |
| 12 | +// integrators want a typed client they can reuse — neither is true | |
| 13 | +// in v1.0. | |
| 14 | +// | |
| 15 | +// **Auth.** Every call goes through `apiFetch`, which reads the | |
| 16 | +// access token from localStorage and adds an `Authorization: Bearer | |
| 17 | +// <token>` header. A 401 response triggers a hard logout (clears | |
| 18 | +// the token + redirects to /login via the auth context) — not a | |
| 19 | +// silent refresh, because v1 keeps the refresh story simple. | |
| 20 | +// Refreshing on 401 is a v1.x improvement. | |
| 21 | +// | |
| 22 | +// **Errors.** A non-2xx response throws an `ApiError` carrying the | |
| 23 | +// status, message, and the parsed JSON body if any. Pages catch | |
| 24 | +// this and render the message inline. Network failures throw the | |
| 25 | +// underlying TypeError, also caught by the page boundary. | |
| 26 | + | |
| 27 | +import type { | |
| 28 | + Item, | |
| 29 | + JournalEntry, | |
| 30 | + Location, | |
| 31 | + MetaInfo, | |
| 32 | + Partner, | |
| 33 | + PurchaseOrder, | |
| 34 | + SalesOrder, | |
| 35 | + ShopFloorEntry, | |
| 36 | + StockBalance, | |
| 37 | + StockMovement, | |
| 38 | + TokenPair, | |
| 39 | + Uom, | |
| 40 | + WorkOrder, | |
| 41 | +} from '@/types/api' | |
| 42 | + | |
| 43 | +const TOKEN_KEY = 'vibeerp.accessToken' | |
| 44 | + | |
| 45 | +export function getAccessToken(): string | null { | |
| 46 | + return localStorage.getItem(TOKEN_KEY) | |
| 47 | +} | |
| 48 | + | |
| 49 | +export function setAccessToken(token: string | null): void { | |
| 50 | + if (token === null) { | |
| 51 | + localStorage.removeItem(TOKEN_KEY) | |
| 52 | + } else { | |
| 53 | + localStorage.setItem(TOKEN_KEY, token) | |
| 54 | + } | |
| 55 | +} | |
| 56 | + | |
| 57 | +export class ApiError extends Error { | |
| 58 | + constructor( | |
| 59 | + message: string, | |
| 60 | + public readonly status: number, | |
| 61 | + public readonly body: unknown, | |
| 62 | + ) { | |
| 63 | + super(message) | |
| 64 | + this.name = 'ApiError' | |
| 65 | + } | |
| 66 | +} | |
| 67 | + | |
| 68 | +// One-time-set callback that the auth context registers so the | |
| 69 | +// client can trigger a logout on 401 without a circular import. | |
| 70 | +let onUnauthorized: (() => void) | null = null | |
| 71 | +export function registerUnauthorizedHandler(handler: () => void) { | |
| 72 | + onUnauthorized = handler | |
| 73 | +} | |
| 74 | + | |
| 75 | +async function apiFetch<T>( | |
| 76 | + path: string, | |
| 77 | + init: RequestInit = {}, | |
| 78 | + expectJson = true, | |
| 79 | +): Promise<T> { | |
| 80 | + const headers = new Headers(init.headers) | |
| 81 | + headers.set('Accept', 'application/json') | |
| 82 | + if (init.body && !headers.has('Content-Type')) { | |
| 83 | + headers.set('Content-Type', 'application/json') | |
| 84 | + } | |
| 85 | + const token = getAccessToken() | |
| 86 | + if (token) headers.set('Authorization', `Bearer ${token}`) | |
| 87 | + | |
| 88 | + const res = await fetch(path, { ...init, headers }) | |
| 89 | + | |
| 90 | + if (res.status === 401) { | |
| 91 | + if (onUnauthorized) onUnauthorized() | |
| 92 | + throw new ApiError('Not authenticated', 401, null) | |
| 93 | + } | |
| 94 | + if (!res.ok) { | |
| 95 | + let body: unknown = null | |
| 96 | + let message = `${res.status} ${res.statusText}` | |
| 97 | + try { | |
| 98 | + const text = await res.text() | |
| 99 | + if (text) { | |
| 100 | + try { | |
| 101 | + body = JSON.parse(text) | |
| 102 | + const m = (body as { message?: unknown }).message | |
| 103 | + if (typeof m === 'string') message = m | |
| 104 | + } catch { | |
| 105 | + body = text | |
| 106 | + message = text | |
| 107 | + } | |
| 108 | + } | |
| 109 | + } catch { | |
| 110 | + // ignore body parse errors | |
| 111 | + } | |
| 112 | + throw new ApiError(message, res.status, body) | |
| 113 | + } | |
| 114 | + if (!expectJson || res.status === 204) { | |
| 115 | + return undefined as T | |
| 116 | + } | |
| 117 | + return (await res.json()) as T | |
| 118 | +} | |
| 119 | + | |
| 120 | +// ─── Public unauthenticated calls ──────────────────────────────────── | |
| 121 | + | |
| 122 | +export const meta = { | |
| 123 | + info: () => apiFetch<MetaInfo>('/api/v1/_meta/info'), | |
| 124 | +} | |
| 125 | + | |
| 126 | +export const auth = { | |
| 127 | + login: (username: string, password: string) => | |
| 128 | + apiFetch<TokenPair>('/api/v1/auth/login', { | |
| 129 | + method: 'POST', | |
| 130 | + body: JSON.stringify({ username, password }), | |
| 131 | + }), | |
| 132 | +} | |
| 133 | + | |
| 134 | +// ─── Catalog ───────────────────────────────────────────────────────── | |
| 135 | + | |
| 136 | +export const catalog = { | |
| 137 | + listItems: () => apiFetch<Item[]>('/api/v1/catalog/items'), | |
| 138 | + getItem: (id: string) => apiFetch<Item>(`/api/v1/catalog/items/${id}`), | |
| 139 | + listUoms: () => apiFetch<Uom[]>('/api/v1/catalog/uoms'), | |
| 140 | +} | |
| 141 | + | |
| 142 | +// ─── Partners ──────────────────────────────────────────────────────── | |
| 143 | + | |
| 144 | +export const partners = { | |
| 145 | + list: () => apiFetch<Partner[]>('/api/v1/partners/partners'), | |
| 146 | + get: (id: string) => apiFetch<Partner>(`/api/v1/partners/partners/${id}`), | |
| 147 | +} | |
| 148 | + | |
| 149 | +// ─── Inventory ─────────────────────────────────────────────────────── | |
| 150 | + | |
| 151 | +export const inventory = { | |
| 152 | + listLocations: () => apiFetch<Location[]>('/api/v1/inventory/locations'), | |
| 153 | + listBalances: () => apiFetch<StockBalance[]>('/api/v1/inventory/balances'), | |
| 154 | + listMovements: () => apiFetch<StockMovement[]>('/api/v1/inventory/movements'), | |
| 155 | +} | |
| 156 | + | |
| 157 | +// ─── Sales orders ──────────────────────────────────────────────────── | |
| 158 | + | |
| 159 | +export const salesOrders = { | |
| 160 | + list: () => apiFetch<SalesOrder[]>('/api/v1/orders/sales-orders'), | |
| 161 | + get: (id: string) => apiFetch<SalesOrder>(`/api/v1/orders/sales-orders/${id}`), | |
| 162 | + confirm: (id: string) => | |
| 163 | + apiFetch<SalesOrder>(`/api/v1/orders/sales-orders/${id}/confirm`, { | |
| 164 | + method: 'POST', | |
| 165 | + }), | |
| 166 | + cancel: (id: string) => | |
| 167 | + apiFetch<SalesOrder>(`/api/v1/orders/sales-orders/${id}/cancel`, { | |
| 168 | + method: 'POST', | |
| 169 | + }), | |
| 170 | + ship: (id: string, shippingLocationCode: string) => | |
| 171 | + apiFetch<SalesOrder>(`/api/v1/orders/sales-orders/${id}/ship`, { | |
| 172 | + method: 'POST', | |
| 173 | + body: JSON.stringify({ shippingLocationCode }), | |
| 174 | + }), | |
| 175 | +} | |
| 176 | + | |
| 177 | +// ─── Purchase orders ───────────────────────────────────────────────── | |
| 178 | + | |
| 179 | +export const purchaseOrders = { | |
| 180 | + list: () => apiFetch<PurchaseOrder[]>('/api/v1/orders/purchase-orders'), | |
| 181 | + get: (id: string) => apiFetch<PurchaseOrder>(`/api/v1/orders/purchase-orders/${id}`), | |
| 182 | + confirm: (id: string) => | |
| 183 | + apiFetch<PurchaseOrder>(`/api/v1/orders/purchase-orders/${id}/confirm`, { | |
| 184 | + method: 'POST', | |
| 185 | + }), | |
| 186 | + cancel: (id: string) => | |
| 187 | + apiFetch<PurchaseOrder>(`/api/v1/orders/purchase-orders/${id}/cancel`, { | |
| 188 | + method: 'POST', | |
| 189 | + }), | |
| 190 | + receive: (id: string, receivingLocationCode: string) => | |
| 191 | + apiFetch<PurchaseOrder>(`/api/v1/orders/purchase-orders/${id}/receive`, { | |
| 192 | + method: 'POST', | |
| 193 | + body: JSON.stringify({ receivingLocationCode }), | |
| 194 | + }), | |
| 195 | +} | |
| 196 | + | |
| 197 | +// ─── Production ────────────────────────────────────────────────────── | |
| 198 | + | |
| 199 | +export const production = { | |
| 200 | + listWorkOrders: () => apiFetch<WorkOrder[]>('/api/v1/production/work-orders'), | |
| 201 | + getWorkOrder: (id: string) => | |
| 202 | + apiFetch<WorkOrder>(`/api/v1/production/work-orders/${id}`), | |
| 203 | + startWorkOrder: (id: string) => | |
| 204 | + apiFetch<WorkOrder>(`/api/v1/production/work-orders/${id}/start`, { | |
| 205 | + method: 'POST', | |
| 206 | + }), | |
| 207 | + completeWorkOrder: (id: string, outputLocationCode: string) => | |
| 208 | + apiFetch<WorkOrder>(`/api/v1/production/work-orders/${id}/complete`, { | |
| 209 | + method: 'POST', | |
| 210 | + body: JSON.stringify({ outputLocationCode }), | |
| 211 | + }), | |
| 212 | + cancelWorkOrder: (id: string) => | |
| 213 | + apiFetch<WorkOrder>(`/api/v1/production/work-orders/${id}/cancel`, { | |
| 214 | + method: 'POST', | |
| 215 | + }), | |
| 216 | + shopFloor: () => | |
| 217 | + apiFetch<ShopFloorEntry[]>('/api/v1/production/work-orders/shop-floor'), | |
| 218 | +} | |
| 219 | + | |
| 220 | +// ─── Finance ───────────────────────────────────────────────────────── | |
| 221 | + | |
| 222 | +export const finance = { | |
| 223 | + listJournalEntries: () => | |
| 224 | + apiFetch<JournalEntry[]>('/api/v1/finance/journal-entries'), | |
| 225 | +} | ... | ... |
web/src/auth/AuthContext.tsx
0 → 100644
| 1 | +// vibe_erp web auth context. | |
| 2 | +// | |
| 3 | +// **Scope.** Holds the in-memory copy of the access token + the user | |
| 4 | +// it was issued to (decoded from the JWT payload), exposes login / | |
| 5 | +// logout actions, and rerenders subscribers when the token state | |
| 6 | +// flips. Persistence lives in localStorage via the api client's | |
| 7 | +// `getAccessToken/setAccessToken` helpers — the context only mirrors | |
| 8 | +// what's in storage so a hard refresh of the page picks up the | |
| 9 | +// existing session without a re-login. | |
| 10 | +// | |
| 11 | +// **Why decode the JWT in the SPA.** vibe_erp's auth tokens are | |
| 12 | +// signed (HS256) by the framework, so the SPA cannot *trust* the | |
| 13 | +// payload — but it can DISPLAY it (username, role list) without a | |
| 14 | +// round-trip. Anything load-bearing (permission checks) still | |
| 15 | +// happens server-side. The decode is intentionally trivial: split | |
| 16 | +// on '.', base64-decode the middle segment, JSON.parse. No JWT lib | |
| 17 | +// dependency; an invalid token just degrades to "unknown user" and | |
| 18 | +// the next API call gets a 401, triggering a logout. | |
| 19 | +// | |
| 20 | +// **401 handling.** The api client calls registerUnauthorizedHandler | |
| 21 | +// once at boot. When any request returns 401, the handler clears | |
| 22 | +// the token and forces a navigate to /login (preserving the | |
| 23 | +// attempted path so post-login can redirect back). | |
| 24 | + | |
| 25 | +import { | |
| 26 | + createContext, | |
| 27 | + useCallback, | |
| 28 | + useContext, | |
| 29 | + useEffect, | |
| 30 | + useMemo, | |
| 31 | + useState, | |
| 32 | + type ReactNode, | |
| 33 | +} from 'react' | |
| 34 | +import { useLocation, useNavigate } from 'react-router-dom' | |
| 35 | +import { | |
| 36 | + auth as authApi, | |
| 37 | + getAccessToken, | |
| 38 | + registerUnauthorizedHandler, | |
| 39 | + setAccessToken, | |
| 40 | +} from '@/api/client' | |
| 41 | + | |
| 42 | +interface JwtPayload { | |
| 43 | + sub?: string | |
| 44 | + username?: string | |
| 45 | + roles?: string[] | |
| 46 | + exp?: number | |
| 47 | +} | |
| 48 | + | |
| 49 | +function decodeJwt(token: string): JwtPayload | null { | |
| 50 | + try { | |
| 51 | + const parts = token.split('.') | |
| 52 | + if (parts.length !== 3) return null | |
| 53 | + const padded = parts[1] + '==='.slice((parts[1].length + 3) % 4) | |
| 54 | + const json = atob(padded.replace(/-/g, '+').replace(/_/g, '/')) | |
| 55 | + return JSON.parse(json) as JwtPayload | |
| 56 | + } catch { | |
| 57 | + return null | |
| 58 | + } | |
| 59 | +} | |
| 60 | + | |
| 61 | +interface AuthState { | |
| 62 | + token: string | null | |
| 63 | + username: string | null | |
| 64 | + roles: string[] | |
| 65 | + loading: boolean | |
| 66 | + login: (username: string, password: string) => Promise<void> | |
| 67 | + logout: () => void | |
| 68 | +} | |
| 69 | + | |
| 70 | +const AuthContext = createContext<AuthState | null>(null) | |
| 71 | + | |
| 72 | +export function AuthProvider({ children }: { children: ReactNode }) { | |
| 73 | + const navigate = useNavigate() | |
| 74 | + const location = useLocation() | |
| 75 | + const [token, setToken] = useState<string | null>(() => getAccessToken()) | |
| 76 | + const [loading, setLoading] = useState(false) | |
| 77 | + | |
| 78 | + const payload = useMemo(() => (token ? decodeJwt(token) : null), [token]) | |
| 79 | + | |
| 80 | + const logout = useCallback(() => { | |
| 81 | + setAccessToken(null) | |
| 82 | + setToken(null) | |
| 83 | + if (location.pathname !== '/login') { | |
| 84 | + navigate('/login', { | |
| 85 | + replace: true, | |
| 86 | + state: { from: location.pathname + location.search }, | |
| 87 | + }) | |
| 88 | + } | |
| 89 | + }, [navigate, location]) | |
| 90 | + | |
| 91 | + // Wire the api client's 401 handler exactly once. | |
| 92 | + useEffect(() => { | |
| 93 | + registerUnauthorizedHandler(() => { | |
| 94 | + // Defer to next tick so the throw inside apiFetch isn't | |
| 95 | + // swallowed by React's render-phase complaints. | |
| 96 | + setTimeout(() => logout(), 0) | |
| 97 | + }) | |
| 98 | + }, [logout]) | |
| 99 | + | |
| 100 | + const login = useCallback( | |
| 101 | + async (username: string, password: string) => { | |
| 102 | + setLoading(true) | |
| 103 | + try { | |
| 104 | + const pair = await authApi.login(username, password) | |
| 105 | + setAccessToken(pair.accessToken) | |
| 106 | + setToken(pair.accessToken) | |
| 107 | + } finally { | |
| 108 | + setLoading(false) | |
| 109 | + } | |
| 110 | + }, | |
| 111 | + [], | |
| 112 | + ) | |
| 113 | + | |
| 114 | + const value: AuthState = { | |
| 115 | + token, | |
| 116 | + username: payload?.username ?? payload?.sub ?? null, | |
| 117 | + roles: payload?.roles ?? [], | |
| 118 | + loading, | |
| 119 | + login, | |
| 120 | + logout, | |
| 121 | + } | |
| 122 | + | |
| 123 | + return <AuthContext.Provider value={value}>{children}</AuthContext.Provider> | |
| 124 | +} | |
| 125 | + | |
| 126 | +export function useAuth(): AuthState { | |
| 127 | + const ctx = useContext(AuthContext) | |
| 128 | + if (!ctx) throw new Error('useAuth must be used inside <AuthProvider>') | |
| 129 | + return ctx | |
| 130 | +} | ... | ... |
web/src/components/DataTable.tsx
0 → 100644
| 1 | +// Tiny generic table component. | |
| 2 | +// | |
| 3 | +// Each column declares: a header label, a key, and an optional | |
| 4 | +// `render` function that takes the row and returns a ReactNode. | |
| 5 | +// Without `render` the column reads `row[key]` and stringifies it. | |
| 6 | +// | |
| 7 | +// Intentionally not a heavy table lib (TanStack Table, AG Grid). | |
| 8 | +// v1 SPA needs columnar reads, link cells, status badges, and | |
| 9 | +// filtering by a free-text input — all trivially handcoded. A | |
| 10 | +// future chunk can swap this out the day a real consumer asks for | |
| 11 | +// virtualised rows or column resizing. | |
| 12 | + | |
| 13 | +import type { ReactNode } from 'react' | |
| 14 | + | |
| 15 | +export interface Column<T> { | |
| 16 | + header: string | |
| 17 | + key: string | |
| 18 | + render?: (row: T) => ReactNode | |
| 19 | + className?: string | |
| 20 | +} | |
| 21 | + | |
| 22 | +interface Props<T> { | |
| 23 | + rows: T[] | |
| 24 | + columns: Column<T>[] | |
| 25 | + empty?: ReactNode | |
| 26 | + rowKey?: (row: T) => string | |
| 27 | +} | |
| 28 | + | |
| 29 | +export function DataTable<T>({ | |
| 30 | + rows, | |
| 31 | + columns, | |
| 32 | + empty = <div className="p-6 text-sm text-slate-400">No rows.</div>, | |
| 33 | + rowKey, | |
| 34 | +}: Props<T>) { | |
| 35 | + if (rows.length === 0) { | |
| 36 | + return <div className="card">{empty}</div> | |
| 37 | + } | |
| 38 | + return ( | |
| 39 | + <div className="card overflow-x-auto"> | |
| 40 | + <table className="table-base"> | |
| 41 | + <thead className="bg-slate-50"> | |
| 42 | + <tr> | |
| 43 | + {columns.map((c) => ( | |
| 44 | + <th key={c.key} className={c.className}> | |
| 45 | + {c.header} | |
| 46 | + </th> | |
| 47 | + ))} | |
| 48 | + </tr> | |
| 49 | + </thead> | |
| 50 | + <tbody className="divide-y divide-slate-100"> | |
| 51 | + {rows.map((row, idx) => { | |
| 52 | + const key = rowKey | |
| 53 | + ? rowKey(row) | |
| 54 | + : ((row as { id?: string }).id ?? String(idx)) | |
| 55 | + return ( | |
| 56 | + <tr key={key} className="hover:bg-slate-50"> | |
| 57 | + {columns.map((c) => ( | |
| 58 | + <td key={c.key} className={c.className}> | |
| 59 | + {c.render | |
| 60 | + ? c.render(row) | |
| 61 | + : ((row as unknown as Record<string, unknown>)[c.key] as ReactNode) ?? ''} | |
| 62 | + </td> | |
| 63 | + ))} | |
| 64 | + </tr> | |
| 65 | + ) | |
| 66 | + })} | |
| 67 | + </tbody> | |
| 68 | + </table> | |
| 69 | + </div> | |
| 70 | + ) | |
| 71 | +} | ... | ... |
web/src/components/ErrorBox.tsx
0 → 100644
| 1 | +interface Props { | |
| 2 | + error: unknown | |
| 3 | +} | |
| 4 | + | |
| 5 | +export function ErrorBox({ error }: Props) { | |
| 6 | + let message: string | |
| 7 | + if (error instanceof Error) { | |
| 8 | + message = error.message | |
| 9 | + } else if (typeof error === 'string') { | |
| 10 | + message = error | |
| 11 | + } else if (error && typeof error === 'object' && 'message' in error) { | |
| 12 | + message = String((error as { message: unknown }).message) | |
| 13 | + } else { | |
| 14 | + message = 'Unknown error' | |
| 15 | + } | |
| 16 | + return ( | |
| 17 | + <div className="rounded-md border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-800"> | |
| 18 | + <span className="font-semibold">Error:</span> {message} | |
| 19 | + </div> | |
| 20 | + ) | |
| 21 | +} | ... | ... |
web/src/components/Loading.tsx
0 → 100644
web/src/components/PageHeader.tsx
0 → 100644
| 1 | +import type { ReactNode } from 'react' | |
| 2 | + | |
| 3 | +interface Props { | |
| 4 | + title: string | |
| 5 | + subtitle?: string | |
| 6 | + actions?: ReactNode | |
| 7 | +} | |
| 8 | + | |
| 9 | +export function PageHeader({ title, subtitle, actions }: Props) { | |
| 10 | + return ( | |
| 11 | + <div className="mb-6 flex flex-col items-start gap-3 sm:flex-row sm:items-end sm:justify-between"> | |
| 12 | + <div> | |
| 13 | + <h1 className="text-2xl font-semibold text-slate-900">{title}</h1> | |
| 14 | + {subtitle && <p className="mt-1 text-sm text-slate-500">{subtitle}</p>} | |
| 15 | + </div> | |
| 16 | + {actions && <div className="flex flex-wrap items-center gap-2">{actions}</div>} | |
| 17 | + </div> | |
| 18 | + ) | |
| 19 | +} | ... | ... |
web/src/components/ProtectedRoute.tsx
0 → 100644
| 1 | +import { Navigate, useLocation } from 'react-router-dom' | |
| 2 | +import { useAuth } from '@/auth/AuthContext' | |
| 3 | +import type { ReactNode } from 'react' | |
| 4 | + | |
| 5 | +export function ProtectedRoute({ children }: { children: ReactNode }) { | |
| 6 | + const { token } = useAuth() | |
| 7 | + const location = useLocation() | |
| 8 | + if (!token) { | |
| 9 | + return ( | |
| 10 | + <Navigate | |
| 11 | + to="/login" | |
| 12 | + replace | |
| 13 | + state={{ from: location.pathname + location.search }} | |
| 14 | + /> | |
| 15 | + ) | |
| 16 | + } | |
| 17 | + return <>{children}</> | |
| 18 | +} | ... | ... |
web/src/components/StatusBadge.tsx
0 → 100644
| 1 | +interface Props { | |
| 2 | + status: string | |
| 3 | +} | |
| 4 | + | |
| 5 | +// Status → Tailwind class via the shared @layer components classes | |
| 6 | +// declared in index.css. Falls back to 'badge-pending' for unknown | |
| 7 | +// statuses so a new server-side enum value renders as a neutral pill | |
| 8 | +// instead of throwing. | |
| 9 | +export function StatusBadge({ status }: Props) { | |
| 10 | + const cls = | |
| 11 | + { | |
| 12 | + DRAFT: 'badge-draft', | |
| 13 | + CONFIRMED: 'badge-confirmed', | |
| 14 | + SHIPPED: 'badge-shipped', | |
| 15 | + RECEIVED: 'badge-received', | |
| 16 | + COMPLETED: 'badge-completed', | |
| 17 | + CANCELLED: 'badge-cancelled', | |
| 18 | + IN_PROGRESS: 'badge-in-progress', | |
| 19 | + PENDING: 'badge-pending', | |
| 20 | + POSTED: 'badge-confirmed', | |
| 21 | + SETTLED: 'badge-shipped', | |
| 22 | + REVERSED: 'badge-cancelled', | |
| 23 | + }[status] ?? 'badge-pending' | |
| 24 | + return <span className={cls}>{status}</span> | |
| 25 | +} | ... | ... |
web/src/index.css
0 → 100644
| 1 | +@tailwind base; | |
| 2 | +@tailwind components; | |
| 3 | +@tailwind utilities; | |
| 4 | + | |
| 5 | +:root { | |
| 6 | + font-family: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; | |
| 7 | + -webkit-font-smoothing: antialiased; | |
| 8 | + -moz-osx-font-smoothing: grayscale; | |
| 9 | +} | |
| 10 | + | |
| 11 | +@layer components { | |
| 12 | + .btn { | |
| 13 | + @apply inline-flex items-center justify-center rounded-md px-3 py-1.5 text-sm font-medium shadow-sm transition focus:outline-none focus:ring-2 focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50; | |
| 14 | + } | |
| 15 | + .btn-primary { | |
| 16 | + @apply btn bg-brand-500 text-white hover:bg-brand-600 focus:ring-brand-500; | |
| 17 | + } | |
| 18 | + .btn-secondary { | |
| 19 | + @apply btn bg-white text-slate-800 ring-1 ring-inset ring-slate-300 hover:bg-slate-50 focus:ring-brand-500; | |
| 20 | + } | |
| 21 | + .btn-danger { | |
| 22 | + @apply btn bg-rose-600 text-white hover:bg-rose-700 focus:ring-rose-600; | |
| 23 | + } | |
| 24 | + .card { | |
| 25 | + @apply rounded-lg border border-slate-200 bg-white shadow-sm; | |
| 26 | + } | |
| 27 | + .table-base { | |
| 28 | + @apply min-w-full divide-y divide-slate-200 text-sm; | |
| 29 | + } | |
| 30 | + .table-base th { | |
| 31 | + @apply px-3 py-2 text-left text-xs font-semibold uppercase tracking-wide text-slate-500; | |
| 32 | + } | |
| 33 | + .table-base td { | |
| 34 | + @apply whitespace-nowrap px-3 py-2 text-slate-700; | |
| 35 | + } | |
| 36 | + .badge { | |
| 37 | + @apply inline-flex items-center rounded-full px-2 py-0.5 text-xs font-semibold; | |
| 38 | + } | |
| 39 | + .badge-draft { @apply badge bg-slate-100 text-slate-700; } | |
| 40 | + .badge-confirmed { @apply badge bg-amber-100 text-amber-800; } | |
| 41 | + .badge-shipped { @apply badge bg-emerald-100 text-emerald-800; } | |
| 42 | + .badge-received { @apply badge bg-emerald-100 text-emerald-800; } | |
| 43 | + .badge-completed { @apply badge bg-emerald-100 text-emerald-800; } | |
| 44 | + .badge-cancelled { @apply badge bg-rose-100 text-rose-800; } | |
| 45 | + .badge-in-progress { @apply badge bg-sky-100 text-sky-800; } | |
| 46 | + .badge-pending { @apply badge bg-slate-100 text-slate-600; } | |
| 47 | +} | ... | ... |
web/src/layout/AppLayout.tsx
0 → 100644
| 1 | +// vibe_erp main app shell. | |
| 2 | +// | |
| 3 | +// Top bar (logo + version + user + logout) and a permanent left | |
| 4 | +// sidebar grouped by Packaged Business Capability (PBC). The | |
| 5 | +// `<Outlet />` renders whichever child route the router matched. | |
| 6 | +// | |
| 7 | +// **Why a static nav array.** The framework already exposes | |
| 8 | +// menu metadata at /api/v1/_meta/metadata, but plumbing that into | |
| 9 | +// the SPA is a Phase 3 (Tier 1 customization) concern. v1 SPA | |
| 10 | +// hard-codes a sensible default that mirrors the implemented PBCs; | |
| 11 | +// a future chunk replaces this with a metadata-driven sidebar so | |
| 12 | +// plug-ins can contribute new menus through their YAML. | |
| 13 | + | |
| 14 | +import { NavLink, Outlet } from 'react-router-dom' | |
| 15 | +import { useEffect, useState } from 'react' | |
| 16 | +import { useAuth } from '@/auth/AuthContext' | |
| 17 | +import { meta } from '@/api/client' | |
| 18 | +import type { MetaInfo } from '@/types/api' | |
| 19 | + | |
| 20 | +interface NavItem { | |
| 21 | + to: string | |
| 22 | + label: string | |
| 23 | +} | |
| 24 | +interface NavGroup { | |
| 25 | + heading: string | |
| 26 | + items: NavItem[] | |
| 27 | +} | |
| 28 | + | |
| 29 | +const NAV: NavGroup[] = [ | |
| 30 | + { | |
| 31 | + heading: 'Overview', | |
| 32 | + items: [{ to: '/', label: 'Dashboard' }], | |
| 33 | + }, | |
| 34 | + { | |
| 35 | + heading: 'Catalog & Partners', | |
| 36 | + items: [ | |
| 37 | + { to: '/items', label: 'Items' }, | |
| 38 | + { to: '/uoms', label: 'Units of Measure' }, | |
| 39 | + { to: '/partners', label: 'Partners' }, | |
| 40 | + ], | |
| 41 | + }, | |
| 42 | + { | |
| 43 | + heading: 'Inventory', | |
| 44 | + items: [ | |
| 45 | + { to: '/locations', label: 'Locations' }, | |
| 46 | + { to: '/balances', label: 'Stock Balances' }, | |
| 47 | + { to: '/movements', label: 'Stock Movements' }, | |
| 48 | + ], | |
| 49 | + }, | |
| 50 | + { | |
| 51 | + heading: 'Orders', | |
| 52 | + items: [ | |
| 53 | + { to: '/sales-orders', label: 'Sales Orders' }, | |
| 54 | + { to: '/purchase-orders', label: 'Purchase Orders' }, | |
| 55 | + ], | |
| 56 | + }, | |
| 57 | + { | |
| 58 | + heading: 'Production', | |
| 59 | + items: [ | |
| 60 | + { to: '/work-orders', label: 'Work Orders' }, | |
| 61 | + { to: '/shop-floor', label: 'Shop Floor' }, | |
| 62 | + ], | |
| 63 | + }, | |
| 64 | + { | |
| 65 | + heading: 'Finance', | |
| 66 | + items: [{ to: '/journal-entries', label: 'Journal Entries' }], | |
| 67 | + }, | |
| 68 | +] | |
| 69 | + | |
| 70 | +export function AppLayout() { | |
| 71 | + const { username, logout } = useAuth() | |
| 72 | + const [info, setInfo] = useState<MetaInfo | null>(null) | |
| 73 | + | |
| 74 | + useEffect(() => { | |
| 75 | + meta.info().then(setInfo).catch(() => setInfo(null)) | |
| 76 | + }, []) | |
| 77 | + | |
| 78 | + return ( | |
| 79 | + <div className="flex min-h-screen bg-slate-50 text-slate-900"> | |
| 80 | + {/* ─── Sidebar ──────────────────────────────────────────── */} | |
| 81 | + <aside className="hidden w-60 flex-shrink-0 border-r border-slate-200 bg-white sm:flex sm:flex-col"> | |
| 82 | + <div className="flex h-14 items-center px-4 border-b border-slate-200"> | |
| 83 | + <span className="text-lg font-bold text-brand-600">vibe_erp</span> | |
| 84 | + {info && ( | |
| 85 | + <span className="ml-2 text-xs text-slate-400"> | |
| 86 | + {info.implementationVersion} | |
| 87 | + </span> | |
| 88 | + )} | |
| 89 | + </div> | |
| 90 | + <nav className="flex-1 overflow-y-auto px-3 py-4"> | |
| 91 | + {NAV.map((group) => ( | |
| 92 | + <div key={group.heading} className="mb-5"> | |
| 93 | + <div className="px-2 text-xs font-semibold uppercase tracking-wider text-slate-400"> | |
| 94 | + {group.heading} | |
| 95 | + </div> | |
| 96 | + <ul className="mt-1 space-y-0.5"> | |
| 97 | + {group.items.map((item) => ( | |
| 98 | + <li key={item.to}> | |
| 99 | + <NavLink | |
| 100 | + to={item.to} | |
| 101 | + end={item.to === '/'} | |
| 102 | + className={({ isActive }) => | |
| 103 | + [ | |
| 104 | + 'block rounded-md px-2 py-1.5 text-sm transition', | |
| 105 | + isActive | |
| 106 | + ? 'bg-brand-50 text-brand-700 font-semibold' | |
| 107 | + : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900', | |
| 108 | + ].join(' ') | |
| 109 | + } | |
| 110 | + > | |
| 111 | + {item.label} | |
| 112 | + </NavLink> | |
| 113 | + </li> | |
| 114 | + ))} | |
| 115 | + </ul> | |
| 116 | + </div> | |
| 117 | + ))} | |
| 118 | + </nav> | |
| 119 | + <div className="border-t border-slate-200 px-3 py-3 text-xs text-slate-400"> | |
| 120 | + {info?.activeProfiles?.length ? ( | |
| 121 | + <div>profiles: {info.activeProfiles.join(', ')}</div> | |
| 122 | + ) : null} | |
| 123 | + {info?.buildTime ? ( | |
| 124 | + <div>built: {new Date(info.buildTime).toLocaleString()}</div> | |
| 125 | + ) : null} | |
| 126 | + </div> | |
| 127 | + </aside> | |
| 128 | + | |
| 129 | + {/* ─── Main column ─────────────────────────────────────── */} | |
| 130 | + <div className="flex flex-1 flex-col"> | |
| 131 | + <header className="flex h-14 items-center justify-between border-b border-slate-200 bg-white px-6 sm:justify-end"> | |
| 132 | + <span className="text-lg font-bold text-brand-600 sm:hidden">vibe_erp</span> | |
| 133 | + <div className="flex items-center gap-3"> | |
| 134 | + <span className="text-sm text-slate-500"> | |
| 135 | + {username ? ( | |
| 136 | + <> | |
| 137 | + Signed in as <span className="font-medium text-slate-700">{username}</span> | |
| 138 | + </> | |
| 139 | + ) : ( | |
| 140 | + 'Signed in' | |
| 141 | + )} | |
| 142 | + </span> | |
| 143 | + <button className="btn-secondary" onClick={logout}> | |
| 144 | + Logout | |
| 145 | + </button> | |
| 146 | + </div> | |
| 147 | + </header> | |
| 148 | + <main className="flex-1 overflow-y-auto p-6"> | |
| 149 | + <Outlet /> | |
| 150 | + </main> | |
| 151 | + </div> | |
| 152 | + </div> | |
| 153 | + ) | |
| 154 | +} | ... | ... |
web/src/main.tsx
0 → 100644
| 1 | +import React from 'react' | |
| 2 | +import ReactDOM from 'react-dom/client' | |
| 3 | +import { BrowserRouter } from 'react-router-dom' | |
| 4 | +import App from './App' | |
| 5 | +import { AuthProvider } from './auth/AuthContext' | |
| 6 | +import './index.css' | |
| 7 | + | |
| 8 | +ReactDOM.createRoot(document.getElementById('root')!).render( | |
| 9 | + <React.StrictMode> | |
| 10 | + <BrowserRouter> | |
| 11 | + <AuthProvider> | |
| 12 | + <App /> | |
| 13 | + </AuthProvider> | |
| 14 | + </BrowserRouter> | |
| 15 | + </React.StrictMode>, | |
| 16 | +) | ... | ... |
web/src/pages/BalancesPage.tsx
0 → 100644
| 1 | +import { useEffect, useState } from 'react' | |
| 2 | +import { inventory } from '@/api/client' | |
| 3 | +import type { Location, StockBalance } from '@/types/api' | |
| 4 | +import { PageHeader } from '@/components/PageHeader' | |
| 5 | +import { Loading } from '@/components/Loading' | |
| 6 | +import { ErrorBox } from '@/components/ErrorBox' | |
| 7 | +import { DataTable, type Column } from '@/components/DataTable' | |
| 8 | + | |
| 9 | +interface Row extends Record<string, unknown> { | |
| 10 | + id: string | |
| 11 | + itemCode: string | |
| 12 | + locationCode: string | |
| 13 | + quantity: string | number | |
| 14 | +} | |
| 15 | + | |
| 16 | +export function BalancesPage() { | |
| 17 | + const [rows, setRows] = useState<Row[]>([]) | |
| 18 | + const [error, setError] = useState<Error | null>(null) | |
| 19 | + const [loading, setLoading] = useState(true) | |
| 20 | + | |
| 21 | + useEffect(() => { | |
| 22 | + Promise.all([inventory.listBalances(), inventory.listLocations()]) | |
| 23 | + .then(([balances, locs]: [StockBalance[], Location[]]) => { | |
| 24 | + const byId = new Map(locs.map((l) => [l.id, l.code])) | |
| 25 | + setRows( | |
| 26 | + balances.map((b) => ({ | |
| 27 | + id: b.id, | |
| 28 | + itemCode: b.itemCode, | |
| 29 | + locationCode: byId.get(b.locationId) ?? b.locationId, | |
| 30 | + quantity: b.quantity, | |
| 31 | + })), | |
| 32 | + ) | |
| 33 | + }) | |
| 34 | + .catch((e: unknown) => setError(e instanceof Error ? e : new Error(String(e)))) | |
| 35 | + .finally(() => setLoading(false)) | |
| 36 | + }, []) | |
| 37 | + | |
| 38 | + const columns: Column<Row>[] = [ | |
| 39 | + { header: 'Item', key: 'itemCode', render: (r) => <span className="font-mono">{r.itemCode}</span> }, | |
| 40 | + { | |
| 41 | + header: 'Location', | |
| 42 | + key: 'locationCode', | |
| 43 | + render: (r) => <span className="font-mono">{r.locationCode}</span>, | |
| 44 | + }, | |
| 45 | + { | |
| 46 | + header: 'Quantity', | |
| 47 | + key: 'quantity', | |
| 48 | + render: (r) => <span className="font-mono tabular-nums">{String(r.quantity)}</span>, | |
| 49 | + }, | |
| 50 | + ] | |
| 51 | + | |
| 52 | + return ( | |
| 53 | + <div> | |
| 54 | + <PageHeader | |
| 55 | + title="Stock Balances" | |
| 56 | + subtitle="On-hand quantities per (item, location). Updates atomically with every movement." | |
| 57 | + /> | |
| 58 | + {loading && <Loading />} | |
| 59 | + {error && <ErrorBox error={error} />} | |
| 60 | + {!loading && !error && <DataTable rows={rows} columns={columns} />} | |
| 61 | + </div> | |
| 62 | + ) | |
| 63 | +} | ... | ... |
web/src/pages/DashboardPage.tsx
0 → 100644
| 1 | +import { useEffect, useState } from 'react' | |
| 2 | +import { Link } from 'react-router-dom' | |
| 3 | +import { | |
| 4 | + catalog, | |
| 5 | + finance, | |
| 6 | + inventory, | |
| 7 | + partners, | |
| 8 | + production, | |
| 9 | + purchaseOrders, | |
| 10 | + salesOrders, | |
| 11 | +} from '@/api/client' | |
| 12 | +import { PageHeader } from '@/components/PageHeader' | |
| 13 | +import { Loading } from '@/components/Loading' | |
| 14 | +import { ErrorBox } from '@/components/ErrorBox' | |
| 15 | +import { useAuth } from '@/auth/AuthContext' | |
| 16 | + | |
| 17 | +interface DashboardCounts { | |
| 18 | + items: number | |
| 19 | + partners: number | |
| 20 | + locations: number | |
| 21 | + salesOrders: number | |
| 22 | + purchaseOrders: number | |
| 23 | + workOrders: number | |
| 24 | + journalEntries: number | |
| 25 | + inProgressWorkOrders: number | |
| 26 | +} | |
| 27 | + | |
| 28 | +export function DashboardPage() { | |
| 29 | + const { username } = useAuth() | |
| 30 | + const [counts, setCounts] = useState<DashboardCounts | null>(null) | |
| 31 | + const [error, setError] = useState<Error | null>(null) | |
| 32 | + const [loading, setLoading] = useState(true) | |
| 33 | + | |
| 34 | + useEffect(() => { | |
| 35 | + let active = true | |
| 36 | + setLoading(true) | |
| 37 | + Promise.all([ | |
| 38 | + catalog.listItems(), | |
| 39 | + partners.list(), | |
| 40 | + inventory.listLocations(), | |
| 41 | + salesOrders.list(), | |
| 42 | + purchaseOrders.list(), | |
| 43 | + production.listWorkOrders(), | |
| 44 | + finance.listJournalEntries(), | |
| 45 | + production.shopFloor(), | |
| 46 | + ]) | |
| 47 | + .then(([items, parts, locs, sos, pos, wos, jes, sf]) => { | |
| 48 | + if (!active) return | |
| 49 | + setCounts({ | |
| 50 | + items: items.length, | |
| 51 | + partners: parts.length, | |
| 52 | + locations: locs.length, | |
| 53 | + salesOrders: sos.length, | |
| 54 | + purchaseOrders: pos.length, | |
| 55 | + workOrders: wos.length, | |
| 56 | + journalEntries: jes.length, | |
| 57 | + inProgressWorkOrders: sf.length, | |
| 58 | + }) | |
| 59 | + }) | |
| 60 | + .catch((e: unknown) => { | |
| 61 | + if (active) setError(e instanceof Error ? e : new Error(String(e))) | |
| 62 | + }) | |
| 63 | + .finally(() => active && setLoading(false)) | |
| 64 | + return () => { | |
| 65 | + active = false | |
| 66 | + } | |
| 67 | + }, []) | |
| 68 | + | |
| 69 | + return ( | |
| 70 | + <div> | |
| 71 | + <PageHeader | |
| 72 | + title={`Welcome${username ? ', ' + username : ''}`} | |
| 73 | + subtitle="The framework's buy-make-sell loop, end to end through the same Postgres." | |
| 74 | + /> | |
| 75 | + {loading && <Loading />} | |
| 76 | + {error && <ErrorBox error={error} />} | |
| 77 | + {counts && ( | |
| 78 | + <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4"> | |
| 79 | + <DashboardCard label="Items" value={counts.items} to="/items" /> | |
| 80 | + <DashboardCard label="Partners" value={counts.partners} to="/partners" /> | |
| 81 | + <DashboardCard label="Locations" value={counts.locations} to="/locations" /> | |
| 82 | + <DashboardCard | |
| 83 | + label="Work orders in progress" | |
| 84 | + value={counts.inProgressWorkOrders} | |
| 85 | + to="/shop-floor" | |
| 86 | + highlight | |
| 87 | + /> | |
| 88 | + <DashboardCard label="Sales orders" value={counts.salesOrders} to="/sales-orders" /> | |
| 89 | + <DashboardCard | |
| 90 | + label="Purchase orders" | |
| 91 | + value={counts.purchaseOrders} | |
| 92 | + to="/purchase-orders" | |
| 93 | + /> | |
| 94 | + <DashboardCard label="Work orders" value={counts.workOrders} to="/work-orders" /> | |
| 95 | + <DashboardCard | |
| 96 | + label="Journal entries" | |
| 97 | + value={counts.journalEntries} | |
| 98 | + to="/journal-entries" | |
| 99 | + /> | |
| 100 | + </div> | |
| 101 | + )} | |
| 102 | + <div className="mt-8 card p-5 text-sm text-slate-600"> | |
| 103 | + <h2 className="mb-2 text-base font-semibold text-slate-800">Try the demo</h2> | |
| 104 | + <ol className="list-decimal space-y-1 pl-5"> | |
| 105 | + <li> | |
| 106 | + Open a <Link to="/sales-orders" className="text-brand-600 hover:underline">sales order</Link>{' '} | |
| 107 | + in DRAFT, click <span className="font-mono">Confirm</span>. | |
| 108 | + </li> | |
| 109 | + <li> | |
| 110 | + With the same sales order CONFIRMED, click <span className="font-mono">Ship</span>. | |
| 111 | + The framework atomically debits stock, flips status to SHIPPED, and emits a domain | |
| 112 | + event. | |
| 113 | + </li> | |
| 114 | + <li> | |
| 115 | + Watch <Link to="/balances" className="text-brand-600 hover:underline">stock balances</Link>{' '} | |
| 116 | + drop, <Link to="/movements" className="text-brand-600 hover:underline">movements</Link>{' '} | |
| 117 | + grow by one row, and{' '} | |
| 118 | + <Link to="/journal-entries" className="text-brand-600 hover:underline"> | |
| 119 | + journal entries | |
| 120 | + </Link>{' '} | |
| 121 | + settle from POSTED to SETTLED — all from the cross-PBC event subscriber in pbc-finance. | |
| 122 | + </li> | |
| 123 | + </ol> | |
| 124 | + </div> | |
| 125 | + </div> | |
| 126 | + ) | |
| 127 | +} | |
| 128 | + | |
| 129 | +function DashboardCard({ | |
| 130 | + label, | |
| 131 | + value, | |
| 132 | + to, | |
| 133 | + highlight = false, | |
| 134 | +}: { | |
| 135 | + label: string | |
| 136 | + value: number | |
| 137 | + to: string | |
| 138 | + highlight?: boolean | |
| 139 | +}) { | |
| 140 | + return ( | |
| 141 | + <Link | |
| 142 | + to={to} | |
| 143 | + className={[ | |
| 144 | + 'card flex flex-col gap-1 p-5 transition hover:shadow-md', | |
| 145 | + highlight ? 'ring-2 ring-brand-200' : '', | |
| 146 | + ].join(' ')} | |
| 147 | + > | |
| 148 | + <div className="text-xs font-semibold uppercase tracking-wide text-slate-500">{label}</div> | |
| 149 | + <div className="text-3xl font-bold text-slate-900">{value}</div> | |
| 150 | + </Link> | |
| 151 | + ) | |
| 152 | +} | ... | ... |
web/src/pages/ItemsPage.tsx
0 → 100644
| 1 | +import { useEffect, useState } from 'react' | |
| 2 | +import { catalog } from '@/api/client' | |
| 3 | +import type { Item } from '@/types/api' | |
| 4 | +import { PageHeader } from '@/components/PageHeader' | |
| 5 | +import { Loading } from '@/components/Loading' | |
| 6 | +import { ErrorBox } from '@/components/ErrorBox' | |
| 7 | +import { DataTable, type Column } from '@/components/DataTable' | |
| 8 | + | |
| 9 | +export function ItemsPage() { | |
| 10 | + const [rows, setRows] = useState<Item[]>([]) | |
| 11 | + const [error, setError] = useState<Error | null>(null) | |
| 12 | + const [loading, setLoading] = useState(true) | |
| 13 | + | |
| 14 | + useEffect(() => { | |
| 15 | + catalog | |
| 16 | + .listItems() | |
| 17 | + .then(setRows) | |
| 18 | + .catch((e: unknown) => setError(e instanceof Error ? e : new Error(String(e)))) | |
| 19 | + .finally(() => setLoading(false)) | |
| 20 | + }, []) | |
| 21 | + | |
| 22 | + const columns: Column<Item>[] = [ | |
| 23 | + { header: 'Code', key: 'code', render: (r) => <span className="font-mono">{r.code}</span> }, | |
| 24 | + { header: 'Name', key: 'name' }, | |
| 25 | + { header: 'Type', key: 'itemType' }, | |
| 26 | + { header: 'UoM', key: 'baseUomCode', render: (r) => <span className="font-mono">{r.baseUomCode}</span> }, | |
| 27 | + { | |
| 28 | + header: 'Active', | |
| 29 | + key: 'active', | |
| 30 | + render: (r) => (r.active ? <span className="text-emerald-600">●</span> : <span className="text-slate-300">●</span>), | |
| 31 | + }, | |
| 32 | + ] | |
| 33 | + | |
| 34 | + return ( | |
| 35 | + <div> | |
| 36 | + <PageHeader | |
| 37 | + title="Items" | |
| 38 | + subtitle="Catalog of items the framework can transact: raw materials, components, finished goods, services." | |
| 39 | + /> | |
| 40 | + {loading && <Loading />} | |
| 41 | + {error && <ErrorBox error={error} />} | |
| 42 | + {!loading && !error && <DataTable rows={rows} columns={columns} />} | |
| 43 | + </div> | |
| 44 | + ) | |
| 45 | +} | ... | ... |
web/src/pages/JournalEntriesPage.tsx
0 → 100644
| 1 | +import { useEffect, useState } from 'react' | |
| 2 | +import { finance } from '@/api/client' | |
| 3 | +import type { JournalEntry } from '@/types/api' | |
| 4 | +import { PageHeader } from '@/components/PageHeader' | |
| 5 | +import { Loading } from '@/components/Loading' | |
| 6 | +import { ErrorBox } from '@/components/ErrorBox' | |
| 7 | +import { DataTable, type Column } from '@/components/DataTable' | |
| 8 | +import { StatusBadge } from '@/components/StatusBadge' | |
| 9 | + | |
| 10 | +export function JournalEntriesPage() { | |
| 11 | + const [rows, setRows] = useState<JournalEntry[]>([]) | |
| 12 | + const [error, setError] = useState<Error | null>(null) | |
| 13 | + const [loading, setLoading] = useState(true) | |
| 14 | + | |
| 15 | + useEffect(() => { | |
| 16 | + finance | |
| 17 | + .listJournalEntries() | |
| 18 | + .then((rs) => | |
| 19 | + setRows([...rs].sort((a, b) => (b.postedAt ?? '').localeCompare(a.postedAt ?? ''))), | |
| 20 | + ) | |
| 21 | + .catch((e: unknown) => setError(e instanceof Error ? e : new Error(String(e)))) | |
| 22 | + .finally(() => setLoading(false)) | |
| 23 | + }, []) | |
| 24 | + | |
| 25 | + const columns: Column<JournalEntry>[] = [ | |
| 26 | + { | |
| 27 | + header: 'Posted', | |
| 28 | + key: 'postedAt', | |
| 29 | + render: (r) => (r.postedAt ? new Date(r.postedAt).toLocaleString() : '—'), | |
| 30 | + }, | |
| 31 | + { header: 'Code', key: 'code', render: (r) => <span className="font-mono text-xs">{r.code}</span> }, | |
| 32 | + { header: 'Type', key: 'type' }, | |
| 33 | + { header: 'Status', key: 'status', render: (r) => <StatusBadge status={r.status} /> }, | |
| 34 | + { header: 'Order', key: 'orderCode', render: (r) => <span className="font-mono">{r.orderCode}</span> }, | |
| 35 | + { header: 'Partner', key: 'partnerCode', render: (r) => <span className="font-mono">{r.partnerCode}</span> }, | |
| 36 | + { | |
| 37 | + header: 'Amount', | |
| 38 | + key: 'amount', | |
| 39 | + render: (r) => ( | |
| 40 | + <span className="font-mono tabular-nums"> | |
| 41 | + {Number(r.amount).toLocaleString(undefined, { minimumFractionDigits: 2 })}{' '} | |
| 42 | + {r.currencyCode} | |
| 43 | + </span> | |
| 44 | + ), | |
| 45 | + }, | |
| 46 | + ] | |
| 47 | + | |
| 48 | + return ( | |
| 49 | + <div> | |
| 50 | + <PageHeader | |
| 51 | + title="Journal Entries" | |
| 52 | + subtitle="AR / AP entries created reactively by pbc-finance from order lifecycle events." | |
| 53 | + /> | |
| 54 | + {loading && <Loading />} | |
| 55 | + {error && <ErrorBox error={error} />} | |
| 56 | + {!loading && !error && <DataTable rows={rows} columns={columns} />} | |
| 57 | + </div> | |
| 58 | + ) | |
| 59 | +} | ... | ... |
web/src/pages/LocationsPage.tsx
0 → 100644
| 1 | +import { useEffect, useState } from 'react' | |
| 2 | +import { inventory } from '@/api/client' | |
| 3 | +import type { Location } from '@/types/api' | |
| 4 | +import { PageHeader } from '@/components/PageHeader' | |
| 5 | +import { Loading } from '@/components/Loading' | |
| 6 | +import { ErrorBox } from '@/components/ErrorBox' | |
| 7 | +import { DataTable, type Column } from '@/components/DataTable' | |
| 8 | + | |
| 9 | +export function LocationsPage() { | |
| 10 | + const [rows, setRows] = useState<Location[]>([]) | |
| 11 | + const [error, setError] = useState<Error | null>(null) | |
| 12 | + const [loading, setLoading] = useState(true) | |
| 13 | + | |
| 14 | + useEffect(() => { | |
| 15 | + inventory | |
| 16 | + .listLocations() | |
| 17 | + .then(setRows) | |
| 18 | + .catch((e: unknown) => setError(e instanceof Error ? e : new Error(String(e)))) | |
| 19 | + .finally(() => setLoading(false)) | |
| 20 | + }, []) | |
| 21 | + | |
| 22 | + const columns: Column<Location>[] = [ | |
| 23 | + { header: 'Code', key: 'code', render: (r) => <span className="font-mono">{r.code}</span> }, | |
| 24 | + { header: 'Name', key: 'name' }, | |
| 25 | + { header: 'Type', key: 'type' }, | |
| 26 | + { | |
| 27 | + header: 'Active', | |
| 28 | + key: 'active', | |
| 29 | + render: (r) => (r.active ? <span className="text-emerald-600">●</span> : <span className="text-slate-300">●</span>), | |
| 30 | + }, | |
| 31 | + ] | |
| 32 | + | |
| 33 | + return ( | |
| 34 | + <div> | |
| 35 | + <PageHeader title="Locations" subtitle="Warehouses, bins, and virtual locations the inventory PBC tracks." /> | |
| 36 | + {loading && <Loading />} | |
| 37 | + {error && <ErrorBox error={error} />} | |
| 38 | + {!loading && !error && <DataTable rows={rows} columns={columns} />} | |
| 39 | + </div> | |
| 40 | + ) | |
| 41 | +} | ... | ... |
web/src/pages/LoginPage.tsx
0 → 100644
| 1 | +import { useEffect, useState, type FormEvent } from 'react' | |
| 2 | +import { useLocation, useNavigate } from 'react-router-dom' | |
| 3 | +import { useAuth } from '@/auth/AuthContext' | |
| 4 | +import { meta } from '@/api/client' | |
| 5 | +import type { MetaInfo } from '@/types/api' | |
| 6 | +import { ErrorBox } from '@/components/ErrorBox' | |
| 7 | + | |
| 8 | +interface LocationState { | |
| 9 | + from?: string | |
| 10 | +} | |
| 11 | + | |
| 12 | +export function LoginPage() { | |
| 13 | + const { login, token, loading } = useAuth() | |
| 14 | + const navigate = useNavigate() | |
| 15 | + const location = useLocation() | |
| 16 | + const [username, setUsername] = useState('admin') | |
| 17 | + const [password, setPassword] = useState('') | |
| 18 | + const [error, setError] = useState<Error | null>(null) | |
| 19 | + const [info, setInfo] = useState<MetaInfo | null>(null) | |
| 20 | + | |
| 21 | + useEffect(() => { | |
| 22 | + meta.info().then(setInfo).catch(() => setInfo(null)) | |
| 23 | + }, []) | |
| 24 | + | |
| 25 | + // If already logged in (e.g. nav back to /login), bounce to dashboard. | |
| 26 | + useEffect(() => { | |
| 27 | + if (token) { | |
| 28 | + const dest = (location.state as LocationState | null)?.from ?? '/' | |
| 29 | + navigate(dest, { replace: true }) | |
| 30 | + } | |
| 31 | + }, [token, navigate, location.state]) | |
| 32 | + | |
| 33 | + const onSubmit = async (e: FormEvent) => { | |
| 34 | + e.preventDefault() | |
| 35 | + setError(null) | |
| 36 | + try { | |
| 37 | + await login(username, password) | |
| 38 | + // Effect above handles the redirect; nothing else to do here. | |
| 39 | + } catch (err: unknown) { | |
| 40 | + setError(err instanceof Error ? err : new Error(String(err))) | |
| 41 | + } | |
| 42 | + } | |
| 43 | + | |
| 44 | + return ( | |
| 45 | + <div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-slate-100 to-brand-50 p-6"> | |
| 46 | + <div className="w-full max-w-md"> | |
| 47 | + <div className="mb-6 text-center"> | |
| 48 | + <h1 className="text-3xl font-bold text-brand-600">vibe_erp</h1> | |
| 49 | + <p className="mt-1 text-sm text-slate-500"> | |
| 50 | + Composable ERP framework for the printing industry | |
| 51 | + </p> | |
| 52 | + </div> | |
| 53 | + <div className="card p-6"> | |
| 54 | + <form onSubmit={onSubmit} className="space-y-4"> | |
| 55 | + <div> | |
| 56 | + <label className="block text-sm font-medium text-slate-700">Username</label> | |
| 57 | + <input | |
| 58 | + type="text" | |
| 59 | + value={username} | |
| 60 | + onChange={(e) => setUsername(e.target.value)} | |
| 61 | + className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2 text-sm shadow-sm focus:border-brand-500 focus:ring-brand-500" | |
| 62 | + required | |
| 63 | + autoFocus | |
| 64 | + /> | |
| 65 | + </div> | |
| 66 | + <div> | |
| 67 | + <label className="block text-sm font-medium text-slate-700">Password</label> | |
| 68 | + <input | |
| 69 | + type="password" | |
| 70 | + value={password} | |
| 71 | + onChange={(e) => setPassword(e.target.value)} | |
| 72 | + className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2 text-sm shadow-sm focus:border-brand-500 focus:ring-brand-500" | |
| 73 | + required | |
| 74 | + /> | |
| 75 | + <p className="mt-1 text-xs text-slate-400"> | |
| 76 | + The bootstrap admin password is printed to the application boot log on first start. | |
| 77 | + </p> | |
| 78 | + </div> | |
| 79 | + {error ? <ErrorBox error={error} /> : null} | |
| 80 | + <button type="submit" className="btn-primary w-full" disabled={loading}> | |
| 81 | + {loading ? 'Signing in…' : 'Sign in'} | |
| 82 | + </button> | |
| 83 | + </form> | |
| 84 | + </div> | |
| 85 | + <p className="mt-4 text-center text-xs text-slate-400"> | |
| 86 | + {info ? ( | |
| 87 | + <> | |
| 88 | + Connected to <span className="font-mono">{info.name}</span>{' '} | |
| 89 | + <span className="font-mono">{info.implementationVersion}</span> | |
| 90 | + </> | |
| 91 | + ) : ( | |
| 92 | + 'Connecting…' | |
| 93 | + )} | |
| 94 | + </p> | |
| 95 | + </div> | |
| 96 | + </div> | |
| 97 | + ) | |
| 98 | +} | ... | ... |
web/src/pages/MovementsPage.tsx
0 → 100644
| 1 | +import { useEffect, useState } from 'react' | |
| 2 | +import { inventory } from '@/api/client' | |
| 3 | +import type { Location, StockMovement } from '@/types/api' | |
| 4 | +import { PageHeader } from '@/components/PageHeader' | |
| 5 | +import { Loading } from '@/components/Loading' | |
| 6 | +import { ErrorBox } from '@/components/ErrorBox' | |
| 7 | +import { DataTable, type Column } from '@/components/DataTable' | |
| 8 | + | |
| 9 | +interface Row extends Record<string, unknown> { | |
| 10 | + id: string | |
| 11 | + occurredAt: string | |
| 12 | + itemCode: string | |
| 13 | + locationCode: string | |
| 14 | + delta: string | number | |
| 15 | + reason: string | |
| 16 | + reference: string | null | |
| 17 | +} | |
| 18 | + | |
| 19 | +export function MovementsPage() { | |
| 20 | + const [rows, setRows] = useState<Row[]>([]) | |
| 21 | + const [error, setError] = useState<Error | null>(null) | |
| 22 | + const [loading, setLoading] = useState(true) | |
| 23 | + | |
| 24 | + useEffect(() => { | |
| 25 | + Promise.all([inventory.listMovements(), inventory.listLocations()]) | |
| 26 | + .then(([moves, locs]: [StockMovement[], Location[]]) => { | |
| 27 | + const byId = new Map(locs.map((l) => [l.id, l.code])) | |
| 28 | + const sorted = [...moves].sort((a, b) => | |
| 29 | + (b.occurredAt ?? '').localeCompare(a.occurredAt ?? ''), | |
| 30 | + ) | |
| 31 | + setRows( | |
| 32 | + sorted.map((m) => ({ | |
| 33 | + id: m.id, | |
| 34 | + occurredAt: m.occurredAt, | |
| 35 | + itemCode: m.itemCode, | |
| 36 | + locationCode: byId.get(m.locationId) ?? m.locationId, | |
| 37 | + delta: m.delta, | |
| 38 | + reason: m.reason, | |
| 39 | + reference: m.reference, | |
| 40 | + })), | |
| 41 | + ) | |
| 42 | + }) | |
| 43 | + .catch((e: unknown) => setError(e instanceof Error ? e : new Error(String(e)))) | |
| 44 | + .finally(() => setLoading(false)) | |
| 45 | + }, []) | |
| 46 | + | |
| 47 | + const columns: Column<Row>[] = [ | |
| 48 | + { | |
| 49 | + header: 'Occurred', | |
| 50 | + key: 'occurredAt', | |
| 51 | + render: (r) => | |
| 52 | + r.occurredAt ? new Date(r.occurredAt).toLocaleString() : '—', | |
| 53 | + }, | |
| 54 | + { header: 'Item', key: 'itemCode', render: (r) => <span className="font-mono">{r.itemCode}</span> }, | |
| 55 | + { header: 'Location', key: 'locationCode', render: (r) => <span className="font-mono">{r.locationCode}</span> }, | |
| 56 | + { | |
| 57 | + header: 'Δ', | |
| 58 | + key: 'delta', | |
| 59 | + render: (r) => { | |
| 60 | + const n = Number(r.delta) | |
| 61 | + const cls = n >= 0 ? 'text-emerald-600' : 'text-rose-600' | |
| 62 | + return <span className={`font-mono tabular-nums ${cls}`}>{String(r.delta)}</span> | |
| 63 | + }, | |
| 64 | + }, | |
| 65 | + { header: 'Reason', key: 'reason' }, | |
| 66 | + { header: 'Reference', key: 'reference', render: (r) => r.reference ?? '—' }, | |
| 67 | + ] | |
| 68 | + | |
| 69 | + return ( | |
| 70 | + <div> | |
| 71 | + <PageHeader | |
| 72 | + title="Stock Movements" | |
| 73 | + subtitle="Append-only ledger of every change to inventory. Most-recent first." | |
| 74 | + /> | |
| 75 | + {loading && <Loading />} | |
| 76 | + {error && <ErrorBox error={error} />} | |
| 77 | + {!loading && !error && <DataTable rows={rows} columns={columns} />} | |
| 78 | + </div> | |
| 79 | + ) | |
| 80 | +} | ... | ... |
web/src/pages/PartnersPage.tsx
0 → 100644
| 1 | +import { useEffect, useState } from 'react' | |
| 2 | +import { partners } from '@/api/client' | |
| 3 | +import type { Partner } from '@/types/api' | |
| 4 | +import { PageHeader } from '@/components/PageHeader' | |
| 5 | +import { Loading } from '@/components/Loading' | |
| 6 | +import { ErrorBox } from '@/components/ErrorBox' | |
| 7 | +import { DataTable, type Column } from '@/components/DataTable' | |
| 8 | + | |
| 9 | +export function PartnersPage() { | |
| 10 | + const [rows, setRows] = useState<Partner[]>([]) | |
| 11 | + const [error, setError] = useState<Error | null>(null) | |
| 12 | + const [loading, setLoading] = useState(true) | |
| 13 | + | |
| 14 | + useEffect(() => { | |
| 15 | + partners | |
| 16 | + .list() | |
| 17 | + .then(setRows) | |
| 18 | + .catch((e: unknown) => setError(e instanceof Error ? e : new Error(String(e)))) | |
| 19 | + .finally(() => setLoading(false)) | |
| 20 | + }, []) | |
| 21 | + | |
| 22 | + const columns: Column<Partner>[] = [ | |
| 23 | + { header: 'Code', key: 'code', render: (r) => <span className="font-mono">{r.code}</span> }, | |
| 24 | + { header: 'Name', key: 'name' }, | |
| 25 | + { header: 'Type', key: 'type' }, | |
| 26 | + { header: 'Email', key: 'email', render: (r) => r.email ?? '—' }, | |
| 27 | + { header: 'Phone', key: 'phone', render: (r) => r.phone ?? '—' }, | |
| 28 | + { | |
| 29 | + header: 'Active', | |
| 30 | + key: 'active', | |
| 31 | + render: (r) => (r.active ? <span className="text-emerald-600">●</span> : <span className="text-slate-300">●</span>), | |
| 32 | + }, | |
| 33 | + ] | |
| 34 | + | |
| 35 | + return ( | |
| 36 | + <div> | |
| 37 | + <PageHeader | |
| 38 | + title="Partners" | |
| 39 | + subtitle="Customers, suppliers, and partners that play both roles. Seeded by the demo." | |
| 40 | + /> | |
| 41 | + {loading && <Loading />} | |
| 42 | + {error && <ErrorBox error={error} />} | |
| 43 | + {!loading && !error && <DataTable rows={rows} columns={columns} />} | |
| 44 | + </div> | |
| 45 | + ) | |
| 46 | +} | ... | ... |
-
mentioned in commit 87399862