@voltro/cli 0.33.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1968 -0
- package/bin/nodeEnvironment.d.mts +30 -0
- package/bin/nodeEnvironment.mjs +158 -0
- package/bin/voltro.mjs +69 -5
- package/dist/addCommand-BNeoeSxe.js +124 -0
- package/dist/addCommand-aXSQveak.js +2 -0
- package/dist/agentsMd-BTchIZku.js +2 -0
- package/dist/agentsMd-mhQMF1bx.js +254 -0
- package/dist/apiBuild-B8aoJvuw.js +2 -0
- package/dist/{apiBuild-h9VHtnlw.js → apiBuild-DYD_ONLD.js} +46 -46
- package/dist/{appGraph-gQ_6GkQQ.js → appGraph-KGDPTuTy.js} +1 -1
- package/dist/appGraph-zuMGKVYX.js +2 -0
- package/dist/appPort-B_HpJ_ck.js +48 -0
- package/dist/baselineCommand-C2ClWZN3.js +2 -0
- package/dist/baselineCommand-DIttzO8A.js +227 -0
- package/dist/bin.js +71 -28
- package/dist/build-CD8K4XOr.js +711 -0
- package/dist/cacheCommand-DA4OH9xt.js +42 -0
- package/dist/capabilitiesCommand-nq_pz5xd.js +123 -0
- package/dist/checkCommand-Ct9xkTrS.js +232 -0
- package/dist/checkCommand-DKpDLlqu.js +2 -0
- package/dist/{cliArgs-qdZSElM3.js → cliArgs-D4p8n7EE.js} +12 -1
- package/dist/cliError-BmdYnghb.js +10 -0
- package/dist/cliOutput-D1tSBoRM.js +15 -0
- package/dist/{cliRuntime-Oh517vCV.js → cliRuntime-Dh7UDinH.js} +20 -20
- package/dist/cloudClient-DWL-Hw_T.js +67 -0
- package/dist/cloudCmd-Cvv5HGaZ.js +364 -0
- package/dist/clusterCmd-D5wsCmA_.js +54 -0
- package/dist/codegen-CYM3Zqrf.js +605 -0
- package/dist/codegen-ChBi_hVa.js +2 -0
- package/dist/codegenCommand-C4YoQIc2.js +30 -0
- package/dist/codemodRunner-BnFq3Fgu.js +5384 -0
- package/dist/commandRunner-BLAEFLjp.js +47 -0
- package/dist/commands-BE8E7zF3.js +816 -0
- package/dist/connectionConfig-UFlIEiys.js +66 -0
- package/dist/dashboardCommand-D7SgZGaN.js +25 -0
- package/dist/dataCommand-BhYwDgg-.js +537 -0
- package/dist/dataProfile-dW-PsfLB.js +15 -0
- package/dist/dbCommand-DS4b97Is.js +2 -0
- package/dist/{dbCommand-DTLKAfbA.js → dbCommand-O8HA63s2.js} +552 -402
- package/dist/{dev-C_P8FLSx.js → dev-C7sFZq3m.js} +3670 -3169
- package/dist/dev-D2BikO7a.js +3 -0
- package/dist/devActivity-Dx_3nnGv.js +100 -0
- package/dist/devActivity.js +1 -1
- package/dist/dialectDriver-CgXnDfec.js +39 -0
- package/dist/discover-C9XKJDco.js +25 -0
- package/dist/doctorCommand-CM4Ch9C7.js +2 -0
- package/dist/{checkCommand-xGhRFFg2.js → doctorCommand-DnimF5IM.js} +523 -1278
- package/dist/dormancyCommand-QewYug_s.js +69 -0
- package/dist/e2eCmd-BRabZww-.js +147 -0
- package/dist/embeddingsCommand-BfiLS_QI.js +73 -0
- package/dist/envCommand-CCGPRQY1.js +60 -0
- package/dist/evalCommand-6RUfPen4.js +118 -0
- package/dist/evolveCommand-CHsLCtDf.js +281 -0
- package/dist/fileTaxonomy-CJfgOllU.js +457 -0
- package/dist/frameworkTableAssembly-YyVe32Cb.js +2 -0
- package/dist/frameworkTableAssembly-oMPBKqlE.js +511 -0
- package/dist/generateCommand-DbgcUpGw.js +147 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +4 -3
- package/dist/infoCommand-DwOgK1t6.js +60 -0
- package/dist/{inspect-BUUjt773.js → inspect-CjTYzAs_.js} +113 -41
- package/dist/inspect-P4pxoMaV.js +2 -0
- package/dist/inspectCmd-EHFZ9yYu.js +224 -0
- package/dist/inspectFetch-EMuhTG_9.js +151 -0
- package/dist/inspectMetrics-CfdKLh6t.js +72 -0
- package/dist/loadEnv-D9nEOClM.js +44 -0
- package/dist/logFileSink-C_D2wRN1.js +105 -0
- package/dist/logsCmd-D36xK7Zu.js +260 -0
- package/dist/manifestBuild-COkJoyAr.js +2 -0
- package/dist/{manifestBuild-BLrVuSlM.js → manifestBuild-hpPLaGxV.js} +1 -1
- package/dist/metaCommands-7MJfZ5cf.js +196 -0
- package/dist/migrate-BV7I-ZHZ.js +83 -0
- package/dist/mssqlClusterPatch-_4cE_nun.js +44 -0
- package/dist/newCommand-COWOJ1_E.js +156 -0
- package/dist/nodeEnvironment-cGFAj1J8.js +28 -0
- package/dist/packageCommand-Cug_3Ogl.js +271 -0
- package/dist/pageConvention-cEiRxdab.js +5 -0
- package/dist/privacyCommand-C-Df56U_.js +146 -0
- package/dist/probeCommand-CZfaaUOZ.js +122 -0
- package/dist/projectScaffold-DmzEKHib.js +2 -0
- package/dist/projectScaffold-LMMtaavR.js +814 -0
- package/dist/renderModeScan-D7J1B7Kw.js +105 -0
- package/dist/renderProfile-1OWWAAtx.js +81 -0
- package/dist/runtimeRegistry-DMeKfTHP.js +81 -0
- package/dist/runtimeTrace-CH3eUiMw.js +91 -0
- package/dist/scheduleCmd-DQRu6BZC.js +149 -0
- package/dist/scheduleManifestCmd-D2x0CTTY.js +249 -0
- package/dist/schemaIr-UJybUUZW.js +103 -0
- package/dist/{sdkgen-C81QIkiL.js → sdkgen-BLkvGRfX.js} +111 -209
- package/dist/seedRunner-ZmLSqNe2.js +333 -0
- package/dist/serveCommand-CbDHU6l-.js +2 -0
- package/dist/serveCommand-iwlUBNS1.js +1766 -0
- package/dist/serveEntry.js +5 -5
- package/dist/serverlessCommand-CfJZy6dS.js +482 -0
- package/dist/start-BgN62boB.js +3 -0
- package/dist/start-T4VesWiM.js +1087 -0
- package/dist/startEntry.js +2 -2
- package/dist/staticCommand-Dr2M6tpU.js +304 -0
- package/dist/storageCommand-Co6NfLqN.js +42 -0
- package/dist/templates-De8IR5-c.js +102 -0
- package/dist/test-CI6iDsYc.js +115 -0
- package/dist/tracesCmd-DStmCJPi.js +232 -0
- package/dist/tsconfigPaths-BWXBWgcl.js +107 -0
- package/dist/tsxLoader-EuXmSJ1K.js +51 -0
- package/dist/typecheckCommand-BlsWiCNq.js +61 -0
- package/dist/updateCommand-BlMZhWgO.js +2 -0
- package/dist/updateCommand-x0pI_x-B.js +585 -0
- package/dist/webDev-BcykISYQ2.js +2 -0
- package/dist/{inspectMetrics-1xzTKAFx.js → webDev-Dybxew86.js} +988 -1560
- package/dist/webhookDiscovery-CrGAfhIG.js +2 -0
- package/dist/webhookDiscovery-D7VaeMlz.js +51 -0
- package/dist/webhooksCommand-DlAgS2Iw.js +267 -0
- package/dist/workflowsCmd-BGF-mRZ5.js +608 -0
- package/package.json +209 -17
- package/templates/AGENTS.core.md +58 -3
- package/templates/AGENTS.md +64 -7
- package/templates/agent-docs/_index.md +6 -4
- package/templates/agent-docs/_manifest.json +23 -6
- package/templates/agent-docs/ai.md +370 -0
- package/templates/agent-docs/authentication.md +313 -31
- package/templates/agent-docs/caching.md +6 -0
- package/templates/agent-docs/cli.md +853 -50
- package/templates/agent-docs/data.md +608 -12
- package/templates/agent-docs/database/migrations.md +223 -25
- package/templates/agent-docs/database/misc.md +156 -40
- package/templates/agent-docs/database/querying.md +19 -1
- package/templates/agent-docs/database/scaling.md +60 -0
- package/templates/agent-docs/database/schema.md +1 -0
- package/templates/agent-docs/database/seedsdialects.md +208 -19
- package/templates/agent-docs/database/transactions.md +68 -0
- package/templates/agent-docs/deployment.md +284 -25
- package/templates/agent-docs/introduction.md +88 -17
- package/templates/agent-docs/local-first-mobile.md +79 -4
- package/templates/agent-docs/multi-tenancy.md +188 -42
- package/templates/agent-docs/observability.md +58 -3
- package/templates/agent-docs/plugins/ai-flows.md +247 -2
- package/templates/agent-docs/plugins/analytics-postgres.md +1 -1
- package/templates/agent-docs/plugins/audit.md +37 -1
- package/templates/agent-docs/plugins/auth-social.md +143 -0
- package/templates/agent-docs/plugins/auth-workos.md +4 -2
- package/templates/agent-docs/plugins/auth.md +131 -6
- package/templates/agent-docs/plugins/billing.md +132 -15
- package/templates/agent-docs/plugins/cdc-out.md +46 -7
- package/templates/agent-docs/plugins/clickhouse.md +32 -1
- package/templates/agent-docs/plugins/duckdb.md +1 -1
- package/templates/agent-docs/plugins/flags.md +132 -0
- package/templates/agent-docs/plugins/governance.md +105 -7
- package/templates/agent-docs/plugins/multitenancy.md +9 -4
- package/templates/agent-docs/plugins/presence.md +13 -2
- package/templates/agent-docs/plugins/ratelimit.md +9 -0
- package/templates/agent-docs/plugins/search.md +162 -8
- package/templates/agent-docs/plugins/sso-saml.md +47 -8
- package/templates/agent-docs/plugins/storage.md +11 -0
- package/templates/agent-docs/plugins/webhooks.md +105 -0
- package/templates/agent-docs/plugins.md +152 -18
- package/templates/agent-docs/reference.md +60 -3
- package/templates/agent-docs/releases.md +1117 -0
- package/templates/agent-docs/routing.md +43 -25
- package/templates/agent-docs/scheduling.md +27 -0
- package/templates/agent-docs/schema-driven-ui.md +92 -12
- package/templates/agent-docs/security.md +426 -0
- package/templates/agent-docs/templates/apibackends.md +87 -18
- package/templates/agent-docs/templates/appshells.md +32 -14
- package/templates/agent-docs/templates/overview.md +13 -8
- package/templates/agent-docs/testing.md +211 -14
- package/templates/agent-docs/whats-new.md +98 -136
- package/templates/agent-docs/workflows.md +231 -14
- package/templates/apps/api-ai/actions/summarize.action.ts +11 -0
- package/templates/apps/api-ai/package.json +8 -7
- package/templates/apps/api-auth/actions/me.action.ts +13 -0
- package/templates/apps/api-auth/package.json +9 -8
- package/templates/apps/api-backend/mutations/notes.create.mutation.ts +9 -0
- package/templates/apps/api-backend/package.json +12 -8
- package/templates/apps/api-backend/queries/notes.query.ts +30 -8
- package/templates/apps/api-backend-deactivation/actions/users.get.action.ts +14 -0
- package/templates/apps/api-backend-deactivation/mutations/users.create.mutation.ts +8 -0
- package/templates/apps/api-backend-deactivation/mutations/users.deactivate.mutation.ts +10 -0
- package/templates/apps/api-backend-deactivation/package.json +8 -7
- package/templates/apps/api-backend-mail/actions/sendWelcome.action.ts +17 -0
- package/templates/apps/api-backend-mail/mutations/notes.create.mutation.ts +9 -0
- package/templates/apps/api-backend-mail/package.json +9 -8
- package/templates/apps/api-backend-mail/queries/notes.query.ts +30 -8
- package/templates/apps/api-backend-mariadb/.env.example +14 -0
- package/templates/apps/api-backend-mariadb/mutations/notes.create.mutation.ts +9 -0
- package/templates/apps/api-backend-mariadb/package.json +10 -9
- package/templates/apps/api-backend-mariadb/queries/notes.query.ts +30 -8
- package/templates/apps/api-backend-sqlite/.env.example +14 -0
- package/templates/apps/api-backend-sqlite/mutations/notes.create.mutation.ts +9 -0
- package/templates/apps/api-backend-sqlite/package.json +9 -8
- package/templates/apps/api-backend-sqlite/queries/notes.query.ts +30 -8
- package/templates/apps/api-backend-storage/actions/uploadAvatar.action.ts +13 -0
- package/templates/apps/api-backend-storage/actions/uploadDocument.action.ts +12 -0
- package/templates/apps/api-backend-storage/mutations/notes.create.mutation.ts +9 -0
- package/templates/apps/api-backend-storage/package.json +9 -8
- package/templates/apps/api-backend-storage/queries/notes.query.ts +30 -8
- package/templates/apps/api-cms/actions/content.get.action.ts +7 -0
- package/templates/apps/api-cms/actions/content.types.action.ts +6 -0
- package/templates/apps/api-cms/actions/me.action.ts +13 -0
- package/templates/apps/api-cms/app.config.ts +19 -0
- package/templates/apps/api-cms/authz.ts +63 -0
- package/templates/apps/api-cms/mutations/content.publish.mutation.ts +10 -0
- package/templates/apps/api-cms/mutations/content.saveDraft.mutation.ts +10 -0
- package/templates/apps/api-cms/mutations/content.unpublish.mutation.ts +5 -0
- package/templates/apps/api-cms/package.json +11 -10
- package/templates/apps/api-cms/queries/content.list.query.ts +23 -7
- package/templates/apps/api-cms/tests/accessDecisions.test.ts +121 -0
- package/templates/apps/api-cms/tests/content.descriptors.test.ts +8 -4
- package/templates/apps/api-collab/mutations/documents.create.mutation.ts +8 -0
- package/templates/apps/api-collab/mutations/documents.setBody.mutation.ts +13 -0
- package/templates/apps/api-collab/package.json +9 -8
- package/templates/apps/api-collab/queries/documents.query.ts +19 -7
- package/templates/apps/api-data-advanced/package.json +9 -8
- package/templates/apps/api-data-advanced/queries/authors.withBooks.query.ts +12 -0
- package/templates/apps/api-data-advanced/queries/books.search.query.ts +9 -0
- package/templates/apps/api-durable/mutations/orders.approve.mutation.ts +17 -0
- package/templates/apps/api-durable/mutations/orders.place.mutation.ts +9 -0
- package/templates/apps/api-durable/package.json +9 -8
- package/templates/apps/api-feature-flags/actions/notes.export.action.ts +16 -0
- package/templates/apps/api-feature-flags/mutations/notes.create.mutation.ts +12 -0
- package/templates/apps/api-feature-flags/package.json +10 -9
- package/templates/apps/api-governance/README.md +30 -8
- package/templates/apps/api-governance/actions/profiles.get.action.server.ts +17 -1
- package/templates/apps/api-governance/actions/profiles.get.action.ts +29 -4
- package/templates/apps/api-governance/database/schema.ts +16 -4
- package/templates/apps/api-governance/mutations/profiles.create.mutation.ts +12 -0
- package/templates/apps/api-governance/package.json +9 -8
- package/templates/apps/api-kv/actions/sync.pull.action.ts +15 -0
- package/templates/apps/api-kv/actions/sync.reset.action.ts +13 -0
- package/templates/apps/api-kv/actions/sync.status.action.ts +7 -0
- package/templates/apps/api-kv/package.json +9 -8
- package/templates/apps/api-kv/queries/events.list.query.ts +19 -8
- package/templates/apps/api-moderation/mutations/comments.create.mutation.ts +11 -0
- package/templates/apps/api-moderation/mutations/posts.create.mutation.ts +13 -0
- package/templates/apps/api-moderation/package.json +9 -8
- package/templates/apps/api-observability/mutations/notes.create.mutation.ts +8 -0
- package/templates/apps/api-observability/package.json +9 -8
- package/templates/apps/api-observability/queries/notes.list.query.ts +13 -0
- package/templates/apps/api-ratelimit/mutations/notes.create.mutation.ts +14 -0
- package/templates/apps/api-ratelimit/package.json +9 -8
- package/templates/apps/api-rbac/package.json +9 -8
- package/templates/apps/api-rest/package.json +8 -7
- package/templates/apps/api-saas/mutations/projects.create.mutation.ts +13 -0
- package/templates/apps/api-saas/package.json +12 -11
- package/templates/apps/api-saas/queries/projects.list.query.ts +11 -0
- package/templates/apps/api-saas-starter/actions/me.action.ts +13 -0
- package/templates/apps/api-saas-starter/app.config.ts +19 -0
- package/templates/apps/api-saas-starter/authz.ts +75 -0
- package/templates/apps/api-saas-starter/mutations/invites.create.mutation.ts +10 -0
- package/templates/apps/api-saas-starter/mutations/projects.create.mutation.ts +9 -0
- package/templates/apps/api-saas-starter/package.json +15 -11
- package/templates/apps/api-saas-starter/queries/invites.list.query.ts +19 -8
- package/templates/apps/api-saas-starter/queries/projects.list.query.ts +18 -7
- package/templates/apps/api-saas-starter/tests/accessDecisions.test.ts +135 -0
- package/templates/apps/api-search/mutations/articles.create.mutation.ts +14 -0
- package/templates/apps/api-search/package.json +9 -8
- package/templates/apps/api-search/queries/articles.list.query.ts +21 -8
- package/templates/apps/api-status/README.md +10 -3
- package/templates/apps/api-status/app.config.ts +8 -3
- package/templates/apps/api-status/authz.ts +5 -3
- package/templates/apps/api-status/package.json +9 -8
- package/templates/apps/api-status/queries/components.list.query.ts +14 -6
- package/templates/apps/api-status/queries/incidents.live.query.ts +23 -10
- package/templates/apps/api-status/queries/updates.list.query.ts +16 -9
- package/templates/apps/api-status/tests/status.test.ts +9 -1
- package/templates/apps/api-versioning/actions/documents.asOf.action.ts +12 -0
- package/templates/apps/api-versioning/actions/documents.history.action.ts +13 -0
- package/templates/apps/api-versioning/mutations/documents.create.mutation.ts +9 -0
- package/templates/apps/api-versioning/mutations/documents.update.mutation.ts +12 -0
- package/templates/apps/api-versioning/package.json +9 -8
- package/templates/apps/api-webhooks/mutations/orders.fulfill.mutation.ts +16 -0
- package/templates/apps/api-webhooks/package.json +10 -9
- package/templates/apps/api-webhooks/queries/orders.list.query.ts +10 -0
- package/templates/apps/changelog/package.json +8 -6
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +10 -8
- package/templates/apps/frontend-admin/src/lib/admin.ts +20 -9
- package/templates/apps/frontend-admin/src/locales/de.ts +11 -1
- package/templates/apps/frontend-admin/src/locales/en.ts +13 -1
- package/templates/apps/frontend-admin/src/pages/admin/[entity]/page.tsx +65 -22
- package/templates/apps/frontend-admin/src/pages/admin/entity.test.tsx +130 -23
- package/templates/apps/frontend-admin/src/pages/admin/layout.tsx +3 -2
- package/templates/apps/frontend-admin/src/pages/admin/page.test.tsx +19 -2
- package/templates/apps/frontend-admin/src/pages/admin/page.tsx +9 -4
- package/templates/apps/frontend-app/app.config.ts +4 -3
- package/templates/apps/frontend-app/package.json +11 -8
- package/templates/apps/frontend-app/src/lib/api.ts +25 -0
- package/templates/apps/frontend-app/src/pages/page.test.tsx +130 -82
- package/templates/apps/frontend-app/src/pages/page.tsx +14 -18
- package/templates/apps/frontend-auth/package.json +10 -8
- package/templates/apps/frontend-blank/package.json +9 -7
- package/templates/apps/frontend-cms/package.json +11 -9
- package/templates/apps/frontend-collab/package.json +12 -9
- package/templates/apps/frontend-collab/src/pages/page.test.tsx +122 -78
- package/templates/apps/frontend-contact/package.json +9 -7
- package/templates/apps/frontend-dashboard/package.json +9 -7
- package/templates/apps/frontend-docs/package.json +9 -7
- package/templates/apps/frontend-i18n/package.json +8 -6
- package/templates/apps/frontend-landing/package.json +9 -7
- package/templates/apps/frontend-portal/package.json +10 -8
- package/templates/apps/frontend-saas/app.config.ts +10 -6
- package/templates/apps/frontend-saas/package.json +10 -8
- package/templates/apps/frontend-saas/src/lib/api.ts +27 -32
- package/templates/apps/frontend-saas/src/pages/dashboard/billing/page.tsx +7 -8
- package/templates/apps/frontend-saas/src/pages/dashboard/page.test.tsx +27 -3
- package/templates/apps/frontend-saas/src/pages/dashboard/page.tsx +4 -4
- package/templates/apps/frontend-saas/src/pages/dashboard/team/page.tsx +3 -4
- package/templates/apps/frontend-spa/package.json +9 -7
- package/templates/apps/frontend-ssr/package.json +9 -7
- package/templates/apps/frontend-ssr-api/package.json +10 -8
- package/templates/apps/frontend-static-blog/package.json +8 -6
- package/templates/apps/frontend-status/package.json +10 -8
- package/templates/apps/mobile-app/README.md +1 -0
- package/templates/apps/mobile-app/package.json +4 -2
- package/templates/apps/mobile-app/src/app/index.tsx +22 -12
- package/templates/apps/mobile-app/src/app/orders/[id].tsx +1 -1
- package/templates/apps/mobile-app/src/lib/api.ts +34 -0
- package/templates/apps/mobile-app/voltro.mobile.ts +4 -2
- package/templates/baselines/bare/.env.example +14 -0
- package/templates/baselines/bare/baseline.json +4 -4
- package/templates/baselines/compose/.env.example +14 -0
- package/templates/baselines/compose/README.md +1 -1
- package/templates/baselines/compose/baseline.json +5 -5
- package/templates/baselines/compose-mariadb/.env.example +14 -0
- package/templates/baselines/compose-mariadb/README.md +1 -1
- package/templates/baselines/compose-mariadb/baseline.json +5 -5
- package/templates/baselines/helm/.env.example +14 -0
- package/templates/baselines/helm/baseline.json +4 -4
- package/dist/apiBuild-C-x9YacA.js +0 -2
- package/dist/appGraph-CvQCte0z.js +0 -2
- package/dist/checkCommand-DRovTKza.js +0 -2
- package/dist/commands-CJfepbm4.js +0 -11541
- package/dist/dbCommand-b1gum4td.js +0 -2
- package/dist/dev-iiMtlkfs.js +0 -3
- package/dist/devActivity-BhIu6ncs.js +0 -159
- package/dist/frameworkTableAssembly-BwIrO5nv.js +0 -638
- package/dist/frameworkTableAssembly-D-EebUQX.js +0 -2
- package/dist/inspect-mmBuRXmy.js +0 -2
- package/dist/manifestBuild-Dj8Jjoto.js +0 -2
- package/dist/seedRunner-Bqxgp7HZ.js +0 -230
- package/dist/serveCommand-DdaM4Hup.js +0 -1608
- package/dist/start-C0koT0UO.js +0 -1084
- /package/templates/apps/api-ai/actions/{summarize.action.server.tsx → summarize.action.server.ts} +0 -0
|
@@ -1,720 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { r as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}, ue = (e) => {
|
|
17
|
-
for (let t = 0; t < e.length; t++) {
|
|
18
|
-
let n = e[t];
|
|
19
|
-
if (n === "--json" || n === "--format=json" || n === "--format" && e[t + 1] === "json") return !0;
|
|
20
|
-
}
|
|
21
|
-
return !1;
|
|
22
|
-
}, de = (e, t, n = !1) => {
|
|
23
|
-
let r = { ...e ?? {} }, i = t ?? process.env.VOLTRO_INSPECT_TOKEN;
|
|
24
|
-
if (i !== void 0 && i !== "" && (r.authorization = `Bearer ${i}`), n) {
|
|
25
|
-
let e = process.env.VOLTRO_INSPECT_WRITE_TOKEN;
|
|
26
|
-
e !== void 0 && e !== "" && (r["x-voltro-inspect-write"] = e);
|
|
27
|
-
}
|
|
28
|
-
return r;
|
|
29
|
-
}, fe = /* @__PURE__ */ new Set([
|
|
30
|
-
"POST",
|
|
31
|
-
"PUT",
|
|
32
|
-
"PATCH",
|
|
33
|
-
"DELETE"
|
|
34
|
-
]), N = async (e, t, n) => {
|
|
35
|
-
let r = `${e.url.replace(/\/+$/, "")}${t.startsWith("/") ? t : `/${t}`}`, i = n?.method ?? "GET", a = n?.body !== void 0;
|
|
36
|
-
try {
|
|
37
|
-
let t = await fetch(r, {
|
|
38
|
-
method: i,
|
|
39
|
-
headers: de(a ? { "content-type": "application/json" } : void 0, e.inspectToken, fe.has(i)),
|
|
40
|
-
...a ? { body: JSON.stringify(n.body) } : {}
|
|
41
|
-
}), o = await t.text(), s;
|
|
42
|
-
if (o.length > 0) try {
|
|
43
|
-
s = JSON.parse(o);
|
|
44
|
-
} catch {
|
|
45
|
-
s = o;
|
|
46
|
-
}
|
|
47
|
-
if (!t.ok) {
|
|
48
|
-
let n = s, r = typeof n == "object" && n ? String(n.message ?? n.reason ?? n.error ?? `HTTP ${t.status}`) : `HTTP ${t.status}`, a = fe.has(i) && (process.env.VOLTRO_INSPECT_WRITE_TOKEN ?? "") === "" && /write/i.test(r);
|
|
49
|
-
return {
|
|
50
|
-
kind: "err",
|
|
51
|
-
name: e.name,
|
|
52
|
-
error: a ? `${r}\n VOLTRO_INSPECT_WRITE_TOKEN is not set in this shell. \`voltro dev\` mints it into the project's \`.env.local\`; export it (or run from a shell that loaded it).` : r
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
return {
|
|
56
|
-
kind: "ok",
|
|
57
|
-
name: e.name,
|
|
58
|
-
status: t.status,
|
|
59
|
-
data: s
|
|
60
|
-
};
|
|
61
|
-
} catch (t) {
|
|
62
|
-
return {
|
|
63
|
-
kind: "err",
|
|
64
|
-
name: e.name,
|
|
65
|
-
error: t.message || String(t)
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
}, P = (e, t = process.env) => {
|
|
69
|
-
let n = [], r, i;
|
|
70
|
-
for (let t = 0; t < e.length; t++) {
|
|
71
|
-
let a = e[t];
|
|
72
|
-
if (a === "--url") {
|
|
73
|
-
r = e[++t];
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
if (a.startsWith("--url=")) {
|
|
77
|
-
r = a.slice(6);
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
if (a === "--token") {
|
|
81
|
-
i = e[++t];
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
if (a.startsWith("--token=")) {
|
|
85
|
-
i = a.slice(8);
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
n.push(a);
|
|
89
|
-
}
|
|
90
|
-
if (r ??= t.VOLTRO_INSPECT_URL, r === void 0 || r === "") return {
|
|
91
|
-
target: void 0,
|
|
92
|
-
rest: n
|
|
93
|
-
};
|
|
94
|
-
let a;
|
|
95
|
-
try {
|
|
96
|
-
a = new URL(r);
|
|
97
|
-
} catch {
|
|
98
|
-
return {
|
|
99
|
-
target: void 0,
|
|
100
|
-
rest: n,
|
|
101
|
-
error: `--url: not a valid URL: "${r}"`
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
let o = i ?? t.VOLTRO_INSPECT_TOKEN;
|
|
105
|
-
return {
|
|
106
|
-
target: {
|
|
107
|
-
url: r.replace(/\/+$/, ""),
|
|
108
|
-
name: a.host,
|
|
109
|
-
...o !== void 0 && o !== "" ? { token: o } : {}
|
|
110
|
-
},
|
|
111
|
-
rest: n
|
|
112
|
-
};
|
|
113
|
-
}, pe = (e) => ({
|
|
114
|
-
id: `remote:${e.url}`,
|
|
115
|
-
name: e.name,
|
|
116
|
-
kind: "api",
|
|
117
|
-
url: e.url,
|
|
118
|
-
pid: 0,
|
|
119
|
-
registeredAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
120
|
-
heartbeatAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
121
|
-
status: "live",
|
|
122
|
-
logFile: "",
|
|
123
|
-
...e.token === void 0 ? {} : { inspectToken: e.token }
|
|
124
|
-
}), me = async (t, n) => n === void 0 ? (await e()).apps.filter((e) => e.kind === "api" && e.status === "live" && e.url.length > 0 && (t === void 0 || e.name === t)) : [pe(n)], he = (e) => {
|
|
125
|
-
let t = "pretty", n = [];
|
|
126
|
-
for (let r = 0; r < e.length; r++) {
|
|
127
|
-
let i = e[r];
|
|
128
|
-
if (i === "--format") {
|
|
129
|
-
let n = e[++r];
|
|
130
|
-
if (n !== "pretty" && n !== "json") return { error: "--format: must be 'pretty' | 'json'" };
|
|
131
|
-
t = n;
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
if (i === "--json" || i === "--format=json") {
|
|
135
|
-
t = "json";
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
if (i === "--format=pretty") {
|
|
139
|
-
t = "pretty";
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
n.push(i);
|
|
143
|
-
}
|
|
144
|
-
return {
|
|
145
|
-
format: t,
|
|
146
|
-
rest: n
|
|
147
|
-
};
|
|
148
|
-
}, ge = (e, t = process.env) => {
|
|
149
|
-
let { target: n, rest: r, error: i } = P(e, t), a, o = [];
|
|
150
|
-
for (let e = 0; e < r.length; e++) {
|
|
151
|
-
let t = r[e];
|
|
152
|
-
if (t === "--process") {
|
|
153
|
-
a = r[++e];
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
if (t.startsWith("--process=")) {
|
|
157
|
-
a = t.slice(10);
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
o.push(t);
|
|
161
|
-
}
|
|
162
|
-
return {
|
|
163
|
-
process: a,
|
|
164
|
-
target: n,
|
|
165
|
-
rest: o,
|
|
166
|
-
...i === void 0 ? {} : { error: i }
|
|
167
|
-
};
|
|
168
|
-
}, _e = (e) => {
|
|
169
|
-
let t = "", n = !1;
|
|
170
|
-
for (let r = 0; r < e.length; r++) {
|
|
171
|
-
let i = e[r];
|
|
172
|
-
if (n) {
|
|
173
|
-
if (t += i, i === "\\") {
|
|
174
|
-
t += e[r + 1] ?? "", r++;
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
i === "\"" && (n = !1);
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
if (i === "\"") {
|
|
181
|
-
n = !0, t += i;
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
if (i === "/" && e[r + 1] === "*") {
|
|
185
|
-
let t = e.indexOf("*/", r + 2);
|
|
186
|
-
r = t === -1 ? e.length : t + 1;
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
if (i === "/" && e[r + 1] === "/") {
|
|
190
|
-
let t = e.indexOf("\n", r);
|
|
191
|
-
r = t === -1 ? e.length : t - 1;
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
t += i;
|
|
195
|
-
}
|
|
196
|
-
return JSON.parse(ve(t));
|
|
197
|
-
}, ve = (e) => {
|
|
198
|
-
let t = "", n = !1;
|
|
199
|
-
for (let r = 0; r < e.length; r++) {
|
|
200
|
-
let i = e[r];
|
|
201
|
-
if (n) {
|
|
202
|
-
if (t += i, i === "\\") {
|
|
203
|
-
t += e[r + 1] ?? "", r++;
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
i === "\"" && (n = !1);
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
if (i === "\"") {
|
|
210
|
-
n = !0, t += i;
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
if (i === ",") {
|
|
214
|
-
let t = e.slice(r + 1);
|
|
215
|
-
if (/^\s*[}\]]/.exec(t)) continue;
|
|
216
|
-
}
|
|
217
|
-
t += i;
|
|
218
|
-
}
|
|
219
|
-
return t;
|
|
220
|
-
}, F = (e, t) => {
|
|
221
|
-
if (t.has(e) || !T(e)) return;
|
|
222
|
-
t.add(e);
|
|
223
|
-
let n;
|
|
224
|
-
try {
|
|
225
|
-
n = _e(E(e, "utf8"));
|
|
226
|
-
} catch {
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
if (n.extends === void 0) return n;
|
|
230
|
-
let r = n.extends;
|
|
231
|
-
if (!r.startsWith(".")) return n;
|
|
232
|
-
let i = F(C(b(e), r), t);
|
|
233
|
-
return i === void 0 ? n : { compilerOptions: {
|
|
234
|
-
...i.compilerOptions,
|
|
235
|
-
...n.compilerOptions,
|
|
236
|
-
paths: {
|
|
237
|
-
...i.compilerOptions?.paths,
|
|
238
|
-
...n.compilerOptions?.paths
|
|
239
|
-
}
|
|
240
|
-
} };
|
|
241
|
-
}, ye = (e, t = "tsconfig.json") => {
|
|
242
|
-
let n = ae(t) ? t : C(e, t), r = F(n, /* @__PURE__ */ new Set()), i = r?.compilerOptions?.paths;
|
|
243
|
-
if (i === void 0) return;
|
|
244
|
-
let a = {};
|
|
245
|
-
for (let [e, t] of Object.entries(i)) Array.isArray(t) && (a[e] = t.filter((e) => typeof e == "string"));
|
|
246
|
-
if (Object.keys(a).length !== 0) return {
|
|
247
|
-
baseUrl: C(b(n), r?.compilerOptions?.baseUrl ?? "."),
|
|
248
|
-
paths: a
|
|
249
|
-
};
|
|
250
|
-
}, I = (e, t = "tsconfig.json") => {
|
|
251
|
-
let n = ae(t) ? t : C(e, t), r = F(n, /* @__PURE__ */ new Set()), i = r?.compilerOptions?.paths;
|
|
252
|
-
if (i === void 0) return [];
|
|
253
|
-
let a = C(b(n), r?.compilerOptions?.baseUrl ?? "."), o = [];
|
|
254
|
-
for (let [e, t] of Object.entries(i)) {
|
|
255
|
-
let n = t[0];
|
|
256
|
-
if (typeof n != "string") continue;
|
|
257
|
-
let r = e.endsWith("*");
|
|
258
|
-
o.push({
|
|
259
|
-
prefix: r ? e.slice(0, -1) : e,
|
|
260
|
-
target: C(a, r ? n.replace(/\*$/, "") : n),
|
|
261
|
-
wildcard: r
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
return o.sort((e, t) => t.prefix.length - e.prefix.length);
|
|
265
|
-
}, be = (e, t) => {
|
|
266
|
-
for (let n of t) if (n.wildcard) {
|
|
267
|
-
if (e.startsWith(n.prefix)) return x(n.target, e.slice(n.prefix.length));
|
|
268
|
-
} else if (e === n.prefix) return n.target;
|
|
269
|
-
}, L = ".voltro-vendored", xe = (e, t) => {
|
|
270
|
-
let n = [], r = /* @__PURE__ */ new Set(), i = (e, t) => {
|
|
271
|
-
let i = C(e);
|
|
272
|
-
r.has(i) || !T(i) || (r.add(i), n.push({
|
|
273
|
-
dir: i,
|
|
274
|
-
source: t
|
|
275
|
-
}));
|
|
276
|
-
}, a = x(e, "components.json");
|
|
277
|
-
if (T(a)) try {
|
|
278
|
-
let t = JSON.parse(E(a, "utf8"));
|
|
279
|
-
for (let n of ["ui"]) {
|
|
280
|
-
let r = t.aliases?.[n];
|
|
281
|
-
if (typeof r != "string" || r === "") continue;
|
|
282
|
-
let a = r.replace(/^@\//, "").replace(/^~\//, "");
|
|
283
|
-
for (let t of [x(e, a), x(e, "src", a)]) if (T(t)) {
|
|
284
|
-
i(t, `components.json (aliases.${n})`);
|
|
285
|
-
break;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
} catch {}
|
|
289
|
-
for (let e of t) {
|
|
290
|
-
let t = e.slice(0, e.lastIndexOf(w)), n = x(t, L);
|
|
291
|
-
if (!r.has(C(t)) && T(n)) {
|
|
292
|
-
let e = "";
|
|
293
|
-
try {
|
|
294
|
-
e = E(n, "utf8").trim().split("\n")[0] ?? "";
|
|
295
|
-
} catch {}
|
|
296
|
-
i(t, e === "" ? L : `${L}: ${e}`);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return n;
|
|
300
|
-
}, Se = (e, t) => t.some((t) => e === t.dir || e.startsWith(`${t.dir}${w}`)), Ce = (e, t) => t.map((t) => `${S(e, t.dir) || "."} (${t.source})`), we = [
|
|
301
|
-
{
|
|
302
|
-
id: "component.ui",
|
|
303
|
-
re: _,
|
|
304
|
-
needsTest: !0
|
|
305
|
-
},
|
|
306
|
-
{
|
|
307
|
-
id: "component",
|
|
308
|
-
re: ee,
|
|
309
|
-
needsTest: !0
|
|
310
|
-
},
|
|
311
|
-
{
|
|
312
|
-
id: "hook",
|
|
313
|
-
re: te,
|
|
314
|
-
needsTest: !0
|
|
315
|
-
},
|
|
316
|
-
{
|
|
317
|
-
id: "tracking",
|
|
318
|
-
re: f,
|
|
319
|
-
needsTest: !0
|
|
320
|
-
},
|
|
321
|
-
{
|
|
322
|
-
id: "store",
|
|
323
|
-
re: d,
|
|
324
|
-
needsTest: !0
|
|
325
|
-
},
|
|
326
|
-
{
|
|
327
|
-
id: "internal",
|
|
328
|
-
re: h,
|
|
329
|
-
needsTest: !0
|
|
330
|
-
},
|
|
331
|
-
{
|
|
332
|
-
id: "fixture",
|
|
333
|
-
re: v,
|
|
334
|
-
needsTest: !1
|
|
335
|
-
},
|
|
336
|
-
{
|
|
337
|
-
id: "types",
|
|
338
|
-
re: p,
|
|
339
|
-
needsTest: !1
|
|
340
|
-
}
|
|
341
|
-
], R = (e) => we.find((t) => t.re.test(e))?.id ?? null, Te = (e) => m.test(e), Ee = /* @__PURE__ */ new Set([
|
|
342
|
-
"useMutation",
|
|
343
|
-
"useAction",
|
|
344
|
-
"useUpload",
|
|
345
|
-
"useUndo",
|
|
346
|
-
"useOutbox",
|
|
347
|
-
"useAgentChat",
|
|
348
|
-
"useAgentStream",
|
|
349
|
-
"useResumableAgentStream",
|
|
350
|
-
"useWorkflow"
|
|
351
|
-
]), De = /* @__PURE__ */ new Set(["defineTracking"]), Oe = /* @__PURE__ */ new Set([
|
|
352
|
-
"useSubscription",
|
|
353
|
-
"useWindowedSubscription",
|
|
354
|
-
"useRecord",
|
|
355
|
-
"useQuery"
|
|
356
|
-
]), ke = /* @__PURE__ */ new Set([
|
|
357
|
-
"node_modules",
|
|
358
|
-
"dist",
|
|
359
|
-
"build",
|
|
360
|
-
".framework",
|
|
361
|
-
".turbo",
|
|
362
|
-
".vite",
|
|
363
|
-
".next",
|
|
364
|
-
".git",
|
|
365
|
-
"coverage",
|
|
366
|
-
".publish"
|
|
367
|
-
]), Ae = /\.tsx?$/, je = (e, t = [], n = 0) => {
|
|
368
|
-
if (n > 12) return t;
|
|
369
|
-
let r;
|
|
370
|
-
try {
|
|
371
|
-
r = D(e);
|
|
372
|
-
} catch {
|
|
373
|
-
return t;
|
|
374
|
-
}
|
|
375
|
-
for (let i of r) {
|
|
376
|
-
if (ke.has(i)) continue;
|
|
377
|
-
let r = x(e, i), a;
|
|
378
|
-
try {
|
|
379
|
-
a = O(r);
|
|
380
|
-
} catch {
|
|
381
|
-
continue;
|
|
382
|
-
}
|
|
383
|
-
a.isDirectory() ? je(r, t, n + 1) : a.isFile() && Ae.test(i) && !i.endsWith(".d.ts") && t.push(r);
|
|
384
|
-
}
|
|
385
|
-
return t;
|
|
386
|
-
}, Me = /\b(?:FC|FunctionComponent|ComponentType|ForwardRefExoticComponent|MemoExoticComponent)\b/, Ne = /\b(?:memo|forwardRef|observer|styled)\b/, Pe = (e) => k.isArrowFunction(e) || k.isFunctionExpression(e) || k.isFunctionDeclaration(e) || k.isClassDeclaration(e) || k.isClassExpression(e), z = (e) => {
|
|
387
|
-
if (Pe(e)) return !0;
|
|
388
|
-
if (k.isExportAssignment(e) || k.isAsExpression(e) || k.isSatisfiesExpression(e) || k.isParenthesizedExpression(e)) return z(e.getExpression());
|
|
389
|
-
if (k.isVariableDeclaration(e)) {
|
|
390
|
-
let t = e.getTypeNode();
|
|
391
|
-
if (t !== void 0 && Me.test(t.getText())) return !0;
|
|
392
|
-
let n = e.getInitializer();
|
|
393
|
-
return n === void 0 || z(n);
|
|
394
|
-
}
|
|
395
|
-
return k.isCallExpression(e) ? Ne.test(e.getExpression().getText()) ? !0 : e.getArguments().some((e) => Pe(e) || k.isIdentifier(e)) : k.isTaggedTemplateExpression(e) ? !0 : !(k.isObjectLiteralExpression(e) || k.isArrayLiteralExpression(e) || k.isStringLiteral(e) || k.isNoSubstitutionTemplateLiteral(e) || k.isNumericLiteral(e) || k.isTemplateExpression(e) || k.isNewExpression(e) || k.isPropertyAccessExpression(e) || e.getKind() === j.TrueKeyword || e.getKind() === j.FalseKeyword || e.getKind() === j.NullKeyword);
|
|
396
|
-
}, Fe = (e) => {
|
|
397
|
-
let t = [], n = [], r = [], i = /* @__PURE__ */ new Set();
|
|
398
|
-
for (let [a, o] of e.getExportedDeclarations()) {
|
|
399
|
-
let e = o[0];
|
|
400
|
-
if (e === void 0 || k.isInterfaceDeclaration(e) || k.isTypeAliasDeclaration(e) || i.has(e)) continue;
|
|
401
|
-
i.add(e);
|
|
402
|
-
let s = a === "default" ? (k.isFunctionDeclaration(e) || k.isClassDeclaration(e) || k.isVariableDeclaration(e) ? e.getName() : void 0) ?? "Default" : a;
|
|
403
|
-
if (/^use[A-Z]/.test(s)) {
|
|
404
|
-
n.push(s);
|
|
405
|
-
continue;
|
|
406
|
-
}
|
|
407
|
-
if (/^[A-Z]/.test(s) && z(e)) {
|
|
408
|
-
t.push(s);
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
r.push(s);
|
|
412
|
-
}
|
|
413
|
-
return {
|
|
414
|
-
components: t,
|
|
415
|
-
hooks: n,
|
|
416
|
-
others: r,
|
|
417
|
-
hasRuntimeExport: t.length + n.length + r.length > 0
|
|
418
|
-
};
|
|
419
|
-
}, B = [], Ie = (e, t, n) => {
|
|
420
|
-
let r = b(e), i = [];
|
|
421
|
-
for (let e = r;; e = b(e)) {
|
|
422
|
-
let r = n.get(e);
|
|
423
|
-
if (r !== void 0) {
|
|
424
|
-
for (let e of i) n.set(e, r);
|
|
425
|
-
return r;
|
|
426
|
-
}
|
|
427
|
-
if (i.push(e), T(x(e, "tsconfig.json"))) {
|
|
428
|
-
let t = I(e);
|
|
429
|
-
if (t.length > 0) {
|
|
430
|
-
for (let e of i) n.set(e, t);
|
|
431
|
-
return t;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
if (e === t || b(e) === e) break;
|
|
435
|
-
}
|
|
436
|
-
for (let e of i) n.set(e, B);
|
|
437
|
-
return B;
|
|
438
|
-
}, Le = (e, t, n) => {
|
|
439
|
-
let r = t.startsWith(".") ? C(b(e), t) : be(t, n);
|
|
440
|
-
if (r === void 0) return null;
|
|
441
|
-
for (let e of [
|
|
442
|
-
r,
|
|
443
|
-
`${r}.tsx`,
|
|
444
|
-
`${r}.ts`,
|
|
445
|
-
x(r, "index.tsx"),
|
|
446
|
-
x(r, "index.ts")
|
|
447
|
-
]) try {
|
|
448
|
-
if (O(e).isFile()) return e;
|
|
449
|
-
} catch {}
|
|
450
|
-
return null;
|
|
451
|
-
}, Re = (e, t, n) => {
|
|
452
|
-
let r = [], i = /* @__PURE__ */ new Set(), a = /* @__PURE__ */ new Map(), o = (e) => {
|
|
453
|
-
let i = Le(t, e, n);
|
|
454
|
-
return i !== null && r.push(i), i;
|
|
455
|
-
};
|
|
456
|
-
for (let t of e.getImportDeclarations()) if (o(t.getModuleSpecifierValue()), !t.isTypeOnly()) for (let e of t.getNamedImports()) e.isTypeOnly() || i.add(e.getName());
|
|
457
|
-
for (let t of e.getExportDeclarations()) {
|
|
458
|
-
let e = t.getModuleSpecifierValue();
|
|
459
|
-
if (e === void 0) continue;
|
|
460
|
-
let n = o(e);
|
|
461
|
-
if (n === null || t.isTypeOnly()) continue;
|
|
462
|
-
let r = t.getNamedExports();
|
|
463
|
-
if (r.length === 0) {
|
|
464
|
-
a.set(n, "*");
|
|
465
|
-
continue;
|
|
466
|
-
}
|
|
467
|
-
let i = r.filter((e) => !e.isTypeOnly()).map((e) => e.getAliasNode()?.getText() ?? e.getName()), s = a.get(n);
|
|
468
|
-
a.set(n, s === "*" ? "*" : [...s ?? [], ...i]);
|
|
469
|
-
}
|
|
470
|
-
for (let t of e.getDescendantsOfKind(j.CallExpression)) {
|
|
471
|
-
if (t.getExpression().getKind() !== j.ImportKeyword) continue;
|
|
472
|
-
let e = t.getArguments()[0];
|
|
473
|
-
e !== void 0 && k.isStringLiteral(e) && o(e.getLiteralValue());
|
|
474
|
-
}
|
|
475
|
-
return {
|
|
476
|
-
localTargets: r,
|
|
477
|
-
namedBindings: i,
|
|
478
|
-
reExports: a
|
|
479
|
-
};
|
|
480
|
-
}, ze = (e) => {
|
|
481
|
-
let t = /* @__PURE__ */ new Map();
|
|
482
|
-
for (let n of e.getDescendantsOfKind(j.CallExpression)) {
|
|
483
|
-
let e = n.getExpression(), r = k.isPropertyAccessExpression(e) ? e.getName() : e.getText();
|
|
484
|
-
t.set(r, (t.get(r) ?? 0) + 1);
|
|
485
|
-
}
|
|
486
|
-
return t;
|
|
487
|
-
}, Be = (e) => {
|
|
488
|
-
if (e.length <= 1) return e.length === 1;
|
|
489
|
-
let t = [...e].sort((e, t) => e.length - t.length)[0];
|
|
490
|
-
return e.every((e) => e.startsWith(t));
|
|
491
|
-
}, Ve = (e, t) => {
|
|
492
|
-
let n = b(t);
|
|
493
|
-
return e.startsWith(`${n}${w}`) || b(e) === n;
|
|
494
|
-
}, V = (e) => {
|
|
495
|
-
let t = je(e);
|
|
496
|
-
if (t.length === 0) return {
|
|
497
|
-
findings: [],
|
|
498
|
-
declared: 0,
|
|
499
|
-
scanned: 0,
|
|
500
|
-
vendored: []
|
|
501
|
-
};
|
|
502
|
-
let n = xe(e, t), r = t.filter((e) => !Se(e, n));
|
|
503
|
-
if (r.length === 0) return {
|
|
504
|
-
findings: [],
|
|
505
|
-
declared: 0,
|
|
506
|
-
scanned: 0,
|
|
507
|
-
vendored: n
|
|
508
|
-
};
|
|
509
|
-
let i = new A({
|
|
510
|
-
useInMemoryFileSystem: !0,
|
|
511
|
-
skipAddingFilesFromTsConfig: !0
|
|
512
|
-
}), a = [], o = (t) => S(e, t), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
|
|
513
|
-
for (let t of r) {
|
|
514
|
-
let n;
|
|
515
|
-
try {
|
|
516
|
-
n = E(t, "utf8");
|
|
517
|
-
} catch {
|
|
518
|
-
continue;
|
|
519
|
-
}
|
|
520
|
-
let r;
|
|
521
|
-
try {
|
|
522
|
-
r = i.createSourceFile(t.endsWith("x") ? "/scan.tsx" : "/scan.ts", n, { overwrite: !0 });
|
|
523
|
-
} catch {
|
|
524
|
-
continue;
|
|
525
|
-
}
|
|
526
|
-
s.set(t, Fe(r)), c.set(t, Re(r, t, Ie(t, e, u))), l.set(t, ze(r));
|
|
527
|
-
}
|
|
528
|
-
let d = /* @__PURE__ */ new Map();
|
|
529
|
-
for (let [e, t] of c) for (let n of t.localTargets) {
|
|
530
|
-
let t = d.get(n) ?? [];
|
|
531
|
-
t.push(e), d.set(n, t);
|
|
532
|
-
}
|
|
533
|
-
let f = (e) => {
|
|
534
|
-
let t = s.get(e), n = /* @__PURE__ */ new Set([
|
|
535
|
-
...t?.components ?? [],
|
|
536
|
-
...t?.hooks ?? [],
|
|
537
|
-
...t?.others ?? []
|
|
538
|
-
]), r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set([e]), a = (e, t) => {
|
|
539
|
-
for (let o of d.get(e) ?? []) {
|
|
540
|
-
if (i.has(o)) continue;
|
|
541
|
-
let s = c.get(o);
|
|
542
|
-
if (s === void 0) continue;
|
|
543
|
-
let l = s.reExports.get(e);
|
|
544
|
-
if (l !== void 0) {
|
|
545
|
-
i.add(o), r.add(o);
|
|
546
|
-
let e = l === "*" ? t ?? n : new Set(l), s = t === null ? e : new Set([...e].filter((e) => t.has(e)));
|
|
547
|
-
s.size > 0 && a(o, s);
|
|
548
|
-
continue;
|
|
549
|
-
}
|
|
550
|
-
(t === null || [...s.namedBindings].some((e) => t.has(e))) && r.add(o);
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
return a(e, null), [...r];
|
|
554
|
-
}, p = 0;
|
|
555
|
-
for (let e of r) {
|
|
556
|
-
let t = R(e), n = s.get(e), r = c.get(e);
|
|
557
|
-
if (n === void 0 || r === void 0) continue;
|
|
558
|
-
let i = Te(e);
|
|
559
|
-
t !== null && !i && (p += 1);
|
|
560
|
-
let u = d.get(e) ?? [];
|
|
561
|
-
if ((t === "component" || t === "component.ui") && !i) {
|
|
562
|
-
let t = Be(n.components);
|
|
563
|
-
(n.components.length === 0 || !t) && a.push({
|
|
564
|
-
rule: "component/one-per-file",
|
|
565
|
-
file: o(e),
|
|
566
|
-
severity: "error",
|
|
567
|
-
detail: n.components.length === 0 ? "declares a component file but exports no component" : `exports ${n.components.length} unrelated components: ${n.components.join(", ")}`,
|
|
568
|
-
fix: "a component file exports ONE component — or one NAMESPACED family sharing its name (`Card`, `CardHeader`, `CardTitle`), which is one component with parts. Types alongside are fine. Unrelated components belong in their own files"
|
|
569
|
-
}), n.hooks.length > 0 && a.push({
|
|
570
|
-
rule: "component/no-hook-export",
|
|
571
|
-
file: o(e),
|
|
572
|
-
severity: "error",
|
|
573
|
-
detail: `also exports hook(s): ${n.hooks.join(", ")}`,
|
|
574
|
-
fix: "move the hook into its own *.hook.ts — a file exporting both is two files wearing one name, and neither can be reused without dragging the other"
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
if (t === "hook" && !i && (n.hooks.length !== 1 && a.push({
|
|
578
|
-
rule: "hook/one-per-file",
|
|
579
|
-
file: o(e),
|
|
580
|
-
severity: "error",
|
|
581
|
-
detail: n.hooks.length === 0 ? "declares a hook file but exports no use* hook" : `exports ${n.hooks.length} hooks: ${n.hooks.join(", ")}`,
|
|
582
|
-
fix: "a hook file exports exactly ONE use* hook (types alongside are fine)"
|
|
583
|
-
}), n.components.length > 0 && a.push({
|
|
584
|
-
rule: "hook/no-component-export",
|
|
585
|
-
file: o(e),
|
|
586
|
-
severity: "error",
|
|
587
|
-
detail: `also exports component(s): ${n.components.join(", ")}`,
|
|
588
|
-
fix: "move the component into its own *.component.tsx"
|
|
589
|
-
})), t === "types" && n.hasRuntimeExport && a.push({
|
|
590
|
-
rule: "types/runtime-export",
|
|
591
|
-
file: o(e),
|
|
592
|
-
severity: "error",
|
|
593
|
-
detail: `exports runtime value(s): ${[
|
|
594
|
-
...n.components,
|
|
595
|
-
...n.hooks,
|
|
596
|
-
...n.others
|
|
597
|
-
].join(", ")}`,
|
|
598
|
-
fix: "a *.types.ts has NO runtime export. That is what makes importing it provably free AND makes it impossible for it to sit in a runtime import cycle — the guarantee is the point, so move the value to a sibling module"
|
|
599
|
-
}), t === "internal") {
|
|
600
|
-
let t = u.filter((t) => !Ve(t, e));
|
|
601
|
-
t.length > 0 && a.push({
|
|
602
|
-
rule: "internal/foreign-import",
|
|
603
|
-
file: o(e),
|
|
604
|
-
severity: "error",
|
|
605
|
-
detail: `imported from outside its own directory by: ${t.slice(0, 5).map(o).join(", ")}${t.length > 5 ? ` (+${t.length - 5})` : ""}`,
|
|
606
|
-
fix: "export it through the feature's public module, or move the importer inside. `.internal` is the promise that refactoring within this directory breaks nobody — a foreign import revokes it silently, which is how a feature boundary rots without a single review comment"
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
if (t === "fixture") {
|
|
610
|
-
let t = u.filter((e) => !Te(e) && R(e) !== "fixture");
|
|
611
|
-
t.length > 0 && a.push({
|
|
612
|
-
rule: "fixture/production-import",
|
|
613
|
-
file: o(e),
|
|
614
|
-
severity: "error",
|
|
615
|
-
detail: `imported by non-test file(s): ${t.slice(0, 5).map(o).join(", ")}`,
|
|
616
|
-
fix: "test material must not be reachable from a production path — it ships in the bundle, and it is written to be convenient rather than correct"
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
if (t === "component.ui" && !i) {
|
|
620
|
-
let t = [...r.namedBindings].filter((e) => Ee.has(e)).sort();
|
|
621
|
-
t.length > 0 && a.push({
|
|
622
|
-
rule: "ui/no-write",
|
|
623
|
-
file: o(e),
|
|
624
|
-
severity: "error",
|
|
625
|
-
detail: `imports write hook(s): ${t.join(", ")}`,
|
|
626
|
-
fix: "a *.component.ui.tsx renders; it does not write. Lift the mutation into the *.component.tsx that owns it and pass a handler down. Reading (useT / useCan / usePermissions) stays allowed — threading those through props is prop-drilling, not reusability"
|
|
627
|
-
});
|
|
628
|
-
let n = f(e), i = n.filter((e) => {
|
|
629
|
-
let t = R(e);
|
|
630
|
-
return t === "component" || t === "component.ui" || y.test(e) || g.has(e.slice(e.lastIndexOf(w) + 1));
|
|
631
|
-
});
|
|
632
|
-
n.length === 0 ? a.push({
|
|
633
|
-
rule: "ui/orphaned",
|
|
634
|
-
file: o(e),
|
|
635
|
-
severity: "warn",
|
|
636
|
-
detail: "nothing imports it",
|
|
637
|
-
fix: "render it from a *.component.tsx or a page, or delete it — an unrendered presentational component is carried, reviewed and refactored forever without reaching a user"
|
|
638
|
-
}) : i.length === 0 && a.push({
|
|
639
|
-
rule: "ui/unlinked",
|
|
640
|
-
file: o(e),
|
|
641
|
-
severity: "warn",
|
|
642
|
-
detail: `reached only by: ${n.slice(0, 5).map(o).join(", ")}${n.length > 5 ? ` (+${n.length - 5})` : ""} — barrels resolved through`,
|
|
643
|
-
fix: "a presentational component should be reached from a *.component.tsx, another *.component.ui.tsx, or a page"
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
if (t === "store" && !i) {
|
|
647
|
-
let t = l.get(e)?.get("defineStore") ?? 0;
|
|
648
|
-
t !== 1 && a.push({
|
|
649
|
-
rule: "store/one-per-file",
|
|
650
|
-
file: o(e),
|
|
651
|
-
severity: "error",
|
|
652
|
-
detail: t === 0 ? "declares a store file but calls no defineStore()" : `declares ${t} stores`,
|
|
653
|
-
fix: "a store file defines exactly ONE store — the SSR seed pass and devtools address stores by name, so two in a file makes both harder to find and neither easier to reason about"
|
|
654
|
-
});
|
|
655
|
-
let n = [...l.get(e)?.keys() ?? []].filter((e) => Oe.has(e)).sort();
|
|
656
|
-
n.length > 0 && a.push({
|
|
657
|
-
rule: "store/mirrors-server-state",
|
|
658
|
-
file: o(e),
|
|
659
|
-
severity: "error",
|
|
660
|
-
detail: `reads server state here: ${n.join(", ")}`,
|
|
661
|
-
fix: "a store holds CLIENT state. A subscription is already live server state; copying it into a store gives you a second copy that does not live, and the page then shows the stale one — a bug that reads as \"reactivity is broken\". Read the subscription where you render it"
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
|
-
if (t !== "tracking" && !i) {
|
|
665
|
-
let t = [...l.get(e)?.keys() ?? []].filter((e) => De.has(e)).sort();
|
|
666
|
-
t.length > 0 && a.push({
|
|
667
|
-
rule: "tracking/outside-tracking-file",
|
|
668
|
-
file: o(e),
|
|
669
|
-
severity: "error",
|
|
670
|
-
detail: `calls ${t.join(", ")} outside a *.tracking.ts`,
|
|
671
|
-
fix: "move the defineTracking(...) spec into a *.tracking.ts and import it here. The event catalogue — names, property bags, the decisions about what personal data goes out — belongs in one place you can list; calling useTracking with a spec from there is fine"
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
if (we.find((e) => e.id === t)?.needsTest === !0 && !i) {
|
|
675
|
-
let t = e.replace(/\.tsx?$/, ""), n = e.endsWith("x") ? "tsx" : "ts";
|
|
676
|
-
["test", "spec"].flatMap((e) => [`${t}.${e}.ts`, `${t}.${e}.tsx`]).some((e) => {
|
|
677
|
-
try {
|
|
678
|
-
return O(e).isFile();
|
|
679
|
-
} catch {
|
|
680
|
-
return !1;
|
|
681
|
-
}
|
|
682
|
-
}) || a.push({
|
|
683
|
-
rule: "convention/missing-test",
|
|
684
|
-
file: o(e),
|
|
685
|
-
severity: "warn",
|
|
686
|
-
detail: "no test beside it",
|
|
687
|
-
fix: `add ${o(t)}.test.${n} — the name is derivable, so write it with the file. A shallow test earns its place: what it buys is that something MOUNTS the component, and a render loop, a crashing effect or a missing provider is invisible until then. One app wrote 251 of these and the first run found a render loop that made a page take 423 s and exhaust the heap — in a browser it had looked usable`
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
return a.sort((e, t) => e.file === t.file ? e.rule.localeCompare(t.rule) : e.file.localeCompare(t.file)), {
|
|
692
|
-
findings: a,
|
|
693
|
-
declared: p,
|
|
694
|
-
scanned: r.length,
|
|
695
|
-
vendored: n
|
|
696
|
-
};
|
|
697
|
-
}, He = 10, Ue = (e) => {
|
|
698
|
-
let { findings: t, declared: n, scanned: r, vendored: i } = V(e);
|
|
699
|
-
console.log("");
|
|
700
|
-
for (let t of Ce(e, i)) console.log(`· not ours, exempt: ${t}`);
|
|
701
|
-
if (r === 0) return console.log("• file-taxonomy scan: no sources here — nothing to check."), 0;
|
|
702
|
-
if (t.length === 0) return console.log(`✓ file-taxonomy scan: ${n} declared file(s) keep their contract (${r} scanned).`), 0;
|
|
703
|
-
let a = /* @__PURE__ */ new Map();
|
|
704
|
-
for (let e of t) {
|
|
705
|
-
let t = a.get(e.rule) ?? [];
|
|
706
|
-
t.push(e), a.set(e.rule, t);
|
|
707
|
-
}
|
|
708
|
-
let o = t.filter((e) => e.severity === "error").length;
|
|
709
|
-
console.log(`• file taxonomy: ${t.length} finding(s) — ${o} error, ${t.length - o} advisory (${n} declared / ${r} scanned):`);
|
|
710
|
-
for (let [e, t] of [...a].sort()) {
|
|
711
|
-
console.log(` ${t[0].severity === "error" ? "✗" : "·"} ${e} (${t.length})`);
|
|
712
|
-
for (let e of t.slice(0, He)) console.log(` ${e.file} — ${e.detail}`);
|
|
713
|
-
let n = t.length - He;
|
|
714
|
-
n > 0 && console.log(` (+${n} more — \`voltro doctor --json\` prints every one.)`), console.log(` → ${t[0].fix}`);
|
|
715
|
-
}
|
|
716
|
-
return t.length;
|
|
717
|
-
}, We = /* @__PURE__ */ new Set([
|
|
1
|
+
import { Bt as e, Rt as t, zt as n } from "./dev-C7sFZq3m.js";
|
|
2
|
+
import { $ as r, Q as i, X as a, Z as o, s, t as c, y as l } from "./webDev-Dybxew86.js";
|
|
3
|
+
import { w as u } from "./frameworkTableAssembly-oMPBKqlE.js";
|
|
4
|
+
import { n as d } from "./pageConvention-cEiRxdab.js";
|
|
5
|
+
import { r as f, u as p } from "./apiBuild-DYD_ONLD.js";
|
|
6
|
+
import { n as m, r as h, t as g } from "./cliOutput-D1tSBoRM.js";
|
|
7
|
+
import { n as ee, t as te } from "./tsconfigPaths-BWXBWgcl.js";
|
|
8
|
+
import { n as ne, r as re } from "./fileTaxonomy-CJfgOllU.js";
|
|
9
|
+
import { basename as ie, dirname as ae, join as _, relative as v, resolve as y, sep as b } from "node:path";
|
|
10
|
+
import { existsSync as x, readFileSync as S, readdirSync as C, statSync as w, writeFileSync as oe } from "node:fs";
|
|
11
|
+
import { pathToFileURL as se } from "node:url";
|
|
12
|
+
import { createLogger as ce } from "@voltro/logger";
|
|
13
|
+
import { Node as T, Project as E, SyntaxKind as D } from "ts-morph";
|
|
14
|
+
//#region src/predicateColumnAudit.ts
|
|
15
|
+
var le = /* @__PURE__ */ new Set([
|
|
718
16
|
"eq",
|
|
719
17
|
"ne",
|
|
720
18
|
"gt",
|
|
@@ -732,43 +30,43 @@ var le = () => {
|
|
|
732
30
|
"endsWith",
|
|
733
31
|
"between",
|
|
734
32
|
"jsonField"
|
|
735
|
-
]),
|
|
736
|
-
if (
|
|
737
|
-
if (
|
|
738
|
-
},
|
|
33
|
+
]), ue = (e) => {
|
|
34
|
+
if (T.isPropertyAccessExpression(e)) return e.getName();
|
|
35
|
+
if (T.isIdentifier(e)) return e.getText();
|
|
36
|
+
}, de = (e) => {
|
|
739
37
|
let t = e;
|
|
740
|
-
for (;
|
|
38
|
+
for (; T.isCallExpression(t);) {
|
|
741
39
|
let e = t.getExpression();
|
|
742
|
-
if (!
|
|
40
|
+
if (!T.isPropertyAccessExpression(e)) break;
|
|
743
41
|
t = e.getExpression();
|
|
744
42
|
}
|
|
745
43
|
return t;
|
|
746
|
-
},
|
|
747
|
-
let r = new Map(t.map((e) => [e.name, new Set(e.columns)])), i = new Map(t.map((e) => [e.name, e.columns])), a = [], o = 0, s = new
|
|
44
|
+
}, fe = (e, t, n) => {
|
|
45
|
+
let r = new Map(t.map((e) => [e.name, new Set(e.columns)])), i = new Map(t.map((e) => [e.name, e.columns])), a = [], o = 0, s = new E({
|
|
748
46
|
useInMemoryFileSystem: !0,
|
|
749
47
|
skipAddingFilesFromTsConfig: !0
|
|
750
48
|
});
|
|
751
49
|
for (let t of e) {
|
|
752
50
|
let e;
|
|
753
51
|
try {
|
|
754
|
-
let n =
|
|
52
|
+
let n = S(t, "utf8");
|
|
755
53
|
if (!n.includes(".where(") && !n.includes(".select(")) continue;
|
|
756
54
|
e = s.createSourceFile(`/scan${t.endsWith("x") ? ".tsx" : ".ts"}`, n, { overwrite: !0 });
|
|
757
55
|
} catch {
|
|
758
56
|
continue;
|
|
759
57
|
}
|
|
760
|
-
let c =
|
|
761
|
-
for (let t of e.getDescendantsOfKind(
|
|
58
|
+
let c = v(n, t);
|
|
59
|
+
for (let t of e.getDescendantsOfKind(D.CallExpression)) {
|
|
762
60
|
let e = t.getExpression();
|
|
763
|
-
if (!
|
|
764
|
-
let n =
|
|
61
|
+
if (!T.isPropertyAccessExpression(e) || e.getName() !== "where") continue;
|
|
62
|
+
let n = ue(de(e.getExpression()));
|
|
765
63
|
if (n === void 0 || !r.has(n)) {
|
|
766
64
|
let t = e.getExpression();
|
|
767
|
-
if (
|
|
65
|
+
if (T.isCallExpression(t)) {
|
|
768
66
|
let e = t.getExpression();
|
|
769
|
-
if (
|
|
67
|
+
if (T.isPropertyAccessExpression(e) && e.getName() === "select") {
|
|
770
68
|
let e = t.getArguments()[0];
|
|
771
|
-
e !== void 0 &&
|
|
69
|
+
e !== void 0 && T.isStringLiteral(e) && (n = e.getLiteralValue());
|
|
772
70
|
}
|
|
773
71
|
}
|
|
774
72
|
}
|
|
@@ -777,16 +75,16 @@ var le = () => {
|
|
|
777
75
|
if (s === void 0) continue;
|
|
778
76
|
let l = [], [u] = t.getArguments();
|
|
779
77
|
if (u !== void 0) {
|
|
780
|
-
if (
|
|
78
|
+
if (T.isStringLiteral(u)) l.push({
|
|
781
79
|
column: u.getLiteralValue(),
|
|
782
80
|
line: u.getStartLineNumber()
|
|
783
81
|
});
|
|
784
|
-
else for (let e of [u, ...u.getDescendantsOfKind(
|
|
785
|
-
if (!
|
|
786
|
-
let t = e.getExpression(), n =
|
|
787
|
-
if (n === void 0 || !
|
|
82
|
+
else for (let e of [u, ...u.getDescendantsOfKind(D.CallExpression)]) {
|
|
83
|
+
if (!T.isCallExpression(e)) continue;
|
|
84
|
+
let t = e.getExpression(), n = T.isIdentifier(t) ? t.getText() : T.isPropertyAccessExpression(t) ? t.getName() : void 0;
|
|
85
|
+
if (n === void 0 || !le.has(n)) continue;
|
|
788
86
|
let r = e.getArguments()[0];
|
|
789
|
-
r !== void 0 &&
|
|
87
|
+
r !== void 0 && T.isStringLiteral(r) && l.push({
|
|
790
88
|
column: r.getLiteralValue(),
|
|
791
89
|
line: r.getStartLineNumber()
|
|
792
90
|
});
|
|
@@ -805,16 +103,16 @@ var le = () => {
|
|
|
805
103
|
issues: a,
|
|
806
104
|
checked: o
|
|
807
105
|
};
|
|
808
|
-
},
|
|
106
|
+
}, O = 12, pe = (e) => {
|
|
809
107
|
if (e.checked === 0) return null;
|
|
810
108
|
if (e.issues.length === 0) return `predicate columns: ${e.checked} checked — every filtered column exists on its table`;
|
|
811
109
|
let t = [`predicate columns: ${e.issues.length} filter${e.issues.length, "s"} on a column that does not exist (${e.checked} checked)`];
|
|
812
110
|
for (let n of e.issues) {
|
|
813
|
-
let e = n.available.slice(0,
|
|
111
|
+
let e = n.available.slice(0, O).join(", "), r = n.available.length > O ? `, … (${n.available.length - O} more)` : "";
|
|
814
112
|
t.push(` ${n.file}:${n.line} '${n.table}' has no column '${n.column}'`), t.push(` columns: ${e}${r}`);
|
|
815
113
|
}
|
|
816
114
|
return t.push(" This type-checks today and fails at runtime as a bare SQL error."), t.join("\n");
|
|
817
|
-
},
|
|
115
|
+
}, me = /* @__PURE__ */ new Set([
|
|
818
116
|
"id",
|
|
819
117
|
"tenantId",
|
|
820
118
|
"tenant_id",
|
|
@@ -834,7 +132,7 @@ var le = () => {
|
|
|
834
132
|
"idempotency_id",
|
|
835
133
|
"idempotencyKey",
|
|
836
134
|
"idempotency_key"
|
|
837
|
-
]),
|
|
135
|
+
]), he = /* @__PURE__ */ new Set([
|
|
838
136
|
"id",
|
|
839
137
|
"createdAt",
|
|
840
138
|
"created_at",
|
|
@@ -844,10 +142,10 @@ var le = () => {
|
|
|
844
142
|
"deleted_at",
|
|
845
143
|
"tenantId",
|
|
846
144
|
"tenant_id"
|
|
847
|
-
]),
|
|
145
|
+
]), ge = (e) => me.has(e) ? !1 : /[a-z0-9]Id$/.test(e) || /_id$/.test(e), _e = (e) => e.type === "reference", ve = (e) => {
|
|
848
146
|
let t = [];
|
|
849
147
|
for (let n of e) {
|
|
850
|
-
let e = n.columns.filter((e) =>
|
|
148
|
+
let e = n.columns.filter((e) => ge(e.name)), r = e.filter((e) => !_e(e)).map((e) => e.name);
|
|
851
149
|
if (r.length === 0) continue;
|
|
852
150
|
let i = new Set(n.primaryKey), a = r.filter((e) => i.has(e));
|
|
853
151
|
if (n.primaryKey.length >= 2 && a.length > 0) {
|
|
@@ -858,7 +156,7 @@ var le = () => {
|
|
|
858
156
|
});
|
|
859
157
|
continue;
|
|
860
158
|
}
|
|
861
|
-
if (e.some(
|
|
159
|
+
if (e.some(_e) && e.length >= 2) {
|
|
862
160
|
t.push({
|
|
863
161
|
table: n.name,
|
|
864
162
|
columns: r,
|
|
@@ -866,7 +164,7 @@ var le = () => {
|
|
|
866
164
|
});
|
|
867
165
|
continue;
|
|
868
166
|
}
|
|
869
|
-
e.length >= 2 && n.columns.filter((e) => !
|
|
167
|
+
e.length >= 2 && n.columns.filter((e) => !ge(e.name) && !he.has(e.name)).length === 0 && t.push({
|
|
870
168
|
table: n.name,
|
|
871
169
|
columns: r,
|
|
872
170
|
signal: "link-only"
|
|
@@ -876,20 +174,20 @@ var le = () => {
|
|
|
876
174
|
issues: t,
|
|
877
175
|
tablesChecked: e.length
|
|
878
176
|
};
|
|
879
|
-
},
|
|
177
|
+
}, ye = {
|
|
880
178
|
"composite-pk": "part of a composite primary key",
|
|
881
179
|
"reference-sibling": "sits beside a wired reference() on this table",
|
|
882
180
|
"link-only": "this table is nothing but link columns"
|
|
883
|
-
},
|
|
181
|
+
}, be = (e) => {
|
|
884
182
|
if (e.tablesChecked === 0) return null;
|
|
885
183
|
if (e.issues.length === 0) return `junction FKs: ${e.tablesChecked} table${e.tablesChecked === 1 ? "" : "s"} checked — every link column is a wired reference()`;
|
|
886
184
|
let t = e.issues.length, n = [`junction FKs: ${t} junction table${t === 1 ? "" : "s"} with an id column that is a plain text() and not a reference()`];
|
|
887
185
|
for (let t of e.issues) {
|
|
888
186
|
let e = t.columns.map((e) => `'${e}'`).join(", ");
|
|
889
|
-
n.push(` '${t.table}': ${e} (${
|
|
187
|
+
n.push(` '${t.table}': ${e} (${ye[t.signal]})`);
|
|
890
188
|
}
|
|
891
189
|
return n.push(" Declare each as reference(() => <table>): the FK is enforced, the column is auto-indexed,"), n.push(" and the relationship becomes walkable (a plain text() id column is an invisible edge)."), n.join("\n");
|
|
892
|
-
},
|
|
190
|
+
}, xe = /* @__PURE__ */ new Set(/* @__PURE__ */ "position.positions.move.moved.moving.cursor.pointer.mouse.drag.dragging.hover.frame.frames.tick.ticks.ping.heartbeat.typing.scroll.scrolling.zoom.pan.coord.coords.coordinate.coordinates.telemetry.sensor.sample.samples.viewport".split(".")), Se = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter((e) => e.length > 0).map((e) => e.toLowerCase()), Ce = (e) => Se(e).some((e) => xe.has(e)), we = (e) => {
|
|
893
191
|
let t = [], n = [];
|
|
894
192
|
for (let r of e) t.push({
|
|
895
193
|
event: r.name,
|
|
@@ -898,7 +196,7 @@ var le = () => {
|
|
|
898
196
|
event: r.name,
|
|
899
197
|
kind: "wide-key",
|
|
900
198
|
keyFieldCount: r.keyFieldCount
|
|
901
|
-
}), r.hasWebhook &&
|
|
199
|
+
}), r.hasWebhook && Ce(r.name) && n.push({
|
|
902
200
|
event: r.name,
|
|
903
201
|
kind: "webhook-high-rate"
|
|
904
202
|
});
|
|
@@ -907,45 +205,45 @@ var le = () => {
|
|
|
907
205
|
modes: t,
|
|
908
206
|
warnings: n
|
|
909
207
|
};
|
|
910
|
-
},
|
|
208
|
+
}, Te = (e) => {
|
|
911
209
|
if (e.eventsChecked === 0) return null;
|
|
912
210
|
let t = e.eventsChecked, n = [`event delivery: ${t} declared event${t === 1 ? "" : "s"} — the mode decides what a MISSING message means`];
|
|
913
211
|
for (let t of e.modes) n.push(` '${t.event}': ${t.delivery}`);
|
|
914
212
|
return n.push(" each — every delivery matters; a slow subscriber loses the oldest and is TOLD how many (the default)."), n.push(" latest — a newer delivery supersedes a pending one; a slow subscriber gets the current value, told nothing."), n.push(" Two events with identical route/subscriber/buffer numbers mean OPPOSITE things about a drop — the mode is the difference."), n.join("\n");
|
|
915
|
-
},
|
|
213
|
+
}, Ee = (e) => {
|
|
916
214
|
if (e.warnings.length === 0) return null;
|
|
917
215
|
let t = e.warnings.length, n = [`events that may not scale as declared: ${t} finding${t === 1 ? "" : "s"}`];
|
|
918
216
|
for (let t of e.warnings) t.kind === "wide-key" ? (n.push(` '${t.event}': routing key has ${t.keyFieldCount ?? 0} fields — every field FRAGMENTS the subscriber set (distinct routes are the PRODUCT of the fields’ value spaces).`), n.push(" Check each is an ADDRESS the delivery is decided by (arenaId), not a discriminator the handler reads (gameType) — the latter is payload, not key.")) : (n.push(` '${t.event}': a webhook block on a per-frame-looking event — every publish is N HTTP deliveries per second per subscribed target.`), n.push(" The webhook rate limit DEFERS the excess as pending rows rather than failing, so the symptom is a growing table. Publish a coarser event (a summary / state change) for the outside world."));
|
|
919
217
|
return n.push(" WARNING, not a fault — a producer may intend the fan-out. `voltro doctor --json` prints every finding."), n.join("\n");
|
|
920
|
-
},
|
|
218
|
+
}, De = /* @__PURE__ */ new Set([
|
|
921
219
|
"start",
|
|
922
220
|
"run",
|
|
923
221
|
"child"
|
|
924
|
-
]),
|
|
222
|
+
]), Oe = (e) => T.isCallExpression(e) && (() => {
|
|
925
223
|
let t = e.getExpression();
|
|
926
|
-
if (!
|
|
224
|
+
if (!T.isPropertyAccessExpression(t) || !De.has(t.getName())) return !1;
|
|
927
225
|
let n = t.getExpression();
|
|
928
|
-
return
|
|
929
|
-
})(),
|
|
930
|
-
let r = new Map(t.map((e) => [e.name, e])), i = [...r.keys()].sort(), a = [], o = [], s = 0, c = 0, l = new
|
|
226
|
+
return T.isPropertyAccessExpression(n) ? n.getName() === "workflows" : T.isIdentifier(n) && n.getText() === "workflows";
|
|
227
|
+
})(), ke = (e, t, n) => {
|
|
228
|
+
let r = new Map(t.map((e) => [e.name, e])), i = [...r.keys()].sort(), a = [], o = [], s = 0, c = 0, l = new E({
|
|
931
229
|
useInMemoryFileSystem: !0,
|
|
932
230
|
skipAddingFilesFromTsConfig: !0
|
|
933
231
|
});
|
|
934
232
|
for (let t of e) {
|
|
935
233
|
let e;
|
|
936
234
|
try {
|
|
937
|
-
let n =
|
|
235
|
+
let n = S(t, "utf8");
|
|
938
236
|
if (!n.includes("workflows.")) continue;
|
|
939
237
|
e = l.createSourceFile(`/scan${t.endsWith("x") ? ".tsx" : ".ts"}`, n, { overwrite: !0 });
|
|
940
238
|
} catch {
|
|
941
239
|
continue;
|
|
942
240
|
}
|
|
943
|
-
let u =
|
|
944
|
-
for (let t of e.getDescendantsOfKind(
|
|
945
|
-
if (!
|
|
241
|
+
let u = v(n, t);
|
|
242
|
+
for (let t of e.getDescendantsOfKind(D.CallExpression)) {
|
|
243
|
+
if (!Oe(t)) continue;
|
|
946
244
|
c++;
|
|
947
245
|
let e = t.getStartLineNumber(), [n, l] = t.getArguments();
|
|
948
|
-
if (n === void 0 || !
|
|
246
|
+
if (n === void 0 || !T.isStringLiteral(n)) {
|
|
949
247
|
o.push({
|
|
950
248
|
file: u,
|
|
951
249
|
line: e,
|
|
@@ -964,7 +262,7 @@ var le = () => {
|
|
|
964
262
|
});
|
|
965
263
|
continue;
|
|
966
264
|
}
|
|
967
|
-
if (l === void 0 || !
|
|
265
|
+
if (l === void 0 || !T.isObjectLiteralExpression(l)) {
|
|
968
266
|
o.push({
|
|
969
267
|
file: u,
|
|
970
268
|
line: e,
|
|
@@ -972,7 +270,7 @@ var le = () => {
|
|
|
972
270
|
});
|
|
973
271
|
continue;
|
|
974
272
|
}
|
|
975
|
-
if (l.getProperties().some((e) =>
|
|
273
|
+
if (l.getProperties().some((e) => T.isSpreadAssignment(e))) {
|
|
976
274
|
o.push({
|
|
977
275
|
file: u,
|
|
978
276
|
line: e,
|
|
@@ -981,14 +279,14 @@ var le = () => {
|
|
|
981
279
|
continue;
|
|
982
280
|
}
|
|
983
281
|
let p = /* @__PURE__ */ new Set(), m = !1;
|
|
984
|
-
for (let e of l.getProperties()) if (
|
|
985
|
-
let t = (
|
|
986
|
-
if (
|
|
282
|
+
for (let e of l.getProperties()) if (T.isPropertyAssignment(e) || T.isShorthandPropertyAssignment(e)) {
|
|
283
|
+
let t = (T.isPropertyAssignment(e), e.getNameNode());
|
|
284
|
+
if (T.isComputedPropertyName(t)) {
|
|
987
285
|
m = !0;
|
|
988
286
|
break;
|
|
989
287
|
}
|
|
990
288
|
p.add(e.getName().replace(/^['"]|['"]$/g, ""));
|
|
991
|
-
} else (
|
|
289
|
+
} else (T.isMethodDeclaration(e) || T.isGetAccessorDeclaration(e)) && p.add(e.getName());
|
|
992
290
|
if (m) {
|
|
993
291
|
o.push({
|
|
994
292
|
file: u,
|
|
@@ -1026,7 +324,7 @@ var le = () => {
|
|
|
1026
324
|
checked: s,
|
|
1027
325
|
callSites: c
|
|
1028
326
|
};
|
|
1029
|
-
},
|
|
327
|
+
}, Ae = (e) => {
|
|
1030
328
|
if (e.callSites === 0) return null;
|
|
1031
329
|
let t = [];
|
|
1032
330
|
t.push(`workflow starts: ${e.checked}/${e.callSites} call sites checked`);
|
|
@@ -1039,7 +337,7 @@ var le = () => {
|
|
|
1039
337
|
for (let n of e.unchecked) t.push(` ${n.file}:${n.line} — ${n.reason}`);
|
|
1040
338
|
}
|
|
1041
339
|
return e.issues.length === 0 && e.unchecked.length === 0 && t.push(" every call site matches its workflow"), t.join("\n");
|
|
1042
|
-
},
|
|
340
|
+
}, je = /(^|[/\\])(\.env(\.[\w.-]+)?|.*\.ya?ml|Dockerfile|.*\.tf|.*\.toml|Procfile)$/i, k = "VOLTRO_FIELD_ENCRYPTION_KEY", Me = (e) => {
|
|
1043
341
|
let t;
|
|
1044
342
|
for (let [n, r] of e.sources) if (t === void 0 && /\bfieldEncryption\b/.test(r) && (t = {
|
|
1045
343
|
reason: "fieldEncryption",
|
|
@@ -1047,13 +345,13 @@ var le = () => {
|
|
|
1047
345
|
}), t === void 0 && /\.encrypted\s*\(/.test(r) && (t = {
|
|
1048
346
|
reason: "encrypted-column",
|
|
1049
347
|
file: n
|
|
1050
|
-
}), r.includes(
|
|
348
|
+
}), r.includes(k)) return;
|
|
1051
349
|
if (t !== void 0) {
|
|
1052
|
-
for (let [, t] of e.envFiles) if (t.includes(
|
|
350
|
+
for (let [, t] of e.envFiles) if (t.includes(k)) return;
|
|
1053
351
|
return t;
|
|
1054
352
|
}
|
|
1055
|
-
},
|
|
1056
|
-
`✗ ${
|
|
353
|
+
}, Ne = (e) => [
|
|
354
|
+
`✗ ${k} is named nowhere under this app root, and the app encrypts columns at rest`,
|
|
1057
355
|
e.reason === "fieldEncryption" ? ` (\`fieldEncryption\` in ${e.file})` : ` (an \`.encrypted()\` column in ${e.file})`,
|
|
1058
356
|
"",
|
|
1059
357
|
" A deploy without it does not degrade — it refuses to boot at `onActivate`,",
|
|
@@ -1069,14 +367,14 @@ var le = () => {
|
|
|
1069
367
|
" reaches your deploy target is not knowable from here — a secret manager",
|
|
1070
368
|
" or a CI variable is not a file. Naming it nowhere is conclusive; naming",
|
|
1071
369
|
" it somewhere is not a guarantee."
|
|
1072
|
-
].join("\n"),
|
|
370
|
+
].join("\n"), Pe = (e) => {
|
|
1073
371
|
if (e.catalogCodes.length === 0) return;
|
|
1074
372
|
let t = new Set(e.declaredLocales), n = e.catalogCodes.filter((e) => !t.has(e)).sort();
|
|
1075
373
|
if (n.length !== 0) return {
|
|
1076
374
|
orphaned: n,
|
|
1077
375
|
noneDeclared: t.size === 0
|
|
1078
376
|
};
|
|
1079
|
-
},
|
|
377
|
+
}, Fe = (e, t) => {
|
|
1080
378
|
let n = e.orphaned.join(", ");
|
|
1081
379
|
return e.noneDeclared ? [
|
|
1082
380
|
`⚠ ${t} has ${e.orphaned.length} catalog(s) — ${n} — and the app declares no \`locales:\`.`,
|
|
@@ -1088,33 +386,33 @@ var le = () => {
|
|
|
1088
386
|
" …or delete the files. A catalog that is never loaded looks from the inside",
|
|
1089
387
|
" exactly like one that works, which is why this is worth a line."
|
|
1090
388
|
] : [`⚠ ${t} has catalog(s) not in \`locales:\` — ${n} — so they are never imported.`, " Add the code(s) to `locales:` in your app config, or delete the file(s)."];
|
|
1091
|
-
},
|
|
389
|
+
}, A = (e) => e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/[^\n]*/g, "$1"), j = /* @__PURE__ */ new Set([
|
|
1092
390
|
"requireScope",
|
|
1093
391
|
"requireAnyScope",
|
|
1094
392
|
"requireAuth",
|
|
1095
393
|
"assertScope",
|
|
1096
394
|
"assertCan",
|
|
1097
395
|
"can"
|
|
1098
|
-
]),
|
|
396
|
+
]), Ie = (e) => {
|
|
1099
397
|
let t = /* @__PURE__ */ new Set();
|
|
1100
|
-
for (let n of e) for (let e of
|
|
398
|
+
for (let n of e) for (let e of A(n).matchAll(/\bexport\s+(?:const|function|async\s+function)\s+((?:require|assert)[A-Z]\w*)/g)) t.add(e[1]);
|
|
1101
399
|
return t;
|
|
1102
|
-
},
|
|
400
|
+
}, Le = (e, t) => {
|
|
1103
401
|
for (let n of t) if (RegExp(`\\b${n}\\s*\\(`).test(e)) return n;
|
|
1104
|
-
},
|
|
402
|
+
}, Re = (e) => {
|
|
1105
403
|
let t = /\bsubject\s*\.\s*id\b[^\n]*?(?:!==|===|!=|==)|(?:!==|===|!=|==)[^\n]*?\bsubject\s*\.\s*id\b/.test(e), n = /(?:Error|Denied|Forbidden|Unauthorized)\b|\b(?:fail|die|throw)\b/.test(e);
|
|
1106
404
|
return t && n;
|
|
1107
|
-
},
|
|
405
|
+
}, ze = (e, t, n) => {
|
|
1108
406
|
if (e.hasDeclaredGuards) return { kind: "declared-guard" };
|
|
1109
|
-
let r =
|
|
1110
|
-
return i === void 0 ?
|
|
407
|
+
let r = A(e.src), i = Le(r, t);
|
|
408
|
+
return i === void 0 ? Re(r) ? { kind: "inline-ownership" } : n.has(e.tag) ? { kind: "allowlisted" } : { kind: "unchecked" } : {
|
|
1111
409
|
kind: "vocabulary-guard",
|
|
1112
410
|
name: i
|
|
1113
411
|
};
|
|
1114
|
-
},
|
|
412
|
+
}, Be = (e, t, n) => {
|
|
1115
413
|
let r = e.op === "delete" ? 3 : e.op === "update" ? 2 : 1;
|
|
1116
414
|
return e.table !== void 0 && (t.has(e.table) && (r += 2), n.has(e.table) && e.op === "delete" && (r += 2)), r;
|
|
1117
|
-
},
|
|
415
|
+
}, Ve = (e, t, n) => {
|
|
1118
416
|
switch (t.kind) {
|
|
1119
417
|
case "declared-guard": return "guards: on the descriptor";
|
|
1120
418
|
case "vocabulary-guard": return `calls ${t.name}()`;
|
|
@@ -1122,15 +420,15 @@ var le = () => {
|
|
|
1122
420
|
case "allowlisted": return "accepted by a previous run";
|
|
1123
421
|
case "unchecked": return n ? e.op === "read" ? "no check in this file — this app registers a row filter; VERIFY it covers this table, then record it as reviewed= rather than debt" : `${e.op}s${e.table ? ` ${e.table}` : ""} with no check in this file — this app registers a row filter; VERIFY it covers this table, then record it as reviewed= rather than debt` : e.op === "read" ? "returns a row the client selected, with nothing compared to the caller" : `${e.op}s${e.table ? ` ${e.table}` : ""} with nothing constraining WHICH row`;
|
|
1124
422
|
}
|
|
1125
|
-
},
|
|
423
|
+
}, He = (e) => {
|
|
1126
424
|
let t = e.executors.map((t) => {
|
|
1127
|
-
let n =
|
|
425
|
+
let n = ze(t, e.guardVocabulary, e.allowlist);
|
|
1128
426
|
return {
|
|
1129
427
|
tag: t.tag,
|
|
1130
428
|
path: t.path,
|
|
1131
429
|
verdict: n,
|
|
1132
|
-
severity:
|
|
1133
|
-
why:
|
|
430
|
+
severity: Be(t, e.tenantScopedTables, e.referencedTables),
|
|
431
|
+
why: Ve(t, n, e.rowFilterRegistered === !0)
|
|
1134
432
|
};
|
|
1135
433
|
}), n = (e) => t.filter((t) => t.verdict.kind === e).length;
|
|
1136
434
|
return {
|
|
@@ -1144,7 +442,7 @@ var le = () => {
|
|
|
1144
442
|
},
|
|
1145
443
|
unchecked: t.filter((e) => e.verdict.kind === "unchecked").sort((e, t) => t.severity - e.severity || e.tag.localeCompare(t.tag))
|
|
1146
444
|
};
|
|
1147
|
-
},
|
|
445
|
+
}, Ue = [
|
|
1148
446
|
"# voltro doctor — authz allowlist",
|
|
1149
447
|
"#",
|
|
1150
448
|
"# Two KINDS of line, and the difference is the point:",
|
|
@@ -1169,19 +467,19 @@ var le = () => {
|
|
|
1169
467
|
"# `--write-authz-allowlist` PRESERVES reviewed= lines. It only ever adds debt",
|
|
1170
468
|
"# lines for executors that are newly unchecked.",
|
|
1171
469
|
""
|
|
1172
|
-
],
|
|
470
|
+
], We = (e) => {
|
|
1173
471
|
let t = [...e].map((e) => typeof e == "string" ? { tag: e } : e), n = t.filter((e) => e.reviewed === void 0).map((e) => e.tag).sort(), r = t.filter((e) => e.reviewed !== void 0).sort((e, t) => e.tag.localeCompare(t.tag));
|
|
1174
472
|
return [
|
|
1175
|
-
...
|
|
473
|
+
...Ue,
|
|
1176
474
|
...n,
|
|
1177
475
|
...r.length > 0 ? ["", "# reviewed — read by a human, open on purpose"] : [],
|
|
1178
476
|
...r.map((e) => `${e.tag} reviewed=${e.reviewed}`),
|
|
1179
477
|
""
|
|
1180
478
|
].join("\n");
|
|
1181
|
-
},
|
|
479
|
+
}, Ge = (e, t) => {
|
|
1182
480
|
let n = [...new Set(t)].sort();
|
|
1183
|
-
if (e === void 0) return
|
|
1184
|
-
let r =
|
|
481
|
+
if (e === void 0) return We(n.map((e) => ({ tag: e })));
|
|
482
|
+
let r = M(e).tags, i = n.filter((e) => !r.has(e));
|
|
1185
483
|
return i.length === 0 ? e : [
|
|
1186
484
|
e.replace(/\n+$/, ""),
|
|
1187
485
|
"",
|
|
@@ -1190,25 +488,25 @@ var le = () => {
|
|
|
1190
488
|
...i,
|
|
1191
489
|
""
|
|
1192
490
|
].join("\n");
|
|
1193
|
-
},
|
|
491
|
+
}, Ke = (e, t) => {
|
|
1194
492
|
let n = [...new Set(t)];
|
|
1195
493
|
if (e === void 0) return {
|
|
1196
494
|
added: n.length,
|
|
1197
495
|
kept: 0,
|
|
1198
496
|
removed: 0
|
|
1199
497
|
};
|
|
1200
|
-
let r =
|
|
498
|
+
let r = M(e).tags;
|
|
1201
499
|
return {
|
|
1202
500
|
added: n.filter((e) => !r.has(e)).length,
|
|
1203
501
|
kept: r.size,
|
|
1204
502
|
removed: 0
|
|
1205
503
|
};
|
|
1206
|
-
},
|
|
504
|
+
}, qe = class extends Error {
|
|
1207
505
|
tag;
|
|
1208
506
|
constructor(e) {
|
|
1209
507
|
super(`authz allowlist: '${e}' is marked reviewed with no reason. Write \`${e} reviewed=<why it is genuinely open>\`. A bare tag is the other, honest option — it records the executor as DEBT, which is what an unexplained exemption is.`), this.tag = e, this.name = "AllowlistReasonMissingError";
|
|
1210
508
|
}
|
|
1211
|
-
},
|
|
509
|
+
}, M = (e) => {
|
|
1212
510
|
let t = [];
|
|
1213
511
|
for (let n of e.split("\n")) {
|
|
1214
512
|
let e = n.trim();
|
|
@@ -1221,7 +519,7 @@ var le = () => {
|
|
|
1221
519
|
continue;
|
|
1222
520
|
}
|
|
1223
521
|
let a = r[2].trim();
|
|
1224
|
-
if (a.length === 0) throw new
|
|
522
|
+
if (a.length === 0) throw new qe(i);
|
|
1225
523
|
t.push({
|
|
1226
524
|
tag: i,
|
|
1227
525
|
reviewed: a
|
|
@@ -1231,13 +529,13 @@ var le = () => {
|
|
|
1231
529
|
tags: new Set(t.map((e) => e.tag)),
|
|
1232
530
|
entries: t
|
|
1233
531
|
};
|
|
1234
|
-
},
|
|
532
|
+
}, N = (e) => {
|
|
1235
533
|
let t = e.indexOf(".");
|
|
1236
534
|
return t <= 0 ? void 0 : e.slice(0, t);
|
|
1237
|
-
},
|
|
535
|
+
}, Je = (e, t) => {
|
|
1238
536
|
let n = new Set(t.tables), r = new Set(t.rpcTags), i = /* @__PURE__ */ new Map();
|
|
1239
537
|
for (let e of t.rpcTags) {
|
|
1240
|
-
let t =
|
|
538
|
+
let t = N(e);
|
|
1241
539
|
if (t === void 0) continue;
|
|
1242
540
|
let n = i.get(t);
|
|
1243
541
|
n ? n.push(e) : i.set(t, [e]);
|
|
@@ -1266,7 +564,7 @@ var le = () => {
|
|
|
1266
564
|
});
|
|
1267
565
|
continue;
|
|
1268
566
|
}
|
|
1269
|
-
let s = new Set(o.rpcTags.map(
|
|
567
|
+
let s = new Set(o.rpcTags.map(N).filter((e) => e !== void 0));
|
|
1270
568
|
for (let e of s) {
|
|
1271
569
|
let t = i.get(e);
|
|
1272
570
|
t !== void 0 && a.push({
|
|
@@ -1279,40 +577,83 @@ var le = () => {
|
|
|
1279
577
|
}
|
|
1280
578
|
}
|
|
1281
579
|
return a;
|
|
1282
|
-
},
|
|
580
|
+
}, Ye = (e, t) => {
|
|
1283
581
|
let n = e.reduce((e, t) => e + t.tables.length, 0), r = [`plugin surface · ${e.length} plugin(s) contribute ${n} table(s)`];
|
|
1284
582
|
if (t.length === 0) return r.push(" ✓ no overlap with your own tables or rpc namespaces"), r;
|
|
1285
583
|
for (let e of t) r.push(` ${e.fatal ? "✗" : "⚠"} ${e.plugin} — ${e.detail}`), r.push(` ${e.fix}`);
|
|
1286
584
|
return r;
|
|
1287
|
-
},
|
|
1288
|
-
|
|
585
|
+
}, Xe = async (e) => {
|
|
586
|
+
if (!await e.hasWorkflows().catch(() => !1)) return { kind: "not-applicable" };
|
|
587
|
+
let t;
|
|
588
|
+
try {
|
|
589
|
+
t = await e.openStore();
|
|
590
|
+
} catch (e) {
|
|
591
|
+
return {
|
|
592
|
+
kind: "skipped",
|
|
593
|
+
reason: e instanceof Error ? e.message : String(e)
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
try {
|
|
597
|
+
return {
|
|
598
|
+
kind: "checked",
|
|
599
|
+
result: await e.sweep(t.store)
|
|
600
|
+
};
|
|
601
|
+
} catch (e) {
|
|
602
|
+
return {
|
|
603
|
+
kind: "skipped",
|
|
604
|
+
reason: e instanceof Error ? e.message : String(e)
|
|
605
|
+
};
|
|
606
|
+
} finally {
|
|
607
|
+
await t.close().catch(() => {});
|
|
608
|
+
}
|
|
609
|
+
}, Ze = (e) => {
|
|
610
|
+
let t = Math.round(e / 6e4);
|
|
611
|
+
if (t < 90) return `${t}m`;
|
|
612
|
+
let n = Math.round(t / 60);
|
|
613
|
+
return n < 48 ? `${n}h` : `${Math.round(n / 24)}d`;
|
|
614
|
+
}, Qe = (e) => {
|
|
615
|
+
if (e.kind === "not-applicable") return null;
|
|
616
|
+
if (e.kind === "skipped") return `• stuck runs: NOT CHECKED — ${e.reason}\n This is the one doctor rule that reads the database rather than your source.
|
|
617
|
+
Run it where the app's DB env vars are set, or ignore this line locally.`;
|
|
618
|
+
let { result: t } = e, n = [];
|
|
619
|
+
if (t.stalled.length === 0) n.push(`✓ stuck runs: none (${t.examined} live run(s) old enough to examine${t.waitingOnTimer > 0 ? `, ${t.waitingOnTimer} waiting on a durable timer` : ""}${t.alreadyReported > 0 ? `, ${t.alreadyReported} already reported` : ""})`);
|
|
620
|
+
else {
|
|
621
|
+
n.push(`✗ stuck runs: ${t.stalled.length} run(s) have made no progress`);
|
|
622
|
+
for (let e of t.stalled.slice(0, 20)) n.push(` ${e.tag} ${e.runId} idle ${Ze(e.idleMs)} ${e.reason}`);
|
|
623
|
+
t.stalled.length > 20 && n.push(` … and ${t.stalled.length - 20} more`), n.push(" Nothing was changed — this is a SUSPICION, not a verdict. `awaiting-signal` usually means the sender never came.");
|
|
624
|
+
}
|
|
625
|
+
t.sawFullRunPage && n.push(" (the examined page filled up — more old runs exist than were read this pass)");
|
|
626
|
+
for (let e of t.failures) n.push(` ! could not examine ${e.subject}: ${e.detail}`);
|
|
627
|
+
return n.join("\n");
|
|
628
|
+
}, $e = (e) => x(y(e, "app.config.ts")) || x(y(e, "app.config.js")), P = (e) => {
|
|
629
|
+
let t = p(e), n = f(e);
|
|
1289
630
|
return {
|
|
1290
631
|
root: e,
|
|
1291
632
|
serveBundle: t,
|
|
1292
|
-
serveBundlePresent:
|
|
633
|
+
serveBundlePresent: x(t),
|
|
1293
634
|
apiBundle: n,
|
|
1294
|
-
apiBundlePresent:
|
|
635
|
+
apiBundlePresent: x(n)
|
|
1295
636
|
};
|
|
1296
|
-
},
|
|
637
|
+
}, et = [
|
|
1297
638
|
"Production `voltro serve` requires a precompiled serve bundle and never transpiles on demand.",
|
|
1298
639
|
"Add a `voltro build .` step BEFORE `voltro serve .` in your Dockerfile / start script.",
|
|
1299
640
|
"(Generated Dockerfiles from `voltro create-project` already do this.)"
|
|
1300
|
-
].join("\n "),
|
|
1301
|
-
let n =
|
|
1302
|
-
if (console.log(`voltro doctor — serve preflight for ${n.root}`),
|
|
641
|
+
].join("\n "), tt = (e, t = !1) => {
|
|
642
|
+
let n = P(e);
|
|
643
|
+
if (console.log(`voltro doctor — serve preflight for ${n.root}`), !$e(e)) return console.log("• no app.config found here — nothing to check (run against an app root)."), 0;
|
|
1303
644
|
console.log(`${n.serveBundlePresent ? "✓" : "✗"} serve bundle: ${n.serveBundlePresent ? "present" : "MISSING"} (${n.serveBundle})`), console.log(`${n.apiBundlePresent ? "✓" : "•"} api bundle: ${n.apiBundlePresent ? "present" : "absent"} (${n.apiBundle})`);
|
|
1304
645
|
let r = process.env.NODE_ENV === "production";
|
|
1305
646
|
if (n.serveBundlePresent) return console.log("✓ serve preflight: ready for `voltro serve`."), 0;
|
|
1306
647
|
let i = t || r;
|
|
1307
|
-
return console.error(`${i ? "✗" : "•"} serve bundle missing — ${i ? "production `voltro serve` will FAIL." : "a non-production `voltro serve` still runs via tsx."}`), console.error(` ${
|
|
1308
|
-
},
|
|
1309
|
-
let r = new Set(e), i =
|
|
648
|
+
return console.error(`${i ? "✗" : "•"} serve bundle missing — ${i ? "production `voltro serve` will FAIL." : "a non-production `voltro serve` still runs via tsx."}`), console.error(` ${et}`), +!!i;
|
|
649
|
+
}, nt = (e, t, n) => {
|
|
650
|
+
let r = new Set(e), i = ee(n), a = (e) => (/\.(tsx?|jsx?)$/.test(e) ? [e] : [
|
|
1310
651
|
`${e}.ts`,
|
|
1311
652
|
`${e}.tsx`,
|
|
1312
653
|
`${e}.js`,
|
|
1313
654
|
`${e}.jsx`,
|
|
1314
|
-
|
|
1315
|
-
|
|
655
|
+
_(e, "index.ts"),
|
|
656
|
+
_(e, "index.tsx")
|
|
1316
657
|
]).find((e) => r.has(e)), o = (e) => {
|
|
1317
658
|
let n = (t.get(e) ?? "").replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1"), r = [];
|
|
1318
659
|
for (let e of [
|
|
@@ -1325,13 +666,13 @@ var le = () => {
|
|
|
1325
666
|
}
|
|
1326
667
|
let o = [];
|
|
1327
668
|
for (let t of r) {
|
|
1328
|
-
let n = t.startsWith(".") ?
|
|
669
|
+
let n = t.startsWith(".") ? y(ae(e), t) : te(t, i);
|
|
1329
670
|
if (n === void 0) continue;
|
|
1330
671
|
let r = a(n);
|
|
1331
672
|
r !== void 0 && o.push(r);
|
|
1332
673
|
}
|
|
1333
674
|
return o;
|
|
1334
|
-
}, s = /* @__PURE__ */ new Set(), c = e.filter((e) =>
|
|
675
|
+
}, s = /* @__PURE__ */ new Set(), c = e.filter((e) => ct.test(v(n, e))), l = new Set(c);
|
|
1335
676
|
for (; c.length > 0;) {
|
|
1336
677
|
let e = c.shift();
|
|
1337
678
|
if (!s.has(e)) {
|
|
@@ -1344,11 +685,11 @@ var le = () => {
|
|
|
1344
685
|
let d = /* @__PURE__ */ new Set();
|
|
1345
686
|
for (let e of s) l.has(e) || u.has(e) || d.add(e);
|
|
1346
687
|
return d;
|
|
1347
|
-
},
|
|
688
|
+
}, rt = (e) => e.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1").replace(/'(?:[^'\\\n]|\\.)*'/g, "''").replace(/"(?:[^"\\\n]|\\.)*"/g, "\"\""), it = (e, t) => (e.match(t) ?? []).length, F = (e, t) => e.getDescendantsOfKind(D.CallExpression).filter((e) => t.test(e.getExpression().getText())).length, at = (e, t) => {
|
|
1348
689
|
if (e.src.includes(t)) return !0;
|
|
1349
690
|
let n = t.replace(/^@voltro\/plugin-/, "");
|
|
1350
|
-
return n !== t &&
|
|
1351
|
-
},
|
|
691
|
+
return n !== t && F(e.file, RegExp(`^${n}Plugin$`)) > 0;
|
|
692
|
+
}, ot = (e, t) => {
|
|
1352
693
|
let n = [], r = RegExp(`\\b${t}\\s*(?:<[^()]*>)?\\s*\\(`, "g"), i;
|
|
1353
694
|
for (; (i = r.exec(e)) !== null;) {
|
|
1354
695
|
let t = i.index + i[0].length, r = 1, a = t;
|
|
@@ -1356,10 +697,10 @@ var le = () => {
|
|
|
1356
697
|
n.push(e.slice(t, a - 1));
|
|
1357
698
|
}
|
|
1358
699
|
return n;
|
|
1359
|
-
},
|
|
700
|
+
}, st = (e, t) => {
|
|
1360
701
|
let n = t.getSymbol()?.getDeclarations()?.[0];
|
|
1361
|
-
return n === void 0 ? [] : e.getDescendantsOfKind(
|
|
1362
|
-
},
|
|
702
|
+
return n === void 0 ? [] : e.getDescendantsOfKind(D.Identifier).filter((e) => e.getSymbol()?.getDeclarations()?.[0] === n);
|
|
703
|
+
}, ct = /\.(server|cron|workflow\.server|subscribe|reaction|outbox|startup|trigger|webhook|migration|seed)\.tsx?$/, lt = /\.(query|mutation|action|stream)\.server\.tsx?$/, ut = [
|
|
1363
704
|
"Id",
|
|
1364
705
|
"ID",
|
|
1365
706
|
"Hash",
|
|
@@ -1385,7 +726,7 @@ var le = () => {
|
|
|
1385
726
|
"Comment",
|
|
1386
727
|
"Provider",
|
|
1387
728
|
"Format"
|
|
1388
|
-
],
|
|
729
|
+
], dt = RegExp(`(?:${ut.join("|")})$|_(?:${ut.map((e) => e.toLowerCase()).join("|")})$`), ft = [
|
|
1389
730
|
{
|
|
1390
731
|
id: "hardcoded-framework-cookie",
|
|
1391
732
|
scope: "client",
|
|
@@ -1398,7 +739,7 @@ var le = () => {
|
|
|
1398
739
|
scope: "server",
|
|
1399
740
|
smell: "an executor that never names its own descriptor — its input type is hand-written, so it can drift from the contract it implements",
|
|
1400
741
|
use: "import the descriptor and type the input as `ExecutorInput<typeof descriptor>`, so the wire contract and the handler cannot disagree",
|
|
1401
|
-
match: ({ src: e, path: t }) =>
|
|
742
|
+
match: ({ src: e, path: t }) => lt.test(t) && !/\bimport\s[^;]*\bfrom\s*['"][^'"]*['"]/.test((e.match(/^\s*import\s[^;]*from\s*['"]\.[^'"]*['"]/gm) ?? []).join("\n"))
|
|
1402
743
|
},
|
|
1403
744
|
{
|
|
1404
745
|
id: "subject-write-no-guard",
|
|
@@ -1406,8 +747,8 @@ var le = () => {
|
|
|
1406
747
|
smell: "writes ctx.request.subject.id into the database with no guard on the descriptor — an anonymous caller reaches the write and gets a raw statement failure instead of a typed refusal",
|
|
1407
748
|
use: "add a guard (e.g. `.guard(requireScope('…'))`) or an explicit auth check, so an unauthenticated caller is refused BEFORE the write",
|
|
1408
749
|
match: ({ src: e, path: t }) => {
|
|
1409
|
-
if (!
|
|
1410
|
-
let n =
|
|
750
|
+
if (!lt.test(t)) return !1;
|
|
751
|
+
let n = rt(e);
|
|
1411
752
|
return /\bsubject\s*\.\s*id\b/.test(n) && /\b(?:insert|update|upsert|insertMany|updateMany|insertRow|upsertRow|updateManyRow)\s*\(/.test(n) && !/\b(?:guard|guards|requireScope|requireAnyScope|assertCan|assertScope|requireAuth|Unauthenticated)\b/.test(n);
|
|
1412
753
|
}
|
|
1413
754
|
},
|
|
@@ -1416,14 +757,14 @@ var le = () => {
|
|
|
1416
757
|
scope: "server",
|
|
1417
758
|
smell: "raw fetch() for outbound HTTP on the server (no traceparent, no retry, and no SSRF guard where the URL is not a constant)",
|
|
1418
759
|
use: "yield* HttpClient — the framework client. It injects traceparent, retries, and honours `http.allowHosts`. Where the URL is built from anything a caller supplied it ALSO blocks loopback / RFC-1918 / link-local targets (incl. 169.254.169.254) on the initial request AND every redirect hop — for a hard-coded host that half is redundant, and the first three are still the reason to switch",
|
|
1419
|
-
match: ({ file: e, path: t, serverOnly: n }) => (
|
|
760
|
+
match: ({ file: e, path: t, serverOnly: n }) => (ct.test(t) || n) && F(e, /^(?:globalThis\.|window\.|self\.)?fetch$/) > 0
|
|
1420
761
|
},
|
|
1421
762
|
{
|
|
1422
763
|
id: "form",
|
|
1423
764
|
scope: "client",
|
|
1424
765
|
smell: "per-field useState + a submit flag (hand-rolled form)",
|
|
1425
766
|
use: "useFormBinding — fields + validation from the mutation input Schema; server ValidationError({ field }) routes to the field",
|
|
1426
|
-
match: ({ src: e, file: t }) =>
|
|
767
|
+
match: ({ src: e, file: t }) => F(t, /^useState$/) >= 4 && /onSubmit|handleSubmit|isSubmitting/.test(e)
|
|
1427
768
|
},
|
|
1428
769
|
{
|
|
1429
770
|
id: "table",
|
|
@@ -1451,7 +792,7 @@ var le = () => {
|
|
|
1451
792
|
scope: "client",
|
|
1452
793
|
smell: "useMemo fanning in multiple subscriptions (hand-maintained dep array)",
|
|
1453
794
|
use: "useDerived — dependency-tracked, referentially stable",
|
|
1454
|
-
match: ({ src: e }) =>
|
|
795
|
+
match: ({ src: e }) => it(e, /\buseSubscription\s*[(<]/g) >= 3 && it(e, /\buseMemo\s*\(/g) >= 3
|
|
1455
796
|
},
|
|
1456
797
|
{
|
|
1457
798
|
id: "next-shim",
|
|
@@ -1473,16 +814,16 @@ var le = () => {
|
|
|
1473
814
|
scope: "client",
|
|
1474
815
|
smell: "checking a subscription's `data === undefined` (or `!data`) instead of its `loading` / `idle` flag",
|
|
1475
816
|
use: "branch on `loading` (and `idle` when you pass `skip`) — `loading` narrows `data`, so the settled branch needs no `?? []` and no `!`",
|
|
1476
|
-
match: ({ file: e }) => e.getDescendantsOfKind(
|
|
1477
|
-
let t = e.getFirstAncestorByKind(
|
|
1478
|
-
return t === void 0 ? [] :
|
|
1479
|
-
}).some((t) =>
|
|
817
|
+
match: ({ file: e }) => e.getDescendantsOfKind(D.CallExpression).filter((e) => /(^|\.)useSubscription$/.test(e.getExpression().getText())).flatMap((e) => {
|
|
818
|
+
let t = e.getFirstAncestorByKind(D.VariableDeclaration)?.getNameNode();
|
|
819
|
+
return t === void 0 ? [] : T.isObjectBindingPattern(t) ? t.getElements().filter((e) => (e.getPropertyNameNode() ?? e.getNameNode()).getText() === "data").map((e) => e.getNameNode()) : [];
|
|
820
|
+
}).some((t) => st(e, t).some((e) => {
|
|
1480
821
|
let t = e.getParent();
|
|
1481
|
-
if (
|
|
822
|
+
if (T.isBinaryExpression(t)) {
|
|
1482
823
|
let n = t.getOperatorToken().getText(), r = t.getLeft() === e ? t.getRight() : t.getLeft();
|
|
1483
824
|
return /^[!=]==?$/.test(n) && /^(undefined|null)$/.test(r.getText());
|
|
1484
825
|
}
|
|
1485
|
-
return
|
|
826
|
+
return T.isPrefixUnaryExpression(t) && t.getOperatorToken() === D.ExclamationToken;
|
|
1486
827
|
}))
|
|
1487
828
|
},
|
|
1488
829
|
{
|
|
@@ -1504,7 +845,7 @@ var le = () => {
|
|
|
1504
845
|
scope: "server",
|
|
1505
846
|
smell: "several sequential store reads assembling related data (an N+1 you are writing by hand)",
|
|
1506
847
|
use: "reads are a parent→child walk on ONE key → relations() in a *.relations.ts + .with({ … }) (ONE JSON-aggregate query, every dialect); reads collect ids from SEVERAL sources → keep the assembly and run the independent leading reads under Effect.all (same queries, same results, only concurrent — zero parity risk)",
|
|
1507
|
-
match: (e) =>
|
|
848
|
+
match: (e) => F(e.file, /\bstore\.query$/) >= 3 || F(e.file, /\bstore\.query$/) >= 2 && /\bnew Map\s*\(/.test(e.src)
|
|
1508
849
|
},
|
|
1509
850
|
{
|
|
1510
851
|
id: "store-effect-lift",
|
|
@@ -1527,7 +868,7 @@ var le = () => {
|
|
|
1527
868
|
use: ".encrypted() on the column — boot fails loudly if no cipher is configured, so it can never silently persist plaintext (a *Hash column and a vault* handle are NOT plaintext secrets: the hash IS the protection — often a unique lookup key — and the handle only names a secret held elsewhere, so both are skipped)",
|
|
1528
869
|
match: ({ src: e }) => e.split("\n").some((e) => [...e.matchAll(/\b([A-Za-z_$][\w$]*)\s*:\s*(?:text|varchar|string|longtext)\s*\(/g)].some((t) => {
|
|
1529
870
|
let n = t[1];
|
|
1530
|
-
return !/token|secret|credential|password|apikey|accesskey/i.test(n) ||
|
|
871
|
+
return !/token|secret|credential|password|apikey|accesskey/i.test(n) || dt.test(n) || /^vault/i.test(n) || /\.unique\s*\(/.test(e) ? !1 : !/\.encrypted\s*\(/.test(e);
|
|
1531
872
|
}))
|
|
1532
873
|
},
|
|
1533
874
|
{
|
|
@@ -1537,7 +878,7 @@ var le = () => {
|
|
|
1537
878
|
use: "you cannot look a value up by equality AND encrypt it non-deterministically — `.encrypted()` would make this column unfindable. Store the HASH as the unique lookup key and keep the secret itself either `.encrypted()` in a second column or nowhere (the framework's own `_voltro_api_keys.hashedKey` is the shape: the presented value is hashed and compared, never read back). A deterministic cipher is the other option and buys equality lookup at the cost of leaking which rows share a value",
|
|
1538
879
|
match: ({ src: e }) => e.split("\n").some((e) => [...e.matchAll(/\b([A-Za-z_$][\w$]*)\s*:\s*(?:text|varchar|string|longtext)\s*\(/g)].some((t) => {
|
|
1539
880
|
let n = t[1];
|
|
1540
|
-
return !/token|secret|credential|password|apikey|accesskey/i.test(n) ||
|
|
881
|
+
return !/token|secret|credential|password|apikey|accesskey/i.test(n) || dt.test(n) || /^vault/i.test(n) ? !1 : /\.unique\s*\(/.test(e) && !/\.encrypted\s*\(/.test(e);
|
|
1541
882
|
}))
|
|
1542
883
|
},
|
|
1543
884
|
{
|
|
@@ -1601,9 +942,9 @@ var le = () => {
|
|
|
1601
942
|
scope: "server",
|
|
1602
943
|
smell: "setRowFilter is registered, but no test passes `rowFilter:` to makeTestContext",
|
|
1603
944
|
use: "wire the filter into at least one test (`makeTestContext({ subject, store, rowFilter })`) and sabotage the predicate once to watch it go red — an untested row filter is a visibility rule nothing verifies",
|
|
1604
|
-
match: ({ file: e, testSources: t }) =>
|
|
1605
|
-
let t =
|
|
1606
|
-
return
|
|
945
|
+
match: ({ file: e, testSources: t }) => F(e, /^setRowFilter$/) > 0 && ![...t.values()].some((e) => {
|
|
946
|
+
let t = rt(e);
|
|
947
|
+
return ot(t, "makeTestContext").length > 0 && /\browFilter\b/.test(t);
|
|
1607
948
|
})
|
|
1608
949
|
},
|
|
1609
950
|
{
|
|
@@ -1613,84 +954,84 @@ var le = () => {
|
|
|
1613
954
|
use: "timestampMs / timestampMsOrNull from @voltro/database/wire in the descriptor's output struct — `output` IS the serializer (it is the rpc success schema), so a Date field declared as timestampMs crosses as epoch ms on its own; declaring Schema.Number describes the WIRE type and leaves nothing to convert",
|
|
1614
955
|
match: ({ src: e }) => /\.(getTime|toISOString)\s*\(\s*\)/.test(e) && /\.map\s*\(|=>\s*\(\{/.test(e) && /\bexport\s+default\b/.test(e)
|
|
1615
956
|
}
|
|
1616
|
-
],
|
|
957
|
+
], pt = (e) => R(e), mt = (e, t) => {
|
|
1617
958
|
let n = /* @__PURE__ */ new Set();
|
|
1618
959
|
for (let r of t) {
|
|
1619
|
-
let t =
|
|
960
|
+
let t = v(e, r), i = t.split(/[\\/]/)[0];
|
|
1620
961
|
n.add(i !== void 0 && i !== t ? i : ".");
|
|
1621
962
|
}
|
|
1622
963
|
return [...n].sort();
|
|
1623
|
-
},
|
|
1624
|
-
if (n > 12 ||
|
|
964
|
+
}, I = /\.(tsx?|jsx?)$/, L = /(^|\/)(node_modules|dist|build|\.framework|\.next|coverage)(\/|$)/, ht = /(^|[\\/])[^\\/]*\.generated\.[cm]?[jt]sx?$|\.d\.[cm]?ts$/, gt = (e, t = [], n = 0) => {
|
|
965
|
+
if (n > 12 || L.test(e)) return t;
|
|
1625
966
|
let r;
|
|
1626
967
|
try {
|
|
1627
|
-
r =
|
|
968
|
+
r = C(e, { withFileTypes: !0 });
|
|
1628
969
|
} catch {
|
|
1629
970
|
return t;
|
|
1630
971
|
}
|
|
1631
972
|
for (let i of r) {
|
|
1632
|
-
let r =
|
|
973
|
+
let r = _(e, i.name);
|
|
1633
974
|
if (i.isDirectory()) {
|
|
1634
975
|
if (i.name.startsWith(".")) continue;
|
|
1635
|
-
|
|
1636
|
-
} else
|
|
976
|
+
gt(r, t, n + 1);
|
|
977
|
+
} else je.test(i.name) && t.push(r);
|
|
1637
978
|
}
|
|
1638
979
|
return t;
|
|
1639
|
-
},
|
|
1640
|
-
if (n > 12 ||
|
|
980
|
+
}, R = (e, t = [], n = 0) => {
|
|
981
|
+
if (n > 12 || L.test(e)) return t;
|
|
1641
982
|
let r;
|
|
1642
983
|
try {
|
|
1643
|
-
r =
|
|
984
|
+
r = C(e);
|
|
1644
985
|
} catch {
|
|
1645
986
|
return t;
|
|
1646
987
|
}
|
|
1647
988
|
for (let i of r) {
|
|
1648
|
-
let r =
|
|
989
|
+
let r = _(e, i), a;
|
|
1649
990
|
try {
|
|
1650
|
-
a =
|
|
991
|
+
a = w(r);
|
|
1651
992
|
} catch {
|
|
1652
993
|
continue;
|
|
1653
994
|
}
|
|
1654
|
-
a.isDirectory() ?
|
|
995
|
+
a.isDirectory() ? R(r, t, n + 1) : I.test(i) && !/\.(test|spec)\./.test(i) && !ht.test(i) && t.push(r);
|
|
1655
996
|
}
|
|
1656
997
|
return t;
|
|
1657
|
-
},
|
|
1658
|
-
if (n > 12 ||
|
|
998
|
+
}, _t = (e, t = [], n = 0) => {
|
|
999
|
+
if (n > 12 || L.test(e)) return t;
|
|
1659
1000
|
let r;
|
|
1660
1001
|
try {
|
|
1661
|
-
r =
|
|
1002
|
+
r = C(e);
|
|
1662
1003
|
} catch {
|
|
1663
1004
|
return t;
|
|
1664
1005
|
}
|
|
1665
1006
|
for (let i of r) {
|
|
1666
|
-
let r =
|
|
1007
|
+
let r = _(e, i), a;
|
|
1667
1008
|
try {
|
|
1668
|
-
a =
|
|
1009
|
+
a = w(r);
|
|
1669
1010
|
} catch {
|
|
1670
1011
|
continue;
|
|
1671
1012
|
}
|
|
1672
|
-
a.isDirectory() ?
|
|
1013
|
+
a.isDirectory() ? _t(r, t, n + 1) : I.test(i) && /\.(test|spec)\./.test(i) && t.push(r);
|
|
1673
1014
|
}
|
|
1674
1015
|
return t;
|
|
1675
|
-
},
|
|
1676
|
-
let t =
|
|
1016
|
+
}, vt = (e) => {
|
|
1017
|
+
let t = pt(e);
|
|
1677
1018
|
return {
|
|
1678
|
-
dirs:
|
|
1019
|
+
dirs: mt(e, t),
|
|
1679
1020
|
fileCount: t.length
|
|
1680
1021
|
};
|
|
1681
|
-
},
|
|
1682
|
-
let t =
|
|
1022
|
+
}, yt = (e) => {
|
|
1023
|
+
let t = pt(e);
|
|
1683
1024
|
if (t.length === 0) return [];
|
|
1684
|
-
let n = new
|
|
1025
|
+
let n = new E({
|
|
1685
1026
|
useInMemoryFileSystem: !0,
|
|
1686
1027
|
skipAddingFilesFromTsConfig: !0
|
|
1687
1028
|
}), r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map();
|
|
1688
1029
|
for (let e of t) try {
|
|
1689
|
-
i.set(e,
|
|
1030
|
+
i.set(e, S(e, "utf8"));
|
|
1690
1031
|
} catch {}
|
|
1691
|
-
let a =
|
|
1692
|
-
for (let t of
|
|
1693
|
-
o.set(t,
|
|
1032
|
+
let a = nt([...i.keys()], i, e), o = /* @__PURE__ */ new Map();
|
|
1033
|
+
for (let t of _t(e)) try {
|
|
1034
|
+
o.set(t, S(t, "utf8"));
|
|
1694
1035
|
} catch {}
|
|
1695
1036
|
for (let s of t) {
|
|
1696
1037
|
let t = i.get(s);
|
|
@@ -1700,30 +1041,30 @@ var le = () => {
|
|
|
1700
1041
|
c = {
|
|
1701
1042
|
src: t,
|
|
1702
1043
|
file: n.createSourceFile(`/scan${s.endsWith("x") ? ".tsx" : ".ts"}`, t, { overwrite: !0 }),
|
|
1703
|
-
path:
|
|
1044
|
+
path: v(e, s),
|
|
1704
1045
|
serverOnly: a.has(s),
|
|
1705
1046
|
testSources: o
|
|
1706
1047
|
};
|
|
1707
1048
|
} catch {
|
|
1708
1049
|
continue;
|
|
1709
1050
|
}
|
|
1710
|
-
for (let t of
|
|
1711
|
-
if (t.plugin !== void 0 &&
|
|
1051
|
+
for (let t of ft) {
|
|
1052
|
+
if (t.plugin !== void 0 && at(c, t.plugin) || !t.match(c)) continue;
|
|
1712
1053
|
let n = r.get(t.id) ?? [];
|
|
1713
|
-
n.push(
|
|
1054
|
+
n.push(v(e, s)), r.set(t.id, n);
|
|
1714
1055
|
}
|
|
1715
1056
|
}
|
|
1716
|
-
return
|
|
1057
|
+
return ft.filter((e) => r.has(e.id)).map((e) => ({
|
|
1717
1058
|
rule: e,
|
|
1718
1059
|
files: r.get(e.id)
|
|
1719
1060
|
})).sort((e, t) => t.files.length - e.files.length || e.rule.id.localeCompare(t.rule.id));
|
|
1720
|
-
},
|
|
1721
|
-
let t =
|
|
1061
|
+
}, z = (e) => {
|
|
1062
|
+
let t = vt(e);
|
|
1722
1063
|
return {
|
|
1723
1064
|
root: e,
|
|
1724
1065
|
scannedFiles: t.fileCount,
|
|
1725
1066
|
scannedDirs: t.dirs,
|
|
1726
|
-
findings:
|
|
1067
|
+
findings: yt(e).map(({ rule: e, files: t }) => ({
|
|
1727
1068
|
id: e.id,
|
|
1728
1069
|
scope: e.scope,
|
|
1729
1070
|
smell: e.smell,
|
|
@@ -1731,22 +1072,22 @@ var le = () => {
|
|
|
1731
1072
|
files: t
|
|
1732
1073
|
}))
|
|
1733
1074
|
};
|
|
1734
|
-
},
|
|
1075
|
+
}, bt = 3, xt = .2, St = 50, Ct = (e, t) => e >= St && t > 0 && e / t >= xt, wt = (e) => {
|
|
1735
1076
|
let t = /* @__PURE__ */ new Map();
|
|
1736
|
-
for (let n of
|
|
1737
|
-
t.set(
|
|
1077
|
+
for (let n of R(e)) try {
|
|
1078
|
+
t.set(v(e, n), S(n, "utf8"));
|
|
1738
1079
|
} catch {}
|
|
1739
1080
|
let n = /* @__PURE__ */ new Map();
|
|
1740
|
-
for (let t of
|
|
1741
|
-
n.set(
|
|
1081
|
+
for (let t of gt(e)) try {
|
|
1082
|
+
n.set(v(e, t), S(t, "utf8"));
|
|
1742
1083
|
} catch {}
|
|
1743
|
-
let r =
|
|
1084
|
+
let r = Me({
|
|
1744
1085
|
sources: t,
|
|
1745
1086
|
envFiles: n
|
|
1746
1087
|
});
|
|
1747
|
-
r !== void 0 && (console.log(""), console.log(
|
|
1748
|
-
},
|
|
1749
|
-
let t =
|
|
1088
|
+
r !== void 0 && (console.log(""), console.log(Ne(r)));
|
|
1089
|
+
}, Tt = (e) => {
|
|
1090
|
+
let t = z(e);
|
|
1750
1091
|
if (console.log(""), t.scannedFiles === 0) return console.log(`\u2022 hand-roll scan: no source directories found under ${e}.`), console.log(" Walked the app root recursively (node_modules / dist / .framework pruned). Run `voltro doctor <app>` against an app root."), 0;
|
|
1751
1092
|
if (t.findings.length === 0) return console.log(`\u2713 hand-roll scan: ${t.scannedFiles} file${t.scannedFiles === 1 ? "" : "s"} across ${t.scannedDirs.join(", ")} \u2014 no hand-rolled primitives found.`), 0;
|
|
1752
1093
|
console.log(`\u2022 Shipped primitives you may be hand-rolling (scanned ${t.scannedFiles} files across ${t.scannedDirs.join(", ")}):`);
|
|
@@ -1757,56 +1098,56 @@ var le = () => {
|
|
|
1757
1098
|
console.log(` [${e}]`);
|
|
1758
1099
|
let i = [...r].sort((e, t) => e.files.length - t.files.length);
|
|
1759
1100
|
for (let e of i) {
|
|
1760
|
-
if (
|
|
1101
|
+
if (Ct(e.files.length, t.scannedFiles)) {
|
|
1761
1102
|
let n = Math.round(e.files.length / t.scannedFiles * 100);
|
|
1762
1103
|
console.log(` ${e.smell} \u2014 ${e.files.length} file(s), ${n}% of those scanned: a codebase-wide pattern rather than a work list`), console.log(` \u2192 ${e.use}`), console.log(" (paths omitted on purpose at this scale — `voltro doctor --json` has every one.)");
|
|
1763
1104
|
continue;
|
|
1764
1105
|
}
|
|
1765
|
-
let r = e.files.slice(0,
|
|
1106
|
+
let r = e.files.slice(0, bt).join(", "), i = e.files.length - bt;
|
|
1766
1107
|
i > 0 && (n += i);
|
|
1767
1108
|
let a = i > 0 ? `, +${i} more` : "";
|
|
1768
1109
|
console.log(` ${e.smell} \u2014 ${e.files.length} file(s): ${r}${a}`), console.log(` \u2192 ${e.use}`);
|
|
1769
1110
|
}
|
|
1770
1111
|
}
|
|
1771
1112
|
return n > 0 && console.log(` (${n} more file path${n === 1 ? "" : "s"} not shown \u2014 \`voltro doctor --json\` prints every one.)`), console.log(" (See the \"Pick the SERVER primitive\" / \"Pick the CLIENT primitive\" rubrics in AGENTS.md / CLAUDE.md.)"), t.findings.length;
|
|
1772
|
-
},
|
|
1773
|
-
let t =
|
|
1113
|
+
}, Et = "renderMode:'spa' skips this page's SSR compile while its layout shell still renders server-side — adopt it if the page BODY does not need SSR (internal/authenticated pages); keep 'ssr' if the page content needs SEO or server first-paint.", Dt = /export\s+const\s+renderMode\s*(?::[^=]+)?=\s*['"]([^'"]*)['"]/, Ot = /export\s+const\s+dynamic\s*=\s*['"]force-(dynamic|static)['"]/, kt = (e) => {
|
|
1114
|
+
let t = Dt.exec(e)?.[1];
|
|
1774
1115
|
if (t !== void 0) return t === "ssr" ? "ssr" : null;
|
|
1775
|
-
let n =
|
|
1116
|
+
let n = Ot.exec(e)?.[1];
|
|
1776
1117
|
return n === "dynamic" ? "ssr" : n === "static" ? null : "default";
|
|
1777
|
-
},
|
|
1778
|
-
let t =
|
|
1779
|
-
if (!
|
|
1780
|
-
let { pages:
|
|
1781
|
-
for (let
|
|
1782
|
-
if (!
|
|
1783
|
-
let
|
|
1118
|
+
}, B = async (e) => {
|
|
1119
|
+
let t = _(e, "src", "pages");
|
|
1120
|
+
if (!x(t)) return [];
|
|
1121
|
+
let { pages: n, dirs: r } = await l(t), i = [];
|
|
1122
|
+
for (let a of n) {
|
|
1123
|
+
if (!s(r, a.dirChain).some((e) => e.hasLayout)) continue;
|
|
1124
|
+
let n;
|
|
1784
1125
|
try {
|
|
1785
|
-
|
|
1126
|
+
n = S(_(t, a.file), "utf8");
|
|
1786
1127
|
} catch {
|
|
1787
1128
|
continue;
|
|
1788
1129
|
}
|
|
1789
|
-
if (
|
|
1790
|
-
let
|
|
1791
|
-
|
|
1792
|
-
file:
|
|
1793
|
-
pattern:
|
|
1794
|
-
currentMode:
|
|
1130
|
+
if (c.test(n)) continue;
|
|
1131
|
+
let o = kt(n);
|
|
1132
|
+
o !== null && i.push({
|
|
1133
|
+
file: v(e, _(t, a.file)),
|
|
1134
|
+
pattern: a.pattern,
|
|
1135
|
+
currentMode: o
|
|
1795
1136
|
});
|
|
1796
1137
|
}
|
|
1797
|
-
return
|
|
1798
|
-
},
|
|
1799
|
-
if (console.log(""), !
|
|
1800
|
-
let t = await
|
|
1138
|
+
return i;
|
|
1139
|
+
}, At = 10, jt = async (e) => {
|
|
1140
|
+
if (console.log(""), !x(_(e, "src", "pages"))) return console.log("• spa-candidate scan: no src/pages here — nothing to check (web apps only)."), 0;
|
|
1141
|
+
let t = await B(e);
|
|
1801
1142
|
if (t.length === 0) return console.log("✓ spa-candidate scan: no loader-free, layout-backed pages to flag."), 0;
|
|
1802
1143
|
console.log(`• renderMode:'spa' candidates (${t.length} page${t.length === 1 ? "" : "s"} — loader-free, under a layout, currently ssr/default):`);
|
|
1803
|
-
for (let e of t.slice(0,
|
|
1144
|
+
for (let e of t.slice(0, At)) {
|
|
1804
1145
|
let t = e.currentMode === "ssr" ? "renderMode:'ssr'" : "default renderMode";
|
|
1805
1146
|
console.log(` ${e.file} (${e.pattern}) — ${t}`);
|
|
1806
1147
|
}
|
|
1807
|
-
let n = t.length -
|
|
1808
|
-
return n > 0 && console.log(` (+${n} more page${n === 1 ? "" : "s"} not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${
|
|
1809
|
-
},
|
|
1148
|
+
let n = t.length - At;
|
|
1149
|
+
return n > 0 && console.log(` (+${n} more page${n === 1 ? "" : "s"} not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${Et}`), t.length;
|
|
1150
|
+
}, Mt = /* @__PURE__ */ new Set([
|
|
1810
1151
|
"node_modules",
|
|
1811
1152
|
"dist",
|
|
1812
1153
|
"build",
|
|
@@ -1814,69 +1155,69 @@ var le = () => {
|
|
|
1814
1155
|
".vite",
|
|
1815
1156
|
".framework",
|
|
1816
1157
|
".git"
|
|
1817
|
-
]),
|
|
1158
|
+
]), Nt = (e, t, n) => {
|
|
1818
1159
|
let r;
|
|
1819
1160
|
try {
|
|
1820
|
-
r =
|
|
1161
|
+
r = C(e, { withFileTypes: !0 });
|
|
1821
1162
|
} catch {
|
|
1822
1163
|
return;
|
|
1823
1164
|
}
|
|
1824
1165
|
for (let i of r) {
|
|
1825
|
-
if (
|
|
1826
|
-
let r =
|
|
1827
|
-
i.isDirectory() ?
|
|
1166
|
+
if (Mt.has(i.name)) continue;
|
|
1167
|
+
let r = _(e, i.name);
|
|
1168
|
+
i.isDirectory() ? Nt(r, t, n) : i.isFile() && i.name.endsWith(".tsx") && n.push(v(t, r));
|
|
1828
1169
|
}
|
|
1829
|
-
},
|
|
1170
|
+
}, Pt = (e, t) => {
|
|
1830
1171
|
let n = /* @__PURE__ */ new Set(), r = /(?:from|import)\s*\(?\s*['"](\.[^'"]*)['"]/g;
|
|
1831
1172
|
for (let i of t) {
|
|
1832
1173
|
let t;
|
|
1833
1174
|
try {
|
|
1834
|
-
t =
|
|
1175
|
+
t = S(_(e, i), "utf8");
|
|
1835
1176
|
} catch {
|
|
1836
1177
|
continue;
|
|
1837
1178
|
}
|
|
1838
|
-
let a =
|
|
1179
|
+
let a = ae(_(e, i));
|
|
1839
1180
|
for (let i of t.matchAll(r)) {
|
|
1840
|
-
let t =
|
|
1181
|
+
let t = y(a, i[1]);
|
|
1841
1182
|
for (let r of [
|
|
1842
1183
|
t,
|
|
1843
1184
|
`${t}.tsx`,
|
|
1844
1185
|
`${t}.ts`,
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
]) if (
|
|
1848
|
-
n.add(
|
|
1186
|
+
_(t, "index.tsx"),
|
|
1187
|
+
_(t, "index.ts")
|
|
1188
|
+
]) if (x(r) && w(r).isFile()) {
|
|
1189
|
+
n.add(v(e, r));
|
|
1849
1190
|
break;
|
|
1850
1191
|
}
|
|
1851
1192
|
}
|
|
1852
1193
|
}
|
|
1853
1194
|
return n;
|
|
1854
|
-
},
|
|
1855
|
-
let n = e.slice(t.length).split(
|
|
1195
|
+
}, Ft = (e, t) => {
|
|
1196
|
+
let n = e.slice(t.length).split(b).slice(0, -1)[0];
|
|
1856
1197
|
return n !== void 0 && n.startsWith("[");
|
|
1857
|
-
},
|
|
1858
|
-
let t = [], n =
|
|
1859
|
-
if (!
|
|
1198
|
+
}, V = (e) => {
|
|
1199
|
+
let t = [], n = _(e, "src", "pages"), r = _(e, "src");
|
|
1200
|
+
if (!x(r)) return t;
|
|
1860
1201
|
let i = [];
|
|
1861
|
-
|
|
1862
|
-
let a = `${
|
|
1863
|
-
|
|
1202
|
+
Nt(r, e, i), i.sort();
|
|
1203
|
+
let a = `${v(e, n)}${b}`;
|
|
1204
|
+
Pt(e, i);
|
|
1864
1205
|
for (let n of i) {
|
|
1865
|
-
let r = n.slice(n.lastIndexOf(
|
|
1206
|
+
let r = n.slice(n.lastIndexOf(b) + 1);
|
|
1866
1207
|
if (n.startsWith(a)) {
|
|
1867
1208
|
if (r !== "page.tsx") continue;
|
|
1868
|
-
|
|
1209
|
+
x(_(e, d(n))) || t.push({
|
|
1869
1210
|
kind: "page-without-test",
|
|
1870
1211
|
file: n
|
|
1871
1212
|
});
|
|
1872
1213
|
try {
|
|
1873
|
-
let r =
|
|
1214
|
+
let r = S(_(e, n), "utf8");
|
|
1874
1215
|
!/\bexport\s+(?:const|let|var|function)\s+meta\b/.test(r) && !/\bexport\s*\{[^}]*\bmeta\b/.test(r) && t.push({
|
|
1875
1216
|
kind: "page-without-meta",
|
|
1876
1217
|
file: n
|
|
1877
1218
|
});
|
|
1878
1219
|
} catch {}
|
|
1879
|
-
|
|
1220
|
+
Ft(n, a) && t.push({
|
|
1880
1221
|
kind: "top-level-dynamic-route",
|
|
1881
1222
|
file: n
|
|
1882
1223
|
});
|
|
@@ -1888,33 +1229,33 @@ var le = () => {
|
|
|
1888
1229
|
});
|
|
1889
1230
|
}
|
|
1890
1231
|
return t;
|
|
1891
|
-
},
|
|
1232
|
+
}, It = 10, H = (e, t, n) => {
|
|
1892
1233
|
if (t.length === 0) return;
|
|
1893
1234
|
console.log(`• ${e} (${t.length}):`);
|
|
1894
|
-
for (let e of t.slice(0,
|
|
1895
|
-
let r = t.length -
|
|
1235
|
+
for (let e of t.slice(0, It)) console.log(` ${e}`);
|
|
1236
|
+
let r = t.length - It;
|
|
1896
1237
|
r > 0 && console.log(` (+${r} more not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${n}`);
|
|
1897
|
-
},
|
|
1898
|
-
if (console.log(""), !
|
|
1899
|
-
let t =
|
|
1238
|
+
}, Lt = (e) => {
|
|
1239
|
+
if (console.log(""), !x(_(e, "src", "pages"))) return console.log("• page-convention scan: no src/pages here — nothing to check (web apps only)."), 0;
|
|
1240
|
+
let t = V(e);
|
|
1900
1241
|
if (t.length === 0) return console.log("✓ page-convention scan: every route is a page.tsx in its own directory, with a test beside it."), 0;
|
|
1901
1242
|
let n = (e) => t.filter((t) => t.kind === e).map((e) => e.file);
|
|
1902
|
-
return
|
|
1903
|
-
},
|
|
1904
|
-
let t =
|
|
1243
|
+
return H("will NEVER route — page.tsx outside src/pages/", n("suffixed-outside-pages"), "the name says \"route\", the location says otherwise, and the location wins: the URL comes from the directory chain under src/pages/. Move it into the directory whose route it serves, or rename it."), H("pages with no test beside them", n("page-without-test"), "a page and its test belong together — `users/[id].page.tsx` → `users/[id].page.test.tsx`. Advisory only; a missing test is a gap, not a defect."), H("routes whose FIRST segment is dynamic — they claim every URL of that depth", n("top-level-dynamic-route"), "a route like `[id]/[code]` answers `/api/health`, `/internal/readiness` and anything else with that many segments on this origin. That is what the pattern means, so it is not a bug — but it is invisible until something requests such a URL, and then the page renders, its loader runs, and the failure reads as an application error rather than as a route claiming a path nobody meant it to. Give the route a literal first segment (`scan/[id]/[code]`) if you did not intend to own the whole origin at that depth. Advisory."), H("pages with no `meta` export — they serve the APP name as their title", n("page-without-meta"), "a page with no `meta` sets no title, so the response carries `<title>{your app.config name}</title>` — the same string on every such page, to every reader that does not run JavaScript: link previews, crawlers, a bookmark taken before hydration, a screen reader announcing the document. The client router fixes the tab after hydration, which is exactly why this is easy to miss. Add `export const meta = { title: \"…\" }` (or a function of params/loaderData). If such a page also has no real body — it redirects from an effect, say — the missing title is the symptom rather than the bug: on an `ssr` route the server answers 200 with an empty document and the redirect happens only once JavaScript runs. Move it into the loader (`redirect(...)`, a real 303). Advisory."), t.length;
|
|
1244
|
+
}, Rt = "voltro doctor [app] [flags]\n\nPreflight an app root: verify it can boot under production `voltro serve`,\nreport shipped primitives the source looks to be hand-rolling, and flag pages\nthat are safe `renderMode:'spa'` candidates.\n\nArguments:\n [app] App directory to check (default: the current directory).\n\nFlags:\n --strict Treat a missing serve bundle as blocking regardless of NODE_ENV\n (the `voltro serve --preflight` behavior). Without it, a missing\n bundle only fails when NODE_ENV=production.\n --json Print the complete scan as JSON — hand-roll findings AND spa\n candidates, every file path, nothing elided. Skips the preflight output.\n --write-authz-allowlist\n Record today's executors that reference NO access check into\n voltro-authz-allowlist.txt, so every later run fails only on\n ADDITIONS. The list is DEBT, not approval — an entry stops\n mattering as soon as its executor gains a guard.\n -h, --help Print this help.", zt = /* @__PURE__ */ new Map(), U = (e) => {
|
|
1245
|
+
let t = zt.get(e);
|
|
1905
1246
|
if (t !== void 0) return t;
|
|
1906
1247
|
let n = (async () => {
|
|
1907
|
-
let { walk: t, loadDiscovered: n } = await import("./dev-
|
|
1248
|
+
let { walk: t, loadDiscovered: n } = await import("./dev-D2BikO7a.js"), r = await t(e);
|
|
1908
1249
|
return {
|
|
1909
1250
|
files: r,
|
|
1910
1251
|
discovered: await n(r)
|
|
1911
1252
|
};
|
|
1912
1253
|
})();
|
|
1913
|
-
return
|
|
1914
|
-
},
|
|
1254
|
+
return zt.set(e, n), n;
|
|
1255
|
+
}, Bt = async (e) => {
|
|
1915
1256
|
let t;
|
|
1916
1257
|
try {
|
|
1917
|
-
let { files: n, discovered: r } = await
|
|
1258
|
+
let { files: n, discovered: r } = await U(e), i = r.tables.map((e) => {
|
|
1918
1259
|
let t = e;
|
|
1919
1260
|
return {
|
|
1920
1261
|
name: t.tableName,
|
|
@@ -1922,12 +1263,12 @@ var le = () => {
|
|
|
1922
1263
|
};
|
|
1923
1264
|
});
|
|
1924
1265
|
if (i.length === 0) return;
|
|
1925
|
-
t =
|
|
1266
|
+
t = pe(fe(n, i, e));
|
|
1926
1267
|
} catch {
|
|
1927
1268
|
return;
|
|
1928
1269
|
}
|
|
1929
1270
|
t !== null && (console.log(""), console.log(`${t.includes("does not exist") ? "✗" : "✓"} ${t}`));
|
|
1930
|
-
},
|
|
1271
|
+
}, Vt = (e) => e.map((e) => {
|
|
1931
1272
|
let t = e, n = Object.entries(t.fields ?? {}).map(([e, t]) => ({
|
|
1932
1273
|
name: e,
|
|
1933
1274
|
type: typeof t?.type == "string" ? t.type : ""
|
|
@@ -1937,22 +1278,22 @@ var le = () => {
|
|
|
1937
1278
|
columns: n,
|
|
1938
1279
|
primaryKey: t.appliedPrimaryKey ?? []
|
|
1939
1280
|
};
|
|
1940
|
-
}),
|
|
1281
|
+
}), W = async (e) => {
|
|
1941
1282
|
try {
|
|
1942
|
-
let { discovered: t } = await
|
|
1943
|
-
return
|
|
1283
|
+
let { discovered: t } = await U(e);
|
|
1284
|
+
return ve(Vt(t.tables));
|
|
1944
1285
|
} catch {
|
|
1945
1286
|
return null;
|
|
1946
1287
|
}
|
|
1947
|
-
},
|
|
1948
|
-
let t = await
|
|
1288
|
+
}, Ht = async (e) => {
|
|
1289
|
+
let t = await W(e);
|
|
1949
1290
|
if (t === null || t.tablesChecked === 0) return;
|
|
1950
|
-
let n =
|
|
1291
|
+
let n = be(t);
|
|
1951
1292
|
n !== null && (console.log(""), console.log(`${t.issues.length > 0 ? "✗" : "✓"} ${n}`));
|
|
1952
|
-
},
|
|
1293
|
+
}, G = async (e) => {
|
|
1953
1294
|
try {
|
|
1954
|
-
let { payloadPropertyNames: t } = await import("@voltro/runtime"), { discovered: n } = await
|
|
1955
|
-
return
|
|
1295
|
+
let { payloadPropertyNames: t } = await import("@voltro/runtime"), { discovered: n } = await U(e);
|
|
1296
|
+
return we(n.events.map((e) => {
|
|
1956
1297
|
let n = e.descriptor, r = t(n.key);
|
|
1957
1298
|
return {
|
|
1958
1299
|
name: n.name,
|
|
@@ -1964,19 +1305,19 @@ var le = () => {
|
|
|
1964
1305
|
} catch {
|
|
1965
1306
|
return null;
|
|
1966
1307
|
}
|
|
1967
|
-
},
|
|
1968
|
-
let t = await
|
|
1308
|
+
}, Ut = async (e) => {
|
|
1309
|
+
let t = await G(e);
|
|
1969
1310
|
if (t === null || t.eventsChecked === 0) return;
|
|
1970
|
-
let n =
|
|
1311
|
+
let n = Te(t);
|
|
1971
1312
|
n !== null && (console.log(""), console.log(`• ${n}`));
|
|
1972
|
-
let r =
|
|
1313
|
+
let r = Ee(t);
|
|
1973
1314
|
r !== null && (console.log(""), console.log(`⚠️ ${r}`));
|
|
1974
|
-
},
|
|
1315
|
+
}, K = (e) => {
|
|
1975
1316
|
let t = e, n = typeof t?.message == "string" && t.message !== "" ? t.message : String(e), r = typeof t?.code == "string" ? t.code : void 0;
|
|
1976
1317
|
return r === void 0 ? n : `${r}: ${n}`;
|
|
1977
|
-
},
|
|
1318
|
+
}, q = async (e) => {
|
|
1978
1319
|
try {
|
|
1979
|
-
let { formatServerOnlyLeaks: t } = await import("@voltro/runtime"), n = (await
|
|
1320
|
+
let { formatServerOnlyLeaks: t } = await import("@voltro/runtime"), n = (await U(e)).discovered.serverOnlyLeaks, r = t(n);
|
|
1980
1321
|
return {
|
|
1981
1322
|
status: {
|
|
1982
1323
|
checked: !0,
|
|
@@ -1987,17 +1328,117 @@ var le = () => {
|
|
|
1987
1328
|
} catch (e) {
|
|
1988
1329
|
return { status: {
|
|
1989
1330
|
checked: !1,
|
|
1990
|
-
reason:
|
|
1331
|
+
reason: K(e)
|
|
1991
1332
|
} };
|
|
1992
1333
|
}
|
|
1993
|
-
},
|
|
1994
|
-
|
|
1334
|
+
}, J = async (r) => {
|
|
1335
|
+
try {
|
|
1336
|
+
let { loadApiConfig: i } = await import("./dev-D2BikO7a.js"), { discovered: a } = await U(r), o = n(await i(r)), s = {
|
|
1337
|
+
queries: a.queries,
|
|
1338
|
+
mutations: a.mutations,
|
|
1339
|
+
actions: a.actions,
|
|
1340
|
+
streams: a.streams,
|
|
1341
|
+
events: a.events
|
|
1342
|
+
};
|
|
1343
|
+
return {
|
|
1344
|
+
checked: !0,
|
|
1345
|
+
defaultDeny: o,
|
|
1346
|
+
undecided: e(s),
|
|
1347
|
+
openAccess: t(s)
|
|
1348
|
+
};
|
|
1349
|
+
} catch (e) {
|
|
1350
|
+
return {
|
|
1351
|
+
checked: !1,
|
|
1352
|
+
reason: K(e),
|
|
1353
|
+
defaultDeny: !0,
|
|
1354
|
+
undecided: [],
|
|
1355
|
+
openAccess: []
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
}, Wt = async (e) => {
|
|
1359
|
+
let t = await J(e);
|
|
1360
|
+
if (!t.checked) return console.log(""), console.log("• access decisions: NOT CHECKED — the app's descriptors could not be loaded (not a pass)"), console.log(` reason: ${t.reason ?? "unknown"}`), !1;
|
|
1361
|
+
console.log(""), console.log(`access decisions · security.defaultDeny ${t.defaultDeny ? "ON" : "OFF"}`), console.log(` ✗ no access decision ${String(t.undecided.length).padStart(4)}`), console.log(` ✓ openAccess, declared on purpose ${String(t.openAccess.length).padStart(4)}`);
|
|
1362
|
+
for (let e of t.openAccess) console.log(` ${e.tag} — ${e.openAccess ?? ""}`);
|
|
1363
|
+
if (t.undecided.length === 0) return !1;
|
|
1364
|
+
console.log("");
|
|
1365
|
+
for (let e of t.undecided) console.log(` ✗ ${e.tag} (${e.kind})${e.file === void 0 ? "" : `\n ${e.file}`}`);
|
|
1366
|
+
return console.log(""), t.defaultDeny ? (console.log(" `voltro dev` and `voltro serve` REFUSE to boot on these. Give each a decision:"), Gt(), !0) : (console.log(" This app sets `security: { defaultDeny: false }`, so these BOOT — and each one is"), console.log(" callable by any authenticated session. Advisory here for that reason only."), Gt(), !1);
|
|
1367
|
+
}, Gt = () => {
|
|
1368
|
+
console.log(" `guards: [{ scope: '…' }]` authority the SUBJECT holds globally"), console.log(" `guards: [{ action, resourceType, resource }]` authority per ROW / per resource"), console.log(" — backed by `defineResourcePolicy` + a tuple source you register. If your"), console.log(" relationships already live in your own tables (a `teamMembers` row), register"), console.log(" a tuple source over them; nothing is copied. Fail-closed without a resolver."), console.log(" Docs: Authentication → Authorization."), console.log(" `openAccess: '<why anyone may call it>'` deliberately unauthenticated");
|
|
1369
|
+
}, Y = async (e) => {
|
|
1370
|
+
try {
|
|
1371
|
+
let { files: t } = await U(e), { WEBHOOK_PATTERN: n, loadWebhookFiles: r } = await import("./webhookDiscovery-CrGAfhIG.js"), { incomingWebhookVerification: i } = await import("@voltro/plugin-webhooks"), a = t.filter((e) => n.test(ie(e)));
|
|
1372
|
+
if (a.length === 0) return {
|
|
1373
|
+
checked: !0,
|
|
1374
|
+
unverified: [],
|
|
1375
|
+
open: [],
|
|
1376
|
+
verified: 0
|
|
1377
|
+
};
|
|
1378
|
+
let o = await r(a, ce({ scope: "voltro:doctor" })), s = [], c = [], l = 0;
|
|
1379
|
+
for (let e of o.incoming) {
|
|
1380
|
+
let t = i(e.descriptor);
|
|
1381
|
+
t === null ? s.push({
|
|
1382
|
+
id: e.descriptor.id,
|
|
1383
|
+
file: e.file
|
|
1384
|
+
}) : t === "none" ? c.push({
|
|
1385
|
+
id: e.descriptor.id,
|
|
1386
|
+
file: e.file
|
|
1387
|
+
}) : l += 1;
|
|
1388
|
+
}
|
|
1389
|
+
return {
|
|
1390
|
+
checked: !0,
|
|
1391
|
+
unverified: s,
|
|
1392
|
+
open: c,
|
|
1393
|
+
verified: l
|
|
1394
|
+
};
|
|
1395
|
+
} catch (e) {
|
|
1396
|
+
return {
|
|
1397
|
+
checked: !1,
|
|
1398
|
+
reason: K(e),
|
|
1399
|
+
unverified: [],
|
|
1400
|
+
open: [],
|
|
1401
|
+
verified: 0
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
}, Kt = async (e) => {
|
|
1405
|
+
let t = await Y(e);
|
|
1406
|
+
if (!t.checked) return console.log(""), console.log("• webhook verification: NOT CHECKED — the app's webhooks could not be loaded (not a pass)"), console.log(` reason: ${t.reason ?? "unknown"}`), !1;
|
|
1407
|
+
if (t.verified + t.open.length + t.unverified.length === 0) return !1;
|
|
1408
|
+
console.log(""), console.log("incoming webhook verification"), console.log(` ✓ signature / provider verified ${String(t.verified).padStart(4)}`), console.log(` ! deliberately public (none) ${String(t.open.length).padStart(4)}`), console.log(` ✗ nothing verifies the caller ${String(t.unverified.length).padStart(4)}`);
|
|
1409
|
+
for (let e of t.open) console.log(` ${e.id} — \`verification: 'none'\`; a gateway / IP allow-list owns this URL`), console.log(` ${e.file}`);
|
|
1410
|
+
if (t.unverified.length === 0) return !1;
|
|
1411
|
+
console.log("");
|
|
1412
|
+
for (let e of t.unverified) console.log(` ✗ ${e.id}\n ${e.file}`);
|
|
1413
|
+
return console.log(""), console.log(" These are public POSTs that run your application code, and `voltro dev` /"), console.log(" `voltro serve` REFUSE to mount them. Declare one of: `provider:` (a preset),"), console.log(" `signature:` (your own HMAC scheme), `verification: 'provider'` (your handler"), console.log(" verifies), or `verification: 'none'` (deliberately public)."), !0;
|
|
1414
|
+
}, qt = async (e) => {
|
|
1415
|
+
let { status: t, message: n } = await q(e);
|
|
1995
1416
|
return t.checked ? n === void 0 ? !1 : (console.log(""), console.log(`✗ [serverOnly] ${n}`), !0) : (console.log(""), console.log("• serverOnly: NOT CHECKED — the app's descriptors could not be loaded (not a pass)"), console.log(` reason: ${t.reason ?? "unknown"}`), console.log(" This audit is the automated check that a hash or a token cannot reach the wire, so"), console.log(" an app that leans on `.serverOnly()` should treat a persistent skip as a failure."), console.log(" `voltro doctor --json` reports it as `serverOnly.checked: false` for a CI gate."), !1);
|
|
1996
|
-
},
|
|
1417
|
+
}, Jt = async (e) => Xe({
|
|
1418
|
+
hasWorkflows: async () => (await U(e)).discovered.workflows.length > 0,
|
|
1419
|
+
openStore: async () => {
|
|
1420
|
+
let { loadApiConfig: t, buildStore: n } = await import("./dev-D2BikO7a.js"), r = await t(e);
|
|
1421
|
+
if (r === null) throw Error("no app.config.ts in this directory");
|
|
1422
|
+
let i = await n(r, void 0);
|
|
1423
|
+
return {
|
|
1424
|
+
store: i,
|
|
1425
|
+
close: async () => {
|
|
1426
|
+
await i.close?.();
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
},
|
|
1430
|
+
sweep: async (e) => {
|
|
1431
|
+
let { sweepStalledRuns: t } = await import("@voltro/workflow");
|
|
1432
|
+
return t({ store: e });
|
|
1433
|
+
}
|
|
1434
|
+
}), Yt = async (e) => {
|
|
1435
|
+
let t = Qe(await Jt(e));
|
|
1436
|
+
t !== null && (console.log(""), console.log(t));
|
|
1437
|
+
}, Xt = async (e) => {
|
|
1997
1438
|
let t;
|
|
1998
1439
|
try {
|
|
1999
|
-
let { payloadPropertyNames: n } = await import("@voltro/runtime"), { files: r, discovered: i } = await
|
|
2000
|
-
t =
|
|
1440
|
+
let { payloadPropertyNames: n } = await import("@voltro/runtime"), { files: r, discovered: i } = await U(e);
|
|
1441
|
+
t = ke(r, i.workflows.map((e) => {
|
|
2001
1442
|
let t = e.definition, r = t.payloadSchema === void 0 ? {
|
|
2002
1443
|
required: [],
|
|
2003
1444
|
optional: []
|
|
@@ -2011,68 +1452,68 @@ var le = () => {
|
|
|
2011
1452
|
} catch {
|
|
2012
1453
|
return;
|
|
2013
1454
|
}
|
|
2014
|
-
let n =
|
|
1455
|
+
let n = Ae(t);
|
|
2015
1456
|
if (n === null) return;
|
|
2016
1457
|
console.log("");
|
|
2017
1458
|
let r = t.issues.length > 0 ? "✗" : t.unchecked.length > 0 ? "•" : "✓";
|
|
2018
1459
|
console.log(`${r} ${n}`);
|
|
2019
|
-
},
|
|
1460
|
+
}, X = "voltro-authz-allowlist.txt", Zt = (e, t = (e) => {
|
|
2020
1461
|
try {
|
|
2021
|
-
return
|
|
1462
|
+
return S(e, "utf8");
|
|
2022
1463
|
} catch {
|
|
2023
1464
|
return "";
|
|
2024
1465
|
}
|
|
2025
|
-
}) => /* @__PURE__ */ new Set([...
|
|
1466
|
+
}) => /* @__PURE__ */ new Set([...j, ...Ie(R(e).map(t))]), Qt = (e, t) => {
|
|
2026
1467
|
let n = t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2027
1468
|
return new RegExp(String.raw`(?:export\s+)?const\s+([A-Za-z_$][\w$]*)\s*=\s*define[A-Za-z]*Event\s*\(\s*\{[^}]*?name\s*:\s*['"\`]${n}['"\`]`, "s").exec(e)?.[1];
|
|
2028
|
-
},
|
|
1469
|
+
}, Z = async (e) => {
|
|
2029
1470
|
try {
|
|
2030
|
-
let { discovered: t } = await
|
|
1471
|
+
let { discovered: t } = await U(e), n = t.events.map((e) => ({
|
|
2031
1472
|
name: e.descriptor.name,
|
|
2032
1473
|
file: e.file
|
|
2033
1474
|
})).filter((e) => typeof e.name == "string");
|
|
2034
1475
|
if (n.length === 0) return [];
|
|
2035
1476
|
let r = new Set(t.events.map((e) => e.file)), i = (e) => {
|
|
2036
1477
|
try {
|
|
2037
|
-
return
|
|
1478
|
+
return S(e, "utf8");
|
|
2038
1479
|
} catch {
|
|
2039
1480
|
return "";
|
|
2040
1481
|
}
|
|
2041
|
-
}, a =
|
|
1482
|
+
}, a = R(e).filter((e) => !r.has(e)).map(i).join("\n");
|
|
2042
1483
|
return n.filter(({ name: e, file: t }) => {
|
|
2043
1484
|
if (a.includes(e)) return !1;
|
|
2044
|
-
let n =
|
|
1485
|
+
let n = Qt(i(t), e);
|
|
2045
1486
|
return n === void 0 || !new RegExp(String.raw`\b${n}\b`).test(a);
|
|
2046
1487
|
}).map((e) => e.name).sort();
|
|
2047
1488
|
} catch {
|
|
2048
1489
|
return [];
|
|
2049
1490
|
}
|
|
2050
|
-
},
|
|
2051
|
-
let t = await
|
|
1491
|
+
}, $t = async (e) => {
|
|
1492
|
+
let t = await Z(e);
|
|
2052
1493
|
if (t.length !== 0) {
|
|
2053
1494
|
console.log(""), console.log(`• ${t.length} declared event(s) with no emit call site found:`);
|
|
2054
1495
|
for (let e of t) console.log(` ${e}`);
|
|
2055
1496
|
console.log(""), console.log(" An event you declare is an event partners can subscribe to. One that"), console.log(" never fires is indistinguishable, from their side, from a broken"), console.log(" endpoint of their own — so they debug your contract instead of"), console.log(" reading it."), console.log(" ADVISORY: this looks for the event name AND the identifier its"), console.log(" declaration binds it to, so `emit(ctx, apiKeyCreated, …)` counts. An"), console.log(" emit through an ASSEMBLED name still reads as missing here.");
|
|
2056
1497
|
}
|
|
2057
|
-
},
|
|
1498
|
+
}, en = async (e) => {
|
|
2058
1499
|
try {
|
|
2059
|
-
let { loadApiConfig: t } = await import("./dev-
|
|
1500
|
+
let { loadApiConfig: t } = await import("./dev-D2BikO7a.js"), n = (await t(e))?.doctor?.scopeVocabulary;
|
|
2060
1501
|
if (typeof n != "string") return [];
|
|
2061
1502
|
let [r, i] = n.split("#");
|
|
2062
1503
|
if (r === void 0 || i === void 0) return [];
|
|
2063
|
-
let a = (await import(
|
|
1504
|
+
let a = (await import(se(y(e, r)).href))[i];
|
|
2064
1505
|
return Array.isArray(a) ? a.filter((e) => typeof e == "string") : [];
|
|
2065
1506
|
} catch {
|
|
2066
1507
|
return [];
|
|
2067
1508
|
}
|
|
2068
|
-
},
|
|
1509
|
+
}, Q = async (e) => {
|
|
2069
1510
|
let t = {
|
|
2070
1511
|
evaluated: !1,
|
|
2071
1512
|
vocabularySize: 0,
|
|
2072
1513
|
findings: []
|
|
2073
1514
|
};
|
|
2074
1515
|
try {
|
|
2075
|
-
let { offlineManifest: n, toInput: r } = await import("./checkCommand-
|
|
1516
|
+
let { offlineManifest: n, toInput: r } = await import("./checkCommand-DKpDLlqu.js"), { buildAppGraph: i, checkAppGraph: a } = await import("./appGraph-zuMGKVYX.js"), o = await n(e), s = r(o.manifest, o.tables), c = await en(e), l = [.../* @__PURE__ */ new Set([...s.scopes ?? [], ...c])];
|
|
2076
1517
|
if (l.length === 0) return t;
|
|
2077
1518
|
let u = i({
|
|
2078
1519
|
...s,
|
|
@@ -2091,8 +1532,8 @@ var le = () => {
|
|
|
2091
1532
|
} catch {
|
|
2092
1533
|
return t;
|
|
2093
1534
|
}
|
|
2094
|
-
},
|
|
2095
|
-
let { findings: t, evaluated: n } = await
|
|
1535
|
+
}, tn = async (e) => {
|
|
1536
|
+
let { findings: t, evaluated: n } = await Q(e);
|
|
2096
1537
|
if (!n) {
|
|
2097
1538
|
console.log(""), console.log("• scope vocabulary: none found — the unknown-scope rule did NOT run."), console.log(" It compares each procedure's required scopes against the scopes your"), console.log(" roles actually grant, and needs somewhere to read that set from."), console.log(" `@voltro/plugin-rbac` publishes it from its `roles` map; an app doing"), console.log(" RBAC without the plugin can point at its own:"), console.log(""), console.log(" // app.config.ts"), console.log(" doctor: { scopeVocabulary: './lib/teamRoles.ts#ALL_TEAM_SCOPES' }"), console.log(""), console.log(" Until then this section is NOT a clean bill of health — a procedure"), console.log(" requiring a scope no role grants is uncallable by everyone, and"), console.log(" nothing here would have told you.");
|
|
2098
1539
|
return;
|
|
@@ -2102,35 +1543,35 @@ var le = () => {
|
|
|
2102
1543
|
for (let e of t) console.log(` ${e.node}${e.file === void 0 ? "" : ` ${e.file}`}`), console.log(` ${e.detail}`);
|
|
2103
1544
|
console.log(""), console.log(" Each of these is uncallable by every user in every role — not"), console.log(" misconfigured, DEAD — and nothing fails at boot to say so. The"), console.log(" usual cause is a typo or a rename that moved one side only."), console.log(` ${t[0].fix}`);
|
|
2104
1545
|
}
|
|
2105
|
-
},
|
|
1546
|
+
}, nn = async (e) => {
|
|
2106
1547
|
let t = ["src/locales", "locales"], n, r = [];
|
|
2107
1548
|
for (let i of t) {
|
|
2108
|
-
let t =
|
|
2109
|
-
if (
|
|
2110
|
-
r =
|
|
1549
|
+
let t = _(e, i);
|
|
1550
|
+
if (x(t)) try {
|
|
1551
|
+
r = C(t).filter((e) => /\.tsx?$/.test(e) && !e.endsWith(".d.ts")).map((e) => e.replace(/\.tsx?$/, "")), n = i;
|
|
2111
1552
|
break;
|
|
2112
1553
|
} catch {}
|
|
2113
1554
|
}
|
|
2114
1555
|
if (n === void 0 || r.length === 0) return;
|
|
2115
1556
|
let i = [];
|
|
2116
1557
|
try {
|
|
2117
|
-
let { loadApiConfig: t } = await import("./dev-
|
|
1558
|
+
let { loadApiConfig: t } = await import("./dev-D2BikO7a.js");
|
|
2118
1559
|
i = (await t(e))?.locales ?? [];
|
|
2119
1560
|
} catch {
|
|
2120
1561
|
return;
|
|
2121
1562
|
}
|
|
2122
|
-
let a =
|
|
1563
|
+
let a = Pe({
|
|
2123
1564
|
catalogCodes: r,
|
|
2124
1565
|
declaredLocales: i
|
|
2125
1566
|
});
|
|
2126
1567
|
if (a !== void 0) {
|
|
2127
1568
|
console.log("");
|
|
2128
|
-
for (let e of
|
|
1569
|
+
for (let e of Fe(a, n)) console.log(e);
|
|
2129
1570
|
}
|
|
2130
|
-
},
|
|
2131
|
-
let { discovered: t } = await
|
|
1571
|
+
}, rn = async (e) => {
|
|
1572
|
+
let { discovered: t } = await U(e), n = [];
|
|
2132
1573
|
try {
|
|
2133
|
-
let { loadApiConfig: t } = await import("./dev-
|
|
1574
|
+
let { loadApiConfig: t } = await import("./dev-D2BikO7a.js");
|
|
2134
1575
|
n = ((await t(e))?.plugins ?? []).map((e) => {
|
|
2135
1576
|
let t = (e.extendSchema?.tables ?? []).map((e) => {
|
|
2136
1577
|
let t = e;
|
|
@@ -2138,16 +1579,13 @@ var le = () => {
|
|
|
2138
1579
|
tableName: typeof t.tableName == "string" ? t.tableName : "",
|
|
2139
1580
|
renamedFrom: typeof t.renamedFrom == "string" ? t.renamedFrom : void 0
|
|
2140
1581
|
};
|
|
2141
|
-
}).filter((e) => e.tableName !== ""), n = (e.rpcClientDescriptors ?? []).map((e) =>
|
|
2142
|
-
let t = e, n = t.descriptor?.name ?? t.name;
|
|
2143
|
-
return typeof n == "string" ? n : "";
|
|
2144
|
-
}).filter((e) => e !== "");
|
|
1582
|
+
}).filter((e) => e.tableName !== ""), n = (e.rpcClientDescriptors ?? []).map((t) => typeof t.tag == "string" ? u(e, t.tag) : "").filter((e) => e !== ""), r = e.baseName ?? e.name;
|
|
2145
1583
|
return {
|
|
2146
1584
|
plugin: e.name,
|
|
2147
1585
|
tables: t,
|
|
2148
1586
|
rpcTags: n,
|
|
2149
|
-
canDisableTables:
|
|
2150
|
-
canAlias:
|
|
1587
|
+
canDisableTables: an.has(r),
|
|
1588
|
+
canAlias: on.has(r)
|
|
2151
1589
|
};
|
|
2152
1590
|
});
|
|
2153
1591
|
} catch {
|
|
@@ -2159,14 +1597,36 @@ var le = () => {
|
|
|
2159
1597
|
...t.mutations,
|
|
2160
1598
|
...t.actions,
|
|
2161
1599
|
...t.streams ?? []
|
|
2162
|
-
].map((e) => String(e.descriptor.name)), i =
|
|
1600
|
+
].map((e) => String(e.descriptor.name)), i = Je(n, {
|
|
2163
1601
|
tables: t.tables.map((e) => e.tableName),
|
|
2164
1602
|
rpcTags: r
|
|
2165
1603
|
});
|
|
2166
1604
|
console.log();
|
|
2167
|
-
for (let e of
|
|
2168
|
-
},
|
|
2169
|
-
|
|
1605
|
+
for (let e of Ye(n, i)) console.log(e);
|
|
1606
|
+
}, an = /* @__PURE__ */ new Set([
|
|
1607
|
+
"@voltro/plugin-rbac",
|
|
1608
|
+
"@voltro/plugin-audit",
|
|
1609
|
+
"@voltro/plugin-notifications",
|
|
1610
|
+
"aiFlows"
|
|
1611
|
+
]), on = /* @__PURE__ */ new Set([
|
|
1612
|
+
"aiFlows",
|
|
1613
|
+
"@voltro/plugin-audit",
|
|
1614
|
+
"@voltro/plugin-billing",
|
|
1615
|
+
"@voltro/plugin-cdc-out",
|
|
1616
|
+
"@voltro/plugin-flags",
|
|
1617
|
+
"@voltro/plugin-governance",
|
|
1618
|
+
"@voltro/plugin-mail",
|
|
1619
|
+
"@voltro/plugin-moderation",
|
|
1620
|
+
"@voltro/plugin-notifications",
|
|
1621
|
+
"@voltro/plugin-presence",
|
|
1622
|
+
"@voltro/plugin-rbac",
|
|
1623
|
+
"@voltro/plugin-scim",
|
|
1624
|
+
"@voltro/plugin-search",
|
|
1625
|
+
"@voltro/plugin-sso-saml",
|
|
1626
|
+
"@voltro/plugin-storage",
|
|
1627
|
+
"@voltro/plugin-versioning"
|
|
1628
|
+
]), $ = 20, sn = async (e) => {
|
|
1629
|
+
let t = await cn(e), n = new Set(t.reviewed.map((e) => e.tag)), r = t.result.findings.filter((e) => e.verdict.kind === "allowlisted" && !n.has(e.tag)).length;
|
|
2170
1630
|
return {
|
|
2171
1631
|
executors: t.executorCount,
|
|
2172
1632
|
guardVocabulary: [...t.vocabulary].sort(),
|
|
@@ -2178,15 +1638,15 @@ var le = () => {
|
|
|
2178
1638
|
},
|
|
2179
1639
|
unchecked: t.result.unchecked
|
|
2180
1640
|
};
|
|
2181
|
-
},
|
|
2182
|
-
let { discovered: t } = await
|
|
1641
|
+
}, cn = async (e) => {
|
|
1642
|
+
let { discovered: t } = await U(e), n = /* @__PURE__ */ new Map(), r = (e) => {
|
|
2183
1643
|
if (!n.has(e)) try {
|
|
2184
|
-
n.set(e,
|
|
1644
|
+
n.set(e, S(e, "utf8"));
|
|
2185
1645
|
} catch {
|
|
2186
1646
|
n.set(e, "");
|
|
2187
1647
|
}
|
|
2188
1648
|
return n.get(e);
|
|
2189
|
-
}, i =
|
|
1649
|
+
}, i = Zt(e, r), a = /* @__PURE__ */ new Set();
|
|
2190
1650
|
for (let e of t.tables) for (let t of Object.values(e.fields ?? {})) try {
|
|
2191
1651
|
let e = t.references?.();
|
|
2192
1652
|
e && a.add(e.tableName);
|
|
@@ -2218,12 +1678,12 @@ var le = () => {
|
|
|
2218
1678
|
op: a?.op === "delete" || a?.op === "insert" ? a.op : t,
|
|
2219
1679
|
...a?.table === void 0 ? {} : { table: a.table }
|
|
2220
1680
|
};
|
|
2221
|
-
})), s =
|
|
2222
|
-
for (let t of
|
|
1681
|
+
})), s = _(e, X), c = x(s) ? M(S(s, "utf8")) : void 0, l = !1;
|
|
1682
|
+
for (let t of R(e)) if (/\bsetRowFilter\s*[<(]/.test(r(t))) {
|
|
2223
1683
|
l = !0;
|
|
2224
1684
|
break;
|
|
2225
1685
|
}
|
|
2226
|
-
let u =
|
|
1686
|
+
let u = He({
|
|
2227
1687
|
executors: o,
|
|
2228
1688
|
guardVocabulary: i,
|
|
2229
1689
|
tenantScopedTables: t.tenantScopedTables,
|
|
@@ -2240,261 +1700,46 @@ var le = () => {
|
|
|
2240
1700
|
executorCount: o.length,
|
|
2241
1701
|
allowPath: s
|
|
2242
1702
|
};
|
|
2243
|
-
},
|
|
2244
|
-
let { result: n, vocabulary: r, existing: i, reviewed: a, executorCount: o, allowPath: s, rowFilterRegistered: c } = await
|
|
1703
|
+
}, ln = async (e, t) => {
|
|
1704
|
+
let { result: n, vocabulary: r, existing: i, reviewed: a, executorCount: o, allowPath: s, rowFilterRegistered: c } = await cn(e), l = n.counts;
|
|
2245
1705
|
console.log(`\nauthz scan · ${o} executor(s)`);
|
|
2246
|
-
let u = [...r].filter((e) => !
|
|
1706
|
+
let u = [...r].filter((e) => !j.has(e)).sort();
|
|
2247
1707
|
console.log(u.length === 0 ? " guard vocabulary: framework names only — no exported require*/assert* found in this app" : ` guard vocabulary: ${u.length} from your source (${u.slice(0, 6).join(", ")}${u.length > 6 ? ", …" : ""})`), console.log(` ✗ no access check ${String(l.unchecked).padStart(4)}`), console.log(` ⚠ inline ownership check, no named guard${String(l.inlineOwnership).padStart(4)} (informational)`), console.log(` ✓ guards: on the descriptor ${String(l.declaredGuard).padStart(4)}`), console.log(` ✓ calls a guard from the vocabulary ${String(l.vocabularyGuard).padStart(4)}`);
|
|
2248
1708
|
let d = new Set(a.map((e) => e.tag)), f = n.findings.filter((e) => e.verdict.kind === "allowlisted" && !d.has(e.tag)).length, p = l.allowlisted - f;
|
|
2249
|
-
if (console.log(` – accepted as recorded debt ${String(f).padStart(4)} (${
|
|
2250
|
-
let e =
|
|
2251
|
-
return
|
|
1709
|
+
if (console.log(` – accepted as recorded debt ${String(f).padStart(4)} (${X})`), console.log(` ✓ reviewed, open on purpose ${String(p).padStart(4)} (\`reviewed=<why>\` in that file)`), c && console.log(" ⓘ this app registers setRowFilter — the scan CANNOT see through it. A finding below may\n already be constrained; verify the filter covers that table and record it as `reviewed=`."), t) {
|
|
1710
|
+
let e = x(s) ? S(s, "utf8") : void 0, t = n.unchecked.map((e) => e.tag), r = Ke(e, t);
|
|
1711
|
+
return oe(s, Ge(e, t), "utf8"), console.log(`\n${X}: +${r.added} new, ${r.kept} kept, ${r.removed} removed. Doctor now fails on ADDITIONS only.`), r.added === 0 && console.log(" (nothing new was unchecked — the file is unchanged.)"), !1;
|
|
2252
1712
|
}
|
|
2253
1713
|
if (n.unchecked.length === 0) return !1;
|
|
2254
1714
|
console.log("");
|
|
2255
1715
|
for (let e of n.unchecked.slice(0, $)) console.log(` ✗ ${e.tag} — ${e.why}\n ${e.path}`);
|
|
2256
|
-
return n.unchecked.length > $ && console.log(` … and ${n.unchecked.length - $} more — \`voltro doctor --json\` prints every one (\`authz.unchecked\`).`), i === void 0 ? (console.log(`\nNo ${
|
|
2257
|
-
},
|
|
2258
|
-
if (e.includes("--help") || e.includes("-h")) return console.log(
|
|
2259
|
-
let
|
|
2260
|
-
if (
|
|
2261
|
-
...
|
|
2262
|
-
spaCandidates: await
|
|
2263
|
-
pageConvention:
|
|
2264
|
-
fileTaxonomy:
|
|
2265
|
-
junctionFks: (await
|
|
2266
|
-
eventDelivery: await
|
|
2267
|
-
duplicateInstances:
|
|
2268
|
-
versionSkew:
|
|
2269
|
-
serverOnly: (await
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
1716
|
+
return n.unchecked.length > $ && console.log(` … and ${n.unchecked.length - $} more — \`voltro doctor --json\` prints every one (\`authz.unchecked\`).`), i === void 0 ? (console.log(`\nNo ${X} yet, so this run is advisory. To start the ratchet:`), console.log("\n voltro doctor --write-authz-allowlist\n"), console.log("That records today's unchecked handlers as DEBT and fails every run after on"), console.log("anything NEW. Fix a handler and its line stops mattering — the scan reports it as"), console.log("guarded whether or not the line is still there."), !1) : (console.log(`\n${n.unchecked.length} executor(s) with no access check are not in ${X}.`), console.log("Add a guard, or add the tag to that file if it is genuinely open."), console.log("\nBefore hand-rolling another check: `guards: [{ action, resourceType, resource }]`"), console.log("answers \"may this subject act on THIS row\" declaratively, and an app whose"), console.log("relationships live in its own tables registers its own tuple source rather than"), console.log("copying data into a framework table. See the authorization docs."), !0);
|
|
1717
|
+
}, un = async (e) => {
|
|
1718
|
+
if (e.includes("--help") || e.includes("-h")) return console.log(Rt), 0;
|
|
1719
|
+
let t = e.includes("--strict"), n = y(e.find((e) => !e.startsWith("-")) ?? process.cwd());
|
|
1720
|
+
if (h(e)) return g(), m({
|
|
1721
|
+
...z(n),
|
|
1722
|
+
spaCandidates: await B(n),
|
|
1723
|
+
pageConvention: V(n),
|
|
1724
|
+
fileTaxonomy: re(n).findings,
|
|
1725
|
+
junctionFks: (await W(n))?.issues ?? [],
|
|
1726
|
+
eventDelivery: await G(n),
|
|
1727
|
+
duplicateInstances: a(n),
|
|
1728
|
+
versionSkew: o(n) ?? null,
|
|
1729
|
+
serverOnly: (await q(n)).status,
|
|
1730
|
+
accessDecisions: await J(n),
|
|
1731
|
+
webhookVerification: await Y(n),
|
|
1732
|
+
authz: await sn(n),
|
|
1733
|
+
ungrantedScopes: await Q(n),
|
|
1734
|
+
neverEmittedEvents: await Z(n),
|
|
1735
|
+
stalledRuns: await Jt(n)
|
|
2273
1736
|
}), 0;
|
|
2274
|
-
let
|
|
2275
|
-
|
|
2276
|
-
let c = await
|
|
2277
|
-
|
|
2278
|
-
let
|
|
2279
|
-
return
|
|
2280
|
-
}, Xn = (e) => e === void 0 ? [] : typeof e == "string" ? [e] : e, Zn = /* @__PURE__ */ new Set([
|
|
2281
|
-
"insert",
|
|
2282
|
-
"update",
|
|
2283
|
-
"delete",
|
|
2284
|
-
"upsert"
|
|
2285
|
-
]), Qn = (e, t) => {
|
|
2286
|
-
let n = [], r = new Set(t.exercised), i = /* @__PURE__ */ new Map();
|
|
2287
|
-
for (let e of t.edges) {
|
|
2288
|
-
let t = i.get(e.tag);
|
|
2289
|
-
t === void 0 ? i.set(e.tag, [e]) : t.push(e);
|
|
2290
|
-
}
|
|
2291
|
-
let a = [], o = [];
|
|
2292
|
-
for (let t of e) {
|
|
2293
|
-
if (!r.has(t.tag)) {
|
|
2294
|
-
a.push(t.tag);
|
|
2295
|
-
continue;
|
|
2296
|
-
}
|
|
2297
|
-
let e = `${t.kind}(${t.tag})`, s = t.file, c = i.get(t.tag) ?? [], l = new Set(Xn(t.source)), u = new Map((t.targets ?? []).map((e) => [e.table, e.op]));
|
|
2298
|
-
for (let t of c) if (Zn.has(t.op)) {
|
|
2299
|
-
let r = u.get(t.table);
|
|
2300
|
-
r === void 0 ? n.push({
|
|
2301
|
-
rule: "observed/undeclared-write",
|
|
2302
|
-
node: e,
|
|
2303
|
-
...s === void 0 ? {} : { file: s },
|
|
2304
|
-
breaks: [`writes '${t.table}' (${t.op}) but declares no target for it`, "subscriptions on that table are not invalidated by this mutation"],
|
|
2305
|
-
fix: `add { table: '${t.table}', op: '${t.op}' } to this procedure's targets`,
|
|
2306
|
-
severity: "warn"
|
|
2307
|
-
}) : r !== t.op && n.push({
|
|
2308
|
-
rule: "observed/wrong-op",
|
|
2309
|
-
node: e,
|
|
2310
|
-
...s === void 0 ? {} : { file: s },
|
|
2311
|
-
breaks: [`declares op '${r}' on '${t.table}' but performed '${t.op}'`, "the synthesized optimistic patch has the wrong shape"],
|
|
2312
|
-
fix: `change the target op for '${t.table}' to '${t.op}'`,
|
|
2313
|
-
severity: "warn"
|
|
2314
|
-
});
|
|
2315
|
-
} else !l.has(t.table) && !u.has(t.table) && n.push({
|
|
2316
|
-
rule: "observed/undeclared-read",
|
|
2317
|
-
node: e,
|
|
2318
|
-
...s === void 0 ? {} : { file: s },
|
|
2319
|
-
breaks: [`reads '${t.table}' but declares no source for it`],
|
|
2320
|
-
fix: `add '${t.table}' to this procedure's source`,
|
|
2321
|
-
severity: "warn"
|
|
2322
|
-
});
|
|
2323
|
-
if (c.length === 0) {
|
|
2324
|
-
o.push(t.tag);
|
|
2325
|
-
continue;
|
|
2326
|
-
}
|
|
2327
|
-
let d = new Set(c.map((e) => e.table));
|
|
2328
|
-
for (let t of l) d.has(t) || n.push({
|
|
2329
|
-
rule: "observed/declared-but-unobserved",
|
|
2330
|
-
node: e,
|
|
2331
|
-
...s === void 0 ? {} : { file: s },
|
|
2332
|
-
breaks: [`declares source '${t}' but never read it while running`],
|
|
2333
|
-
fix: `remove '${t}' from source, or check whether the handler still reads it`,
|
|
2334
|
-
severity: "warn"
|
|
2335
|
-
});
|
|
2336
|
-
for (let [t] of u) d.has(t) || n.push({
|
|
2337
|
-
rule: "observed/declared-but-unobserved",
|
|
2338
|
-
node: e,
|
|
2339
|
-
...s === void 0 ? {} : { file: s },
|
|
2340
|
-
breaks: [`declares target '${t}' but never wrote it while running`],
|
|
2341
|
-
fix: `remove the '${t}' target, or check whether the handler still writes it`,
|
|
2342
|
-
severity: "warn"
|
|
2343
|
-
});
|
|
2344
|
-
}
|
|
2345
|
-
return {
|
|
2346
|
-
diagnostics: n,
|
|
2347
|
-
unexercised: a.sort(),
|
|
2348
|
-
unobservedReads: o.sort(),
|
|
2349
|
-
coverage: {
|
|
2350
|
-
exercised: e.length - a.length - o.length,
|
|
2351
|
-
total: e.length
|
|
2352
|
-
}
|
|
2353
|
-
};
|
|
2354
|
-
}, $n = (e, t = [], n = 0) => {
|
|
2355
|
-
if (n > 12) return t;
|
|
2356
|
-
let r;
|
|
2357
|
-
try {
|
|
2358
|
-
r = D(e);
|
|
2359
|
-
} catch {
|
|
2360
|
-
return t;
|
|
2361
|
-
}
|
|
2362
|
-
for (let i of r) {
|
|
2363
|
-
if ([
|
|
2364
|
-
"node_modules",
|
|
2365
|
-
"dist",
|
|
2366
|
-
"build",
|
|
2367
|
-
".framework",
|
|
2368
|
-
".turbo",
|
|
2369
|
-
".git",
|
|
2370
|
-
"coverage"
|
|
2371
|
-
].includes(i)) continue;
|
|
2372
|
-
let r = `${e}/${i}`, a;
|
|
2373
|
-
try {
|
|
2374
|
-
a = O(r);
|
|
2375
|
-
} catch {
|
|
2376
|
-
continue;
|
|
2377
|
-
}
|
|
2378
|
-
a.isDirectory() ? $n(r, t, n + 1) : a.isFile() && u.test(i) && t.push(r);
|
|
2379
|
-
}
|
|
2380
|
-
return t;
|
|
2381
|
-
}, er = /* @__PURE__ */ new Set(["actors", "tenants"]), tr = async (e) => {
|
|
2382
|
-
try {
|
|
2383
|
-
let { readFile: t } = await import("node:fs/promises"), { join: n } = await import("node:path"), r = await t(n(e, "app.graph.observed.generated.json"), "utf8"), i = JSON.parse(r);
|
|
2384
|
-
return i?.version !== 1 || !Array.isArray(i.edges) || !Array.isArray(i.exercised) ? null : i;
|
|
2385
|
-
} catch {
|
|
2386
|
-
return null;
|
|
2387
|
-
}
|
|
2388
|
-
}, nr = (e) => {
|
|
2389
|
-
let { exercised: t, total: n } = e.coverage, r = `\nobserved: ${t}/${n} procedures exercised (${n === 0 ? 0 : Math.round(t / n * 100)}%)`;
|
|
2390
|
-
if (e.diagnostics.length === 0) return `${r}\n no declared/observed mismatches${e.unexercised.length > 0 ? `\n ${e.unexercised.length} unexercised — no recording, so nothing is claimed about them` : ""}`;
|
|
2391
|
-
let i = e.diagnostics.map((e) => ` ${e.node}${e.file === void 0 ? "" : ` (${e.file})`}\n` + e.breaks.map((e) => ` ${e}`).join("\n") + `\n fix: ${e.fix}`), a = e.unexercised.length > 0 ? `\n ${e.unexercised.length} unexercised — no recording, so nothing is claimed about them` : "";
|
|
2392
|
-
return `${r}\n${i.join("\n")}${a}`;
|
|
2393
|
-
}, rr = (e, t) => ({
|
|
2394
|
-
tables: (t.tables ?? []).map((e) => e.name),
|
|
2395
|
-
frameworkOwnedTables: (t.tables ?? []).filter((e) => e.framework === !0 || e.name.startsWith("_voltro_") || er.has(e.name)).map((e) => e.name),
|
|
2396
|
-
scopes: e.scopes ?? [],
|
|
2397
|
-
procedures: (e.procedures ?? []).map((e) => {
|
|
2398
|
-
let t = (e.guards ?? []).flatMap((e) => e.kind === "scope" ? e.scope ?? [] : []);
|
|
2399
|
-
return {
|
|
2400
|
-
tag: e.tag,
|
|
2401
|
-
kind: e.kind,
|
|
2402
|
-
...e.source === void 0 ? {} : { source: e.source },
|
|
2403
|
-
...e.targets === void 0 ? {} : { targets: e.targets },
|
|
2404
|
-
...t.length > 0 ? { requiresScopes: [...new Set(t)] } : {},
|
|
2405
|
-
...e.guards === void 0 ? {} : { hasGuards: e.guards.length > 0 }
|
|
2406
|
-
};
|
|
2407
|
-
})
|
|
2408
|
-
}), ir = (e) => e.length === 0 ? "✓ voltro check: no issues" : e.map((e) => {
|
|
2409
|
-
let t = e.severity === "error" ? "✗ error" : "⚠ warn", n = e.file ? ` (${e.file})` : "";
|
|
2410
|
-
return `${t} ${e.rule} ${e.node}${n}\n ${e.breaks.join("; ")}\n fix: ${e.fix}`;
|
|
2411
|
-
}).join("\n"), ar = async (e) => {
|
|
2412
|
-
let { walk: t, loadDiscovered: n, loadApiConfig: r } = await import("./dev-iiMtlkfs.js"), { buildRpcEntry: i } = await import("./manifestBuild-Dj8Jjoto.js"), { buildCapabilityManifest: a } = await import("./inspect-mmBuRXmy.js"), { relative: o } = await import("node:path"), s = await n(await t(e)), c = (t, n, r) => i(t, n, o(e, r)), l = [
|
|
2413
|
-
...s.queries.map((e) => c(e.descriptor, "query", e.file)),
|
|
2414
|
-
...s.mutations.map((e) => c(e.descriptor, "mutation", e.file)),
|
|
2415
|
-
...s.actions.map((e) => c(e.descriptor, "action", e.file)),
|
|
2416
|
-
...s.streams.map((e) => c(e.descriptor, "stream", e.file))
|
|
2417
|
-
], { assembleFrameworkTables: u } = await import("./frameworkTableAssembly-D-EebUQX.js"), d = await u({ root: e }).catch((e) => (console.warn(`\n⚠ plugin tables could not be collected — this report is INCOMPLETE.\n ${e instanceof Error ? e.message.split("\n")[0] : String(e)}\n`), [])), f = new Set(s.tables.map((e) => e.tableName)), p = [...[...f].map((e) => ({ name: e })), ...d.map((e) => e.tableName).filter((e) => !f.has(e)).map((e) => ({
|
|
2418
|
-
name: e,
|
|
2419
|
-
framework: !0
|
|
2420
|
-
}))], m = await r(e).then((e) => e?.plugins ?? []).catch(() => []);
|
|
2421
|
-
return {
|
|
2422
|
-
manifest: a(l, s.workflows.map((t) => ({
|
|
2423
|
-
tag: t.definition.name ?? "",
|
|
2424
|
-
file: o(e, t.file)
|
|
2425
|
-
})), [], m),
|
|
2426
|
-
tables: { tables: p }
|
|
2427
|
-
};
|
|
2428
|
-
}, or = async (e) => {
|
|
2429
|
-
let t = he(e.flatMap((e) => e === "--json" ? ["--format", "json"] : [e]));
|
|
2430
|
-
if ("error" in t) return console.error(`voltro check: ${t.error}`), 1;
|
|
2431
|
-
let { process: n, target: r, rest: i } = ge(t.rest), a;
|
|
2432
|
-
for (let e = 0; e < i.length; e++) if (i[e] === "--diff") {
|
|
2433
|
-
let [t, n] = (i[++e] ?? "").split(":");
|
|
2434
|
-
if (t === "removeTable" && n) a = {
|
|
2435
|
-
kind: "removeTable",
|
|
2436
|
-
name: n
|
|
2437
|
-
};
|
|
2438
|
-
else if (t === "removeProcedure" && n) a = {
|
|
2439
|
-
kind: "removeProcedure",
|
|
2440
|
-
tag: n
|
|
2441
|
-
};
|
|
2442
|
-
else if (t === "removeScope" && n) a = {
|
|
2443
|
-
kind: "removeScope",
|
|
2444
|
-
name: n
|
|
2445
|
-
};
|
|
2446
|
-
else return console.error("voltro check --diff: expected removeTable:<name> | removeProcedure:<tag> | removeScope:<name>"), 1;
|
|
2447
|
-
}
|
|
2448
|
-
let o = i.find((e) => !e.startsWith("-")) ?? process.cwd(), s = i.includes("--offline"), c = s ? [] : await me(n, r), u, d;
|
|
2449
|
-
if (c.length === 0) {
|
|
2450
|
-
if (!s && r !== void 0) return console.error("voltro check: --url given but no api reachable there."), 1;
|
|
2451
|
-
try {
|
|
2452
|
-
let e = await ar(C(o));
|
|
2453
|
-
u = e.manifest, d = e.tables;
|
|
2454
|
-
} catch (e) {
|
|
2455
|
-
return console.error(`voltro check: could not assemble the graph from source: ${e.message}`), console.error(" Start `voltro dev` to check against a running api instead."), 1;
|
|
2456
|
-
}
|
|
2457
|
-
} else {
|
|
2458
|
-
let e = c[0], [t, n] = await Promise.all([N(e, "/_voltro/inspect/manifest"), N(e, "/_voltro/inspect/data/tables")]);
|
|
2459
|
-
if (t.kind === "err") return console.error(`voltro check: manifest fetch failed: ${t.error}`), 1;
|
|
2460
|
-
u = t.data, d = n.kind === "ok" ? n.data : { tables: [] };
|
|
2461
|
-
}
|
|
2462
|
-
let f = rr(u, d), p = ie(f, a), m = [...V(o).findings.map((e) => ({
|
|
2463
|
-
rule: e.rule,
|
|
2464
|
-
node: e.file,
|
|
2465
|
-
file: e.file,
|
|
2466
|
-
breaks: [e.detail],
|
|
2467
|
-
fix: e.fix,
|
|
2468
|
-
severity: e.severity
|
|
2469
|
-
})), ...J(o).map((e) => ({
|
|
2470
|
-
rule: `page/${e.kind}`,
|
|
2471
|
-
node: e.file,
|
|
2472
|
-
file: e.file,
|
|
2473
|
-
breaks: [e.kind === "suffixed-outside-pages" ? "a page.tsx outside src/pages/ never routes" : "no test beside it"],
|
|
2474
|
-
fix: e.kind === "suffixed-outside-pages" ? "move it under src/pages/ into the directory whose route it serves, or rename it — page.tsx is reserved" : "write the test with the file — the name is derivable",
|
|
2475
|
-
severity: e.kind === "suffixed-outside-pages" ? "error" : "warn"
|
|
2476
|
-
}))], h = (await l(o, $n(o))).map((e) => ({
|
|
2477
|
-
rule: "client/not-browser-safe",
|
|
2478
|
-
node: e.file,
|
|
2479
|
-
file: e.file,
|
|
2480
|
-
breaks: [`reaches the server-only '${e.violation.specifier}' — chain: ${e.violation.chain.join(" → ")}`],
|
|
2481
|
-
fix: "move the server-only code behind a *.server.ts, or drop the .client suffix — an unmarked file makes no claim",
|
|
2482
|
-
severity: "error"
|
|
2483
|
-
})), g = [
|
|
2484
|
-
...p.diagnostics,
|
|
2485
|
-
...m,
|
|
2486
|
-
...h
|
|
2487
|
-
], _ = p.ok && !m.some((e) => e.severity === "error") && h.length === 0, v = await tr(C(o)), y = v === null ? null : Qn(f.procedures, v);
|
|
2488
|
-
return t.format === "json" ? M(y === null ? {
|
|
2489
|
-
...p,
|
|
2490
|
-
ok: _,
|
|
2491
|
-
diagnostics: g
|
|
2492
|
-
} : {
|
|
2493
|
-
...p,
|
|
2494
|
-
ok: _,
|
|
2495
|
-
diagnostics: g,
|
|
2496
|
-
observed: y
|
|
2497
|
-
}) : (console.log(ir(g)), y !== null && console.log(nr(y)), console.log(_ ? "\nvoltro check: OK" : "\nvoltro check: FAILED (errors above)")), +!_;
|
|
1737
|
+
let s = tt(n, t);
|
|
1738
|
+
Tt(n), wt(n), Lt(n), ne(n), await jt(n), await Xt(n), await Yt(n), await Bt(n), await Ht(n), await Ut(n), await rn(n), await tn(n), await $t(n), await nn(n);
|
|
1739
|
+
let c = await qt(n), l = await ln(n, e.includes("--write-authz-allowlist")), u = await Wt(n), d = await Kt(n), f = r(o(n));
|
|
1740
|
+
f !== void 0 && (console.log(""), console.log(f));
|
|
1741
|
+
let p = i(a(n));
|
|
1742
|
+
return p !== void 0 && console.log(`\n${p}`), c || l || u || d ? Math.max(s, 1) : s;
|
|
2498
1743
|
};
|
|
2499
1744
|
//#endregion
|
|
2500
|
-
export {
|
|
1745
|
+
export { q as C, B as S, Y as T, jt as _, sn as a, yt as b, G as c, vt as d, P as f, Lt as g, Tt as h, J as i, Zt as l, wt as m, et as n, Z as o, W as p, Et as r, Qt as s, Rt as t, z as u, un as v, Q as w, V as x, tt as y };
|