@runa-ai/runa-cli 0.5.72 → 0.7.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/dist/build-V66FAQXB.js +1719 -0
- package/dist/cache-N7WNPEYF.js +111 -0
- package/dist/check-LOMVIRHX.js +12 -0
- package/dist/chunk-2APB25TT.js +442 -0
- package/dist/chunk-3FDQW524.js +544 -0
- package/dist/chunk-3WDV32GA.js +33 -0
- package/dist/chunk-5FT3F36G.js +59 -0
- package/dist/chunk-5NKWR4FF.js +254 -0
- package/dist/chunk-644FVGIQ.js +194 -0
- package/dist/chunk-6AALH2ED.js +121 -0
- package/dist/chunk-6FAU4IGR.js +63 -0
- package/dist/chunk-6Y3LAUGL.js +35 -0
- package/dist/chunk-7B5C6U2K.js +274 -0
- package/dist/chunk-AAIE4F2U.js +140 -0
- package/dist/chunk-AIP6MR42.js +12 -0
- package/dist/chunk-CCKG5R4Y.js +59 -0
- package/dist/chunk-DRSUEMAK.js +123 -0
- package/dist/chunk-FHG3ILE4.js +2011 -0
- package/dist/chunk-H2AHNI75.js +31 -0
- package/dist/chunk-HD74F6W2.js +460 -0
- package/dist/chunk-HKUWEGUX.js +36 -0
- package/dist/chunk-IBVVGH6X.js +33 -0
- package/dist/chunk-II7VYQEM.js +179 -0
- package/dist/chunk-JMJP4A47.js +204 -0
- package/dist/chunk-JQXOVCOP.js +574 -0
- package/dist/chunk-KE6QJBZG.js +41 -0
- package/dist/chunk-KWX3JHCY.js +85 -0
- package/dist/chunk-MXRWBNIY.js +74 -0
- package/dist/chunk-NPSRD26F.js +149 -0
- package/dist/chunk-QDF7QXBL.js +67 -0
- package/dist/chunk-QM53IQHM.js +209 -0
- package/dist/chunk-RZLYEO4U.js +219 -0
- package/dist/chunk-SGJG3BKD.js +351 -0
- package/dist/chunk-TYIAD6SB.js +74 -0
- package/dist/chunk-UWWSAPDR.js +31 -0
- package/dist/chunk-VM3IWOT5.js +458 -0
- package/dist/chunk-VRXHCR5K.js +42 -0
- package/dist/chunk-WJXC4MVY.js +75 -0
- package/dist/chunk-XDCHRVE3.js +215 -0
- package/dist/chunk-Z4Z5DNW4.js +1196 -0
- package/dist/chunk-ZZOXM6Q4.js +8 -0
- package/dist/ci-ZWRVWNFX.js +9298 -0
- package/dist/cli/contract-output.d.ts +1 -0
- package/dist/cli/index.d.ts +7 -1
- package/dist/cli/requested-command.d.ts +8 -0
- package/dist/cli-2JNBJUBB.js +704 -0
- package/dist/commands/build/actors/db-sync.d.ts +2 -0
- package/dist/commands/build/actors/static-checks.d.ts +7 -6
- package/dist/commands/build/actors/validate.d.ts +2 -0
- package/dist/commands/build/contract.d.ts +30 -30
- package/dist/commands/build/machine-dry-run.d.ts +3 -0
- package/dist/commands/build/machine-e2e-meta.d.ts +120 -0
- package/dist/commands/build/machine.d.ts +22 -22
- package/dist/commands/build/types.d.ts +2 -4
- package/dist/commands/check/commands/check.d.ts +8 -3
- package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts +9 -6
- package/dist/commands/ci/machine/actors/db/schema-canonical-diff.d.ts +55 -0
- package/dist/commands/ci/machine/actors/db/schema-stats.d.ts +11 -0
- package/dist/commands/ci/machine/actors/db/sync-schema.d.ts +9 -1
- package/dist/commands/ci/machine/contract.d.ts +26 -26
- package/dist/commands/ci/machine/formatters/sections/final-comment.d.ts +1 -5
- package/dist/commands/ci/machine/formatters/sections/format-helpers.d.ts +5 -0
- package/dist/commands/ci/machine/formatters/sections/index.d.ts +2 -2
- package/dist/commands/ci/machine/formatters/sections/schema-matrix.d.ts +3 -3
- package/dist/commands/ci/machine/machine-execution-helpers.d.ts +40 -0
- package/dist/commands/ci/machine/machine-state-helpers.d.ts +14 -0
- package/dist/commands/ci/machine/machine.d.ts +12 -12
- package/dist/commands/ci/machine/types.d.ts +2 -5
- package/dist/commands/ci/utils/ci-summary.d.ts +15 -15
- package/dist/commands/ci/utils/execa-helpers.d.ts +2 -0
- package/dist/commands/db/apply/actors/idempotent-actors.d.ts +34 -0
- package/dist/commands/db/apply/actors/lock-actors.d.ts +16 -0
- package/dist/commands/db/apply/actors/pg-schema-diff-actors.d.ts +31 -0
- package/dist/commands/db/apply/actors/seed-actors.d.ts +11 -0
- package/dist/commands/db/apply/actors/shared.d.ts +9 -0
- package/dist/commands/db/apply/actors.d.ts +16 -65
- package/dist/commands/db/apply/contract.d.ts +8 -1
- package/dist/commands/db/apply/helpers/data-compatibility-checker.d.ts +3 -4
- package/dist/commands/db/apply/helpers/data-integrity-verifier.d.ts +37 -0
- package/dist/commands/db/apply/helpers/fresh-db-handler.d.ts +34 -0
- package/dist/commands/db/apply/helpers/hazard-handler.d.ts +60 -0
- package/dist/commands/db/apply/helpers/idempotent-object-registry.d.ts +96 -0
- package/dist/commands/db/apply/helpers/idempotent-transaction.d.ts +20 -0
- package/dist/commands/db/apply/helpers/index.d.ts +6 -0
- package/dist/commands/db/apply/helpers/partition-validator.d.ts +2 -15
- package/dist/commands/db/apply/helpers/pg-schema-diff-helpers.d.ts +18 -162
- package/dist/commands/db/apply/helpers/pg-schema-diff-patterns.d.ts +55 -0
- package/dist/commands/db/apply/helpers/pg-schema-diff-version.d.ts +50 -0
- package/dist/commands/db/apply/helpers/plan-validator.d.ts +4 -10
- package/dist/commands/db/apply/helpers/rbac-password-manager.d.ts +34 -0
- package/dist/commands/db/apply/helpers/retry-logic.d.ts +16 -2
- package/dist/commands/db/apply/helpers/shadow-db-manager.d.ts +1 -1
- package/dist/commands/db/apply/helpers/sql-utils.d.ts +26 -0
- package/dist/commands/db/apply/machine.d.ts +52 -1
- package/dist/commands/db/commands/db-apply.d.ts +18 -0
- package/dist/commands/db/commands/db-sync/boundary-classifier.d.ts +21 -0
- package/dist/commands/db/commands/db-sync/error-classifier.d.ts +9 -0
- package/dist/commands/db/commands/db-sync/plan-hazard-analyzer.d.ts +13 -0
- package/dist/commands/db/commands/db-sync/risk-reporter.d.ts +19 -0
- package/dist/commands/db/commands/db-sync/sql-parser.d.ts +25 -0
- package/dist/commands/db/commands/db-sync/types.d.ts +47 -0
- package/dist/commands/db/commands/db-sync.d.ts +14 -0
- package/dist/commands/db/sync/contract.d.ts +6 -2
- package/dist/commands/db/sync/machine.d.ts +2 -1
- package/dist/commands/db/types.d.ts +2 -0
- package/dist/commands/db/utils/boundary-policy/rule-compiler.d.ts +11 -0
- package/dist/commands/db/utils/boundary-policy/types.d.ts +105 -0
- package/dist/commands/db/utils/boundary-policy/validation.d.ts +20 -0
- package/dist/commands/db/utils/boundary-policy-runtime.d.ts +28 -0
- package/dist/commands/db/utils/boundary-policy.d.ts +5 -0
- package/dist/commands/db/utils/idempotent-risk-context.d.ts +29 -0
- package/dist/commands/db/utils/preflight-check.d.ts +14 -0
- package/dist/commands/db/utils/preflight-checks/domain-naming-checks.d.ts +106 -0
- package/dist/commands/db/utils/preflight-checks/orphan-checks.d.ts +36 -0
- package/dist/commands/db/utils/preflight-checks/schema-risk-checks.d.ts +22 -0
- package/dist/commands/db/utils/preflight-checks/supabase-checks.d.ts +55 -0
- package/dist/commands/db/utils/risk-detector-loader.d.ts +8 -0
- package/dist/commands/db/utils/schema-precheck-budget.d.ts +17 -0
- package/dist/commands/db/utils/sql-boundary-parser.d.ts +12 -0
- package/dist/commands/db/utils/sql-file-collector.d.ts +8 -0
- package/dist/commands/db/utils/sql-filename-parser.d.ts +20 -0
- package/dist/commands/db/utils/sql-table-extractor-ast.d.ts +19 -0
- package/dist/commands/db/utils/sql-table-extractor-regex.d.ts +50 -0
- package/dist/commands/db/utils/sql-table-extractor-rls.d.ts +13 -0
- package/dist/commands/db/utils/sql-table-extractor.d.ts +79 -1
- package/dist/commands/db/utils/table-registry-introspection.d.ts +68 -0
- package/dist/commands/db/utils/table-registry.d.ts +3 -38
- package/dist/commands/dev/actors/app-lifecycle.d.ts +18 -0
- package/dist/commands/dev/actors/index.d.ts +12 -2
- package/dist/commands/dev/actors/process-check.d.ts +12 -0
- package/dist/commands/dev/actors/shared.d.ts +15 -0
- package/dist/commands/dev/actors/tables-manifest.d.ts +16 -0
- package/dist/commands/dev/contract.d.ts +3 -3
- package/dist/commands/dev/guards.d.ts +24 -0
- package/dist/commands/dev/machine.d.ts +27 -32
- package/dist/commands/dev/types.d.ts +2 -0
- package/dist/commands/doctor.d.ts +9 -0
- package/dist/commands/env/commands/env-pull/auth.d.ts +13 -0
- package/dist/commands/env/commands/env-pull/dotenv-files.d.ts +14 -0
- package/dist/commands/env/commands/env-pull/security.d.ts +12 -0
- package/dist/commands/env/commands/env-pull/service.d.ts +8 -0
- package/dist/commands/env/commands/env-pull/shared.d.ts +79 -0
- package/dist/commands/env/commands/setup/types.d.ts +1 -1
- package/dist/commands/env/constants/local-supabase.d.ts +2 -0
- package/dist/commands/inject-test-attrs/defaults.d.ts +9 -0
- package/dist/commands/template-check/contract.d.ts +6 -6
- package/dist/commands/template-check/machine.d.ts +2 -2
- package/dist/commands/template-check/types.d.ts +0 -4
- package/dist/commands/template-check/utils/diff-analyzer.d.ts +0 -4
- package/dist/commands/utils/machine-state-logging.d.ts +20 -0
- package/dist/commands/utils/repo-root.d.ts +2 -0
- package/dist/config/env.d.ts +4 -4
- package/dist/config-loader-GT3HAQ7U.js +7 -0
- package/dist/db-XULCILOU.js +14137 -0
- package/dist/dev-5YXNPTCJ.js +992 -0
- package/dist/doctor-MZLOA53G.js +44 -0
- package/dist/env-HMMRSYCI.js +7 -0
- package/dist/env-SS66PZ4B.js +2623 -0
- package/dist/env-files-2UIUYLLR.js +8 -0
- package/dist/error-handler-HEXBRNVV.js +460 -0
- package/dist/hotfix-YA3DGLOM.js +1477 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +48 -42995
- package/dist/init-ZIL6LRFO.js +631 -0
- package/dist/inject-test-attrs-P44BVTQS.js +23 -0
- package/dist/internal/machines/snapshot-helpers.d.ts +6 -0
- package/dist/lib/sql-comment-utils.d.ts +25 -0
- package/dist/license-OB7GVJQ2.js +468 -0
- package/dist/link-VSNDVZZD.js +59 -0
- package/dist/manifest-TMFLESHW.js +19 -0
- package/dist/prepare-32DOVHTE.js +250 -0
- package/dist/risk-detector-4U6ZJ2G5.js +6 -0
- package/dist/risk-detector-core-TK4OAI3N.js +166 -0
- package/dist/risk-detector-plpgsql-HWKS4OLR.js +1886 -0
- package/dist/sdk-XK6HQU7S.js +348 -0
- package/dist/services-7VK5KZTO.js +177 -0
- package/dist/session-SFW5QSXZ.js +142 -0
- package/dist/signal-handler-DO3OANW5.js +6 -0
- package/dist/status-UTKS63AB.js +94 -0
- package/dist/telemetry-P56UBLZ2.js +93 -0
- package/dist/template-check-3P4HZXVY.js +1944 -0
- package/dist/test-V4KQL574.js +650 -0
- package/dist/test-gen-FS4CEY3P.js +88 -0
- package/dist/ui-RJAMCWUI.js +331 -0
- package/dist/upgrade-NUK3ZBCL.js +637 -0
- package/dist/utils/config-loader.d.ts +0 -3
- package/dist/validate-CAAW4Y44.js +54 -0
- package/dist/validators/risk-detector-content-risks.d.ts +13 -0
- package/dist/validators/risk-detector-core.d.ts +25 -0
- package/dist/validators/risk-detector-patterns.d.ts +15 -0
- package/dist/validators/risk-detector-plpgsql-expression-resolver.d.ts +22 -0
- package/dist/validators/risk-detector-plpgsql-parser.d.ts +5 -0
- package/dist/validators/risk-detector-plpgsql-tokenizer.d.ts +18 -0
- package/dist/validators/risk-detector-plpgsql.d.ts +9 -0
- package/dist/validators/risk-detector-text-utils.d.ts +6 -0
- package/dist/validators/risk-detector-types.d.ts +16 -0
- package/dist/validators/risk-detector.d.ts +7 -26
- package/dist/vuln-check-2W7N5TA2.js +121 -0
- package/dist/vuln-checker-IQJ56RUV.js +3223 -0
- package/dist/watch-PNTKZYFB.js +911 -0
- package/dist/workflow-H75N4BXX.js +897 -0
- package/package.json +5 -2
- package/dist/cli/contract-mode.d.ts.map +0 -1
- package/dist/cli/contract-output.d.ts.map +0 -1
- package/dist/cli/early-flags.d.ts.map +0 -1
- package/dist/cli/error-handler.d.ts.map +0 -1
- package/dist/cli/exec.d.ts.map +0 -1
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/json-output.d.ts.map +0 -1
- package/dist/cli/non-interactive.d.ts.map +0 -1
- package/dist/cli/output-format.d.ts.map +0 -1
- package/dist/cli/signal-handler.d.ts.map +0 -1
- package/dist/commands/build/actors/build.d.ts.map +0 -1
- package/dist/commands/build/actors/clean.d.ts.map +0 -1
- package/dist/commands/build/actors/db-sync.d.ts.map +0 -1
- package/dist/commands/build/actors/index.d.ts.map +0 -1
- package/dist/commands/build/actors/manifest.d.ts.map +0 -1
- package/dist/commands/build/actors/setup.d.ts.map +0 -1
- package/dist/commands/build/actors/static-checks.d.ts.map +0 -1
- package/dist/commands/build/actors/validate.d.ts.map +0 -1
- package/dist/commands/build/commands/build.d.ts.map +0 -1
- package/dist/commands/build/contract.d.ts.map +0 -1
- package/dist/commands/build/guards.d.ts.map +0 -1
- package/dist/commands/build/index.d.ts.map +0 -1
- package/dist/commands/build/machine.d.ts.map +0 -1
- package/dist/commands/build/types.d.ts.map +0 -1
- package/dist/commands/cache.d.ts.map +0 -1
- package/dist/commands/check/commands/check.d.ts.map +0 -1
- package/dist/commands/check/index.d.ts.map +0 -1
- package/dist/commands/ci/commands/ci-checks.d.ts.map +0 -1
- package/dist/commands/ci/commands/ci-layer-content.d.ts.map +0 -1
- package/dist/commands/ci/commands/ci-pr-capabilities.d.ts.map +0 -1
- package/dist/commands/ci/commands/ci-prod-apply.d.ts.map +0 -1
- package/dist/commands/ci/commands/ci-prod-db-operations.d.ts.map +0 -1
- package/dist/commands/ci/commands/ci-prod-github.d.ts.map +0 -1
- package/dist/commands/ci/commands/ci-prod-types.d.ts.map +0 -1
- package/dist/commands/ci/commands/ci-prod-utils.d.ts.map +0 -1
- package/dist/commands/ci/commands/ci-prod-workflow.d.ts.map +0 -1
- package/dist/commands/ci/commands/ci-resolvers.d.ts.map +0 -1
- package/dist/commands/ci/commands/ci-static.d.ts.map +0 -1
- package/dist/commands/ci/commands/ci-supabase-local.d.ts.map +0 -1
- package/dist/commands/ci/index.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/build/app-build.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/build/app-start.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/build/build-and-playwright.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/build/index.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/build/playwright-install.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/build/static-checks.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/db/apply-seeds.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/db/index.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/db/pgtap-install.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/db/production-preview.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/db/pull-production.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/db/reset.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/db/schema-stats.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/db/setup-roles.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/db/sync-schema.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/finalize/github.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/finalize/index.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/finalize/summary.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/index.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/setup/index.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/setup/local.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/setup/pr-common.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/setup/pr-local.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/test/capabilities.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/test/index.d.ts.map +0 -1
- package/dist/commands/ci/machine/actors/test/run-layers.d.ts.map +0 -1
- package/dist/commands/ci/machine/commands/ci-local.d.ts.map +0 -1
- package/dist/commands/ci/machine/commands/ci-pr.d.ts.map +0 -1
- package/dist/commands/ci/machine/commands/index.d.ts.map +0 -1
- package/dist/commands/ci/machine/commands/machine-runner.d.ts.map +0 -1
- package/dist/commands/ci/machine/commands/runtime-env.d.ts.map +0 -1
- package/dist/commands/ci/machine/contract.d.ts.map +0 -1
- package/dist/commands/ci/machine/formatters/github-comment-types.d.ts.map +0 -1
- package/dist/commands/ci/machine/formatters/github-comment.d.ts.map +0 -1
- package/dist/commands/ci/machine/formatters/index.d.ts.map +0 -1
- package/dist/commands/ci/machine/formatters/sections/final-comment.d.ts.map +0 -1
- package/dist/commands/ci/machine/formatters/sections/format-helpers.d.ts.map +0 -1
- package/dist/commands/ci/machine/formatters/sections/index.d.ts.map +0 -1
- package/dist/commands/ci/machine/formatters/sections/progress-comment.d.ts.map +0 -1
- package/dist/commands/ci/machine/formatters/sections/schema-matrix.d.ts.map +0 -1
- package/dist/commands/ci/machine/formatters/summary.d.ts.map +0 -1
- package/dist/commands/ci/machine/guards.d.ts.map +0 -1
- package/dist/commands/ci/machine/helpers.d.ts.map +0 -1
- package/dist/commands/ci/machine/index.d.ts.map +0 -1
- package/dist/commands/ci/machine/machine.d.ts.map +0 -1
- package/dist/commands/ci/machine/types.d.ts.map +0 -1
- package/dist/commands/ci/utils/ai-report.d.ts.map +0 -1
- package/dist/commands/ci/utils/app-process.d.ts.map +0 -1
- package/dist/commands/ci/utils/app-runtime.d.ts.map +0 -1
- package/dist/commands/ci/utils/ci-config.d.ts.map +0 -1
- package/dist/commands/ci/utils/ci-env-schema.d.ts.map +0 -1
- package/dist/commands/ci/utils/ci-logging.d.ts.map +0 -1
- package/dist/commands/ci/utils/ci-summary.d.ts.map +0 -1
- package/dist/commands/ci/utils/config-readers.d.ts.map +0 -1
- package/dist/commands/ci/utils/db-url-utils.d.ts.map +0 -1
- package/dist/commands/ci/utils/e2e-auth-setup.d.ts.map +0 -1
- package/dist/commands/ci/utils/env-security.d.ts.map +0 -1
- package/dist/commands/ci/utils/execa-helpers.d.ts.map +0 -1
- package/dist/commands/ci/utils/exit-code-computation.d.ts.map +0 -1
- package/dist/commands/ci/utils/github-api.d.ts.map +0 -1
- package/dist/commands/ci/utils/github.d.ts.map +0 -1
- package/dist/commands/ci/utils/index.d.ts.map +0 -1
- package/dist/commands/ci/utils/pgtap-installer.d.ts.map +0 -1
- package/dist/commands/ci/utils/rls-verification.d.ts.map +0 -1
- package/dist/commands/ci/utils/schema-operations.d.ts.map +0 -1
- package/dist/commands/ci/utils/seed-operations.d.ts.map +0 -1
- package/dist/commands/ci/utils/test-parallel.d.ts.map +0 -1
- package/dist/commands/ci/utils/timestamp-invariants.d.ts.map +0 -1
- package/dist/commands/ci/utils/workflow-idempotency.d.ts.map +0 -1
- package/dist/commands/db/apply/actors.d.ts.map +0 -1
- package/dist/commands/db/apply/contract.d.ts.map +0 -1
- package/dist/commands/db/apply/helpers/advisory-lock.d.ts.map +0 -1
- package/dist/commands/db/apply/helpers/data-compatibility-checker.d.ts.map +0 -1
- package/dist/commands/db/apply/helpers/index.d.ts.map +0 -1
- package/dist/commands/db/apply/helpers/partition-acl-cleaner.d.ts.map +0 -1
- package/dist/commands/db/apply/helpers/partition-prefilter.d.ts.map +0 -1
- package/dist/commands/db/apply/helpers/partition-validator.d.ts.map +0 -1
- package/dist/commands/db/apply/helpers/pg-schema-diff-helpers.d.ts.map +0 -1
- package/dist/commands/db/apply/helpers/plan-validator.d.ts.map +0 -1
- package/dist/commands/db/apply/helpers/retry-logic.d.ts.map +0 -1
- package/dist/commands/db/apply/helpers/shadow-db-manager.d.ts.map +0 -1
- package/dist/commands/db/apply/index.d.ts.map +0 -1
- package/dist/commands/db/apply/machine.d.ts.map +0 -1
- package/dist/commands/db/commands/db-apply.d.ts.map +0 -1
- package/dist/commands/db/commands/db-audit.d.ts.map +0 -1
- package/dist/commands/db/commands/db-backup.d.ts.map +0 -1
- package/dist/commands/db/commands/db-cleanup.d.ts.map +0 -1
- package/dist/commands/db/commands/db-derive-role-passwords.d.ts.map +0 -1
- package/dist/commands/db/commands/db-derive-urls.d.ts.map +0 -1
- package/dist/commands/db/commands/db-diagram.d.ts.map +0 -1
- package/dist/commands/db/commands/db-drizzle.d.ts.map +0 -1
- package/dist/commands/db/commands/db-extension.d.ts.map +0 -1
- package/dist/commands/db/commands/db-generate-password.d.ts.map +0 -1
- package/dist/commands/db/commands/db-lifecycle.d.ts.map +0 -1
- package/dist/commands/db/commands/db-rollback.d.ts.map +0 -1
- package/dist/commands/db/commands/db-schema.d.ts.map +0 -1
- package/dist/commands/db/commands/db-seed-metadata.d.ts.map +0 -1
- package/dist/commands/db/commands/db-seed-verify.d.ts.map +0 -1
- package/dist/commands/db/commands/db-seed.d.ts.map +0 -1
- package/dist/commands/db/commands/db-snapshot.d.ts.map +0 -1
- package/dist/commands/db/commands/db-stack.d.ts.map +0 -1
- package/dist/commands/db/commands/db-stats.d.ts.map +0 -1
- package/dist/commands/db/commands/db-sync.d.ts.map +0 -1
- package/dist/commands/db/commands/db-test.d.ts.map +0 -1
- package/dist/commands/db/constants.d.ts.map +0 -1
- package/dist/commands/db/extension-registry.d.ts.map +0 -1
- package/dist/commands/db/index.d.ts.map +0 -1
- package/dist/commands/db/preflight/actors.d.ts.map +0 -1
- package/dist/commands/db/preflight/contract.d.ts.map +0 -1
- package/dist/commands/db/preflight/index.d.ts.map +0 -1
- package/dist/commands/db/sync/actors.d.ts.map +0 -1
- package/dist/commands/db/sync/contract.d.ts.map +0 -1
- package/dist/commands/db/sync/index.d.ts.map +0 -1
- package/dist/commands/db/sync/machine.d.ts.map +0 -1
- package/dist/commands/db/types.d.ts.map +0 -1
- package/dist/commands/db/utils/db-target.d.ts.map +0 -1
- package/dist/commands/db/utils/db-url-builder.d.ts.map +0 -1
- package/dist/commands/db/utils/error-handlers.d.ts.map +0 -1
- package/dist/commands/db/utils/import-impact-analyzer.d.ts.map +0 -1
- package/dist/commands/db/utils/preflight-check.d.ts.map +0 -1
- package/dist/commands/db/utils/psql.d.ts.map +0 -1
- package/dist/commands/db/utils/schema-detector.d.ts.map +0 -1
- package/dist/commands/db/utils/schema-sync.d.ts.map +0 -1
- package/dist/commands/db/utils/script-runner.d.ts.map +0 -1
- package/dist/commands/db/utils/seed-manager.d.ts.map +0 -1
- package/dist/commands/db/utils/semantic-mapper.d.ts.map +0 -1
- package/dist/commands/db/utils/sql-table-extractor.d.ts.map +0 -1
- package/dist/commands/db/utils/stack-detector.d.ts.map +0 -1
- package/dist/commands/db/utils/table-registry.d.ts.map +0 -1
- package/dist/commands/db/utils/table-source-classifier.d.ts.map +0 -1
- package/dist/commands/dev/actors/index.d.ts.map +0 -1
- package/dist/commands/dev/commands/dev.d.ts.map +0 -1
- package/dist/commands/dev/contract.d.ts.map +0 -1
- package/dist/commands/dev/guards.d.ts.map +0 -1
- package/dist/commands/dev/helpers/stale-process-detector.d.ts.map +0 -1
- package/dist/commands/dev/machine.d.ts.map +0 -1
- package/dist/commands/dev/types.d.ts.map +0 -1
- package/dist/commands/env/commands/env-check.d.ts.map +0 -1
- package/dist/commands/env/commands/env-encrypt.d.ts.map +0 -1
- package/dist/commands/env/commands/env-pull.d.ts.map +0 -1
- package/dist/commands/env/commands/env-setup.d.ts.map +0 -1
- package/dist/commands/env/commands/env-sync.d.ts.map +0 -1
- package/dist/commands/env/commands/setup/action.d.ts.map +0 -1
- package/dist/commands/env/commands/setup/auth.d.ts.map +0 -1
- package/dist/commands/env/commands/setup/file-export.d.ts.map +0 -1
- package/dist/commands/env/commands/setup/github-api.d.ts.map +0 -1
- package/dist/commands/env/commands/setup/helpers.d.ts.map +0 -1
- package/dist/commands/env/commands/setup/index.d.ts.map +0 -1
- package/dist/commands/env/commands/setup/parsers.d.ts.map +0 -1
- package/dist/commands/env/commands/setup/prompts.d.ts.map +0 -1
- package/dist/commands/env/commands/setup/supabase-api.d.ts.map +0 -1
- package/dist/commands/env/commands/setup/types.d.ts.map +0 -1
- package/dist/commands/env/commands/setup/vercel-api.d.ts.map +0 -1
- package/dist/commands/env/constants/local-supabase.d.ts.map +0 -1
- package/dist/commands/env/index.d.ts.map +0 -1
- package/dist/commands/hotfix/actors.d.ts.map +0 -1
- package/dist/commands/hotfix/commands/hotfix-complete.d.ts.map +0 -1
- package/dist/commands/hotfix/commands/hotfix-create.d.ts.map +0 -1
- package/dist/commands/hotfix/commands/hotfix-deploy.d.ts.map +0 -1
- package/dist/commands/hotfix/commands/hotfix-status.d.ts.map +0 -1
- package/dist/commands/hotfix/contract.d.ts.map +0 -1
- package/dist/commands/hotfix/index.d.ts.map +0 -1
- package/dist/commands/hotfix/machine.d.ts.map +0 -1
- package/dist/commands/hotfix/metadata.d.ts.map +0 -1
- package/dist/commands/hotfix/utils/hotfix-machine-helper.d.ts.map +0 -1
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/inject-test-attrs/action.d.ts.map +0 -1
- package/dist/commands/inject-test-attrs/commands/inject-test-attrs.d.ts.map +0 -1
- package/dist/commands/inject-test-attrs/contract.d.ts.map +0 -1
- package/dist/commands/inject-test-attrs/detection-diagnostics.d.ts.map +0 -1
- package/dist/commands/inject-test-attrs/formatter.d.ts.map +0 -1
- package/dist/commands/inject-test-attrs/index.d.ts.map +0 -1
- package/dist/commands/inject-test-attrs/manifest-generator.d.ts.map +0 -1
- package/dist/commands/inject-test-attrs/processor-utils.d.ts.map +0 -1
- package/dist/commands/inject-test-attrs/processor.d.ts.map +0 -1
- package/dist/commands/inject-test-attrs/types.d.ts.map +0 -1
- package/dist/commands/link.d.ts.map +0 -1
- package/dist/commands/manifest/index.d.ts.map +0 -1
- package/dist/commands/prepare/commands/prepare.d.ts.map +0 -1
- package/dist/commands/prepare/index.d.ts.map +0 -1
- package/dist/commands/sdk/commands/publish.d.ts.map +0 -1
- package/dist/commands/sdk/index.d.ts.map +0 -1
- package/dist/commands/services/index.d.ts.map +0 -1
- package/dist/commands/session/index.d.ts.map +0 -1
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/telemetry.d.ts.map +0 -1
- package/dist/commands/template-check/actors/compare.d.ts.map +0 -1
- package/dist/commands/template-check/actors/discover.d.ts.map +0 -1
- package/dist/commands/template-check/actors/index.d.ts.map +0 -1
- package/dist/commands/template-check/actors/report.d.ts.map +0 -1
- package/dist/commands/template-check/commands/template-check.d.ts.map +0 -1
- package/dist/commands/template-check/config.d.ts.map +0 -1
- package/dist/commands/template-check/contract.d.ts.map +0 -1
- package/dist/commands/template-check/index.d.ts.map +0 -1
- package/dist/commands/template-check/machine.d.ts.map +0 -1
- package/dist/commands/template-check/types.d.ts.map +0 -1
- package/dist/commands/template-check/utils/diff-analyzer.d.ts.map +0 -1
- package/dist/commands/template-check/utils/normalizer.d.ts.map +0 -1
- package/dist/commands/template-check/utils/path-mapping.d.ts.map +0 -1
- package/dist/commands/test/commands/test-db.d.ts.map +0 -1
- package/dist/commands/test/commands/test-e2e.d.ts.map +0 -1
- package/dist/commands/test/commands/test-fast.d.ts.map +0 -1
- package/dist/commands/test/commands/test-integration.d.ts.map +0 -1
- package/dist/commands/test/commands/test-layer.d.ts.map +0 -1
- package/dist/commands/test/commands/test-owasp-generate.d.ts.map +0 -1
- package/dist/commands/test/commands/test-service.d.ts.map +0 -1
- package/dist/commands/test/commands/test-static.d.ts.map +0 -1
- package/dist/commands/test/commands/test.d.ts.map +0 -1
- package/dist/commands/test/index.d.ts.map +0 -1
- package/dist/commands/test-gen.d.ts.map +0 -1
- package/dist/commands/ui.d.ts.map +0 -1
- package/dist/commands/upgrade.d.ts.map +0 -1
- package/dist/commands/validate.d.ts.map +0 -1
- package/dist/commands/vuln-check.d.ts.map +0 -1
- package/dist/commands/watch.d.ts.map +0 -1
- package/dist/commands/workflow/commands/deploy-production.d.ts.map +0 -1
- package/dist/commands/workflow/commands/final-status.d.ts.map +0 -1
- package/dist/commands/workflow/commands/log.d.ts.map +0 -1
- package/dist/commands/workflow/commands/notify.d.ts.map +0 -1
- package/dist/commands/workflow/commands/paths.d.ts.map +0 -1
- package/dist/commands/workflow/commands/sync.d.ts.map +0 -1
- package/dist/commands/workflow/commands/validate.d.ts.map +0 -1
- package/dist/commands/workflow/commands/verify-credentials.d.ts.map +0 -1
- package/dist/commands/workflow/index.d.ts.map +0 -1
- package/dist/commands/workflow/types.d.ts.map +0 -1
- package/dist/config/env-files.d.ts.map +0 -1
- package/dist/config/env.d.ts.map +0 -1
- package/dist/constants/versions.d.ts.map +0 -1
- package/dist/contracts/envelope.d.ts.map +0 -1
- package/dist/errors/catalog.d.ts.map +0 -1
- package/dist/errors/exit-codes.d.ts.map +0 -1
- package/dist/errors/index.d.ts.map +0 -1
- package/dist/incremental/affected-tests.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/internal/machines/index.d.ts.map +0 -1
- package/dist/internal/machines/machine-runner.d.ts.map +0 -1
- package/dist/internal/machines/snapshot-helpers.d.ts.map +0 -1
- package/dist/internal/machines/types.d.ts.map +0 -1
- package/dist/internal/vuln-checker/analyzers/dependency-analyzer.d.ts.map +0 -1
- package/dist/internal/vuln-checker/analyzers/rls-analyzer.d.ts.map +0 -1
- package/dist/internal/vuln-checker/analyzers/secret-analyzer.d.ts.map +0 -1
- package/dist/internal/vuln-checker/analyzers/typescript-analyzer.d.ts.map +0 -1
- package/dist/internal/vuln-checker/config/loader.d.ts.map +0 -1
- package/dist/internal/vuln-checker/constants.d.ts.map +0 -1
- package/dist/internal/vuln-checker/ignore/matcher.d.ts.map +0 -1
- package/dist/internal/vuln-checker/index.d.ts.map +0 -1
- package/dist/internal/vuln-checker/reporters/console-reporter.d.ts.map +0 -1
- package/dist/internal/vuln-checker/reporters/json-reporter.d.ts.map +0 -1
- package/dist/internal/vuln-checker/reporters/markdown-reporter.d.ts.map +0 -1
- package/dist/internal/vuln-checker/reporters/sarif-reporter.d.ts.map +0 -1
- package/dist/internal/vuln-checker/security/path-validation.d.ts.map +0 -1
- package/dist/internal/vuln-checker/types.d.ts.map +0 -1
- package/dist/notifiers/desktop-notifier.d.ts.map +0 -1
- package/dist/ui/components/db-panel.d.ts.map +0 -1
- package/dist/ui/components/status-bar.d.ts.map +0 -1
- package/dist/ui/components/test-panel.d.ts.map +0 -1
- package/dist/ui/dashboard.d.ts.map +0 -1
- package/dist/ui/index.d.ts.map +0 -1
- package/dist/utils/config-loader.d.ts.map +0 -1
- package/dist/utils/config-updater.d.ts.map +0 -1
- package/dist/utils/diagnostics.d.ts.map +0 -1
- package/dist/utils/dotenvx.d.ts.map +0 -1
- package/dist/utils/env-local-bridge.d.ts.map +0 -1
- package/dist/utils/execution-plan.d.ts.map +0 -1
- package/dist/utils/github-output-security.d.ts.map +0 -1
- package/dist/utils/help-system.d.ts.map +0 -1
- package/dist/utils/license/admin-auth.d.ts.map +0 -1
- package/dist/utils/license/allowlist-checker.d.ts.map +0 -1
- package/dist/utils/license/ci-detector.d.ts.map +0 -1
- package/dist/utils/license/index.d.ts.map +0 -1
- package/dist/utils/license/owner-resolver.d.ts.map +0 -1
- package/dist/utils/license/types.d.ts.map +0 -1
- package/dist/utils/license/validate-owner.d.ts.map +0 -1
- package/dist/utils/path-security.d.ts.map +0 -1
- package/dist/utils/port-allocator.d.ts.map +0 -1
- package/dist/utils/secure-exec.d.ts.map +0 -1
- package/dist/utils/template-fetcher.d.ts.map +0 -1
- package/dist/utils/type-guards.d.ts.map +0 -1
- package/dist/utils/vercel-project.d.ts.map +0 -1
- package/dist/utils/workspace-detector.d.ts.map +0 -1
- package/dist/validators/risk-detector.d.ts.map +0 -1
- package/dist/validators/schema-validator.d.ts.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/watchers/schema-watcher.d.ts.map +0 -1
- package/dist/watchers/test-watcher.d.ts.map +0 -1
|
@@ -0,0 +1,1886 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
import { stripSqlCommentsPreserveLines, buildLineStarts, lineNumberFromIndex, stripSqlStringsPreserveLines, detectRisksFromContent, stripSqlForPatternMatching } from './chunk-3FDQW524.js';
|
|
4
|
+
import { init_esm_shims } from './chunk-VRXHCR5K.js';
|
|
5
|
+
|
|
6
|
+
createRequire(import.meta.url);
|
|
7
|
+
|
|
8
|
+
// src/validators/risk-detector-plpgsql.ts
|
|
9
|
+
init_esm_shims();
|
|
10
|
+
|
|
11
|
+
// src/validators/risk-detector-plpgsql-parser.ts
|
|
12
|
+
init_esm_shims();
|
|
13
|
+
|
|
14
|
+
// src/validators/risk-detector-plpgsql-tokenizer.ts
|
|
15
|
+
init_esm_shims();
|
|
16
|
+
function skipWhitespace(content, index) {
|
|
17
|
+
let cursor = index;
|
|
18
|
+
while (cursor < content.length && /\s/.test(content[cursor])) {
|
|
19
|
+
cursor += 1;
|
|
20
|
+
}
|
|
21
|
+
return cursor;
|
|
22
|
+
}
|
|
23
|
+
function isIdentifierStart(ch) {
|
|
24
|
+
if (!ch) return false;
|
|
25
|
+
const code = ch.charCodeAt(0);
|
|
26
|
+
return code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 95;
|
|
27
|
+
}
|
|
28
|
+
function isIdentifierPart(ch) {
|
|
29
|
+
if (!ch) return false;
|
|
30
|
+
const code = ch.charCodeAt(0);
|
|
31
|
+
return code >= 65 && code <= 90 || code >= 97 && code <= 122 || code >= 48 && code <= 57 || code === 95;
|
|
32
|
+
}
|
|
33
|
+
function isWordChar(ch) {
|
|
34
|
+
return isIdentifierPart(ch);
|
|
35
|
+
}
|
|
36
|
+
function readDollarTagAt(content, index) {
|
|
37
|
+
if (content[index] !== "$") return void 0;
|
|
38
|
+
const next = content[index + 1];
|
|
39
|
+
if (next === "$") return "$$";
|
|
40
|
+
if (!next || !isIdentifierStart(next)) return void 0;
|
|
41
|
+
let cursor = index + 2;
|
|
42
|
+
while (cursor < content.length && isIdentifierPart(content[cursor] ?? "")) {
|
|
43
|
+
cursor += 1;
|
|
44
|
+
}
|
|
45
|
+
if (content[cursor] !== "$") return void 0;
|
|
46
|
+
return content.slice(index, cursor + 1);
|
|
47
|
+
}
|
|
48
|
+
function matchesKeywordAt(content, index, keyword) {
|
|
49
|
+
if (index < 0 || index + keyword.length > content.length) return false;
|
|
50
|
+
for (let offset = 0; offset < keyword.length; offset += 1) {
|
|
51
|
+
const char = content[index + offset];
|
|
52
|
+
if (!char) return false;
|
|
53
|
+
const code = char.charCodeAt(0);
|
|
54
|
+
const upperCode = code >= 97 && code <= 122 ? code - 32 : code;
|
|
55
|
+
if (upperCode !== keyword.charCodeAt(offset)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
function isQuoteBoundary(content, index, keyword) {
|
|
62
|
+
if (index < 0 || index + keyword.length > content.length) return false;
|
|
63
|
+
if (!matchesKeywordAt(content, index, keyword)) return false;
|
|
64
|
+
const prev = index > 0 ? content[index - 1] : " ";
|
|
65
|
+
const next = index + keyword.length < content.length ? content[index + keyword.length] : " ";
|
|
66
|
+
return !isWordChar(prev) && !isWordChar(next);
|
|
67
|
+
}
|
|
68
|
+
function consumeSplitLineComment(content, state) {
|
|
69
|
+
if (!state.inLineComment) return false;
|
|
70
|
+
const char = content[state.cursor] ?? "";
|
|
71
|
+
state.current += char;
|
|
72
|
+
if (char === "\n") {
|
|
73
|
+
state.inLineComment = false;
|
|
74
|
+
}
|
|
75
|
+
state.cursor += 1;
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
function consumeSplitBlockComment(content, state) {
|
|
79
|
+
if (!state.inBlockComment) return false;
|
|
80
|
+
const char = content[state.cursor] ?? "";
|
|
81
|
+
const next = content[state.cursor + 1] ?? "";
|
|
82
|
+
if (char === "*" && next === "/") {
|
|
83
|
+
state.current += "*/";
|
|
84
|
+
state.inBlockComment = false;
|
|
85
|
+
state.cursor += 2;
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
state.current += char;
|
|
89
|
+
state.cursor += 1;
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
function consumeSplitSingleQuote(content, state) {
|
|
93
|
+
if (!state.inSingle) return false;
|
|
94
|
+
const char = content[state.cursor] ?? "";
|
|
95
|
+
const next = content[state.cursor + 1] ?? "";
|
|
96
|
+
if (char === "'" && next === "'") {
|
|
97
|
+
state.current += "''";
|
|
98
|
+
state.cursor += 2;
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
if (char === "'") {
|
|
102
|
+
state.inSingle = false;
|
|
103
|
+
}
|
|
104
|
+
state.current += char;
|
|
105
|
+
state.cursor += 1;
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
function tryStartSplitLineComment(content, state) {
|
|
109
|
+
const char = content[state.cursor] ?? "";
|
|
110
|
+
const next = content[state.cursor + 1] ?? "";
|
|
111
|
+
if (char !== "-" || next !== "-") return false;
|
|
112
|
+
state.inLineComment = true;
|
|
113
|
+
state.current += "--";
|
|
114
|
+
state.cursor += 2;
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
function tryStartSplitBlockComment(content, state) {
|
|
118
|
+
const char = content[state.cursor] ?? "";
|
|
119
|
+
const next = content[state.cursor + 1] ?? "";
|
|
120
|
+
if (char !== "/" || next !== "*") return false;
|
|
121
|
+
state.inBlockComment = true;
|
|
122
|
+
state.current += "/*";
|
|
123
|
+
state.cursor += 2;
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
function consumeSplitDoubleQuote(content, state) {
|
|
127
|
+
if (!state.inDouble) return false;
|
|
128
|
+
const char = content[state.cursor] ?? "";
|
|
129
|
+
const next = content[state.cursor + 1] ?? "";
|
|
130
|
+
if (char === '"' && next === '"') {
|
|
131
|
+
state.current += '""';
|
|
132
|
+
state.cursor += 2;
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
if (char === '"') {
|
|
136
|
+
state.inDouble = false;
|
|
137
|
+
}
|
|
138
|
+
state.current += char;
|
|
139
|
+
state.cursor += 1;
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
function consumeSplitDollarQuote(content, state) {
|
|
143
|
+
if (!state.inDollar) return false;
|
|
144
|
+
const close = `$${state.dollarTag}$`;
|
|
145
|
+
if (content.startsWith(close, state.cursor)) {
|
|
146
|
+
state.inDollar = false;
|
|
147
|
+
state.dollarTag = "";
|
|
148
|
+
state.current += close;
|
|
149
|
+
state.cursor += close.length;
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
state.current += content[state.cursor] ?? "";
|
|
153
|
+
state.cursor += 1;
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
function tryStartSplitDollarQuote(content, state) {
|
|
157
|
+
const foundDollarTag = readDollarTagAt(content, state.cursor);
|
|
158
|
+
if (foundDollarTag === void 0) return false;
|
|
159
|
+
state.inDollar = true;
|
|
160
|
+
state.dollarTag = foundDollarTag.slice(1, -1);
|
|
161
|
+
state.current += foundDollarTag;
|
|
162
|
+
state.cursor += foundDollarTag.length;
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
function tryStartSplitSingleQuote(content, state) {
|
|
166
|
+
if (content[state.cursor] !== "'") return false;
|
|
167
|
+
state.inSingle = true;
|
|
168
|
+
state.current += "'";
|
|
169
|
+
state.cursor += 1;
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
function tryStartSplitDoubleQuote(content, state) {
|
|
173
|
+
if (content[state.cursor] !== '"') return false;
|
|
174
|
+
state.inDouble = true;
|
|
175
|
+
state.current += '"';
|
|
176
|
+
state.cursor += 1;
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
function flushCurrentSplitStatement(state) {
|
|
180
|
+
const statement = state.current.trim();
|
|
181
|
+
if (statement.length > 0) {
|
|
182
|
+
state.ranges.push({ statement, startOffset: state.currentStart });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function trySplitAtTerminator(content, state) {
|
|
186
|
+
const char = content[state.cursor] ?? "";
|
|
187
|
+
if (char === "(") {
|
|
188
|
+
state.depth += 1;
|
|
189
|
+
} else if (char === ")" && state.depth > 0) {
|
|
190
|
+
state.depth -= 1;
|
|
191
|
+
}
|
|
192
|
+
if (char !== ";" || state.depth !== 0) return false;
|
|
193
|
+
flushCurrentSplitStatement(state);
|
|
194
|
+
state.current = "";
|
|
195
|
+
state.currentStart = state.cursor + 1;
|
|
196
|
+
state.cursor += 1;
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
function appendSplitChar(content, state) {
|
|
200
|
+
state.current += content[state.cursor] ?? "";
|
|
201
|
+
state.cursor += 1;
|
|
202
|
+
}
|
|
203
|
+
var SPLIT_STEPS = [
|
|
204
|
+
consumeSplitLineComment,
|
|
205
|
+
consumeSplitBlockComment,
|
|
206
|
+
consumeSplitSingleQuote,
|
|
207
|
+
tryStartSplitLineComment,
|
|
208
|
+
tryStartSplitBlockComment,
|
|
209
|
+
consumeSplitDoubleQuote,
|
|
210
|
+
consumeSplitDollarQuote,
|
|
211
|
+
tryStartSplitDollarQuote,
|
|
212
|
+
tryStartSplitSingleQuote,
|
|
213
|
+
tryStartSplitDoubleQuote,
|
|
214
|
+
trySplitAtTerminator
|
|
215
|
+
];
|
|
216
|
+
function runSplitStep(content, state) {
|
|
217
|
+
for (const step of SPLIT_STEPS) {
|
|
218
|
+
if (step(content, state)) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
function splitPlpgsqlStatementsWithOffsets(content) {
|
|
225
|
+
const state = {
|
|
226
|
+
ranges: [],
|
|
227
|
+
current: "",
|
|
228
|
+
currentStart: 0,
|
|
229
|
+
inSingle: false,
|
|
230
|
+
inDouble: false,
|
|
231
|
+
inDollar: false,
|
|
232
|
+
dollarTag: "",
|
|
233
|
+
inLineComment: false,
|
|
234
|
+
inBlockComment: false,
|
|
235
|
+
depth: 0,
|
|
236
|
+
cursor: 0
|
|
237
|
+
};
|
|
238
|
+
while (state.cursor < content.length) {
|
|
239
|
+
if (runSplitStep(content, state)) continue;
|
|
240
|
+
appendSplitChar(content, state);
|
|
241
|
+
}
|
|
242
|
+
flushCurrentSplitStatement(state);
|
|
243
|
+
return state.ranges;
|
|
244
|
+
}
|
|
245
|
+
function skipLineCommentToEol(content, start) {
|
|
246
|
+
let cursor = start;
|
|
247
|
+
while (cursor < content.length && content[cursor] !== "\n") {
|
|
248
|
+
cursor += 1;
|
|
249
|
+
}
|
|
250
|
+
return cursor;
|
|
251
|
+
}
|
|
252
|
+
function skipBlockComment(content, start) {
|
|
253
|
+
let cursor = start;
|
|
254
|
+
while (cursor + 1 < content.length && !(content[cursor] === "*" && content[cursor + 1] === "/")) {
|
|
255
|
+
cursor += 1;
|
|
256
|
+
}
|
|
257
|
+
if (cursor + 1 < content.length) {
|
|
258
|
+
cursor += 2;
|
|
259
|
+
}
|
|
260
|
+
return cursor;
|
|
261
|
+
}
|
|
262
|
+
function consumeExecuteOuterSingle(body, state) {
|
|
263
|
+
if (!state.inSingle) return false;
|
|
264
|
+
const char = body[state.cursor] ?? "";
|
|
265
|
+
const next = body[state.cursor + 1] ?? "";
|
|
266
|
+
if (char === "'" && next === "'") {
|
|
267
|
+
state.cursor += 2;
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
if (char === "'") {
|
|
271
|
+
state.inSingle = false;
|
|
272
|
+
}
|
|
273
|
+
state.cursor += 1;
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
function consumeExecuteOuterDouble(body, state) {
|
|
277
|
+
if (!state.inDouble) return false;
|
|
278
|
+
const char = body[state.cursor] ?? "";
|
|
279
|
+
const next = body[state.cursor + 1] ?? "";
|
|
280
|
+
if (char === '"' && next === '"') {
|
|
281
|
+
state.cursor += 2;
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
if (char === '"') {
|
|
285
|
+
state.inDouble = false;
|
|
286
|
+
}
|
|
287
|
+
state.cursor += 1;
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
function consumeExecuteOuterDollar(body, state) {
|
|
291
|
+
if (!state.inDollar) return false;
|
|
292
|
+
const close = `$${state.dollarTag}$`;
|
|
293
|
+
if (body.startsWith(close, state.cursor)) {
|
|
294
|
+
state.inDollar = false;
|
|
295
|
+
state.dollarTag = "";
|
|
296
|
+
state.cursor += close.length;
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
state.cursor += 1;
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
function tryStartExecuteOuterDollar(body, state) {
|
|
303
|
+
const foundDollarTag = readDollarTagAt(body, state.cursor);
|
|
304
|
+
if (foundDollarTag === void 0) return false;
|
|
305
|
+
state.inDollar = true;
|
|
306
|
+
state.dollarTag = foundDollarTag.slice(1, -1);
|
|
307
|
+
state.cursor += foundDollarTag.length;
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
function tryStartExecuteOuterSingle(body, state) {
|
|
311
|
+
if (body[state.cursor] !== "'") return false;
|
|
312
|
+
state.inSingle = true;
|
|
313
|
+
state.cursor += 1;
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
function tryStartExecuteOuterDouble(body, state) {
|
|
317
|
+
if (body[state.cursor] !== '"') return false;
|
|
318
|
+
state.inDouble = true;
|
|
319
|
+
state.cursor += 1;
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
function trySkipExecuteOuterLineComment(body, state) {
|
|
323
|
+
const char = body[state.cursor] ?? "";
|
|
324
|
+
const next = body[state.cursor + 1] ?? "";
|
|
325
|
+
if (char !== "-" || next !== "-") return false;
|
|
326
|
+
state.cursor = skipLineCommentToEol(body, state.cursor + 2);
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
function trySkipExecuteOuterBlockComment(body, state) {
|
|
330
|
+
const char = body[state.cursor] ?? "";
|
|
331
|
+
const next = body[state.cursor + 1] ?? "";
|
|
332
|
+
if (char !== "/" || next !== "*") return false;
|
|
333
|
+
state.cursor = skipBlockComment(body, state.cursor + 2);
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
var EXECUTE_OUTER_STEPS = [
|
|
337
|
+
consumeExecuteOuterSingle,
|
|
338
|
+
consumeExecuteOuterDouble,
|
|
339
|
+
consumeExecuteOuterDollar,
|
|
340
|
+
tryStartExecuteOuterDollar,
|
|
341
|
+
tryStartExecuteOuterSingle,
|
|
342
|
+
tryStartExecuteOuterDouble,
|
|
343
|
+
trySkipExecuteOuterLineComment,
|
|
344
|
+
trySkipExecuteOuterBlockComment
|
|
345
|
+
];
|
|
346
|
+
function runExecuteOuterStep(body, state) {
|
|
347
|
+
for (const step of EXECUTE_OUTER_STEPS) {
|
|
348
|
+
if (step(body, state)) {
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
function consumeExprSingle(body, state) {
|
|
355
|
+
if (!state.inSingle) return false;
|
|
356
|
+
const ch = body[state.cursor] ?? "";
|
|
357
|
+
const nx = body[state.cursor + 1] ?? "";
|
|
358
|
+
if (ch === "'" && nx === "'") {
|
|
359
|
+
state.cursor += 2;
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
if (ch === "'") {
|
|
363
|
+
state.inSingle = false;
|
|
364
|
+
}
|
|
365
|
+
state.cursor += 1;
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
function consumeExprDouble(body, state) {
|
|
369
|
+
if (!state.inDouble) return false;
|
|
370
|
+
const ch = body[state.cursor] ?? "";
|
|
371
|
+
const nx = body[state.cursor + 1] ?? "";
|
|
372
|
+
if (ch === '"' && nx === '"') {
|
|
373
|
+
state.cursor += 2;
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
if (ch === '"') {
|
|
377
|
+
state.inDouble = false;
|
|
378
|
+
}
|
|
379
|
+
state.cursor += 1;
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
function consumeExprLineComment(body, state) {
|
|
383
|
+
if (!state.inLineComment) return false;
|
|
384
|
+
if (body[state.cursor] === "\n") {
|
|
385
|
+
state.inLineComment = false;
|
|
386
|
+
}
|
|
387
|
+
state.cursor += 1;
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
function consumeExprBlockComment(body, state) {
|
|
391
|
+
if (!state.inBlockComment) return false;
|
|
392
|
+
const ch = body[state.cursor] ?? "";
|
|
393
|
+
const nx = body[state.cursor + 1] ?? "";
|
|
394
|
+
if (ch === "*" && nx === "/") {
|
|
395
|
+
state.inBlockComment = false;
|
|
396
|
+
state.cursor += 2;
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
state.cursor += 1;
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
function consumeExprDollar(body, state) {
|
|
403
|
+
if (!state.inDollar) return false;
|
|
404
|
+
const close = `$${state.dollarTag}$`;
|
|
405
|
+
if (body.startsWith(close, state.cursor)) {
|
|
406
|
+
state.inDollar = false;
|
|
407
|
+
state.dollarTag = "";
|
|
408
|
+
state.cursor += close.length;
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
state.cursor += 1;
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
function tryStartExprDollar(body, state) {
|
|
415
|
+
const localDollarTag = readDollarTagAt(body, state.cursor);
|
|
416
|
+
if (localDollarTag === void 0) return false;
|
|
417
|
+
state.inDollar = true;
|
|
418
|
+
state.dollarTag = localDollarTag.slice(1, -1);
|
|
419
|
+
state.cursor += localDollarTag.length;
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
function tryStartExprLineComment(body, state) {
|
|
423
|
+
const ch = body[state.cursor] ?? "";
|
|
424
|
+
const nx = body[state.cursor + 1] ?? "";
|
|
425
|
+
if (ch !== "-" || nx !== "-") return false;
|
|
426
|
+
state.inLineComment = true;
|
|
427
|
+
state.cursor += 2;
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
function tryStartExprBlockComment(body, state) {
|
|
431
|
+
const ch = body[state.cursor] ?? "";
|
|
432
|
+
const nx = body[state.cursor + 1] ?? "";
|
|
433
|
+
if (ch !== "/" || nx !== "*") return false;
|
|
434
|
+
state.inBlockComment = true;
|
|
435
|
+
state.cursor += 2;
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
function tryStartExprSingle(body, state) {
|
|
439
|
+
if (body[state.cursor] !== "'") return false;
|
|
440
|
+
state.inSingle = true;
|
|
441
|
+
state.cursor += 1;
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
function tryStartExprDouble(body, state) {
|
|
445
|
+
if (body[state.cursor] !== '"') return false;
|
|
446
|
+
state.inDouble = true;
|
|
447
|
+
state.cursor += 1;
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
var EXECUTE_EXPR_STEPS = [
|
|
451
|
+
consumeExprSingle,
|
|
452
|
+
consumeExprDouble,
|
|
453
|
+
consumeExprLineComment,
|
|
454
|
+
consumeExprBlockComment,
|
|
455
|
+
consumeExprDollar,
|
|
456
|
+
tryStartExprDollar,
|
|
457
|
+
tryStartExprLineComment,
|
|
458
|
+
tryStartExprBlockComment,
|
|
459
|
+
tryStartExprSingle,
|
|
460
|
+
tryStartExprDouble
|
|
461
|
+
];
|
|
462
|
+
function runExecuteExprStep(body, state) {
|
|
463
|
+
for (const step of EXECUTE_EXPR_STEPS) {
|
|
464
|
+
if (step(body, state)) {
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
function shouldStopExecuteExpression(body, state) {
|
|
471
|
+
const ch = body[state.cursor] ?? "";
|
|
472
|
+
if (ch === "(") {
|
|
473
|
+
state.depth += 1;
|
|
474
|
+
} else if (ch === ")" && state.depth > 0) {
|
|
475
|
+
state.depth -= 1;
|
|
476
|
+
}
|
|
477
|
+
if (ch === ";") {
|
|
478
|
+
state.exprEnd = state.cursor;
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
if (state.depth === 0 && isQuoteBoundary(body, state.cursor, "USING")) {
|
|
482
|
+
state.hasUsingClause = true;
|
|
483
|
+
state.exprEnd = state.cursor;
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
if (state.depth === 0 && isQuoteBoundary(body, state.cursor, "INTO")) {
|
|
487
|
+
state.hasIntoClause = true;
|
|
488
|
+
state.exprEnd = state.cursor;
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
function buildExecuteExpressionInfo(body, statementStartOffset, state) {
|
|
494
|
+
if (state.exprEnd > state.exprStart) {
|
|
495
|
+
return {
|
|
496
|
+
expression: body.slice(state.exprStart, state.exprEnd).trim(),
|
|
497
|
+
statementStartOffset,
|
|
498
|
+
hasUsingClause: state.hasUsingClause,
|
|
499
|
+
hasIntoClause: state.hasIntoClause
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
if (state.cursor >= body.length && state.cursor > state.exprStart) {
|
|
503
|
+
return {
|
|
504
|
+
expression: body.slice(state.exprStart, state.cursor).trim(),
|
|
505
|
+
statementStartOffset,
|
|
506
|
+
hasUsingClause: state.hasUsingClause,
|
|
507
|
+
hasIntoClause: state.hasIntoClause
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
if (state.cursor < body.length && body[state.cursor] === ";") {
|
|
511
|
+
return {
|
|
512
|
+
expression: body.slice(state.exprStart, state.cursor).trim(),
|
|
513
|
+
statementStartOffset,
|
|
514
|
+
hasUsingClause: state.hasUsingClause,
|
|
515
|
+
hasIntoClause: state.hasIntoClause
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
function parseExecuteExpressionAt(body, statementStartOffset) {
|
|
521
|
+
const indexCursor = skipWhitespace(body, statementStartOffset + 7);
|
|
522
|
+
if (isQuoteBoundary(body, indexCursor, "ON")) {
|
|
523
|
+
return {
|
|
524
|
+
info: null,
|
|
525
|
+
nextCursor: Math.max(indexCursor + 2, statementStartOffset + 1)
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
const state = {
|
|
529
|
+
cursor: indexCursor,
|
|
530
|
+
exprStart: indexCursor,
|
|
531
|
+
exprEnd: indexCursor,
|
|
532
|
+
depth: 0,
|
|
533
|
+
hasUsingClause: false,
|
|
534
|
+
hasIntoClause: false,
|
|
535
|
+
inSingle: false,
|
|
536
|
+
inDouble: false,
|
|
537
|
+
inDollar: false,
|
|
538
|
+
inLineComment: false,
|
|
539
|
+
inBlockComment: false,
|
|
540
|
+
dollarTag: ""
|
|
541
|
+
};
|
|
542
|
+
while (state.cursor < body.length) {
|
|
543
|
+
if (runExecuteExprStep(body, state)) continue;
|
|
544
|
+
if (shouldStopExecuteExpression(body, state)) break;
|
|
545
|
+
state.cursor += 1;
|
|
546
|
+
}
|
|
547
|
+
return {
|
|
548
|
+
info: buildExecuteExpressionInfo(body, statementStartOffset, state),
|
|
549
|
+
nextCursor: Math.max(state.cursor + 1, state.exprStart + 1)
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
function extractExecuteExpressions(body) {
|
|
553
|
+
const statements = [];
|
|
554
|
+
const state = {
|
|
555
|
+
cursor: 0,
|
|
556
|
+
inSingle: false,
|
|
557
|
+
inDouble: false,
|
|
558
|
+
inDollar: false,
|
|
559
|
+
dollarTag: ""
|
|
560
|
+
};
|
|
561
|
+
while (state.cursor < body.length) {
|
|
562
|
+
if (runExecuteOuterStep(body, state)) continue;
|
|
563
|
+
if (isQuoteBoundary(body, state.cursor, "EXECUTE")) {
|
|
564
|
+
const parsed = parseExecuteExpressionAt(body, state.cursor);
|
|
565
|
+
if (parsed.info) {
|
|
566
|
+
statements.push(parsed.info);
|
|
567
|
+
}
|
|
568
|
+
state.cursor = parsed.nextCursor;
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
state.cursor += 1;
|
|
572
|
+
}
|
|
573
|
+
return statements;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// src/validators/risk-detector-plpgsql-expression-resolver.ts
|
|
577
|
+
init_esm_shims();
|
|
578
|
+
function skipWhitespace2(content, index) {
|
|
579
|
+
let cursor = index;
|
|
580
|
+
while (cursor < content.length && /\s/.test(content[cursor])) {
|
|
581
|
+
cursor += 1;
|
|
582
|
+
}
|
|
583
|
+
return cursor;
|
|
584
|
+
}
|
|
585
|
+
function skipIdentifier(content, index) {
|
|
586
|
+
let cursor = index;
|
|
587
|
+
while (cursor < content.length && /[A-Za-z0-9_]/.test(content[cursor])) {
|
|
588
|
+
cursor += 1;
|
|
589
|
+
}
|
|
590
|
+
return cursor;
|
|
591
|
+
}
|
|
592
|
+
function readDollarTagAt2(content, index) {
|
|
593
|
+
if (content[index] !== "$") return void 0;
|
|
594
|
+
const next = content[index + 1];
|
|
595
|
+
if (next === "$") return "$$";
|
|
596
|
+
const isIdentifierStart2 = (char) => {
|
|
597
|
+
if (!char) return false;
|
|
598
|
+
const code = char.charCodeAt(0);
|
|
599
|
+
return code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 95;
|
|
600
|
+
};
|
|
601
|
+
const isIdentifierPart2 = (char) => {
|
|
602
|
+
if (!char) return false;
|
|
603
|
+
const code = char.charCodeAt(0);
|
|
604
|
+
return code >= 65 && code <= 90 || code >= 97 && code <= 122 || code >= 48 && code <= 57 || code === 95;
|
|
605
|
+
};
|
|
606
|
+
if (!next || !isIdentifierStart2(next)) return void 0;
|
|
607
|
+
let cursor = index + 2;
|
|
608
|
+
while (cursor < content.length && isIdentifierPart2(content[cursor] ?? "")) {
|
|
609
|
+
cursor += 1;
|
|
610
|
+
}
|
|
611
|
+
if (content[cursor] !== "$") return void 0;
|
|
612
|
+
return content.slice(index, cursor + 1);
|
|
613
|
+
}
|
|
614
|
+
function emitTopLevelSegment(state) {
|
|
615
|
+
const segment = state.current.trim();
|
|
616
|
+
if (segment.length > 0) {
|
|
617
|
+
state.segments.push(segment);
|
|
618
|
+
}
|
|
619
|
+
state.current = "";
|
|
620
|
+
}
|
|
621
|
+
function consumeTopLevelSingle(content, state) {
|
|
622
|
+
if (!state.inSingle) return false;
|
|
623
|
+
const char = content[state.cursor] ?? "";
|
|
624
|
+
const next = content[state.cursor + 1] ?? "";
|
|
625
|
+
if (char === "'" && next === "'") {
|
|
626
|
+
state.current += "''";
|
|
627
|
+
state.cursor += 2;
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
if (char === "'") {
|
|
631
|
+
state.inSingle = false;
|
|
632
|
+
}
|
|
633
|
+
state.current += char;
|
|
634
|
+
state.cursor += 1;
|
|
635
|
+
return true;
|
|
636
|
+
}
|
|
637
|
+
function consumeTopLevelDouble(content, state) {
|
|
638
|
+
if (!state.inDouble) return false;
|
|
639
|
+
const char = content[state.cursor] ?? "";
|
|
640
|
+
const next = content[state.cursor + 1] ?? "";
|
|
641
|
+
if (char === '"' && next === '"') {
|
|
642
|
+
state.current += '""';
|
|
643
|
+
state.cursor += 2;
|
|
644
|
+
return true;
|
|
645
|
+
}
|
|
646
|
+
if (char === '"') {
|
|
647
|
+
state.inDouble = false;
|
|
648
|
+
}
|
|
649
|
+
state.current += char;
|
|
650
|
+
state.cursor += 1;
|
|
651
|
+
return true;
|
|
652
|
+
}
|
|
653
|
+
function consumeTopLevelDollar(content, state) {
|
|
654
|
+
if (!state.inDollar) return false;
|
|
655
|
+
const close = `$${state.dollarTag}$`;
|
|
656
|
+
if (content.startsWith(close, state.cursor)) {
|
|
657
|
+
state.inDollar = false;
|
|
658
|
+
state.dollarTag = "";
|
|
659
|
+
state.current += close;
|
|
660
|
+
state.cursor += close.length;
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
state.current += content[state.cursor] ?? "";
|
|
664
|
+
state.cursor += 1;
|
|
665
|
+
return true;
|
|
666
|
+
}
|
|
667
|
+
function tryStartTopLevelDollar(content, state) {
|
|
668
|
+
const foundDollarTag = readDollarTagAt2(content, state.cursor);
|
|
669
|
+
if (foundDollarTag === void 0) return false;
|
|
670
|
+
state.inDollar = true;
|
|
671
|
+
state.dollarTag = foundDollarTag.slice(1, -1);
|
|
672
|
+
state.current += foundDollarTag;
|
|
673
|
+
state.cursor += foundDollarTag.length;
|
|
674
|
+
return true;
|
|
675
|
+
}
|
|
676
|
+
function tryStartTopLevelSingle(content, state) {
|
|
677
|
+
if (content[state.cursor] !== "'") return false;
|
|
678
|
+
state.inSingle = true;
|
|
679
|
+
state.current += "'";
|
|
680
|
+
state.cursor += 1;
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
683
|
+
function tryStartTopLevelDouble(content, state) {
|
|
684
|
+
if (content[state.cursor] !== '"') return false;
|
|
685
|
+
state.inDouble = true;
|
|
686
|
+
state.current += '"';
|
|
687
|
+
state.cursor += 1;
|
|
688
|
+
return true;
|
|
689
|
+
}
|
|
690
|
+
function tryConsumeTopLevelLineComment(content, state) {
|
|
691
|
+
if (!state.preserveComments) return false;
|
|
692
|
+
const char = content[state.cursor] ?? "";
|
|
693
|
+
const next = content[state.cursor + 1] ?? "";
|
|
694
|
+
if (char !== "-" || next !== "-") return false;
|
|
695
|
+
while (state.cursor < content.length && content[state.cursor] !== "\n") {
|
|
696
|
+
state.current += content[state.cursor] ?? "";
|
|
697
|
+
state.cursor += 1;
|
|
698
|
+
}
|
|
699
|
+
return true;
|
|
700
|
+
}
|
|
701
|
+
function tryConsumeTopLevelBlockComment(content, state) {
|
|
702
|
+
if (!state.preserveComments) return false;
|
|
703
|
+
const char = content[state.cursor] ?? "";
|
|
704
|
+
const next = content[state.cursor + 1] ?? "";
|
|
705
|
+
if (char !== "/" || next !== "*") return false;
|
|
706
|
+
while (state.cursor + 1 < content.length && !(content[state.cursor] === "*" && content[state.cursor + 1] === "/")) {
|
|
707
|
+
state.current += content[state.cursor] ?? "";
|
|
708
|
+
state.cursor += 1;
|
|
709
|
+
}
|
|
710
|
+
if (state.cursor + 1 < content.length) {
|
|
711
|
+
state.current += "*/";
|
|
712
|
+
state.cursor += 2;
|
|
713
|
+
}
|
|
714
|
+
return true;
|
|
715
|
+
}
|
|
716
|
+
function tryConsumeTopLevelSeparator(content, state) {
|
|
717
|
+
const char = content[state.cursor] ?? "";
|
|
718
|
+
const next = content[state.cursor + 1] ?? "";
|
|
719
|
+
if (char === "(") {
|
|
720
|
+
state.depth += 1;
|
|
721
|
+
} else if (char === ")" && state.depth > 0) {
|
|
722
|
+
state.depth -= 1;
|
|
723
|
+
}
|
|
724
|
+
if (state.depth !== 0) return false;
|
|
725
|
+
if (state.separator === "||") {
|
|
726
|
+
if (char !== "|" || next !== "|") return false;
|
|
727
|
+
emitTopLevelSegment(state);
|
|
728
|
+
state.cursor += 2;
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
if (char !== ",") return false;
|
|
732
|
+
emitTopLevelSegment(state);
|
|
733
|
+
state.cursor += 1;
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
var TOP_LEVEL_SPLIT_STEPS = [
|
|
737
|
+
consumeTopLevelSingle,
|
|
738
|
+
consumeTopLevelDouble,
|
|
739
|
+
consumeTopLevelDollar,
|
|
740
|
+
tryStartTopLevelDollar,
|
|
741
|
+
tryStartTopLevelSingle,
|
|
742
|
+
tryStartTopLevelDouble,
|
|
743
|
+
tryConsumeTopLevelLineComment,
|
|
744
|
+
tryConsumeTopLevelBlockComment,
|
|
745
|
+
tryConsumeTopLevelSeparator
|
|
746
|
+
];
|
|
747
|
+
function runTopLevelSplitStep(content, state) {
|
|
748
|
+
for (const step of TOP_LEVEL_SPLIT_STEPS) {
|
|
749
|
+
if (step(content, state)) return true;
|
|
750
|
+
}
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
753
|
+
function splitTopLevelSegments(content, separator, preserveComments) {
|
|
754
|
+
const state = {
|
|
755
|
+
segments: [],
|
|
756
|
+
current: "",
|
|
757
|
+
cursor: 0,
|
|
758
|
+
depth: 0,
|
|
759
|
+
inSingle: false,
|
|
760
|
+
inDouble: false,
|
|
761
|
+
inDollar: false,
|
|
762
|
+
dollarTag: "",
|
|
763
|
+
separator,
|
|
764
|
+
preserveComments
|
|
765
|
+
};
|
|
766
|
+
while (state.cursor < content.length) {
|
|
767
|
+
if (runTopLevelSplitStep(content, state)) continue;
|
|
768
|
+
state.current += content[state.cursor] ?? "";
|
|
769
|
+
state.cursor += 1;
|
|
770
|
+
}
|
|
771
|
+
emitTopLevelSegment(state);
|
|
772
|
+
return state.segments;
|
|
773
|
+
}
|
|
774
|
+
function extractTopLevelSqlTokens(content) {
|
|
775
|
+
return splitTopLevelSegments(content, "||", true);
|
|
776
|
+
}
|
|
777
|
+
function splitFunctionArguments(content) {
|
|
778
|
+
return splitTopLevelSegments(content, ",", false);
|
|
779
|
+
}
|
|
780
|
+
function parseEQuotedLiteral(trimmed) {
|
|
781
|
+
if (!trimmed.startsWith("E'")) return void 0;
|
|
782
|
+
let cursor = 2;
|
|
783
|
+
let result = "";
|
|
784
|
+
while (cursor < trimmed.length) {
|
|
785
|
+
const char = trimmed[cursor] ?? "";
|
|
786
|
+
if (char === "'") {
|
|
787
|
+
if (trimmed[cursor + 1] === "'") {
|
|
788
|
+
result += "'";
|
|
789
|
+
cursor += 2;
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
return result;
|
|
793
|
+
}
|
|
794
|
+
result += char;
|
|
795
|
+
if (char === "\\" && trimmed[cursor + 1] !== void 0) {
|
|
796
|
+
cursor += 1;
|
|
797
|
+
result += trimmed[cursor] ?? "";
|
|
798
|
+
}
|
|
799
|
+
cursor += 1;
|
|
800
|
+
}
|
|
801
|
+
return void 0;
|
|
802
|
+
}
|
|
803
|
+
function parseStandardQuotedLiteral(trimmed) {
|
|
804
|
+
if (!trimmed.startsWith("'") || !trimmed.endsWith("'")) return void 0;
|
|
805
|
+
let cursor = 1;
|
|
806
|
+
let result = "";
|
|
807
|
+
while (cursor < trimmed.length - 1) {
|
|
808
|
+
const char = trimmed[cursor] ?? "";
|
|
809
|
+
if (char === "'") {
|
|
810
|
+
if (trimmed[cursor + 1] === "'") {
|
|
811
|
+
result += "'";
|
|
812
|
+
cursor += 2;
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
return result;
|
|
816
|
+
}
|
|
817
|
+
result += char;
|
|
818
|
+
if (char === "\\" && cursor + 1 < trimmed.length - 1) {
|
|
819
|
+
cursor += 1;
|
|
820
|
+
const escapedChar = trimmed[cursor];
|
|
821
|
+
if (escapedChar === void 0) {
|
|
822
|
+
return result;
|
|
823
|
+
}
|
|
824
|
+
result += escapedChar;
|
|
825
|
+
}
|
|
826
|
+
cursor += 1;
|
|
827
|
+
}
|
|
828
|
+
return result;
|
|
829
|
+
}
|
|
830
|
+
function parseQuotedLiteral(content) {
|
|
831
|
+
if (!content) return void 0;
|
|
832
|
+
const trimmed = content.trim();
|
|
833
|
+
const eQuoted = parseEQuotedLiteral(trimmed);
|
|
834
|
+
if (eQuoted !== void 0) return eQuoted;
|
|
835
|
+
return parseStandardQuotedLiteral(trimmed);
|
|
836
|
+
}
|
|
837
|
+
function parseDollarLiteral(content) {
|
|
838
|
+
const tagMatch = readDollarTagAt2(content.trim(), 0);
|
|
839
|
+
if (!tagMatch || !content.trim().endsWith(tagMatch)) {
|
|
840
|
+
return void 0;
|
|
841
|
+
}
|
|
842
|
+
const trimmed = content.trim();
|
|
843
|
+
const bodyStart = tagMatch.length;
|
|
844
|
+
const tagText = tagMatch.slice(1, -1);
|
|
845
|
+
const closeTag = `$${tagText}$`;
|
|
846
|
+
if (!trimmed.endsWith(closeTag) || trimmed.length < bodyStart + closeTag.length) return void 0;
|
|
847
|
+
return trimmed.slice(bodyStart, trimmed.length - closeTag.length);
|
|
848
|
+
}
|
|
849
|
+
function parseStaticSqlLiteral(content) {
|
|
850
|
+
const trimmed = content.trim();
|
|
851
|
+
const single = parseQuotedLiteral(trimmed);
|
|
852
|
+
if (single !== void 0) return single;
|
|
853
|
+
return parseDollarLiteral(trimmed);
|
|
854
|
+
}
|
|
855
|
+
function consumeFunctionCallSingle(content, state) {
|
|
856
|
+
if (!state.inSingle) return false;
|
|
857
|
+
const char = content[state.cursor] ?? "";
|
|
858
|
+
const next = content[state.cursor + 1] ?? "";
|
|
859
|
+
if (char === "'" && next === "'") {
|
|
860
|
+
state.cursor += 2;
|
|
861
|
+
return true;
|
|
862
|
+
}
|
|
863
|
+
if (char === "'") {
|
|
864
|
+
state.inSingle = false;
|
|
865
|
+
}
|
|
866
|
+
state.cursor += 1;
|
|
867
|
+
return true;
|
|
868
|
+
}
|
|
869
|
+
function consumeFunctionCallDouble(content, state) {
|
|
870
|
+
if (!state.inDouble) return false;
|
|
871
|
+
const char = content[state.cursor] ?? "";
|
|
872
|
+
const next = content[state.cursor + 1] ?? "";
|
|
873
|
+
if (char === '"' && next === '"') {
|
|
874
|
+
state.cursor += 2;
|
|
875
|
+
return true;
|
|
876
|
+
}
|
|
877
|
+
if (char === '"') {
|
|
878
|
+
state.inDouble = false;
|
|
879
|
+
}
|
|
880
|
+
state.cursor += 1;
|
|
881
|
+
return true;
|
|
882
|
+
}
|
|
883
|
+
function consumeFunctionCallDollar(content, state) {
|
|
884
|
+
if (!state.inDollar) return false;
|
|
885
|
+
const close = `$${state.dollarTag}$`;
|
|
886
|
+
if (content.startsWith(close, state.cursor)) {
|
|
887
|
+
state.inDollar = false;
|
|
888
|
+
state.dollarTag = "";
|
|
889
|
+
state.cursor += close.length;
|
|
890
|
+
return true;
|
|
891
|
+
}
|
|
892
|
+
state.cursor += 1;
|
|
893
|
+
return true;
|
|
894
|
+
}
|
|
895
|
+
function tryStartFunctionCallDollar(content, state) {
|
|
896
|
+
const foundDollarTag = readDollarTagAt2(content, state.cursor);
|
|
897
|
+
if (foundDollarTag === void 0) return false;
|
|
898
|
+
state.inDollar = true;
|
|
899
|
+
state.dollarTag = foundDollarTag.slice(1, -1);
|
|
900
|
+
state.cursor += foundDollarTag.length;
|
|
901
|
+
return true;
|
|
902
|
+
}
|
|
903
|
+
function tryStartFunctionCallSingle(content, state) {
|
|
904
|
+
if (content[state.cursor] !== "'") return false;
|
|
905
|
+
state.inSingle = true;
|
|
906
|
+
state.cursor += 1;
|
|
907
|
+
return true;
|
|
908
|
+
}
|
|
909
|
+
function tryStartFunctionCallDouble(content, state) {
|
|
910
|
+
if (content[state.cursor] !== '"') return false;
|
|
911
|
+
state.inDouble = true;
|
|
912
|
+
state.cursor += 1;
|
|
913
|
+
return true;
|
|
914
|
+
}
|
|
915
|
+
function consumeFunctionCallParens(content, state) {
|
|
916
|
+
const char = content[state.cursor] ?? "";
|
|
917
|
+
if (char === "(") {
|
|
918
|
+
state.depth += 1;
|
|
919
|
+
state.cursor += 1;
|
|
920
|
+
return true;
|
|
921
|
+
}
|
|
922
|
+
if (char !== ")") return false;
|
|
923
|
+
state.depth -= 1;
|
|
924
|
+
if (state.depth === 0) {
|
|
925
|
+
state.closeIndex = state.cursor;
|
|
926
|
+
state.cursor = content.length;
|
|
927
|
+
return true;
|
|
928
|
+
}
|
|
929
|
+
state.cursor += 1;
|
|
930
|
+
return true;
|
|
931
|
+
}
|
|
932
|
+
var FUNCTION_CALL_SCAN_STEPS = [
|
|
933
|
+
consumeFunctionCallSingle,
|
|
934
|
+
consumeFunctionCallDouble,
|
|
935
|
+
consumeFunctionCallDollar,
|
|
936
|
+
tryStartFunctionCallDollar,
|
|
937
|
+
tryStartFunctionCallSingle,
|
|
938
|
+
tryStartFunctionCallDouble,
|
|
939
|
+
consumeFunctionCallParens
|
|
940
|
+
];
|
|
941
|
+
function runFunctionCallScanStep(content, state) {
|
|
942
|
+
for (const step of FUNCTION_CALL_SCAN_STEPS) {
|
|
943
|
+
if (step(content, state)) return true;
|
|
944
|
+
}
|
|
945
|
+
return false;
|
|
946
|
+
}
|
|
947
|
+
function findTopLevelCallCloseIndex(content, openIndex) {
|
|
948
|
+
const state = {
|
|
949
|
+
cursor: openIndex,
|
|
950
|
+
depth: 0,
|
|
951
|
+
closeIndex: -1,
|
|
952
|
+
inSingle: false,
|
|
953
|
+
inDouble: false,
|
|
954
|
+
inDollar: false,
|
|
955
|
+
dollarTag: ""
|
|
956
|
+
};
|
|
957
|
+
while (state.cursor < content.length && state.closeIndex < 0) {
|
|
958
|
+
if (runFunctionCallScanStep(content, state)) continue;
|
|
959
|
+
state.cursor += 1;
|
|
960
|
+
}
|
|
961
|
+
return state.closeIndex;
|
|
962
|
+
}
|
|
963
|
+
function extractTopLevelFunctionCall(content) {
|
|
964
|
+
const normalized = stripOuterParentheses(content.trim());
|
|
965
|
+
if (!normalized) return void 0;
|
|
966
|
+
const match = normalized.match(/^([A-Za-z_][A-Za-z0-9_]*?(?:\.[A-Za-z_][A-Za-z0-9_]*)?)\s*\(/i);
|
|
967
|
+
if (!match || match.index !== 0) return void 0;
|
|
968
|
+
const functionName = match[1];
|
|
969
|
+
if (match[0] === void 0) return void 0;
|
|
970
|
+
const openIndex = match[0].length - 1;
|
|
971
|
+
const closeIndex = findTopLevelCallCloseIndex(normalized, openIndex);
|
|
972
|
+
if (closeIndex !== normalized.length - 1 || closeIndex < 0) return void 0;
|
|
973
|
+
const args = splitFunctionArguments(normalized.slice(openIndex + 1, closeIndex));
|
|
974
|
+
return { functionName: functionName.toUpperCase(), args };
|
|
975
|
+
}
|
|
976
|
+
function resolveIdentifierValue(name, stringEnv) {
|
|
977
|
+
const trimmed = name.trim();
|
|
978
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(trimmed)) return void 0;
|
|
979
|
+
return stringEnv.get(trimmed.toLowerCase());
|
|
980
|
+
}
|
|
981
|
+
function quoteAsLiteral(value) {
|
|
982
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
983
|
+
}
|
|
984
|
+
function staticSql(sql) {
|
|
985
|
+
return { kind: "static", sql };
|
|
986
|
+
}
|
|
987
|
+
function unresolved(reason) {
|
|
988
|
+
return { kind: "unresolved", reason };
|
|
989
|
+
}
|
|
990
|
+
function isDigitChar(char) {
|
|
991
|
+
return char !== void 0 && char >= "0" && char <= "9";
|
|
992
|
+
}
|
|
993
|
+
function consumeDigits(content, start) {
|
|
994
|
+
let cursor = start;
|
|
995
|
+
while (isDigitChar(content[cursor])) {
|
|
996
|
+
cursor += 1;
|
|
997
|
+
}
|
|
998
|
+
return cursor;
|
|
999
|
+
}
|
|
1000
|
+
function consumeFormatSpaces(template, start) {
|
|
1001
|
+
let cursor = start;
|
|
1002
|
+
while (template[cursor] === " ") {
|
|
1003
|
+
cursor += 1;
|
|
1004
|
+
}
|
|
1005
|
+
return cursor;
|
|
1006
|
+
}
|
|
1007
|
+
function consumeFormatFlagsAndWidth(template, start) {
|
|
1008
|
+
let cursor = start;
|
|
1009
|
+
while (cursor < template.length) {
|
|
1010
|
+
const modifier = template[cursor];
|
|
1011
|
+
if (isDigitChar(modifier)) {
|
|
1012
|
+
cursor += 1;
|
|
1013
|
+
continue;
|
|
1014
|
+
}
|
|
1015
|
+
if (modifier === "-" || modifier === "+" || modifier === " " || modifier === "#" || modifier === "0") {
|
|
1016
|
+
cursor += 1;
|
|
1017
|
+
continue;
|
|
1018
|
+
}
|
|
1019
|
+
break;
|
|
1020
|
+
}
|
|
1021
|
+
return cursor;
|
|
1022
|
+
}
|
|
1023
|
+
function consumeFormatPrecision(template, start) {
|
|
1024
|
+
if (template[start] !== ".") return start;
|
|
1025
|
+
return consumeDigits(template, start + 1);
|
|
1026
|
+
}
|
|
1027
|
+
function parseFormatPlaceholder(template, cursor) {
|
|
1028
|
+
if (template[cursor] !== "%") return void 0;
|
|
1029
|
+
const next = template[cursor + 1];
|
|
1030
|
+
if (next === void 0) return void 0;
|
|
1031
|
+
if (next === "%") {
|
|
1032
|
+
return { kind: "escaped", nextCursor: cursor + 2 };
|
|
1033
|
+
}
|
|
1034
|
+
let specCursor = cursor + 1;
|
|
1035
|
+
let positionalArgIndex;
|
|
1036
|
+
if (isDigitChar(template[specCursor])) {
|
|
1037
|
+
const argStart = specCursor;
|
|
1038
|
+
specCursor = consumeDigits(template, specCursor);
|
|
1039
|
+
const rawIndex = template.slice(argStart, specCursor);
|
|
1040
|
+
if (!rawIndex || template[specCursor] !== "$") return void 0;
|
|
1041
|
+
positionalArgIndex = Number(rawIndex) - 1;
|
|
1042
|
+
if (!Number.isInteger(positionalArgIndex) || positionalArgIndex < 0) {
|
|
1043
|
+
return void 0;
|
|
1044
|
+
}
|
|
1045
|
+
specCursor += 1;
|
|
1046
|
+
}
|
|
1047
|
+
specCursor = consumeFormatSpaces(template, specCursor);
|
|
1048
|
+
specCursor = consumeFormatFlagsAndWidth(template, specCursor);
|
|
1049
|
+
specCursor = consumeFormatPrecision(template, specCursor);
|
|
1050
|
+
const specifier = template[specCursor];
|
|
1051
|
+
if (specifier === void 0) return void 0;
|
|
1052
|
+
return {
|
|
1053
|
+
kind: "specifier",
|
|
1054
|
+
specifier,
|
|
1055
|
+
positionalArgIndex,
|
|
1056
|
+
nextCursor: specCursor + 1
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
function updateFormatMode(state, positionalArgIndex) {
|
|
1060
|
+
if (positionalArgIndex === void 0) {
|
|
1061
|
+
if (state.mode === "positional") return false;
|
|
1062
|
+
if (state.mode === "unset") {
|
|
1063
|
+
state.mode = "sequential";
|
|
1064
|
+
}
|
|
1065
|
+
return true;
|
|
1066
|
+
}
|
|
1067
|
+
if (state.mode === "sequential") return false;
|
|
1068
|
+
state.mode = "positional";
|
|
1069
|
+
state.maxArgIndex = Math.max(state.maxArgIndex, positionalArgIndex);
|
|
1070
|
+
return true;
|
|
1071
|
+
}
|
|
1072
|
+
function readFormatArgument(args, state, positionalArgIndex) {
|
|
1073
|
+
if (positionalArgIndex !== void 0) {
|
|
1074
|
+
return args[positionalArgIndex];
|
|
1075
|
+
}
|
|
1076
|
+
const argument = args[state.argIndex];
|
|
1077
|
+
if (argument === void 0) return void 0;
|
|
1078
|
+
state.argIndex += 1;
|
|
1079
|
+
return argument;
|
|
1080
|
+
}
|
|
1081
|
+
function renderFormatSpecifier(specifier, argumentExpression, stringEnv) {
|
|
1082
|
+
const spec = specifier.toLowerCase();
|
|
1083
|
+
if (spec !== "i" && spec !== "l" && spec !== "s") return void 0;
|
|
1084
|
+
const resolved = resolveStringExpression(argumentExpression, stringEnv);
|
|
1085
|
+
if (resolved.kind !== "static") return void 0;
|
|
1086
|
+
if (spec === "l") return quoteAsLiteral(resolved.sql);
|
|
1087
|
+
return resolved.sql;
|
|
1088
|
+
}
|
|
1089
|
+
function consumeFormatLiteralChar(template, cursor, state) {
|
|
1090
|
+
const char = template[cursor] ?? "";
|
|
1091
|
+
if (char === "%") return void 0;
|
|
1092
|
+
state.output += char;
|
|
1093
|
+
return cursor + 1;
|
|
1094
|
+
}
|
|
1095
|
+
function applyFormatPlaceholder(template, cursor, args, state, stringEnv) {
|
|
1096
|
+
const placeholder = parseFormatPlaceholder(template, cursor);
|
|
1097
|
+
if (placeholder === void 0) return void 0;
|
|
1098
|
+
if (placeholder.kind === "escaped") {
|
|
1099
|
+
state.output += "%";
|
|
1100
|
+
return placeholder.nextCursor;
|
|
1101
|
+
}
|
|
1102
|
+
if (!updateFormatMode(state, placeholder.positionalArgIndex)) return void 0;
|
|
1103
|
+
const argument = readFormatArgument(args, state, placeholder.positionalArgIndex);
|
|
1104
|
+
if (argument === void 0) return void 0;
|
|
1105
|
+
const rendered = renderFormatSpecifier(placeholder.specifier, argument, stringEnv);
|
|
1106
|
+
if (rendered === void 0) return void 0;
|
|
1107
|
+
state.output += rendered;
|
|
1108
|
+
return placeholder.nextCursor;
|
|
1109
|
+
}
|
|
1110
|
+
function hasValidFormatPositionalRange(state, argsLength) {
|
|
1111
|
+
return state.mode !== "positional" || state.maxArgIndex < argsLength;
|
|
1112
|
+
}
|
|
1113
|
+
function resolveFormatTemplate(template, args, stringEnv) {
|
|
1114
|
+
const state = {
|
|
1115
|
+
output: "",
|
|
1116
|
+
argIndex: 0,
|
|
1117
|
+
mode: "unset",
|
|
1118
|
+
maxArgIndex: -1
|
|
1119
|
+
};
|
|
1120
|
+
let cursor = 0;
|
|
1121
|
+
while (cursor < template.length) {
|
|
1122
|
+
const literalCursor = consumeFormatLiteralChar(template, cursor, state);
|
|
1123
|
+
if (literalCursor !== void 0) {
|
|
1124
|
+
cursor = literalCursor;
|
|
1125
|
+
continue;
|
|
1126
|
+
}
|
|
1127
|
+
const placeholderCursor = applyFormatPlaceholder(template, cursor, args, state, stringEnv);
|
|
1128
|
+
if (placeholderCursor === void 0) return void 0;
|
|
1129
|
+
cursor = placeholderCursor;
|
|
1130
|
+
}
|
|
1131
|
+
if (!hasValidFormatPositionalRange(state, args.length)) return void 0;
|
|
1132
|
+
return state.output;
|
|
1133
|
+
}
|
|
1134
|
+
function resolveFormatFunctionCall(call, stringEnv) {
|
|
1135
|
+
if (call.args.length === 0) {
|
|
1136
|
+
return unresolved("FORMAT requires arguments");
|
|
1137
|
+
}
|
|
1138
|
+
const formatTemplate = resolveStringExpression(call.args[0] ?? "", stringEnv);
|
|
1139
|
+
if (formatTemplate.kind !== "static" || formatTemplate.sql.length === 0) {
|
|
1140
|
+
return unresolved("FORMAT format string is dynamic");
|
|
1141
|
+
}
|
|
1142
|
+
const rendered = resolveFormatTemplate(formatTemplate.sql, call.args.slice(1), stringEnv);
|
|
1143
|
+
if (rendered === void 0) {
|
|
1144
|
+
return unresolved("FORMAT placeholders or arguments are not statically resolvable");
|
|
1145
|
+
}
|
|
1146
|
+
return staticSql(rendered);
|
|
1147
|
+
}
|
|
1148
|
+
function resolveConcatFunctionCall(call, stringEnv) {
|
|
1149
|
+
if (call.functionName === "CONCAT_WS" && call.args.length < 2) {
|
|
1150
|
+
return unresolved("CONCAT_WS requires separator and value arguments");
|
|
1151
|
+
}
|
|
1152
|
+
let separator = "";
|
|
1153
|
+
let valueStartIndex = 0;
|
|
1154
|
+
if (call.functionName === "CONCAT_WS") {
|
|
1155
|
+
const sep = resolveStringExpression(call.args[0] ?? "", stringEnv);
|
|
1156
|
+
if (sep.kind !== "static") {
|
|
1157
|
+
return unresolved("CONCAT_WS separator is not statically resolvable");
|
|
1158
|
+
}
|
|
1159
|
+
separator = sep.sql;
|
|
1160
|
+
valueStartIndex = 1;
|
|
1161
|
+
}
|
|
1162
|
+
const pieces = [];
|
|
1163
|
+
for (let i = valueStartIndex; i < call.args.length; i += 1) {
|
|
1164
|
+
const value = resolveStringExpression(call.args[i] ?? "", stringEnv);
|
|
1165
|
+
if (value.kind !== "static") {
|
|
1166
|
+
return unresolved("CONCAT function argument is not statically resolvable");
|
|
1167
|
+
}
|
|
1168
|
+
pieces.push(value.sql);
|
|
1169
|
+
}
|
|
1170
|
+
return staticSql(call.functionName === "CONCAT_WS" ? pieces.join(separator) : pieces.join(""));
|
|
1171
|
+
}
|
|
1172
|
+
function resolveQuoteIdentFunctionCall(call, stringEnv) {
|
|
1173
|
+
if (call.args.length !== 1) {
|
|
1174
|
+
return unresolved("QUOTE_IDENT should have one argument");
|
|
1175
|
+
}
|
|
1176
|
+
const ident = resolveStringExpression(call.args[0] ?? "", stringEnv);
|
|
1177
|
+
if (ident.kind !== "static") {
|
|
1178
|
+
return unresolved("QUOTE_IDENT argument is not statically resolvable");
|
|
1179
|
+
}
|
|
1180
|
+
return staticSql(ident.sql);
|
|
1181
|
+
}
|
|
1182
|
+
function resolveCoalesceFunctionCall(call, stringEnv) {
|
|
1183
|
+
if (call.args.length === 0) {
|
|
1184
|
+
return unresolved("COALESCE requires at least one argument");
|
|
1185
|
+
}
|
|
1186
|
+
for (const arg of call.args) {
|
|
1187
|
+
const resolvedArg = resolveStringExpression(arg, stringEnv);
|
|
1188
|
+
if (resolvedArg.kind === "static") {
|
|
1189
|
+
if (resolvedArg.sql.length > 0) return staticSql(resolvedArg.sql);
|
|
1190
|
+
continue;
|
|
1191
|
+
}
|
|
1192
|
+
return unresolved("COALESCE argument is not statically resolvable");
|
|
1193
|
+
}
|
|
1194
|
+
return unresolved("COALESCE arguments are empty/unresolvable");
|
|
1195
|
+
}
|
|
1196
|
+
function resolveKnownFunctionCall(call, stringEnv) {
|
|
1197
|
+
if (call.functionName === "FORMAT") {
|
|
1198
|
+
return resolveFormatFunctionCall(call, stringEnv);
|
|
1199
|
+
}
|
|
1200
|
+
if (call.functionName === "CONCAT" || call.functionName === "CONCAT_WS") {
|
|
1201
|
+
return resolveConcatFunctionCall(call, stringEnv);
|
|
1202
|
+
}
|
|
1203
|
+
if (call.functionName === "QUOTE_IDENT") {
|
|
1204
|
+
return resolveQuoteIdentFunctionCall(call, stringEnv);
|
|
1205
|
+
}
|
|
1206
|
+
if (call.functionName === "COALESCE") {
|
|
1207
|
+
return resolveCoalesceFunctionCall(call, stringEnv);
|
|
1208
|
+
}
|
|
1209
|
+
return void 0;
|
|
1210
|
+
}
|
|
1211
|
+
function resolveTopLevelConcatExpression(normalized, stringEnv) {
|
|
1212
|
+
const topLevelConcat = extractTopLevelSqlTokens(normalized);
|
|
1213
|
+
if (topLevelConcat.length <= 1) return void 0;
|
|
1214
|
+
const pieces = [];
|
|
1215
|
+
for (const piece of topLevelConcat) {
|
|
1216
|
+
const maybe = resolveStringExpression(piece, stringEnv);
|
|
1217
|
+
if (maybe.kind !== "static") {
|
|
1218
|
+
return unresolved("Concat with dynamic fragments");
|
|
1219
|
+
}
|
|
1220
|
+
pieces.push(maybe.sql);
|
|
1221
|
+
}
|
|
1222
|
+
return staticSql(pieces.join(""));
|
|
1223
|
+
}
|
|
1224
|
+
function resolveStringExpression(expression, stringEnv) {
|
|
1225
|
+
const normalized = stripOuterParentheses(expression.trim());
|
|
1226
|
+
if (!normalized) return unresolved("Empty expression");
|
|
1227
|
+
const staticLiteral = parseStaticSqlLiteral(normalized);
|
|
1228
|
+
if (staticLiteral !== void 0) return staticSql(staticLiteral);
|
|
1229
|
+
const envValue = resolveIdentifierValue(normalized, stringEnv);
|
|
1230
|
+
if (envValue !== void 0) return staticSql(envValue);
|
|
1231
|
+
const call = extractTopLevelFunctionCall(normalized);
|
|
1232
|
+
if (call !== void 0) {
|
|
1233
|
+
const knownCallResult = resolveKnownFunctionCall(call, stringEnv);
|
|
1234
|
+
if (knownCallResult !== void 0) return knownCallResult;
|
|
1235
|
+
}
|
|
1236
|
+
const concatResult = resolveTopLevelConcatExpression(normalized, stringEnv);
|
|
1237
|
+
if (concatResult !== void 0) return concatResult;
|
|
1238
|
+
if (isPureLiteralValue(normalized)) {
|
|
1239
|
+
return unresolved("Expression is pure SQL literal but not usable as string SQL text");
|
|
1240
|
+
}
|
|
1241
|
+
return unresolved("Expression cannot be reconstructed statically");
|
|
1242
|
+
}
|
|
1243
|
+
var ASSIGNMENT_CONTROL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
1244
|
+
"IF",
|
|
1245
|
+
"ELSIF",
|
|
1246
|
+
"ELSE",
|
|
1247
|
+
"THEN",
|
|
1248
|
+
"LOOP",
|
|
1249
|
+
"FOR",
|
|
1250
|
+
"WHILE",
|
|
1251
|
+
"CASE",
|
|
1252
|
+
"WHEN",
|
|
1253
|
+
"BEGIN",
|
|
1254
|
+
"END",
|
|
1255
|
+
"RETURN",
|
|
1256
|
+
"PERFORM"
|
|
1257
|
+
]);
|
|
1258
|
+
function consumeAssignmentSingle(content, state) {
|
|
1259
|
+
if (!state.inSingle) return false;
|
|
1260
|
+
const char = content[state.cursor] ?? "";
|
|
1261
|
+
const next = content[state.cursor + 1] ?? "";
|
|
1262
|
+
if (char === "'" && next === "'") {
|
|
1263
|
+
state.cursor += 2;
|
|
1264
|
+
return true;
|
|
1265
|
+
}
|
|
1266
|
+
if (char === "'") {
|
|
1267
|
+
state.inSingle = false;
|
|
1268
|
+
}
|
|
1269
|
+
state.cursor += 1;
|
|
1270
|
+
return true;
|
|
1271
|
+
}
|
|
1272
|
+
function consumeAssignmentDouble(content, state) {
|
|
1273
|
+
if (!state.inDouble) return false;
|
|
1274
|
+
const char = content[state.cursor] ?? "";
|
|
1275
|
+
const next = content[state.cursor + 1] ?? "";
|
|
1276
|
+
if (char === '"' && next === '"') {
|
|
1277
|
+
state.cursor += 2;
|
|
1278
|
+
return true;
|
|
1279
|
+
}
|
|
1280
|
+
if (char === '"') {
|
|
1281
|
+
state.inDouble = false;
|
|
1282
|
+
}
|
|
1283
|
+
state.cursor += 1;
|
|
1284
|
+
return true;
|
|
1285
|
+
}
|
|
1286
|
+
function consumeAssignmentDollar(content, state) {
|
|
1287
|
+
if (!state.inDollar) return false;
|
|
1288
|
+
const close = `$${state.dollarTag}$`;
|
|
1289
|
+
if (content.startsWith(close, state.cursor)) {
|
|
1290
|
+
state.inDollar = false;
|
|
1291
|
+
state.dollarTag = "";
|
|
1292
|
+
state.cursor += close.length;
|
|
1293
|
+
return true;
|
|
1294
|
+
}
|
|
1295
|
+
state.cursor += 1;
|
|
1296
|
+
return true;
|
|
1297
|
+
}
|
|
1298
|
+
function tryStartAssignmentDollar(content, state) {
|
|
1299
|
+
const foundDollarTag = readDollarTagAt2(content, state.cursor);
|
|
1300
|
+
if (foundDollarTag === void 0) return false;
|
|
1301
|
+
state.inDollar = true;
|
|
1302
|
+
state.dollarTag = foundDollarTag.slice(1, -1);
|
|
1303
|
+
state.cursor += foundDollarTag.length;
|
|
1304
|
+
return true;
|
|
1305
|
+
}
|
|
1306
|
+
function trySkipAssignmentLineComment(content, state) {
|
|
1307
|
+
const char = content[state.cursor] ?? "";
|
|
1308
|
+
const next = content[state.cursor + 1] ?? "";
|
|
1309
|
+
if (char !== "-" || next !== "-") return false;
|
|
1310
|
+
state.cursor += 2;
|
|
1311
|
+
while (state.cursor < content.length && content[state.cursor] !== "\n") {
|
|
1312
|
+
state.cursor += 1;
|
|
1313
|
+
}
|
|
1314
|
+
if (content[state.cursor] === "\n") {
|
|
1315
|
+
state.cursor += 1;
|
|
1316
|
+
}
|
|
1317
|
+
return true;
|
|
1318
|
+
}
|
|
1319
|
+
function trySkipAssignmentBlockComment(content, state) {
|
|
1320
|
+
const char = content[state.cursor] ?? "";
|
|
1321
|
+
const next = content[state.cursor + 1] ?? "";
|
|
1322
|
+
if (char !== "/" || next !== "*") return false;
|
|
1323
|
+
state.cursor += 2;
|
|
1324
|
+
while (state.cursor + 1 < content.length) {
|
|
1325
|
+
if (content[state.cursor] === "*" && content[state.cursor + 1] === "/") {
|
|
1326
|
+
state.cursor += 2;
|
|
1327
|
+
return true;
|
|
1328
|
+
}
|
|
1329
|
+
state.cursor += 1;
|
|
1330
|
+
}
|
|
1331
|
+
return true;
|
|
1332
|
+
}
|
|
1333
|
+
function tryStartAssignmentSingle(content, state) {
|
|
1334
|
+
if (content[state.cursor] !== "'") return false;
|
|
1335
|
+
state.inSingle = true;
|
|
1336
|
+
state.cursor += 1;
|
|
1337
|
+
return true;
|
|
1338
|
+
}
|
|
1339
|
+
function tryStartAssignmentDouble(content, state) {
|
|
1340
|
+
if (content[state.cursor] !== '"') return false;
|
|
1341
|
+
state.inDouble = true;
|
|
1342
|
+
state.cursor += 1;
|
|
1343
|
+
return true;
|
|
1344
|
+
}
|
|
1345
|
+
function scanAssignmentOperator(content, state) {
|
|
1346
|
+
const char = content[state.cursor] ?? "";
|
|
1347
|
+
const next = content[state.cursor + 1] ?? "";
|
|
1348
|
+
if (char === "(") {
|
|
1349
|
+
state.depth += 1;
|
|
1350
|
+
state.cursor += 1;
|
|
1351
|
+
return true;
|
|
1352
|
+
}
|
|
1353
|
+
if (char === ")" && state.depth > 0) {
|
|
1354
|
+
state.depth -= 1;
|
|
1355
|
+
state.cursor += 1;
|
|
1356
|
+
return true;
|
|
1357
|
+
}
|
|
1358
|
+
if (char === ":" && next === "=" && state.depth === 0) {
|
|
1359
|
+
state.assignmentIndex = state.cursor;
|
|
1360
|
+
state.cursor = content.length;
|
|
1361
|
+
return true;
|
|
1362
|
+
}
|
|
1363
|
+
state.cursor += 1;
|
|
1364
|
+
return true;
|
|
1365
|
+
}
|
|
1366
|
+
var ASSIGNMENT_SCAN_STEPS = [
|
|
1367
|
+
consumeAssignmentSingle,
|
|
1368
|
+
consumeAssignmentDouble,
|
|
1369
|
+
consumeAssignmentDollar,
|
|
1370
|
+
tryStartAssignmentDollar,
|
|
1371
|
+
trySkipAssignmentLineComment,
|
|
1372
|
+
trySkipAssignmentBlockComment,
|
|
1373
|
+
tryStartAssignmentSingle,
|
|
1374
|
+
tryStartAssignmentDouble,
|
|
1375
|
+
scanAssignmentOperator
|
|
1376
|
+
];
|
|
1377
|
+
function runAssignmentScanStep(content, state) {
|
|
1378
|
+
for (const step of ASSIGNMENT_SCAN_STEPS) {
|
|
1379
|
+
if (step(content, state)) return;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
function findTopLevelAssignmentIndex(content) {
|
|
1383
|
+
const state = {
|
|
1384
|
+
cursor: 0,
|
|
1385
|
+
depth: 0,
|
|
1386
|
+
inSingle: false,
|
|
1387
|
+
inDouble: false,
|
|
1388
|
+
inDollar: false,
|
|
1389
|
+
dollarTag: "",
|
|
1390
|
+
assignmentIndex: -1
|
|
1391
|
+
};
|
|
1392
|
+
while (state.cursor < content.length - 1 && state.assignmentIndex < 0) {
|
|
1393
|
+
runAssignmentScanStep(content, state);
|
|
1394
|
+
}
|
|
1395
|
+
return state.assignmentIndex;
|
|
1396
|
+
}
|
|
1397
|
+
function isConditionalAssignmentContext(left) {
|
|
1398
|
+
return /\b(?:IF|ELSIF|ELSE|CASE|WHEN)\b/i.test(left);
|
|
1399
|
+
}
|
|
1400
|
+
function extractDeclarationAssignmentTarget(left) {
|
|
1401
|
+
const declarationMatch = left.match(
|
|
1402
|
+
/^(?:\s*DECLARE\s+)?([A-Za-z_][A-Za-z0-9_]*)\b(\s+[\s\S]+)$/i
|
|
1403
|
+
);
|
|
1404
|
+
const declarationTarget = declarationMatch?.[1];
|
|
1405
|
+
if (!declarationTarget) return void 0;
|
|
1406
|
+
const normalizedTarget = declarationTarget.toUpperCase();
|
|
1407
|
+
if (ASSIGNMENT_CONTROL_KEYWORDS.has(normalizedTarget)) return void 0;
|
|
1408
|
+
return normalizedTarget.toLowerCase();
|
|
1409
|
+
}
|
|
1410
|
+
function extractTrailingAssignmentTarget(left) {
|
|
1411
|
+
const leftMatch = left.match(/([A-Za-z_][A-Za-z0-9_]*)\s*$/);
|
|
1412
|
+
return leftMatch?.[1]?.toLowerCase();
|
|
1413
|
+
}
|
|
1414
|
+
function parseTopLevelAssignment(statement) {
|
|
1415
|
+
const normalized = statement.trim();
|
|
1416
|
+
if (!normalized) return void 0;
|
|
1417
|
+
if (!normalized.includes(":=")) return void 0;
|
|
1418
|
+
const assignmentIndex = findTopLevelAssignmentIndex(normalized);
|
|
1419
|
+
if (assignmentIndex < 0) return void 0;
|
|
1420
|
+
const left = normalized.slice(0, assignmentIndex).trim();
|
|
1421
|
+
const right = normalized.slice(assignmentIndex + 2).trim();
|
|
1422
|
+
if (!left || !right) return void 0;
|
|
1423
|
+
const isConditionalContext = isConditionalAssignmentContext(left);
|
|
1424
|
+
const declarationTarget = extractDeclarationAssignmentTarget(left);
|
|
1425
|
+
if (declarationTarget !== void 0) {
|
|
1426
|
+
return { target: declarationTarget, expression: right, isConditionalContext };
|
|
1427
|
+
}
|
|
1428
|
+
const fallbackTarget = extractTrailingAssignmentTarget(left);
|
|
1429
|
+
if (fallbackTarget === void 0) return void 0;
|
|
1430
|
+
return { target: fallbackTarget, expression: right, isConditionalContext };
|
|
1431
|
+
}
|
|
1432
|
+
function mergeStringEnvValue(env, target, nextValue) {
|
|
1433
|
+
const previous = env.get(target);
|
|
1434
|
+
if (previous === void 0 || previous === nextValue) {
|
|
1435
|
+
env.set(target, nextValue);
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
env.delete(target);
|
|
1439
|
+
}
|
|
1440
|
+
function extractStaticSqlFromExpression(expression, stringEnv = /* @__PURE__ */ new Map()) {
|
|
1441
|
+
const normalized = stripOuterParentheses(expression.trim());
|
|
1442
|
+
if (!normalized) {
|
|
1443
|
+
return { kind: "unresolved", reason: "Empty expression" };
|
|
1444
|
+
}
|
|
1445
|
+
return resolveStringExpression(normalized, stringEnv);
|
|
1446
|
+
}
|
|
1447
|
+
function hasSingleOuterParentheses(content) {
|
|
1448
|
+
if (!content.startsWith("(") || !content.endsWith(")")) return false;
|
|
1449
|
+
let depth = 0;
|
|
1450
|
+
for (let i = 0; i < content.length; i += 1) {
|
|
1451
|
+
const char = content[i] ?? "";
|
|
1452
|
+
if (char === "(") {
|
|
1453
|
+
depth += 1;
|
|
1454
|
+
continue;
|
|
1455
|
+
}
|
|
1456
|
+
if (char !== ")") {
|
|
1457
|
+
continue;
|
|
1458
|
+
}
|
|
1459
|
+
depth -= 1;
|
|
1460
|
+
if (depth < 0) return false;
|
|
1461
|
+
if (depth === 0 && i !== content.length - 1) {
|
|
1462
|
+
return false;
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
return depth === 0;
|
|
1466
|
+
}
|
|
1467
|
+
function stripOuterParentheses(content) {
|
|
1468
|
+
let trimmed = content.trim();
|
|
1469
|
+
while (hasSingleOuterParentheses(trimmed)) {
|
|
1470
|
+
trimmed = trimmed.slice(1, -1).trim();
|
|
1471
|
+
}
|
|
1472
|
+
return trimmed;
|
|
1473
|
+
}
|
|
1474
|
+
function isPureLiteralValue(value) {
|
|
1475
|
+
const trimmed = value.trim();
|
|
1476
|
+
if (!trimmed) return false;
|
|
1477
|
+
if (/^(?:null|true|false|\d+(?:\.\d+)?|-\d+(?:\.\d+)?)$/i.test(trimmed)) return true;
|
|
1478
|
+
return parseStaticSqlLiteral(trimmed) !== void 0 || parseDollarLiteral(trimmed) !== void 0;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// src/validators/risk-detector-plpgsql.ts
|
|
1482
|
+
var DO_STATEMENT_PATTERN = /\bDO\b/gi;
|
|
1483
|
+
var CREATE_OR_ALTER_FUNCTION_PATTERN = /\b(?:CREATE|ALTER)\s+(?:OR\s+REPLACE\s+)?(?:FUNCTION|PROCEDURE)\b/gi;
|
|
1484
|
+
function readDollarTag(content, index) {
|
|
1485
|
+
const match = content.slice(index).match(/^\$([A-Za-z_][A-Za-z0-9_]*)?\$/);
|
|
1486
|
+
return match ? match[0] : void 0;
|
|
1487
|
+
}
|
|
1488
|
+
function isWordChar2(char) {
|
|
1489
|
+
return /[A-Za-z0-9_]/.test(char);
|
|
1490
|
+
}
|
|
1491
|
+
function isKeywordAt(content, contentIndex, keyword) {
|
|
1492
|
+
const endIndex = contentIndex + keyword.length;
|
|
1493
|
+
if (endIndex > content.length) return false;
|
|
1494
|
+
if (content.slice(contentIndex, endIndex).toUpperCase() !== keyword) return false;
|
|
1495
|
+
const before = contentIndex > 0 ? content[contentIndex - 1] : " ";
|
|
1496
|
+
const after = endIndex < content.length ? content[endIndex] : " ";
|
|
1497
|
+
return !isWordChar2(before) && !isWordChar2(after);
|
|
1498
|
+
}
|
|
1499
|
+
function consumeSingleQuote(content, state) {
|
|
1500
|
+
if (!state.inSingleQuote) return false;
|
|
1501
|
+
if (content[state.cursor] === "'" && content[state.cursor + 1] === "'") {
|
|
1502
|
+
state.cursor += 2;
|
|
1503
|
+
return true;
|
|
1504
|
+
}
|
|
1505
|
+
if (content[state.cursor] === "'") {
|
|
1506
|
+
state.inSingleQuote = false;
|
|
1507
|
+
}
|
|
1508
|
+
state.cursor += 1;
|
|
1509
|
+
return true;
|
|
1510
|
+
}
|
|
1511
|
+
function consumeDoubleQuote(content, state) {
|
|
1512
|
+
if (!state.inDoubleQuote) return false;
|
|
1513
|
+
if (content[state.cursor] === '"' && content[state.cursor + 1] === '"') {
|
|
1514
|
+
state.cursor += 2;
|
|
1515
|
+
return true;
|
|
1516
|
+
}
|
|
1517
|
+
if (content[state.cursor] === '"') {
|
|
1518
|
+
state.inDoubleQuote = false;
|
|
1519
|
+
}
|
|
1520
|
+
state.cursor += 1;
|
|
1521
|
+
return true;
|
|
1522
|
+
}
|
|
1523
|
+
function consumeDollarQuote(content, state) {
|
|
1524
|
+
if (state.inDollarQuote === void 0) return false;
|
|
1525
|
+
const close = `$${state.inDollarQuote}$`;
|
|
1526
|
+
if (content.startsWith(close, state.cursor)) {
|
|
1527
|
+
state.inDollarQuote = void 0;
|
|
1528
|
+
state.cursor += close.length;
|
|
1529
|
+
return true;
|
|
1530
|
+
}
|
|
1531
|
+
state.cursor += 1;
|
|
1532
|
+
return true;
|
|
1533
|
+
}
|
|
1534
|
+
function consumeQuotedSpan(content, state) {
|
|
1535
|
+
if (consumeSingleQuote(content, state)) return true;
|
|
1536
|
+
if (consumeDoubleQuote(content, state)) return true;
|
|
1537
|
+
return consumeDollarQuote(content, state);
|
|
1538
|
+
}
|
|
1539
|
+
function tryStartSingleQuote(content, state) {
|
|
1540
|
+
if (content[state.cursor] !== "'") return false;
|
|
1541
|
+
state.inSingleQuote = true;
|
|
1542
|
+
state.cursor += 1;
|
|
1543
|
+
return true;
|
|
1544
|
+
}
|
|
1545
|
+
function tryStartDoubleQuote(content, state) {
|
|
1546
|
+
if (content[state.cursor] !== '"') return false;
|
|
1547
|
+
state.inDoubleQuote = true;
|
|
1548
|
+
state.cursor += 1;
|
|
1549
|
+
return true;
|
|
1550
|
+
}
|
|
1551
|
+
function tryStartDollarQuote(content, state) {
|
|
1552
|
+
const dollarTag = readDollarTag(content, state.cursor);
|
|
1553
|
+
if (dollarTag === void 0) return false;
|
|
1554
|
+
state.inDollarQuote = dollarTag.slice(1, -1);
|
|
1555
|
+
state.cursor += dollarTag.length;
|
|
1556
|
+
return true;
|
|
1557
|
+
}
|
|
1558
|
+
function tryStartQuotedSpan(content, state) {
|
|
1559
|
+
if (tryStartSingleQuote(content, state)) return true;
|
|
1560
|
+
if (tryStartDoubleQuote(content, state)) return true;
|
|
1561
|
+
return tryStartDollarQuote(content, state);
|
|
1562
|
+
}
|
|
1563
|
+
function findKeywordFromOffset(content, start, keyword) {
|
|
1564
|
+
const state = {
|
|
1565
|
+
cursor: start,
|
|
1566
|
+
inSingleQuote: false,
|
|
1567
|
+
inDoubleQuote: false,
|
|
1568
|
+
inDollarQuote: void 0
|
|
1569
|
+
};
|
|
1570
|
+
while (state.cursor < content.length) {
|
|
1571
|
+
if (consumeQuotedSpan(content, state)) continue;
|
|
1572
|
+
if (tryStartQuotedSpan(content, state)) continue;
|
|
1573
|
+
if (isKeywordAt(content, state.cursor, keyword)) {
|
|
1574
|
+
return state.cursor;
|
|
1575
|
+
}
|
|
1576
|
+
state.cursor += 1;
|
|
1577
|
+
}
|
|
1578
|
+
return void 0;
|
|
1579
|
+
}
|
|
1580
|
+
var MAX_DOLLAR_QUOTE_DEPTH = 50;
|
|
1581
|
+
function skipUntilDollarClose(content, state, openTag, depth) {
|
|
1582
|
+
if (depth >= MAX_DOLLAR_QUOTE_DEPTH) return -1;
|
|
1583
|
+
const nestedTag = readDollarTag(content, state.cursor);
|
|
1584
|
+
if (nestedTag === void 0 || nestedTag === openTag) {
|
|
1585
|
+
return state.cursor;
|
|
1586
|
+
}
|
|
1587
|
+
const nestedStart = state.cursor + nestedTag.length;
|
|
1588
|
+
const nestedEnd = closeIndexForDollarTag(content, nestedStart, nestedTag, depth + 1);
|
|
1589
|
+
if (nestedEnd === -1) return -1;
|
|
1590
|
+
state.cursor = nestedEnd + nestedTag.length;
|
|
1591
|
+
return state.cursor;
|
|
1592
|
+
}
|
|
1593
|
+
function closeIndexForDollarTag(content, bodyStart, openTag, depth = 0) {
|
|
1594
|
+
if (depth >= MAX_DOLLAR_QUOTE_DEPTH) return -1;
|
|
1595
|
+
const state = {
|
|
1596
|
+
cursor: bodyStart,
|
|
1597
|
+
inSingleQuote: false,
|
|
1598
|
+
inDoubleQuote: false,
|
|
1599
|
+
inDollarQuote: void 0
|
|
1600
|
+
};
|
|
1601
|
+
while (state.cursor < content.length) {
|
|
1602
|
+
if (consumeQuotedSpan(content, state)) continue;
|
|
1603
|
+
if (content.startsWith(openTag, state.cursor)) {
|
|
1604
|
+
return state.cursor;
|
|
1605
|
+
}
|
|
1606
|
+
const nestedCursor = skipUntilDollarClose(content, state, openTag, depth);
|
|
1607
|
+
if (nestedCursor !== state.cursor) {
|
|
1608
|
+
continue;
|
|
1609
|
+
}
|
|
1610
|
+
if (tryStartSingleQuote(content, state)) continue;
|
|
1611
|
+
if (tryStartDoubleQuote(content, state)) continue;
|
|
1612
|
+
state.cursor += 1;
|
|
1613
|
+
}
|
|
1614
|
+
return -1;
|
|
1615
|
+
}
|
|
1616
|
+
function resolveBodyRange(content, openTagStart, statementType) {
|
|
1617
|
+
const openTag = readDollarTag(content, openTagStart);
|
|
1618
|
+
if (openTag === void 0) return void 0;
|
|
1619
|
+
const bodyStart = openTagStart + openTag.length;
|
|
1620
|
+
const bodyEnd = closeIndexForDollarTag(content, bodyStart, openTag);
|
|
1621
|
+
if (bodyEnd === -1) return void 0;
|
|
1622
|
+
return {
|
|
1623
|
+
range: { bodyStart, bodyEnd, statementType },
|
|
1624
|
+
nextScanIndex: bodyEnd + openTag.length
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
function skipDoLanguageClause(content, cursor) {
|
|
1628
|
+
if (!isKeywordAt(content, cursor, "LANGUAGE")) {
|
|
1629
|
+
return cursor;
|
|
1630
|
+
}
|
|
1631
|
+
const langNameStart = skipWhitespace2(content, cursor + "LANGUAGE".length);
|
|
1632
|
+
const afterLangName = skipIdentifier(content, langNameStart);
|
|
1633
|
+
return skipWhitespace2(content, afterLangName);
|
|
1634
|
+
}
|
|
1635
|
+
function collectDoBodyRanges(content) {
|
|
1636
|
+
const ranges = [];
|
|
1637
|
+
DO_STATEMENT_PATTERN.lastIndex = 0;
|
|
1638
|
+
let doMatch = DO_STATEMENT_PATTERN.exec(content);
|
|
1639
|
+
while (doMatch !== null) {
|
|
1640
|
+
const doStart = doMatch.index;
|
|
1641
|
+
if (findKeywordFromOffset(content, doStart, "DO") !== doStart) {
|
|
1642
|
+
DO_STATEMENT_PATTERN.lastIndex = doStart + doMatch[0].length;
|
|
1643
|
+
doMatch = DO_STATEMENT_PATTERN.exec(content);
|
|
1644
|
+
continue;
|
|
1645
|
+
}
|
|
1646
|
+
let cursor = skipWhitespace2(content, doStart + doMatch[0].length);
|
|
1647
|
+
cursor = skipDoLanguageClause(content, cursor);
|
|
1648
|
+
const resolved = resolveBodyRange(content, cursor, "do");
|
|
1649
|
+
if (resolved !== void 0) {
|
|
1650
|
+
ranges.push(resolved.range);
|
|
1651
|
+
DO_STATEMENT_PATTERN.lastIndex = resolved.nextScanIndex;
|
|
1652
|
+
}
|
|
1653
|
+
doMatch = DO_STATEMENT_PATTERN.exec(content);
|
|
1654
|
+
}
|
|
1655
|
+
DO_STATEMENT_PATTERN.lastIndex = 0;
|
|
1656
|
+
return ranges;
|
|
1657
|
+
}
|
|
1658
|
+
function collectFunctionBodyRanges(content) {
|
|
1659
|
+
const ranges = [];
|
|
1660
|
+
CREATE_OR_ALTER_FUNCTION_PATTERN.lastIndex = 0;
|
|
1661
|
+
let functionMatch = CREATE_OR_ALTER_FUNCTION_PATTERN.exec(content);
|
|
1662
|
+
while (functionMatch !== null) {
|
|
1663
|
+
const start = functionMatch.index;
|
|
1664
|
+
const asIndex = findKeywordFromOffset(content, start + functionMatch[0].length, "AS");
|
|
1665
|
+
if (asIndex === void 0) {
|
|
1666
|
+
CREATE_OR_ALTER_FUNCTION_PATTERN.lastIndex = start + functionMatch[0].length;
|
|
1667
|
+
functionMatch = CREATE_OR_ALTER_FUNCTION_PATTERN.exec(content);
|
|
1668
|
+
continue;
|
|
1669
|
+
}
|
|
1670
|
+
const bodyStartAt = skipWhitespace2(content, asIndex + 2);
|
|
1671
|
+
const resolved = resolveBodyRange(content, bodyStartAt, "function");
|
|
1672
|
+
if (resolved !== void 0) {
|
|
1673
|
+
ranges.push(resolved.range);
|
|
1674
|
+
CREATE_OR_ALTER_FUNCTION_PATTERN.lastIndex = resolved.nextScanIndex;
|
|
1675
|
+
}
|
|
1676
|
+
functionMatch = CREATE_OR_ALTER_FUNCTION_PATTERN.exec(content);
|
|
1677
|
+
}
|
|
1678
|
+
CREATE_OR_ALTER_FUNCTION_PATTERN.lastIndex = 0;
|
|
1679
|
+
return ranges;
|
|
1680
|
+
}
|
|
1681
|
+
function findFunctionAndDoBodies(content) {
|
|
1682
|
+
return [...collectDoBodyRanges(content), ...collectFunctionBodyRanges(content)];
|
|
1683
|
+
}
|
|
1684
|
+
function enrichStaticExecuteRiskFromSql(sql, sourceLine) {
|
|
1685
|
+
const staticSql2 = stripSqlForPatternMatching(sql);
|
|
1686
|
+
if (!staticSql2.trim()) return [];
|
|
1687
|
+
const staticSqlLineStarts = buildLineStarts(sql);
|
|
1688
|
+
return detectRisksFromContent(staticSql2, sql, staticSqlLineStarts).map((risk) => ({
|
|
1689
|
+
...risk,
|
|
1690
|
+
line: sourceLine,
|
|
1691
|
+
evidence: {
|
|
1692
|
+
source: "static execute body",
|
|
1693
|
+
snippet: sql.trim().slice(0, 200),
|
|
1694
|
+
detail: "Reconstructed PL/pgSQL EXECUTE expression"
|
|
1695
|
+
}
|
|
1696
|
+
}));
|
|
1697
|
+
}
|
|
1698
|
+
function buildDoBlockRisk(commentlessContent, range, declarationLine) {
|
|
1699
|
+
const bodyPreviewStart = Math.max(0, range.bodyStart - 80);
|
|
1700
|
+
const bodyPreviewEnd = range.bodyEnd + 80;
|
|
1701
|
+
return {
|
|
1702
|
+
level: "medium",
|
|
1703
|
+
description: "DO block detected in SQL",
|
|
1704
|
+
mitigation: "Prefer static declarative SQL or move bootstrap logic to idempotent script files with clear intent",
|
|
1705
|
+
line: declarationLine,
|
|
1706
|
+
reasonCode: "PLPGSQL_DO_BLOCK_DETECTED",
|
|
1707
|
+
confidence: "medium",
|
|
1708
|
+
evidence: {
|
|
1709
|
+
source: "findFunctionAndDoBodies",
|
|
1710
|
+
snippet: range.statementType === "do" ? commentlessContent.slice(bodyPreviewStart, bodyPreviewEnd) : void 0,
|
|
1711
|
+
detail: `${range.statementType.toUpperCase()} block detected in schema SQL`
|
|
1712
|
+
}
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
function maybeAddDoBlockRisk(risks, dedupe, commentlessContent, range, declarationLine) {
|
|
1716
|
+
if (range.statementType !== "do") return;
|
|
1717
|
+
const doKey = `do:${range.statementType}:${declarationLine}`;
|
|
1718
|
+
if (dedupe.has(doKey)) return;
|
|
1719
|
+
dedupe.add(doKey);
|
|
1720
|
+
risks.push(buildDoBlockRisk(commentlessContent, range, declarationLine));
|
|
1721
|
+
}
|
|
1722
|
+
function applyStatementAssignment(statement, stringEnv) {
|
|
1723
|
+
const assignment = parseTopLevelAssignment(statement.statement);
|
|
1724
|
+
if (!assignment) return;
|
|
1725
|
+
const resolved = extractStaticSqlFromExpression(assignment.expression, stringEnv);
|
|
1726
|
+
if (resolved.kind !== "static") {
|
|
1727
|
+
stringEnv.delete(assignment.target);
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1730
|
+
if (assignment.isConditionalContext) {
|
|
1731
|
+
mergeStringEnvValue(stringEnv, assignment.target, resolved.sql);
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1734
|
+
stringEnv.set(assignment.target, resolved.sql);
|
|
1735
|
+
}
|
|
1736
|
+
function buildExecuteLine(content, lineStarts, range, statement, execute) {
|
|
1737
|
+
return lineNumberFromIndex(
|
|
1738
|
+
content,
|
|
1739
|
+
range.bodyStart + statement.startOffset + execute.statementStartOffset,
|
|
1740
|
+
lineStarts
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
function buildExecuteKey(range, statement, execute, executeLine) {
|
|
1744
|
+
return `execute:${range.statementType}:${statement.startOffset}:${execute.statementStartOffset}:${executeLine}`;
|
|
1745
|
+
}
|
|
1746
|
+
function buildStaticExecuteRisk(execute, executeLine) {
|
|
1747
|
+
return {
|
|
1748
|
+
level: "medium",
|
|
1749
|
+
description: "Static EXECUTE SQL detected in PL/pgSQL body",
|
|
1750
|
+
mitigation: "Prefer declarative/static SQL when possible; keep this change explicit in declarative schemas",
|
|
1751
|
+
line: executeLine,
|
|
1752
|
+
reasonCode: "PLPGSQL_EXECUTE_STATIC",
|
|
1753
|
+
confidence: "low",
|
|
1754
|
+
evidence: {
|
|
1755
|
+
source: "extractExecuteExpressions",
|
|
1756
|
+
snippet: execute.expression.slice(0, 200),
|
|
1757
|
+
detail: "Static EXECUTE SQL detected from reconstructed expression"
|
|
1758
|
+
}
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
function pushDerivedStaticRisks(risks, dedupe, staticSql2, executeLine, hasUsingClause) {
|
|
1762
|
+
const staticRisks = enrichStaticExecuteRiskFromSql(staticSql2, executeLine);
|
|
1763
|
+
for (const staticRisk of staticRisks) {
|
|
1764
|
+
const key = `${staticRisk.level}:${staticRisk.description}:${executeLine}`;
|
|
1765
|
+
const dedupeKey = `static:${key}`;
|
|
1766
|
+
if (dedupe.has(dedupeKey)) continue;
|
|
1767
|
+
dedupe.add(dedupeKey);
|
|
1768
|
+
risks.push({
|
|
1769
|
+
...staticRisk,
|
|
1770
|
+
reasonCode: staticRisk.reasonCode ?? "PLPGSQL_EXECUTE_STATIC",
|
|
1771
|
+
confidence: staticRisk.confidence ?? "low",
|
|
1772
|
+
evidence: {
|
|
1773
|
+
...staticRisk.evidence,
|
|
1774
|
+
source: staticRisk.evidence?.source ?? "detectRisksFromContent",
|
|
1775
|
+
detail: `Static EXECUTE SQL analyzed from reconstructed expression${hasUsingClause ? " (USING clause present)" : ""}`
|
|
1776
|
+
}
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
function maybeBuildUsingClauseRisk(execute, executeLine) {
|
|
1781
|
+
if (!execute.hasUsingClause) return void 0;
|
|
1782
|
+
return {
|
|
1783
|
+
level: "high",
|
|
1784
|
+
description: "PL/pgSQL EXECUTE uses USING bindings; values are applied at runtime and must be auditable",
|
|
1785
|
+
mitigation: "Avoid runtime-bound SQL mutation in migration paths, or keep this change in idempotent with explicit review",
|
|
1786
|
+
line: executeLine,
|
|
1787
|
+
reasonCode: "PLPGSQL_EXECUTE_DYNAMIC_USING",
|
|
1788
|
+
confidence: "high",
|
|
1789
|
+
evidence: {
|
|
1790
|
+
source: "extractExecuteExpressions",
|
|
1791
|
+
snippet: execute.expression.slice(0, 200),
|
|
1792
|
+
detail: "EXECUTE contains USING clause"
|
|
1793
|
+
}
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
function pushBodyStaticRisks(risks, dedupe, body, bodyStartLine) {
|
|
1797
|
+
const searchableBody = stripSqlStringsPreserveLines(body);
|
|
1798
|
+
if (!searchableBody.trim()) return;
|
|
1799
|
+
const bodyLineStarts = buildLineStarts(body);
|
|
1800
|
+
const bodyRisks = detectRisksFromContent(searchableBody, body, bodyLineStarts);
|
|
1801
|
+
for (const risk of bodyRisks) {
|
|
1802
|
+
const line = bodyStartLine + ((risk.line ?? 1) - 1);
|
|
1803
|
+
const dedupeKey = `body:${risk.reasonCode ?? risk.description}:${line}`;
|
|
1804
|
+
if (dedupe.has(dedupeKey)) continue;
|
|
1805
|
+
dedupe.add(dedupeKey);
|
|
1806
|
+
risks.push({
|
|
1807
|
+
...risk,
|
|
1808
|
+
line,
|
|
1809
|
+
evidence: {
|
|
1810
|
+
source: "plpgsql body",
|
|
1811
|
+
snippet: body.trim().slice(0, 200),
|
|
1812
|
+
detail: `Matched ${risk.reasonCode ?? "pattern"} inside PL/pgSQL body`
|
|
1813
|
+
}
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
function buildUnresolvedExecuteRisk(execute, executeLine, reason) {
|
|
1818
|
+
const clauseSuffix = execute.hasUsingClause ? " includes USING clause" : execute.hasIntoClause ? " includes INTO clause" : "";
|
|
1819
|
+
return {
|
|
1820
|
+
level: "high",
|
|
1821
|
+
description: `UNRESOLVED_DYNAMIC_SQL in PL/PGSQL EXECUTE: ${reason}${clauseSuffix}`,
|
|
1822
|
+
mitigation: "Resolve SQL construction at migration time or add manual review before production apply preview",
|
|
1823
|
+
line: executeLine,
|
|
1824
|
+
reasonCode: execute.hasUsingClause ? "PLPGSQL_EXECUTE_DYNAMIC_USING" : execute.hasIntoClause ? "PLPGSQL_EXECUTE_DYNAMIC_INTO" : "PLPGSQL_EXECUTE_UNRESOLVED",
|
|
1825
|
+
confidence: "high",
|
|
1826
|
+
evidence: {
|
|
1827
|
+
source: "extractStaticSqlFromExpression",
|
|
1828
|
+
snippet: execute.expression.slice(0, 200),
|
|
1829
|
+
detail: `Failed to reconstruct dynamic SQL (${reason})`
|
|
1830
|
+
}
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
function analyzeExecuteStatement(risks, dedupe, content, lineStarts, range, statement, execute, stringEnv) {
|
|
1834
|
+
const executeLine = buildExecuteLine(content, lineStarts, range, statement, execute);
|
|
1835
|
+
const executeKey = buildExecuteKey(range, statement, execute, executeLine);
|
|
1836
|
+
if (dedupe.has(executeKey)) return;
|
|
1837
|
+
dedupe.add(executeKey);
|
|
1838
|
+
const extracted = extractStaticSqlFromExpression(execute.expression, stringEnv);
|
|
1839
|
+
if (extracted.kind !== "static") {
|
|
1840
|
+
risks.push(buildUnresolvedExecuteRisk(execute, executeLine, extracted.reason));
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
risks.push(buildStaticExecuteRisk(execute, executeLine));
|
|
1844
|
+
pushDerivedStaticRisks(risks, dedupe, extracted.sql, executeLine, execute.hasUsingClause);
|
|
1845
|
+
const usingRisk = maybeBuildUsingClauseRisk(execute, executeLine);
|
|
1846
|
+
if (usingRisk !== void 0) {
|
|
1847
|
+
risks.push(usingRisk);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
function analyzeBodyRange(risks, dedupe, content, commentlessContent, lineStarts, range) {
|
|
1851
|
+
const declarationLine = lineNumberFromIndex(commentlessContent, range.bodyStart, lineStarts);
|
|
1852
|
+
maybeAddDoBlockRisk(risks, dedupe, commentlessContent, range, declarationLine);
|
|
1853
|
+
const body = commentlessContent.slice(range.bodyStart, range.bodyEnd);
|
|
1854
|
+
pushBodyStaticRisks(risks, dedupe, body, declarationLine);
|
|
1855
|
+
const stringEnv = /* @__PURE__ */ new Map();
|
|
1856
|
+
for (const statement of splitPlpgsqlStatementsWithOffsets(body)) {
|
|
1857
|
+
applyStatementAssignment(statement, stringEnv);
|
|
1858
|
+
const executeStatements = extractExecuteExpressions(statement.statement);
|
|
1859
|
+
for (const execute of executeStatements) {
|
|
1860
|
+
analyzeExecuteStatement(
|
|
1861
|
+
risks,
|
|
1862
|
+
dedupe,
|
|
1863
|
+
content,
|
|
1864
|
+
lineStarts,
|
|
1865
|
+
range,
|
|
1866
|
+
statement,
|
|
1867
|
+
execute,
|
|
1868
|
+
stringEnv
|
|
1869
|
+
);
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
function detectPlpgsqlDynamicExecutionRisks(content) {
|
|
1874
|
+
const commentlessContent = stripSqlCommentsPreserveLines(content);
|
|
1875
|
+
const lineStarts = buildLineStarts(commentlessContent);
|
|
1876
|
+
const ranges = findFunctionAndDoBodies(commentlessContent);
|
|
1877
|
+
if (ranges.length === 0) return [];
|
|
1878
|
+
const risks = [];
|
|
1879
|
+
const dedupe = /* @__PURE__ */ new Set();
|
|
1880
|
+
for (const range of ranges) {
|
|
1881
|
+
analyzeBodyRange(risks, dedupe, content, commentlessContent, lineStarts, range);
|
|
1882
|
+
}
|
|
1883
|
+
return risks;
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
export { detectPlpgsqlDynamicExecutionRisks };
|