attention-manifest-protocol 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +341 -293
- package/CHANGELOG.zh-CN.md +41 -8
- package/README.md +250 -171
- package/README.zh-CN.md +251 -171
- package/lib/assignment/assign.d.ts +102 -0
- package/lib/assignment/assign.js +556 -0
- package/lib/assignment/binding.d.ts +103 -0
- package/lib/assignment/binding.js +575 -0
- package/lib/assignment/errors.d.ts +14 -0
- package/lib/assignment/errors.js +16 -0
- package/lib/assignment/guards.d.ts +18 -0
- package/lib/assignment/guards.js +28 -0
- package/lib/assignment/index.d.ts +10 -0
- package/lib/assignment/index.js +10 -0
- package/lib/assignment/types.d.ts +169 -0
- package/lib/assignment/types.js +1 -0
- package/lib/attention/budget.d.ts +1 -1
- package/lib/attention/budget.js +31 -9
- package/lib/bayes/engine.js +2 -1
- package/lib/bayes/reliability.js +25 -10
- package/lib/boundary/frame.d.ts +10 -4
- package/lib/boundary/frame.js +346 -109
- package/lib/boundary/kinds.d.ts +11 -0
- package/lib/boundary/kinds.js +33 -0
- package/lib/boundary/refTable.d.ts +19 -9
- package/lib/boundary/refTable.js +128 -30
- package/lib/boundary/toolCall.d.ts +22 -10
- package/lib/boundary/toolCall.js +798 -97
- package/lib/concept/conceptBudget.js +10 -1
- package/lib/concurrency/idempotency.d.ts +4 -3
- package/lib/concurrency/idempotency.js +57 -13
- package/lib/concurrency/retry.js +18 -3
- package/lib/context/agentContext.d.ts +13 -0
- package/lib/context/agentContext.js +14 -1
- package/lib/context/ast.d.ts +4 -1
- package/lib/context/ast.js +13 -3
- package/lib/context/contentStore.js +22 -9
- package/lib/context/contextBuilder.d.ts +457 -0
- package/lib/context/contextBuilder.js +1395 -0
- package/lib/context/contextPackage.d.ts +214 -0
- package/lib/context/contextPackage.js +191 -0
- package/lib/context/index.d.ts +14 -3
- package/lib/context/index.js +9 -1
- package/lib/context/relevance.d.ts +171 -0
- package/lib/context/relevance.js +250 -0
- package/lib/context/workspaceDiff.d.ts +105 -0
- package/lib/context/workspaceDiff.js +238 -0
- package/lib/context/workspaceService.d.ts +236 -1
- package/lib/context/workspaceService.js +1501 -73
- package/lib/cooperation/collaboration.d.ts +9 -0
- package/lib/cooperation/session.d.ts +4 -4
- package/lib/cooperation/session.js +3 -3
- package/lib/database/adapter/filesystemStore.d.ts +145 -0
- package/lib/database/adapter/filesystemStore.js +321 -0
- package/lib/database/adapter/index.d.ts +2 -0
- package/lib/database/adapter/index.js +2 -0
- package/lib/database/adapter/workspaceSessionStore.d.ts +118 -0
- package/lib/database/adapter/workspaceSessionStore.js +120 -0
- package/lib/database/entity/fileGraph.d.ts +53 -0
- package/lib/database/entity/fileGraph.js +15 -0
- package/lib/database/entity/fileTree.d.ts +76 -0
- package/lib/database/entity/fileTree.js +39 -0
- package/lib/database/entity/hook.d.ts +29 -0
- package/lib/database/entity/hook.js +18 -0
- package/lib/database/entity/index.d.ts +4 -0
- package/lib/database/entity/index.js +4 -0
- package/lib/database/entity/workspaceSession.d.ts +53 -0
- package/lib/database/entity/workspaceSession.js +28 -0
- package/lib/database/repository/fileAnnotationRepository.d.ts +75 -0
- package/lib/database/repository/fileAnnotationRepository.js +109 -0
- package/lib/database/repository/fileGraphRepository.d.ts +188 -0
- package/lib/database/repository/fileGraphRepository.js +648 -0
- package/lib/database/repository/fileNodeRepository.d.ts +116 -0
- package/lib/database/repository/fileNodeRepository.js +263 -0
- package/lib/database/repository/fileOutlineRepository.d.ts +40 -0
- package/lib/database/repository/fileOutlineRepository.js +104 -0
- package/lib/database/repository/fileWriteRepository.d.ts +41 -0
- package/lib/database/repository/fileWriteRepository.js +93 -0
- package/lib/database/repository/hookRepository.d.ts +67 -0
- package/lib/database/repository/hookRepository.js +150 -0
- package/lib/database/repository/index.d.ts +7 -0
- package/lib/database/repository/index.js +7 -0
- package/lib/database/repository/workspaceSessionRepository.d.ts +134 -0
- package/lib/database/repository/workspaceSessionRepository.js +327 -0
- package/lib/database/service/database.d.ts +14 -0
- package/lib/database/service/database.js +21 -0
- package/lib/database/sql/schema.js +187 -0
- package/lib/database/store/database.js +6 -1
- package/lib/delegate/team.d.ts +5 -0
- package/lib/delegate/team.js +83 -20
- package/lib/discover/classify.d.ts +20 -0
- package/lib/discover/classify.js +85 -0
- package/lib/discover/host.d.ts +20 -0
- package/lib/discover/host.js +89 -0
- package/lib/discover/index.d.ts +11 -0
- package/lib/discover/index.js +11 -0
- package/lib/discover/kinds.d.ts +14 -0
- package/lib/discover/kinds.js +23 -0
- package/lib/discover/tree.d.ts +7 -0
- package/lib/discover/tree.js +432 -0
- package/lib/discover/types.d.ts +115 -0
- package/lib/discover/types.js +1 -0
- package/lib/error/boundaryRejection.d.ts +1 -1
- package/lib/evaluation/agreement.js +2 -2
- package/lib/execution/handles.js +6 -0
- package/lib/execution/pin.js +2 -2
- package/lib/file/cfb.d.ts +56 -0
- package/lib/file/cfb.js +396 -0
- package/lib/file/dependency.d.ts +151 -0
- package/lib/file/dependency.js +568 -0
- package/lib/file/directory.d.ts +34 -0
- package/lib/file/directory.js +141 -0
- package/lib/file/errors.d.ts +20 -0
- package/lib/file/errors.js +18 -0
- package/lib/file/exportGraph.d.ts +31 -0
- package/lib/file/exportGraph.js +554 -0
- package/lib/file/file.d.ts +40 -0
- package/lib/file/file.js +339 -0
- package/lib/file/importGraph.d.ts +121 -0
- package/lib/file/importGraph.js +1022 -0
- package/lib/file/index.d.ts +45 -0
- package/lib/file/index.js +30 -0
- package/lib/file/metadata.d.ts +66 -0
- package/lib/file/metadata.js +470 -0
- package/lib/file/path.d.ts +60 -0
- package/lib/file/path.js +259 -0
- package/lib/file/read.d.ts +168 -0
- package/lib/file/read.js +541 -0
- package/lib/file/runtime.d.ts +95 -0
- package/lib/file/runtime.js +150 -0
- package/lib/file/service.d.ts +227 -0
- package/lib/file/service.js +663 -0
- package/lib/file/syntaxTree.d.ts +7 -0
- package/lib/file/syntaxTree.js +38 -0
- package/lib/file/time.d.ts +38 -0
- package/lib/file/time.js +44 -0
- package/lib/file/tree.d.ts +53 -0
- package/lib/file/tree.js +417 -0
- package/lib/file/types.d.ts +371 -0
- package/lib/file/types.js +1 -0
- package/lib/file/write.d.ts +159 -0
- package/lib/file/write.js +1028 -0
- package/lib/file/zip.d.ts +35 -0
- package/lib/file/zip.js +349 -0
- package/lib/fingerprint/algorithm.d.ts +97 -0
- package/lib/fingerprint/algorithm.js +271 -0
- package/lib/fingerprint/bytes.d.ts +18 -0
- package/lib/fingerprint/bytes.js +31 -0
- package/lib/fingerprint/error.d.ts +73 -0
- package/lib/fingerprint/error.js +86 -0
- package/lib/fingerprint/file.d.ts +71 -0
- package/lib/fingerprint/file.js +254 -0
- package/lib/fingerprint/index.d.ts +37 -0
- package/lib/fingerprint/index.js +12 -0
- package/lib/fingerprint/json.d.ts +41 -0
- package/lib/fingerprint/json.js +94 -0
- package/lib/fingerprint/number.d.ts +32 -0
- package/lib/fingerprint/number.js +95 -0
- package/lib/fingerprint/policy.d.ts +54 -0
- package/lib/fingerprint/policy.js +50 -0
- package/lib/fingerprint/preimage.d.ts +84 -0
- package/lib/fingerprint/preimage.js +133 -0
- package/lib/fingerprint/raw.d.ts +26 -0
- package/lib/fingerprint/raw.js +45 -0
- package/lib/fingerprint/ref.d.ts +39 -0
- package/lib/fingerprint/ref.js +178 -0
- package/lib/fingerprint/text.d.ts +42 -0
- package/lib/fingerprint/text.js +155 -0
- package/lib/fingerprint/tree.d.ts +56 -0
- package/lib/fingerprint/tree.js +288 -0
- package/lib/fingerprint/verify.d.ts +20 -0
- package/lib/fingerprint/verify.js +99 -0
- package/lib/global/filesystem.d.ts +43 -0
- package/lib/global/filesystem.js +75 -0
- package/lib/global/index.d.ts +8 -0
- package/lib/global/index.js +8 -0
- package/lib/global/service.d.ts +51 -0
- package/lib/global/service.js +98 -0
- package/lib/governance/transport.js +18 -6
- package/lib/hook/ast.d.ts +29 -0
- package/lib/hook/ast.js +22 -0
- package/lib/hook/code.d.ts +55 -0
- package/lib/hook/code.js +47 -0
- package/lib/hook/dependency.d.ts +45 -0
- package/lib/hook/dependency.js +28 -0
- package/lib/hook/diff.d.ts +114 -0
- package/lib/hook/diff.js +327 -0
- package/lib/hook/exportation.d.ts +31 -0
- package/lib/hook/exportation.js +24 -0
- package/lib/hook/file.d.ts +44 -0
- package/lib/hook/file.js +154 -0
- package/lib/hook/importation.d.ts +33 -0
- package/lib/hook/importation.js +22 -0
- package/lib/hook/index.d.ts +30 -0
- package/lib/hook/index.js +23 -0
- package/lib/hook/outline.d.ts +35 -0
- package/lib/hook/outline.js +27 -0
- package/lib/hook/project.d.ts +65 -0
- package/lib/hook/project.js +217 -0
- package/lib/hook/types.d.ts +114 -0
- package/lib/hook/types.js +407 -0
- package/lib/index.d.ts +9 -0
- package/lib/index.js +9 -0
- package/lib/lock/lease.js +28 -3
- package/lib/manifest/cardSign.d.ts +1 -1
- package/lib/manifest/cardSign.js +37 -7
- package/lib/manifest/federation.js +13 -6
- package/lib/manifest/trust.d.ts +2 -0
- package/lib/manifest/trust.js +27 -6
- package/lib/module/index.d.ts +4 -0
- package/lib/module/index.js +4 -0
- package/lib/module/mcp/client.js +21 -2
- package/lib/module/mcp/filesystemServer.d.ts +24 -0
- package/lib/module/mcp/filesystemServer.js +102 -0
- package/lib/module/mcp/http.js +32 -19
- package/lib/module/mcp/workspaceServer.js +20 -3
- package/lib/module/tool/filesystemExecutor.d.ts +18 -0
- package/lib/module/tool/filesystemExecutor.js +49 -0
- package/lib/module/tool/globalExecutor.d.ts +17 -0
- package/lib/module/tool/globalExecutor.js +39 -0
- package/lib/module/tool/globalFilesystemExecutor.d.ts +18 -0
- package/lib/module/tool/globalFilesystemExecutor.js +49 -0
- package/lib/module/tool/workspaceExecutor.d.ts +9 -0
- package/lib/module/tool/workspaceExecutor.js +16 -3
- package/lib/naming/aliases.d.ts +48 -0
- package/lib/naming/aliases.js +79 -0
- package/lib/naming/catalog.d.ts +117 -0
- package/lib/naming/catalog.js +108 -0
- package/lib/naming/grammar.d.ts +67 -0
- package/lib/naming/grammar.js +212 -0
- package/lib/naming/ids.d.ts +157 -0
- package/lib/naming/ids.js +119 -0
- package/lib/naming/index.d.ts +13 -0
- package/lib/naming/index.js +13 -0
- package/lib/outline/index.d.ts +9 -0
- package/lib/outline/index.js +8 -0
- package/lib/outline/office.d.ts +160 -0
- package/lib/outline/office.js +2375 -0
- package/lib/outline/officeBridge.d.ts +32 -0
- package/lib/outline/officeBridge.js +27 -0
- package/lib/outline/outline.d.ts +159 -0
- package/lib/outline/outline.js +837 -0
- package/lib/outline/outlineIndex.d.ts +97 -0
- package/lib/outline/outlineIndex.js +325 -0
- package/lib/outline/outlineKeywords.d.ts +59 -0
- package/lib/outline/outlineKeywords.js +143 -0
- package/lib/outline/outlineStrategies.d.ts +414 -0
- package/lib/outline/outlineStrategies.js +8324 -0
- package/lib/outline/reactOutline.d.ts +37 -0
- package/lib/outline/reactOutline.js +1070 -0
- package/lib/outline/shellOutline.d.ts +65 -0
- package/lib/outline/shellOutline.js +873 -0
- package/lib/outline/sqlOutline.d.ts +170 -0
- package/lib/outline/sqlOutline.js +1536 -0
- package/lib/outline/strategy.d.ts +46 -0
- package/lib/outline/strategy.js +1 -0
- package/lib/outline/types.d.ts +95 -0
- package/lib/outline/types.js +1 -0
- package/lib/outline/visualBasicOutline.d.ts +27 -0
- package/lib/outline/visualBasicOutline.js +750 -0
- package/lib/protocol/filesystem.d.ts +55 -0
- package/lib/protocol/filesystem.js +105 -0
- package/lib/protocol/global.d.ts +42 -0
- package/lib/protocol/global.js +63 -0
- package/lib/protocol/globalFilesystem.d.ts +40 -0
- package/lib/protocol/globalFilesystem.js +59 -0
- package/lib/protocol/index.d.ts +4 -0
- package/lib/protocol/index.js +4 -0
- package/lib/protocol/protocol.d.ts +4 -0
- package/lib/protocol/wire.js +5 -2
- package/lib/protocol/workspace.d.ts +305 -0
- package/lib/protocol/workspace.js +258 -0
- package/lib/receipt/receipt.d.ts +8 -0
- package/lib/registry/content.js +9 -3
- package/lib/runtime/kernelRuntime.js +9 -1
- package/lib/scenario/scenario.js +5 -4
- package/lib/semantic/canonical.d.ts +61 -0
- package/lib/semantic/canonical.js +135 -8
- package/lib/semantic/jsonValue.d.ts +31 -0
- package/lib/semantic/jsonValue.js +122 -0
- package/lib/semantic/sha256.js +3 -0
- package/lib/semantic/time.d.ts +14 -0
- package/lib/semantic/time.js +56 -0
- package/lib/session/context.d.ts +76 -0
- package/lib/session/context.js +419 -0
- package/lib/session/errors.d.ts +24 -0
- package/lib/session/errors.js +22 -0
- package/lib/session/identity.d.ts +24 -0
- package/lib/session/identity.js +56 -0
- package/lib/session/index.d.ts +14 -0
- package/lib/session/index.js +12 -0
- package/lib/session/registry.d.ts +130 -0
- package/lib/session/registry.js +908 -0
- package/lib/session/types.d.ts +227 -0
- package/lib/session/types.js +14 -0
- package/lib/structure/index.d.ts +1 -0
- package/lib/structure/index.js +1 -0
- package/lib/structure/trie.d.ts +88 -0
- package/lib/structure/trie.js +183 -0
- package/lib/syntax/byteIndex.d.ts +44 -0
- package/lib/syntax/byteIndex.js +148 -0
- package/lib/syntax/charset.d.ts +88 -0
- package/lib/syntax/charset.js +276 -0
- package/lib/syntax/index.d.ts +6 -0
- package/lib/syntax/index.js +5 -0
- package/lib/syntax/syntaxRegistry.d.ts +150 -0
- package/lib/syntax/syntaxRegistry.js +314 -0
- package/lib/syntax/syntaxTree.d.ts +106 -0
- package/lib/syntax/syntaxTree.js +668 -0
- package/lib/syntax/types.d.ts +15 -0
- package/lib/syntax/types.js +1 -0
- package/lib/types/common.d.ts +1 -1
- package/package.json +72 -193
- package/.editorconfig +0 -12
- package/.prettierignore +0 -17
- package/MCP-Token/347/250/216/344/270/216/344/270/213/344/270/200/344/273/243/345/267/245/345/205/267/345/215/217/350/256/256.html +0 -1763
- package/docs/annotation-campaign-A.csv +0 -54
- package/docs/annotation-campaign-B.csv +0 -54
- package/docs/annotation-campaign.csv +0 -54
- package/docs/eval/annotations-A.jsonl +0 -16
- package/docs/eval/annotations.json +0 -122
- package/docs/eval/recorded-labels.json +0 -12
- package/docs/eval/replay-cases.json +0 -14
- package/docs/eval/replay-labels-b.json +0 -14
- package/docs/examples/attention-request.json +0 -16
- package/docs/examples/checkpoint.json +0 -15
- package/docs/examples/claim.json +0 -66
- package/docs/examples/collaboration.json +0 -66
- package/docs/examples/concept-checkpoint.json +0 -15
- package/docs/examples/concept-prune-plan.json +0 -45
- package/docs/examples/federation.json +0 -126
- package/docs/examples/frame-chain.json +0 -77
- package/docs/examples/ledger-snapshot.json +0 -108
- package/docs/examples/manifest.json +0 -77
- package/docs/examples/message.json +0 -19
- package/docs/examples/other-model.json +0 -45
- package/docs/examples/plan.json +0 -50
- package/docs/examples/prune-plan.json +0 -48
- package/docs/examples/receipt.json +0 -22
- package/docs/examples/runtime-run.json +0 -573
- package/docs/examples/team.json +0 -66
- package/docs/examples/verb-note.json +0 -11
- package/docs/probes/bayes-scores.json +0 -1452
- package/docs/probes/calibration-imported.json +0 -141
- package/docs/probes/context-ast-deepseek.json +0 -1577
- package/docs/probes/deepseek-probe.json +0 -917
- package/docs/probes/eval-recorded-replay.json +0 -597
- package/docs/probes/eval-replay.json +0 -124
- package/docs/probes/eval-summary.json +0 -42
- package/docs/probes/runtime-eval.json +0 -4230
- package/eslint.config.mts +0 -96
- package/index.html +0 -13
- package/prettier.config.mts +0 -37
- package/public/favicon.svg +0 -1
- package/public/icons.svg +0 -24
- package/src/attention/attention.ts +0 -347
- package/src/attention/budget.ts +0 -72
- package/src/attention/cognition.ts +0 -519
- package/src/attention/index.ts +0 -8
- package/src/attention/interrupts.ts +0 -228
- package/src/attention/resource.ts +0 -316
- package/src/attention/social.ts +0 -217
- package/src/attention/socialTypes.ts +0 -115
- package/src/authz/authz.ts +0 -305
- package/src/authz/index.ts +0 -1
- package/src/bayes/calibration.ts +0 -204
- package/src/bayes/calibrationRegistry.ts +0 -111
- package/src/bayes/calibrationSelection.ts +0 -154
- package/src/bayes/engine.ts +0 -532
- package/src/bayes/index.ts +0 -6
- package/src/bayes/reliability.ts +0 -62
- package/src/bayes/scopedPriors.ts +0 -88
- package/src/boundary/frame.ts +0 -322
- package/src/boundary/ids.ts +0 -14
- package/src/boundary/index.ts +0 -12
- package/src/boundary/refTable.ts +0 -125
- package/src/boundary/toolCall.ts +0 -212
- package/src/concept/concept.ts +0 -75
- package/src/concept/conceptBudget.ts +0 -346
- package/src/concept/conceptBudgetTypes.ts +0 -79
- package/src/concept/index.ts +0 -3
- package/src/concurrency/idempotency.ts +0 -41
- package/src/concurrency/index.ts +0 -5
- package/src/concurrency/mutex.ts +0 -26
- package/src/concurrency/namedLocks.ts +0 -35
- package/src/concurrency/retry.ts +0 -50
- package/src/concurrency/worker.ts +0 -128
- package/src/context/agentContext.ts +0 -308
- package/src/context/ast.ts +0 -499
- package/src/context/contentStore.ts +0 -75
- package/src/context/context.ts +0 -65
- package/src/context/contextIndex.ts +0 -85
- package/src/context/document.ts +0 -137
- package/src/context/fileStorePath.ts +0 -14
- package/src/context/index.ts +0 -43
- package/src/context/lifecycle.ts +0 -221
- package/src/context/projections.ts +0 -151
- package/src/context/workspace.ts +0 -422
- package/src/context/workspaceProtocol.ts +0 -112
- package/src/context/workspaceService.ts +0 -423
- package/src/cooperation/board.ts +0 -104
- package/src/cooperation/collaboration.ts +0 -60
- package/src/cooperation/index.ts +0 -6
- package/src/cooperation/merge.ts +0 -190
- package/src/cooperation/metrics.ts +0 -57
- package/src/cooperation/session.ts +0 -131
- package/src/cooperation/unified.ts +0 -56
- package/src/counter.ts +0 -9
- package/src/database/adapter/adapters.ts +0 -35
- package/src/database/adapter/index.ts +0 -4
- package/src/database/adapter/runtimeSink.ts +0 -74
- package/src/database/adapter/sessionStore.ts +0 -32
- package/src/database/adapter/turnStore.ts +0 -23
- package/src/database/entity/agent.ts +0 -199
- package/src/database/entity/entity.ts +0 -1046
- package/src/database/entity/handle.ts +0 -79
- package/src/database/entity/index.ts +0 -32
- package/src/database/entity/message.ts +0 -74
- package/src/database/entity/meta.ts +0 -11
- package/src/database/entity/object.ts +0 -42
- package/src/database/entity/outbox.ts +0 -15
- package/src/database/entity/process.ts +0 -65
- package/src/database/entity/registry.ts +0 -155
- package/src/database/entity/runtimeApproval.ts +0 -30
- package/src/database/entity/runtimeSession.ts +0 -13
- package/src/database/entity/runtimeTurn.ts +0 -15
- package/src/database/entity/thread.ts +0 -85
- package/src/database/entity/window.ts +0 -85
- package/src/database/entity/worker.ts +0 -90
- package/src/database/index.ts +0 -6
- package/src/database/repository/addressSpaceRepository.ts +0 -72
- package/src/database/repository/agentRepository.ts +0 -460
- package/src/database/repository/base.ts +0 -41
- package/src/database/repository/executionRepository.ts +0 -477
- package/src/database/repository/handleRepository.ts +0 -64
- package/src/database/repository/index.ts +0 -20
- package/src/database/repository/messageRepository.ts +0 -52
- package/src/database/repository/objectRepository.ts +0 -69
- package/src/database/repository/outboxRepository.ts +0 -63
- package/src/database/repository/processRepository.ts +0 -58
- package/src/database/repository/registryRepository.ts +0 -119
- package/src/database/repository/repository.ts +0 -491
- package/src/database/repository/runtimeApprovalRepository.ts +0 -44
- package/src/database/repository/runtimeSessionRepository.ts +0 -27
- package/src/database/repository/runtimeTurnRepository.ts +0 -39
- package/src/database/repository/socialRepository.ts +0 -185
- package/src/database/repository/threadRepository.ts +0 -57
- package/src/database/repository/windowRepository.ts +0 -88
- package/src/database/repository/workerRepository.ts +0 -69
- package/src/database/service/database.ts +0 -90
- package/src/database/service/handleService.ts +0 -48
- package/src/database/service/index.ts +0 -16
- package/src/database/service/messageService.ts +0 -61
- package/src/database/service/objectService.ts +0 -59
- package/src/database/service/processService.ts +0 -61
- package/src/database/service/registryServices.ts +0 -88
- package/src/database/service/service.ts +0 -183
- package/src/database/service/stepService.ts +0 -53
- package/src/database/service/threadService.ts +0 -65
- package/src/database/service/windowService.ts +0 -30
- package/src/database/service/workerService.ts +0 -57
- package/src/database/sql/index.ts +0 -3
- package/src/database/sql/schema.ts +0 -418
- package/src/database/sql/sql.ts +0 -543
- package/src/database/store/database.ts +0 -210
- package/src/database/store/index.ts +0 -3
- package/src/database/store/store.ts +0 -266
- package/src/delegate/index.ts +0 -2
- package/src/delegate/team.ts +0 -268
- package/src/delegate/teamTypes.ts +0 -47
- package/src/diagnosis/checklist.ts +0 -74
- package/src/diagnosis/context.ts +0 -93
- package/src/diagnosis/frame.ts +0 -256
- package/src/diagnosis/index.ts +0 -5
- package/src/diagnosis/router.ts +0 -72
- package/src/diagnosis/types.ts +0 -89
- package/src/error/agentError.ts +0 -16
- package/src/error/ampError.ts +0 -20
- package/src/error/authzError.ts +0 -14
- package/src/error/boundaryRejection.ts +0 -20
- package/src/error/index.ts +0 -7
- package/src/error/kernelError.ts +0 -27
- package/src/error/mailboxError.ts +0 -9
- package/src/error/windowError.ts +0 -12
- package/src/evaluation/agreement.ts +0 -96
- package/src/evaluation/index.ts +0 -2
- package/src/evaluation/labels.ts +0 -241
- package/src/evidence/evidenceGraph.ts +0 -205
- package/src/evidence/index.ts +0 -4
- package/src/evidence/revalidation.ts +0 -191
- package/src/execution/exec.ts +0 -408
- package/src/execution/handles.ts +0 -65
- package/src/execution/index.ts +0 -3
- package/src/execution/pin.ts +0 -154
- package/src/frame/frame.ts +0 -70
- package/src/frame/index.ts +0 -1
- package/src/goal/goal.ts +0 -43
- package/src/goal/index.ts +0 -2
- package/src/goal/plan.ts +0 -196
- package/src/governance/attentionManifest.ts +0 -802
- package/src/governance/audit.ts +0 -362
- package/src/governance/budget.ts +0 -628
- package/src/governance/context.ts +0 -356
- package/src/governance/control.ts +0 -159
- package/src/governance/cooperation.ts +0 -479
- package/src/governance/credential.ts +0 -359
- package/src/governance/httpServer.ts +0 -99
- package/src/governance/index.ts +0 -32
- package/src/governance/legal.ts +0 -574
- package/src/governance/matrix.ts +0 -460
- package/src/governance/perspectives.ts +0 -406
- package/src/governance/signing.ts +0 -407
- package/src/governance/store.ts +0 -125
- package/src/governance/switching.ts +0 -459
- package/src/governance/toolPolicy.ts +0 -80
- package/src/governance/transport.ts +0 -204
- package/src/governance/types.ts +0 -380
- package/src/governance/workflow.ts +0 -743
- package/src/index.ts +0 -11
- package/src/interaction/index.ts +0 -1
- package/src/interaction/interaction.ts +0 -391
- package/src/ipc/index.ts +0 -4
- package/src/ipc/mailbox.ts +0 -132
- package/src/ipc/memorySocialStore.ts +0 -93
- package/src/ipc/social.ts +0 -93
- package/src/kernel/index.ts +0 -14
- package/src/kernel/scheduler.ts +0 -498
- package/src/ledger/checkpoint.ts +0 -304
- package/src/ledger/index.ts +0 -4
- package/src/ledger/ledger.ts +0 -31
- package/src/ledger/persistence.ts +0 -159
- package/src/ledger/share.ts +0 -80
- package/src/lock/index.ts +0 -2
- package/src/lock/lease.ts +0 -74
- package/src/main.ts +0 -199
- package/src/manifest/agentCard.ts +0 -72
- package/src/manifest/cardSign.ts +0 -87
- package/src/manifest/federation.ts +0 -237
- package/src/manifest/federationTypes.ts +0 -55
- package/src/manifest/index.ts +0 -8
- package/src/manifest/keys.ts +0 -643
- package/src/manifest/manifest.ts +0 -114
- package/src/manifest/sign.ts +0 -134
- package/src/manifest/trust.ts +0 -146
- package/src/mission/index.ts +0 -2
- package/src/mission/missionQueue.ts +0 -75
- package/src/module/index.ts +0 -9
- package/src/module/llm/deepseek.ts +0 -214
- package/src/module/llm/llm.ts +0 -84
- package/src/module/mcp/client.ts +0 -110
- package/src/module/mcp/http.ts +0 -102
- package/src/module/mcp/protocol.ts +0 -77
- package/src/module/mcp/server.ts +0 -148
- package/src/module/mcp/transport.ts +0 -30
- package/src/module/mcp/workspaceServer.ts +0 -111
- package/src/module/sandbox/sandbox.ts +0 -29
- package/src/module/tool/mcpExecutor.ts +0 -54
- package/src/module/tool/policy.ts +0 -52
- package/src/module/tool/redact.ts +0 -29
- package/src/module/tool/tool.ts +0 -53
- package/src/module/tool/workspaceExecutor.ts +0 -35
- package/src/policy/guard.ts +0 -240
- package/src/policy/index.ts +0 -2
- package/src/policy/policy.ts +0 -561
- package/src/process/agent.ts +0 -341
- package/src/process/agentService.ts +0 -236
- package/src/process/agentStore.ts +0 -378
- package/src/process/executionStore.ts +0 -151
- package/src/process/index.ts +0 -5
- package/src/process/process.ts +0 -240
- package/src/process/transitions.ts +0 -19
- package/src/progress/index.ts +0 -2
- package/src/progress/progress.ts +0 -85
- package/src/protocol/a2a.ts +0 -120
- package/src/protocol/channel.ts +0 -44
- package/src/protocol/envelope.ts +0 -655
- package/src/protocol/httpChannel.ts +0 -87
- package/src/protocol/index.ts +0 -6
- package/src/protocol/protocol.ts +0 -138
- package/src/protocol/wire.ts +0 -60
- package/src/receipt/index.ts +0 -1
- package/src/receipt/receipt.ts +0 -47
- package/src/registry/content.ts +0 -69
- package/src/registry/index.ts +0 -23
- package/src/registry/registry.ts +0 -599
- package/src/runtime/action.ts +0 -87
- package/src/runtime/behavior.ts +0 -330
- package/src/runtime/behaviorTuning.ts +0 -163
- package/src/runtime/cognitiveRuntime.ts +0 -703
- package/src/runtime/escalation.ts +0 -35
- package/src/runtime/governedTask.ts +0 -233
- package/src/runtime/index.ts +0 -15
- package/src/runtime/kernelRuntime.ts +0 -144
- package/src/runtime/operation.ts +0 -35
- package/src/runtime/replay.ts +0 -71
- package/src/runtime/routing.ts +0 -58
- package/src/runtime/safety.ts +0 -48
- package/src/runtime/session.ts +0 -341
- package/src/runtime/sessionStore.ts +0 -43
- package/src/runtime/telemetry.ts +0 -55
- package/src/runtime/turnStore.ts +0 -30
- package/src/scenario/index.ts +0 -1
- package/src/scenario/scenario.ts +0 -299
- package/src/semantic/canonical.ts +0 -24
- package/src/semantic/index.ts +0 -2
- package/src/semantic/sha256.ts +0 -82
- package/src/storage/appendLog.ts +0 -372
- package/src/storage/fileAppendSink.ts +0 -40
- package/src/storage/index.ts +0 -2
- package/src/store/bindCache.ts +0 -22
- package/src/store/cache.ts +0 -191
- package/src/store/context.ts +0 -31
- package/src/store/handleStore.ts +0 -31
- package/src/store/index.ts +0 -9
- package/src/store/messageStore.ts +0 -31
- package/src/store/objectStore.ts +0 -31
- package/src/store/processStore.ts +0 -31
- package/src/store/threadStore.ts +0 -31
- package/src/store/windowStore.ts +0 -31
- package/src/structure/abstractSyntaxTree.ts +0 -493
- package/src/structure/avlTree.ts +0 -408
- package/src/structure/binarySearchTree.ts +0 -352
- package/src/structure/binaryTree.ts +0 -525
- package/src/structure/bloomFilter.ts +0 -231
- package/src/structure/compare.ts +0 -110
- package/src/structure/cuckooFilter.ts +0 -337
- package/src/structure/deque.ts +0 -223
- package/src/structure/filterKey.ts +0 -36
- package/src/structure/hash.ts +0 -279
- package/src/structure/heap.ts +0 -268
- package/src/structure/index.ts +0 -20
- package/src/structure/iterator.ts +0 -22
- package/src/structure/lru.ts +0 -619
- package/src/structure/ordered.ts +0 -399
- package/src/structure/priority.ts +0 -915
- package/src/structure/queue.ts +0 -168
- package/src/structure/redBlackTree.ts +0 -680
- package/src/structure/segmentTree.ts +0 -248
- package/src/structure/stack.ts +0 -118
- package/src/structure/unordered.ts +0 -407
- package/src/style.css +0 -278
- package/src/thread/index.ts +0 -2
- package/src/thread/thread.ts +0 -176
- package/src/thread/transitions.ts +0 -17
- package/src/types/common.ts +0 -119
- package/src/types/index.ts +0 -23
- package/src/warrant/epistemic.ts +0 -154
- package/src/warrant/index.ts +0 -3
- package/src/warrant/inquiry.ts +0 -166
- package/src/warrant/warrant.ts +0 -179
- package/src/window/compaction.ts +0 -403
- package/src/window/compactionTypes.ts +0 -119
- package/src/window/index.ts +0 -7
- package/src/window/page.ts +0 -18
- package/src/window/window.ts +0 -244
- package/tools/_helpers.ts +0 -34
- package/tools/a2a-check.ts +0 -110
- package/tools/a2a-flow-check.ts +0 -78
- package/tools/agent-card-check.ts +0 -76
- package/tools/agent-check.ts +0 -236
- package/tools/agent-store-check.ts +0 -153
- package/tools/agreement-check.ts +0 -27
- package/tools/annotation-server-check.ts +0 -48
- package/tools/annotation-server.ts +0 -230
- package/tools/annotation-status.ts +0 -65
- package/tools/approval-access-check.ts +0 -66
- package/tools/arch-check.ts +0 -72
- package/tools/attention-core-check.ts +0 -48
- package/tools/attention-resource-check.ts +0 -169
- package/tools/audit-check.ts +0 -207
- package/tools/authority-check.ts +0 -124
- package/tools/authz-check.ts +0 -138
- package/tools/bayes-check.ts +0 -162
- package/tools/bayes-probe.ts +0 -154
- package/tools/behavior-tuning-check.ts +0 -53
- package/tools/board-check.ts +0 -87
- package/tools/boundary-check.ts +0 -199
- package/tools/bundle-trust-check.ts +0 -96
- package/tools/calibrate-imported.ts +0 -108
- package/tools/calibration-check.ts +0 -44
- package/tools/calibration-registry-check.ts +0 -40
- package/tools/calibration-sim.ts +0 -40
- package/tools/card-sign-check.ts +0 -71
- package/tools/card-trust-check.ts +0 -78
- package/tools/channel-check.ts +0 -86
- package/tools/checkpoint-check.ts +0 -620
- package/tools/cleanup-style-configs.ts +0 -29
- package/tools/cognition-check.ts +0 -648
- package/tools/cognitive-runtime-check.ts +0 -609
- package/tools/collaboration-demo.ts +0 -78
- package/tools/compaction-check.ts +0 -266
- package/tools/concept-check.ts +0 -260
- package/tools/concurrency-check.ts +0 -242
- package/tools/conformance-runtime.ts +0 -389
- package/tools/conformance.ts +0 -651
- package/tools/context-ast-check.ts +0 -90
- package/tools/context-ast-deepseek.ts +0 -242
- package/tools/context-graph-bench.ts +0 -50
- package/tools/context-graph-check.ts +0 -229
- package/tools/context-index-check.ts +0 -48
- package/tools/context-lifecycle-check.ts +0 -69
- package/tools/context-projection-check.ts +0 -73
- package/tools/context-scope-check.ts +0 -134
- package/tools/context-storage-check.ts +0 -113
- package/tools/cooperation-check.ts +0 -517
- package/tools/cooperation-http-check.ts +0 -125
- package/tools/deepseek-llm-check.ts +0 -147
- package/tools/deepseek-probe.ts +0 -494
- package/tools/diagnosis-check.ts +0 -109
- package/tools/diagnosis-router-check.ts +0 -50
- package/tools/document-context-check.ts +0 -237
- package/tools/envelope-check.ts +0 -659
- package/tools/eslint-rules/index.ts +0 -26
- package/tools/eslint-rules/rules/jsdoc-comment-format.ts +0 -105
- package/tools/eslint-rules/rules/no-function-keyword.ts +0 -60
- package/tools/eslint-rules/rules/no-multiline-parameters.ts +0 -71
- package/tools/eslint-rules/rules/no-multiline-type-parameters.ts +0 -57
- package/tools/eslint-rules/rules/no-undefined-comparison.ts +0 -115
- package/tools/eslint-rules/rules/prefer-template-for-string-conversion.ts +0 -97
- package/tools/eslint-rules/rules/require-typeof-parentheses.ts +0 -49
- package/tools/eslint-rules/rules/require-variable-type.ts +0 -36
- package/tools/eslint-rules/rules/variable-naming.ts +0 -175
- package/tools/eval-recorded-replay.ts +0 -215
- package/tools/eval-replay.ts +0 -139
- package/tools/eval-summary.ts +0 -136
- package/tools/evidence-revalidation-check.ts +0 -235
- package/tools/exec-check.ts +0 -217
- package/tools/federation-check.ts +0 -166
- package/tools/federation-demo.ts +0 -45
- package/tools/filter-check.ts +0 -243
- package/tools/governance-check.ts +0 -680
- package/tools/governance-store-check.ts +0 -131
- package/tools/governed-task-check.ts +0 -227
- package/tools/guard-check.ts +0 -122
- package/tools/http-channel-check.ts +0 -125
- package/tools/import-campaign.ts +0 -28
- package/tools/inquiry-check.ts +0 -126
- package/tools/interaction-check.ts +0 -171
- package/tools/interrupts-check.ts +0 -173
- package/tools/ipc-check.ts +0 -170
- package/tools/kernel-async-check.ts +0 -136
- package/tools/kernel-check.ts +0 -369
- package/tools/kernel-runtime-check.ts +0 -276
- package/tools/keys-check.ts +0 -559
- package/tools/label-audit.ts +0 -70
- package/tools/labels-workflow-check.ts +0 -49
- package/tools/linear-structure-check.ts +0 -524
- package/tools/live-exec.ts +0 -62
- package/tools/live-governance-eval.ts +0 -813
- package/tools/live-legacy.ts +0 -50
- package/tools/lru-check.ts +0 -182
- package/tools/make-annotation-campaign.ts +0 -57
- package/tools/mcp-executor-check.ts +0 -33
- package/tools/merge-annotations.ts +0 -32
- package/tools/merge-check.ts +0 -98
- package/tools/metrics-check.ts +0 -84
- package/tools/net-probe.ts +0 -107
- package/tools/oauth-probe.ts +0 -117
- package/tools/outbox-check.ts +0 -50
- package/tools/persistence-check.ts +0 -144
- package/tools/pin-check.ts +0 -116
- package/tools/platform-check.ts +0 -281
- package/tools/policy-check.ts +0 -514
- package/tools/prettier-plugin/index.mts +0 -84
- package/tools/priority-check.ts +0 -627
- package/tools/probe-context7.ts +0 -63
- package/tools/registry-check.ts +0 -252
- package/tools/reliability-check.ts +0 -25
- package/tools/retry-check.ts +0 -85
- package/tools/rewrite-declaration-extensions.ts +0 -98
- package/tools/runtime-check.ts +0 -165
- package/tools/runtime-deepseek-check.ts +0 -85
- package/tools/runtime-eval.ts +0 -358
- package/tools/scenario-check.ts +0 -161
- package/tools/scenario-incident.ts +0 -162
- package/tools/scoped-priors-check.ts +0 -35
- package/tools/scoped-priors-pooling.ts +0 -34
- package/tools/segment-ast-check.ts +0 -649
- package/tools/session-check.ts +0 -196
- package/tools/session-store-check.ts +0 -73
- package/tools/share-check.ts +0 -88
- package/tools/sign-check.ts +0 -95
- package/tools/signing-check.ts +0 -275
- package/tools/snapshot-demo.ts +0 -65
- package/tools/social-check.ts +0 -146
- package/tools/structure-compare-check.ts +0 -95
- package/tools/support/documentScanner.ts +0 -577
- package/tools/support/texScanner.ts +0 -631
- package/tools/support/workspaceScanner.ts +0 -476
- package/tools/team-check.ts +0 -163
- package/tools/team-demo.ts +0 -36
- package/tools/telemetry-check.ts +0 -44
- package/tools/tex-context-check.ts +0 -263
- package/tools/tool-gate-check.ts +0 -98
- package/tools/tree-structure-check.ts +0 -786
- package/tools/trust-flow-check.ts +0 -158
- package/tools/turn-store-check.ts +0 -57
- package/tools/unified-check.ts +0 -83
- package/tools/validate.ts +0 -309
- package/tools/wire-check.ts +0 -96
- package/tools/workspace-agent-check.ts +0 -486
- package/tools/workspace-agent-demo.ts +0 -62
- package/tools/workspace-agent-server.ts +0 -101
- package/tools/workspace-context-check.ts +0 -331
- package/tools/workspace-context.ts +0 -131
- package/tsconfig.build.json +0 -14
- package/tsconfig.json +0 -24
- package/tsconfig.tools.json +0 -31
- package/viz/context-ast.json +0 -1577
|
@@ -0,0 +1,1536 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The five supported dialects, in built-in registration order. Ties in
|
|
3
|
+
* detection scoring are resolved by this order.
|
|
4
|
+
*/
|
|
5
|
+
export const SqlDialects = ["mysql", "sqlite", "postgresql", "sqlserver", "oracle"];
|
|
6
|
+
/**
|
|
7
|
+
* Fallback dialect when no marker matches. A plain ANSI script is closer to
|
|
8
|
+
* PostgreSQL than to any other supported dialect, so PostgreSQL is the honest
|
|
9
|
+
* "unknown but standard" answer; SQLite has to earn detection with a real
|
|
10
|
+
* SQLite marker.
|
|
11
|
+
*/
|
|
12
|
+
const SqlFallbackDialect = "postgresql";
|
|
13
|
+
/**
|
|
14
|
+
* Flags every marker is compiled with. `m` makes line anchors work, `i` matches
|
|
15
|
+
* SQL keyword casing, and `g` allows the registry to count occurrences.
|
|
16
|
+
*/
|
|
17
|
+
const SqlMarkerFlags = "gim";
|
|
18
|
+
/**
|
|
19
|
+
* Occurrences one marker may contribute. The cap keeps a generated dump with
|
|
20
|
+
* 100k `GO` lines from dominating the score and bounds detection work.
|
|
21
|
+
*/
|
|
22
|
+
const MaxSqlMarkerHits = 16;
|
|
23
|
+
/**
|
|
24
|
+
* Whether a string is one of the five dialect ids.
|
|
25
|
+
*
|
|
26
|
+
* @param value - Candidate dialect string.
|
|
27
|
+
* @returns `true` when the value is a supported dialect.
|
|
28
|
+
*/
|
|
29
|
+
const isSqlDialect = (value) => SqlDialects.includes(value);
|
|
30
|
+
/**
|
|
31
|
+
* Compile one marker and resolve its weight.
|
|
32
|
+
*
|
|
33
|
+
* @param dialect - Dialect the marker belongs to, used in error messages.
|
|
34
|
+
* @param marker - Marker source or descriptor.
|
|
35
|
+
* @returns Compiled marker.
|
|
36
|
+
*/
|
|
37
|
+
const compileSqlMarker = (dialect, marker) => {
|
|
38
|
+
const descriptor = typeof (marker) === "string" ? { pattern: marker } : marker;
|
|
39
|
+
if (typeof (descriptor) !== "object" || descriptor === null || typeof (descriptor.pattern) !== "string" || descriptor.pattern.length === 0) {
|
|
40
|
+
throw new Error(`invalid SQL dialect marker: ${dialect}`);
|
|
41
|
+
}
|
|
42
|
+
const declared = descriptor.weight;
|
|
43
|
+
const weight = declared === (void 0) || !Number.isFinite(declared) || declared <= 0 ? 1 : declared;
|
|
44
|
+
try {
|
|
45
|
+
return { regex: new RegExp(descriptor.pattern, SqlMarkerFlags), weight };
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
throw new Error(`invalid SQL dialect marker: ${dialect}`);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Registry of SQL dialect definitions.
|
|
53
|
+
*
|
|
54
|
+
* Registration order is tie-break priority. A registry is intentionally small:
|
|
55
|
+
* five built-in dialects, each described by markers instead of a grammar. A
|
|
56
|
+
* consumer that only needs detection can use `DefaultSqlDialectRegistry`
|
|
57
|
+
* directly; a consumer that wants an isolated table can create an empty
|
|
58
|
+
* registry and register its own definitions.
|
|
59
|
+
*/
|
|
60
|
+
export class SqlDialectRegistry {
|
|
61
|
+
order = [];
|
|
62
|
+
definitions = new Map();
|
|
63
|
+
compiled = new Map();
|
|
64
|
+
constructor(definitions = []) {
|
|
65
|
+
for (let index = 0; index < definitions.length; index += 1) {
|
|
66
|
+
this.register(definitions[index].dialect, definitions[index]);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Append one dialect definition.
|
|
71
|
+
*
|
|
72
|
+
* @param dialect - Dialect id; must match `definition.dialect`.
|
|
73
|
+
* @param definition - Label and markers.
|
|
74
|
+
*/
|
|
75
|
+
register(dialect, definition) {
|
|
76
|
+
if (!isSqlDialect(dialect)) {
|
|
77
|
+
throw new Error("invalid SQL dialect");
|
|
78
|
+
}
|
|
79
|
+
if (typeof (definition) !== "object" || definition === null) {
|
|
80
|
+
throw new Error("invalid SQL dialect definition");
|
|
81
|
+
}
|
|
82
|
+
if (definition.dialect !== dialect) {
|
|
83
|
+
throw new Error(`SQL dialect mismatch: ${dialect}`);
|
|
84
|
+
}
|
|
85
|
+
if (this.definitions.has(dialect)) {
|
|
86
|
+
throw new Error(`duplicate SQL dialect: ${dialect}`);
|
|
87
|
+
}
|
|
88
|
+
if (!Array.isArray(definition.markers) || definition.markers.length === 0) {
|
|
89
|
+
throw new Error(`SQL dialect has no markers: ${dialect}`);
|
|
90
|
+
}
|
|
91
|
+
const compiled = definition.markers.map((marker) => compileSqlMarker(dialect, marker));
|
|
92
|
+
this.definitions.set(dialect, definition);
|
|
93
|
+
this.compiled.set(dialect, compiled);
|
|
94
|
+
this.order.push(dialect);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Definitions in registration order.
|
|
98
|
+
*
|
|
99
|
+
* @returns Registered definitions.
|
|
100
|
+
*/
|
|
101
|
+
list() {
|
|
102
|
+
return this.order.map((dialect) => this.definitions.get(dialect));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Registered dialect ids in registration order.
|
|
106
|
+
*
|
|
107
|
+
* @returns Dialect ids.
|
|
108
|
+
*/
|
|
109
|
+
dialects() {
|
|
110
|
+
return [...this.order];
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Definition registered for one id.
|
|
114
|
+
*
|
|
115
|
+
* @param dialect - Dialect id.
|
|
116
|
+
* @returns The definition, or `undefined` when it is not registered.
|
|
117
|
+
*/
|
|
118
|
+
definitionOf(dialect) {
|
|
119
|
+
return this.definitions.get(dialect);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Additive marker score for every registered dialect.
|
|
123
|
+
*
|
|
124
|
+
* Each marker contributes `min(occurrences, 16) * weight`; occurrences are
|
|
125
|
+
* capped so algorithmically generated dumps cannot distort the score.
|
|
126
|
+
*
|
|
127
|
+
* @param text - Raw file text (markers are also searched in comments, which
|
|
128
|
+
* is intentional: dump headers usually name the dialect).
|
|
129
|
+
* @returns Score per registered dialect, in registration order.
|
|
130
|
+
*/
|
|
131
|
+
scores(text) {
|
|
132
|
+
const result = new Map();
|
|
133
|
+
for (let index = 0; index < this.order.length; index += 1) {
|
|
134
|
+
const dialect = this.order[index];
|
|
135
|
+
const markers = this.compiled.get(dialect) ?? [];
|
|
136
|
+
let total = 0;
|
|
137
|
+
for (let markerIndex = 0; markerIndex < markers.length; markerIndex += 1) {
|
|
138
|
+
const marker = markers[markerIndex];
|
|
139
|
+
let hits = 0;
|
|
140
|
+
marker.regex.lastIndex = 0;
|
|
141
|
+
let match = marker.regex.exec(text);
|
|
142
|
+
while (match !== null && hits < MaxSqlMarkerHits) {
|
|
143
|
+
hits += 1;
|
|
144
|
+
if (match[0].length === 0) {
|
|
145
|
+
marker.regex.lastIndex += 1;
|
|
146
|
+
}
|
|
147
|
+
match = marker.regex.exec(text);
|
|
148
|
+
}
|
|
149
|
+
marker.regex.lastIndex = 0;
|
|
150
|
+
total += hits * marker.weight;
|
|
151
|
+
}
|
|
152
|
+
result.set(dialect, total);
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Best-scoring registered dialect.
|
|
158
|
+
*
|
|
159
|
+
* The highest score wins; a tie keeps the earlier registered dialect. When
|
|
160
|
+
* every score is zero the fallback is PostgreSQL if it is registered (or if
|
|
161
|
+
* the registry is empty), otherwise the first registered dialect.
|
|
162
|
+
*
|
|
163
|
+
* @param text - Raw file text.
|
|
164
|
+
* @returns Detected dialect.
|
|
165
|
+
*/
|
|
166
|
+
detect(text) {
|
|
167
|
+
const scores = this.scores(text);
|
|
168
|
+
let best = this.order[0] ?? SqlFallbackDialect;
|
|
169
|
+
let bestScore = 0;
|
|
170
|
+
for (let index = 0; index < this.order.length; index += 1) {
|
|
171
|
+
const dialect = this.order[index];
|
|
172
|
+
const score = scores.get(dialect) ?? 0;
|
|
173
|
+
if (score > bestScore) {
|
|
174
|
+
best = dialect;
|
|
175
|
+
bestScore = score;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (bestScore > 0) {
|
|
179
|
+
return best;
|
|
180
|
+
}
|
|
181
|
+
return this.definitions.has(SqlFallbackDialect) ? SqlFallbackDialect : best;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Built-in marker sets.
|
|
186
|
+
*
|
|
187
|
+
* The markers are the documented dialect fingerprints: MySQL
|
|
188
|
+
* `AUTO_INCREMENT`/`ENGINE=`/`UNSIGNED` plus the client-side `DELIMITER`
|
|
189
|
+
* directive, SQLite `AUTOINCREMENT`/`PRAGMA`/`WITHOUT ROWID`, PostgreSQL
|
|
190
|
+
* `SERIAL`/`BIGSERIAL`/`::`/`RETURNING`, SQL Server `IDENTITY`/bracketed
|
|
191
|
+
* identifiers/`GO`/`NVARCHAR`, and Oracle
|
|
192
|
+
* `NUMBER`/`VARCHAR2`/`NVARCHAR2`/`DUAL`/`ROWNUM`/`CONNECT BY`/`SEQ_`/`IDENTITY`/
|
|
193
|
+
* `PL/SQL`/the `/` block terminator.
|
|
194
|
+
*/
|
|
195
|
+
const BuiltinSqlDialectDefinitions = [
|
|
196
|
+
{
|
|
197
|
+
dialect: "mysql",
|
|
198
|
+
label: "MySQL",
|
|
199
|
+
markers: [
|
|
200
|
+
{ pattern: "AUTO_INCREMENT", weight: 3 },
|
|
201
|
+
{ pattern: "ENGINE\\s*=", weight: 3 },
|
|
202
|
+
{ pattern: "\\bUNSIGNED\\b", weight: 1 },
|
|
203
|
+
{ pattern: "^[ \\t]*DELIMITER\\b", weight: 3 },
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
dialect: "sqlite",
|
|
208
|
+
label: "SQLite",
|
|
209
|
+
markers: [
|
|
210
|
+
{ pattern: "\\bAUTOINCREMENT\\b", weight: 3 },
|
|
211
|
+
{ pattern: "\\bPRAGMA\\b", weight: 2 },
|
|
212
|
+
{ pattern: "WITHOUT\\s+ROWID", weight: 3 },
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
dialect: "postgresql",
|
|
217
|
+
label: "PostgreSQL",
|
|
218
|
+
markers: [
|
|
219
|
+
{ pattern: "\\bBIGSERIAL\\b", weight: 3 },
|
|
220
|
+
{ pattern: "\\bSERIAL\\b", weight: 2 },
|
|
221
|
+
{ pattern: "::", weight: 2 },
|
|
222
|
+
{ pattern: "\\bRETURNING\\b", weight: 2 },
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
dialect: "sqlserver",
|
|
227
|
+
label: "SQL Server",
|
|
228
|
+
markers: [
|
|
229
|
+
{ pattern: "\\bIDENTITY\\b", weight: 2 },
|
|
230
|
+
{ pattern: "\\[[^\\]]+\\]", weight: 1 },
|
|
231
|
+
{ pattern: "^[ \\t]*GO(?:[ \\t]+\\d+)?[ \\t]*$", weight: 3 },
|
|
232
|
+
{ pattern: "\\bNVARCHAR\\b", weight: 3 },
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
dialect: "oracle",
|
|
237
|
+
label: "Oracle",
|
|
238
|
+
markers: [
|
|
239
|
+
{ pattern: "\\bNUMBER\\b", weight: 1 },
|
|
240
|
+
{ pattern: "\\bVARCHAR2\\b", weight: 3 },
|
|
241
|
+
{ pattern: "\\bNVARCHAR2\\b", weight: 3 },
|
|
242
|
+
{ pattern: "\\bDUAL\\b", weight: 3 },
|
|
243
|
+
{ pattern: "\\bROWNUM\\b", weight: 3 },
|
|
244
|
+
{ pattern: "CONNECT\\s+BY", weight: 3 },
|
|
245
|
+
{ pattern: "\\bSEQ_", weight: 2 },
|
|
246
|
+
{ pattern: "GENERATED\\s+(?:ALWAYS|BY\\s+DEFAULT)\\s+AS\\s+IDENTITY", weight: 3 },
|
|
247
|
+
{ pattern: "\\bIDENTITY\\b", weight: 1 },
|
|
248
|
+
{ pattern: "PL/SQL", weight: 3 },
|
|
249
|
+
{ pattern: "^[ \\t]*/[ \\t]*$", weight: 2 },
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
];
|
|
253
|
+
/**
|
|
254
|
+
* Registry with every built-in dialect. Detection uses this instance through
|
|
255
|
+
* `detectSqlDialect`.
|
|
256
|
+
*/
|
|
257
|
+
export const DefaultSqlDialectRegistry = new SqlDialectRegistry(BuiltinSqlDialectDefinitions);
|
|
258
|
+
export { DefaultSqlDialectRegistry as defaultSqlDialectRegistry };
|
|
259
|
+
/**
|
|
260
|
+
* Create an empty dialect registry without built-ins.
|
|
261
|
+
*
|
|
262
|
+
* @returns A registry with no definitions.
|
|
263
|
+
*/
|
|
264
|
+
export const createSqlDialectRegistry = () => new SqlDialectRegistry();
|
|
265
|
+
/**
|
|
266
|
+
* Detect the dialect of one SQL text using the built-in marker sets.
|
|
267
|
+
*
|
|
268
|
+
* @param text - Raw SQL text.
|
|
269
|
+
* @returns Detected dialect; PostgreSQL when no marker matches.
|
|
270
|
+
*/
|
|
271
|
+
export const detectSqlDialect = (text) => DefaultSqlDialectRegistry.detect(text);
|
|
272
|
+
/**
|
|
273
|
+
* Line starts of a text, using `\n` as the only separator so a `\r` still
|
|
274
|
+
* belongs to its line for offset purposes.
|
|
275
|
+
*
|
|
276
|
+
* @param text - Decoded text.
|
|
277
|
+
* @returns Absolute offset of each line start.
|
|
278
|
+
*/
|
|
279
|
+
const lineStartsOf = (text) => {
|
|
280
|
+
if (text.length === 0) {
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
const starts = [0];
|
|
284
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
285
|
+
if (text.charCodeAt(index) === 10 && index + 1 < text.length) {
|
|
286
|
+
starts.push(index + 1);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return starts;
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* Split into logical lines, stripping a trailing carriage return and dropping
|
|
293
|
+
* the phantom empty line a trailing newline would create.
|
|
294
|
+
*
|
|
295
|
+
* @param text - Decoded text.
|
|
296
|
+
* @returns Lines without terminators.
|
|
297
|
+
*/
|
|
298
|
+
const splitLines = (text) => {
|
|
299
|
+
if (text.length === 0) {
|
|
300
|
+
return [];
|
|
301
|
+
}
|
|
302
|
+
const lines = text.split("\n");
|
|
303
|
+
if (lines[lines.length - 1] === "") {
|
|
304
|
+
lines.pop();
|
|
305
|
+
}
|
|
306
|
+
return lines.map((line) => (line.endsWith("\r") ? line.slice(0, -1) : line));
|
|
307
|
+
};
|
|
308
|
+
/**
|
|
309
|
+
* 1-based line number containing one absolute offset.
|
|
310
|
+
*
|
|
311
|
+
* @param starts - Line start offsets.
|
|
312
|
+
* @param offset - Absolute offset; values past the end clamp to the last line.
|
|
313
|
+
* @returns 1-based line number.
|
|
314
|
+
*/
|
|
315
|
+
const lineAtOffset = (starts, offset) => {
|
|
316
|
+
if (starts.length === 0) {
|
|
317
|
+
return 1;
|
|
318
|
+
}
|
|
319
|
+
const bounded = Math.max(0, Math.min(offset, Math.max(0, starts[starts.length - 1])));
|
|
320
|
+
let low = 0;
|
|
321
|
+
let high = starts.length - 1;
|
|
322
|
+
while (low < high) {
|
|
323
|
+
const middle = Math.ceil((low + high) / 2);
|
|
324
|
+
if (starts[middle] <= bounded) {
|
|
325
|
+
low = middle;
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
high = middle - 1;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return low + 1;
|
|
332
|
+
};
|
|
333
|
+
/**
|
|
334
|
+
* Read a source once and keep every line view consistent.
|
|
335
|
+
*
|
|
336
|
+
* @param text - Decoded text.
|
|
337
|
+
* @returns Source view.
|
|
338
|
+
*/
|
|
339
|
+
const readSourceLines = (text) => {
|
|
340
|
+
const lines = splitLines(text);
|
|
341
|
+
return { text, lines, starts: lineStartsOf(text), totalLines: lines.length };
|
|
342
|
+
};
|
|
343
|
+
/**
|
|
344
|
+
* Skip whitespace from one absolute offset. Newlines are included because SQL
|
|
345
|
+
* freely wraps a name, a `REFERENCES` clause or a table body across lines.
|
|
346
|
+
*
|
|
347
|
+
* @param text - Text being scanned.
|
|
348
|
+
* @param offset - Start offset.
|
|
349
|
+
* @returns First offset whose character is not whitespace.
|
|
350
|
+
*/
|
|
351
|
+
const skipSqlSpaces = (text, offset) => {
|
|
352
|
+
let cursor = offset;
|
|
353
|
+
while (cursor < text.length && /\s/.test(text[cursor])) {
|
|
354
|
+
cursor += 1;
|
|
355
|
+
}
|
|
356
|
+
return cursor;
|
|
357
|
+
};
|
|
358
|
+
/**
|
|
359
|
+
* Whether an offset is preceded only by spaces/tabs on its line. Used for `#`,
|
|
360
|
+
* which is a MySQL comment anywhere but is a SQL Server temp-table prefix only
|
|
361
|
+
* after an identifier, so a line-leading `#` can be masked in every dialect.
|
|
362
|
+
*
|
|
363
|
+
* @param text - Raw text.
|
|
364
|
+
* @param index - Absolute offset of the `#`.
|
|
365
|
+
* @returns `true` when nothing but indentation precedes the character.
|
|
366
|
+
*/
|
|
367
|
+
const atSqlLineStart = (text, index) => {
|
|
368
|
+
for (let cursor = index - 1; cursor >= 0; cursor -= 1) {
|
|
369
|
+
const character = text[cursor];
|
|
370
|
+
if (character === "\n") {
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
if (character !== " " && character !== "\t") {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return true;
|
|
378
|
+
};
|
|
379
|
+
/**
|
|
380
|
+
* Mask comments, strings and quoted identifiers while preserving every offset
|
|
381
|
+
* and line break.
|
|
382
|
+
*
|
|
383
|
+
* Masked regions become spaces; a line break inside a multi-line string or
|
|
384
|
+
* block comment stays a line break so line numbers do not shift. The scanner
|
|
385
|
+
* is single-pass and bounded: it never allocates a token list, and an
|
|
386
|
+
* unterminated construct runs to the end of the text instead of throwing.
|
|
387
|
+
*
|
|
388
|
+
* @param text - Decoded SQL text.
|
|
389
|
+
* @param options - Dialect-specific masking switches.
|
|
390
|
+
* @returns Masked text with identical length and line breaks.
|
|
391
|
+
*/
|
|
392
|
+
const maskSql = (text, options) => {
|
|
393
|
+
const length = text.length;
|
|
394
|
+
const masked = new Array(length);
|
|
395
|
+
const blank = (from, to) => {
|
|
396
|
+
const stop = Math.min(to, length);
|
|
397
|
+
for (let cursor = from; cursor < stop; cursor += 1) {
|
|
398
|
+
masked[cursor] = text[cursor] === "\n" ? "\n" : " ";
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
let index = 0;
|
|
402
|
+
while (index < length) {
|
|
403
|
+
const character = text[index];
|
|
404
|
+
if (character === "-" && text[index + 1] === "-") {
|
|
405
|
+
const newline = text.indexOf("\n", index);
|
|
406
|
+
const stop = newline === -1 ? length : newline;
|
|
407
|
+
blank(index, stop);
|
|
408
|
+
index = stop;
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (character === "#" && (options.hashComments || atSqlLineStart(text, index))) {
|
|
412
|
+
const newline = text.indexOf("\n", index);
|
|
413
|
+
const stop = newline === -1 ? length : newline;
|
|
414
|
+
blank(index, stop);
|
|
415
|
+
index = stop;
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (character === "/" && text[index + 1] === "*") {
|
|
419
|
+
let cursor = index + 2;
|
|
420
|
+
let depth = 1;
|
|
421
|
+
while (cursor < length) {
|
|
422
|
+
if (options.nestedBlockComments && text[cursor] === "/" && text[cursor + 1] === "*") {
|
|
423
|
+
depth += 1;
|
|
424
|
+
cursor += 2;
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
if (text[cursor] === "*" && text[cursor + 1] === "/") {
|
|
428
|
+
depth -= 1;
|
|
429
|
+
cursor += 2;
|
|
430
|
+
if (depth === 0) {
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
cursor += 1;
|
|
436
|
+
}
|
|
437
|
+
blank(index, cursor);
|
|
438
|
+
index = cursor;
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
if (character === "'" || character === '"' || character === "`") {
|
|
442
|
+
let cursor = index + 1;
|
|
443
|
+
while (cursor < length) {
|
|
444
|
+
const current = text[cursor];
|
|
445
|
+
if (options.backslashEscapes && current === "\\") {
|
|
446
|
+
cursor += 2;
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
if (current === character) {
|
|
450
|
+
if (text[cursor + 1] === character) {
|
|
451
|
+
cursor += 2;
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
cursor += 1;
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
cursor += 1;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* A single-quoted value is a string literal and disappears
|
|
461
|
+
* completely. A double-quoted or backtick-quoted value is an
|
|
462
|
+
* identifier: its body still disappears (so a comma or semicolon
|
|
463
|
+
* inside a quoted name is never syntax), but the opening delimiter
|
|
464
|
+
* stays visible so a table-body item that starts with a quoted
|
|
465
|
+
* column name keeps its real start offset.
|
|
466
|
+
*/
|
|
467
|
+
blank(index + 1, cursor);
|
|
468
|
+
masked[index] = character === "'" ? " " : character;
|
|
469
|
+
index = cursor;
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (options.bracketQuotes && character === "[") {
|
|
473
|
+
let cursor = index + 1;
|
|
474
|
+
while (cursor < length) {
|
|
475
|
+
if (text[cursor] === "]") {
|
|
476
|
+
if (text[cursor + 1] === "]") {
|
|
477
|
+
cursor += 2;
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
cursor += 1;
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
cursor += 1;
|
|
484
|
+
}
|
|
485
|
+
blank(index + 1, cursor);
|
|
486
|
+
masked[index] = "[";
|
|
487
|
+
index = cursor;
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
if (options.dollarQuotes && character === "$") {
|
|
491
|
+
const opener = /^\$[A-Za-z0-9_]*\$/.exec(text.slice(index, index + 64));
|
|
492
|
+
if (opener !== null) {
|
|
493
|
+
const closing = text.indexOf(opener[0], index + opener[0].length);
|
|
494
|
+
const stop = closing === -1 ? length : closing + opener[0].length;
|
|
495
|
+
blank(index, stop);
|
|
496
|
+
index = stop;
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
masked[index] = character;
|
|
501
|
+
index += 1;
|
|
502
|
+
}
|
|
503
|
+
for (let cursor = 0; cursor < length; cursor += 1) {
|
|
504
|
+
if (masked[cursor] === (void 0)) {
|
|
505
|
+
masked[cursor] = " ";
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return masked.join("");
|
|
509
|
+
};
|
|
510
|
+
/**
|
|
511
|
+
* Client directive line: `DELIMITER $$`, `DELIMITER ;`.
|
|
512
|
+
*/
|
|
513
|
+
const SqlDelimiterLine = /^\s*DELIMITER\s+(\S+)\s*$/i;
|
|
514
|
+
/**
|
|
515
|
+
* Collect `DELIMITER` directives by 0-based line number.
|
|
516
|
+
*
|
|
517
|
+
* @param lines - Source lines without terminators.
|
|
518
|
+
* @returns Directives in source order.
|
|
519
|
+
*/
|
|
520
|
+
const findSqlDelimiterDirectives = (lines) => {
|
|
521
|
+
const directives = [];
|
|
522
|
+
for (let line = 0; line < lines.length; line += 1) {
|
|
523
|
+
const match = SqlDelimiterLine.exec(lines[line]);
|
|
524
|
+
if (match !== null) {
|
|
525
|
+
directives.push({ line, token: match[1] });
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return directives;
|
|
529
|
+
};
|
|
530
|
+
/**
|
|
531
|
+
* Blank `DELIMITER` directive lines in the masked text. The directive is client
|
|
532
|
+
* syntax rather than a SQL statement, so it must not become statement content;
|
|
533
|
+
* blanking preserves offsets and line breaks.
|
|
534
|
+
*
|
|
535
|
+
* @param masked - Masked text.
|
|
536
|
+
* @param text - Raw text, used to preserve line breaks.
|
|
537
|
+
* @param starts - Line start offsets.
|
|
538
|
+
* @param directives - Directives to blank.
|
|
539
|
+
* @returns Masked text with directive lines removed.
|
|
540
|
+
*/
|
|
541
|
+
const blankSqlDirectiveLines = (masked, text, starts, directives) => {
|
|
542
|
+
if (directives.length === 0) {
|
|
543
|
+
return masked;
|
|
544
|
+
}
|
|
545
|
+
const characters = masked.split("");
|
|
546
|
+
for (let index = 0; index < directives.length; index += 1) {
|
|
547
|
+
const line = directives[index].line;
|
|
548
|
+
const from = starts[line] ?? 0;
|
|
549
|
+
const to = line + 1 < starts.length ? starts[line + 1] - 1 : text.length;
|
|
550
|
+
for (let cursor = from; cursor < to; cursor += 1) {
|
|
551
|
+
characters[cursor] = text[cursor] === "\n" ? "\n" : " ";
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return characters.join("");
|
|
555
|
+
};
|
|
556
|
+
/**
|
|
557
|
+
* SQL Server batch separator, optionally with a repeat count (`GO 5`).
|
|
558
|
+
*/
|
|
559
|
+
const SqlBatchLine = /^[ \t]*GO(?:[ \t]+\d+)?[ \t]*$/i;
|
|
560
|
+
/**
|
|
561
|
+
* Oracle SQL*Plus block terminator: a line containing only `/`.
|
|
562
|
+
*/
|
|
563
|
+
const SqlSlashLine = /^[ \t]*\/[ \t]*$/;
|
|
564
|
+
/**
|
|
565
|
+
* Build the line-keyed boundary table.
|
|
566
|
+
*
|
|
567
|
+
* `GO` is accepted in every dialect because a standalone `GO` line is not valid
|
|
568
|
+
* SQL content anywhere; the Oracle `/` terminator is only accepted for Oracle
|
|
569
|
+
* so a division line is never mistaken for structure.
|
|
570
|
+
*
|
|
571
|
+
* @param starts - Line start offsets.
|
|
572
|
+
* @param maskedLines - Masked lines.
|
|
573
|
+
* @param directives - `DELIMITER` directives.
|
|
574
|
+
* @param dialect - Detected dialect.
|
|
575
|
+
* @returns Boundary per line-start offset.
|
|
576
|
+
*/
|
|
577
|
+
const sqlBoundaries = (starts, maskedLines, directives, dialect) => {
|
|
578
|
+
const boundaries = new Map();
|
|
579
|
+
const directiveLines = new Set();
|
|
580
|
+
for (let index = 0; index < directives.length; index += 1) {
|
|
581
|
+
directiveLines.add(directives[index].line);
|
|
582
|
+
boundaries.set(starts[directives[index].line] ?? 0, { kind: "delimiter", token: directives[index].token });
|
|
583
|
+
}
|
|
584
|
+
for (let line = 0; line < maskedLines.length; line += 1) {
|
|
585
|
+
if (directiveLines.has(line)) {
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
if (SqlBatchLine.test(maskedLines[line])) {
|
|
589
|
+
boundaries.set(starts[line] ?? 0, { kind: "batch" });
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
if (dialect === "oracle" && SqlSlashLine.test(maskedLines[line])) {
|
|
593
|
+
boundaries.set(starts[line] ?? 0, { kind: "slash" });
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return boundaries;
|
|
597
|
+
};
|
|
598
|
+
/**
|
|
599
|
+
* Split masked SQL into statements.
|
|
600
|
+
*
|
|
601
|
+
* The scanner tracks parenthesis depth so a `;` inside a function argument list
|
|
602
|
+
* is not a terminator, follows `DELIMITER` tokens, and treats `GO`/`/` lines as
|
|
603
|
+
* hard boundaries. It is not a grammar: a semicolon inside a MySQL routine body
|
|
604
|
+
* still terminates the statement unless the script declares `DELIMITER`, which
|
|
605
|
+
* is exactly the rule the MySQL client itself applies.
|
|
606
|
+
*
|
|
607
|
+
* @param masked - Masked SQL text.
|
|
608
|
+
* @param starts - Line start offsets.
|
|
609
|
+
* @param boundaries - Line-keyed hard boundaries.
|
|
610
|
+
* @returns Trimmed statements in source order.
|
|
611
|
+
*/
|
|
612
|
+
const splitSqlStatements = (masked, starts, boundaries) => {
|
|
613
|
+
const segments = [];
|
|
614
|
+
let delimiter = ";";
|
|
615
|
+
let statementStart = 0;
|
|
616
|
+
let parenDepth = 0;
|
|
617
|
+
const close = (end, hardEnd) => {
|
|
618
|
+
let from = statementStart;
|
|
619
|
+
let to = Math.min(end, masked.length);
|
|
620
|
+
while (from < to && /\s/.test(masked[from])) {
|
|
621
|
+
from += 1;
|
|
622
|
+
}
|
|
623
|
+
while (to > from && /\s/.test(masked[to - 1])) {
|
|
624
|
+
to -= 1;
|
|
625
|
+
}
|
|
626
|
+
if (to > from) {
|
|
627
|
+
segments.push({ start: from, end: to, hardEnd });
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
for (let line = 0; line < starts.length; line += 1) {
|
|
631
|
+
const lineStart = starts[line];
|
|
632
|
+
const lineEnd = line + 1 < starts.length ? starts[line + 1] - 1 : masked.length;
|
|
633
|
+
const boundary = boundaries.get(lineStart);
|
|
634
|
+
if (boundary !== (void 0)) {
|
|
635
|
+
close(lineStart, true);
|
|
636
|
+
if (boundary.kind === "delimiter") {
|
|
637
|
+
delimiter = boundary.token ?? ";";
|
|
638
|
+
}
|
|
639
|
+
statementStart = Math.min(masked.length, lineEnd + 1);
|
|
640
|
+
parenDepth = 0;
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
let index = lineStart;
|
|
644
|
+
while (index < lineEnd) {
|
|
645
|
+
const character = masked[index];
|
|
646
|
+
if (character === "(") {
|
|
647
|
+
parenDepth += 1;
|
|
648
|
+
index += 1;
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
if (character === ")") {
|
|
652
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
653
|
+
index += 1;
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
if (parenDepth === 0 && masked.startsWith(delimiter, index) && index + delimiter.length <= masked.length) {
|
|
657
|
+
close(index, false);
|
|
658
|
+
index += delimiter.length;
|
|
659
|
+
statementStart = index;
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
index += 1;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
close(masked.length, true);
|
|
666
|
+
return segments;
|
|
667
|
+
};
|
|
668
|
+
/**
|
|
669
|
+
* Statements that own an executable body and therefore may need to swallow
|
|
670
|
+
* soft-terminated fragments: PL/SQL blocks, T-SQL procedures and triggers.
|
|
671
|
+
*/
|
|
672
|
+
const RoutineStatement = /^\s*CREATE\s+(?:(?:OR\s+(?:REPLACE|ALTER))\s+)?(?:DEFINER\s*=\s*\S+\s+)?(?:(?:TEMP|TEMPORARY|UNLOGGED|MATERIALIZED|UNIQUE|NONCLUSTERED|CLUSTERED)\s+)*(?:FUNCTION|PROCEDURE|TRIGGER|PACKAGE)\b/i;
|
|
673
|
+
/**
|
|
674
|
+
* Tail of a closed routine body: `END`, `END;` or `END name;`.
|
|
675
|
+
*/
|
|
676
|
+
const RoutineEndTail = /\bEND(?:\s+([\p{L}_][\p{L}\p{N}_$]*))?\s*;?\s*$/iu;
|
|
677
|
+
/**
|
|
678
|
+
* Words after `END` that belong to a nested block (`END IF`, `END LOOP`, ...)
|
|
679
|
+
* rather than to the routine body.
|
|
680
|
+
*/
|
|
681
|
+
const RoutineBlockTailWords = new Set(["if", "loop", "case", "while", "repeat", "for"]);
|
|
682
|
+
/**
|
|
683
|
+
* Tokens used to track whether a routine body's block structure is closed.
|
|
684
|
+
* Only words and parentheses matter; punctuation is ignored.
|
|
685
|
+
*/
|
|
686
|
+
const SqlRoutineTokens = /[A-Za-z_][A-Za-z0-9_$]*|[()]/g;
|
|
687
|
+
/**
|
|
688
|
+
* Words that turn a `BEGIN` into a transaction statement instead of a block.
|
|
689
|
+
*/
|
|
690
|
+
const RoutineTransactionWords = new Set(["TRANSACTION", "TRAN", "DISTRIBUTED", "WORK"]);
|
|
691
|
+
/**
|
|
692
|
+
* Words that close a construct other than the routine body itself. They follow
|
|
693
|
+
* `END` and were already consumed by the matching opener.
|
|
694
|
+
*/
|
|
695
|
+
const RoutineEndLabels = new Set(["IF", "LOOP", "CASE", "WHILE", "REPEAT", "FOR"]);
|
|
696
|
+
/**
|
|
697
|
+
* Whether the accumulated routine text closes on a real `END`.
|
|
698
|
+
*
|
|
699
|
+
* @param masked - Accumulated masked routine statement.
|
|
700
|
+
* @returns `true` when the last token is `END` (not `END IF`, `END LOOP`, ...).
|
|
701
|
+
*/
|
|
702
|
+
const endsWithRoutineEnd = (masked) => {
|
|
703
|
+
const match = RoutineEndTail.exec(masked);
|
|
704
|
+
if (match === null) {
|
|
705
|
+
return false;
|
|
706
|
+
}
|
|
707
|
+
return !RoutineBlockTailWords.has((match[1] ?? "").toLowerCase());
|
|
708
|
+
};
|
|
709
|
+
/**
|
|
710
|
+
* Whether a routine statement has reached its complete form.
|
|
711
|
+
*
|
|
712
|
+
* The scanner keeps a small block stack instead of counting `BEGIN` and `END`
|
|
713
|
+
* occurrences: a `CASE ... END` expression, an `IF ... END IF` statement and a
|
|
714
|
+
* `LOOP ... END LOOP` statement each close their own construct, so the routine
|
|
715
|
+
* only ends when the stack is empty and the tail is a real `END`. Counting
|
|
716
|
+
* alone would stop a procedure at the first `CASE ... END` inside a `SELECT`.
|
|
717
|
+
*
|
|
718
|
+
* Oracle routines additionally require the `BEGIN` because their declarations
|
|
719
|
+
* run from `AS`/`IS` to the first `BEGIN`; for every other dialect a routine
|
|
720
|
+
* without `BEGIN` (`CREATE PROCEDURE ... AS SELECT ...;`) is complete at its
|
|
721
|
+
* own terminator.
|
|
722
|
+
*
|
|
723
|
+
* @param masked - Accumulated masked statement.
|
|
724
|
+
* @param dialect - Detected dialect.
|
|
725
|
+
* @returns `true` when no further fragment belongs to the routine.
|
|
726
|
+
*/
|
|
727
|
+
const routineBodyComplete = (masked, dialect) => {
|
|
728
|
+
const tokens = [];
|
|
729
|
+
SqlRoutineTokens.lastIndex = 0;
|
|
730
|
+
let match = SqlRoutineTokens.exec(masked);
|
|
731
|
+
while (match !== null) {
|
|
732
|
+
tokens.push(match[0].toUpperCase());
|
|
733
|
+
match = SqlRoutineTokens.exec(masked);
|
|
734
|
+
}
|
|
735
|
+
const stack = [];
|
|
736
|
+
let opened = false;
|
|
737
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
738
|
+
const token = tokens[index];
|
|
739
|
+
const next = tokens[index + 1] ?? "";
|
|
740
|
+
if (token === "BEGIN") {
|
|
741
|
+
if (RoutineTransactionWords.has(next)) {
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
stack.push(token);
|
|
745
|
+
opened = true;
|
|
746
|
+
continue;
|
|
747
|
+
}
|
|
748
|
+
if (token === "CASE") {
|
|
749
|
+
stack.push(token);
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
if (token === "IF" && (dialect === "oracle" || dialect === "mysql")) {
|
|
753
|
+
if (next !== "NOT" && next !== "EXISTS" && next !== "(") {
|
|
754
|
+
stack.push(token);
|
|
755
|
+
}
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
if (token === "LOOP" && (dialect === "oracle" || dialect === "mysql")) {
|
|
759
|
+
stack.push(token);
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if (token === "WHILE" && dialect === "mysql") {
|
|
763
|
+
stack.push(token);
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
766
|
+
if (token === "REPEAT" && dialect === "mysql") {
|
|
767
|
+
stack.push(token);
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
if (token === "END") {
|
|
771
|
+
stack.pop();
|
|
772
|
+
if (RoutineEndLabels.has(next)) {
|
|
773
|
+
index += 1;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
if (!opened) {
|
|
778
|
+
return dialect !== "oracle";
|
|
779
|
+
}
|
|
780
|
+
return stack.length === 0 && endsWithRoutineEnd(masked);
|
|
781
|
+
};
|
|
782
|
+
/**
|
|
783
|
+
* Merge soft fragments that still belong to one routine body.
|
|
784
|
+
*
|
|
785
|
+
* The MySQL client needs `DELIMITER` and PostgreSQL has dollar quoting, but
|
|
786
|
+
* SQL Server, SQLite and Oracle scripts commonly write a `BEGIN ... END` body
|
|
787
|
+
* with plain semicolons. Without merging, such a routine node would end at its
|
|
788
|
+
* first inner statement. Merging stops at a hard boundary (`GO`, `/`, a
|
|
789
|
+
* `DELIMITER` line), at a completed body, or after 512 fragments.
|
|
790
|
+
*
|
|
791
|
+
* @param segments - Statements from `splitSqlStatements`.
|
|
792
|
+
* @param masked - Masked SQL text.
|
|
793
|
+
* @param dialect - Detected dialect.
|
|
794
|
+
* @returns Statements with routine bodies joined.
|
|
795
|
+
*/
|
|
796
|
+
const mergeRoutineStatements = (segments, masked, dialect) => {
|
|
797
|
+
const merged = [];
|
|
798
|
+
let index = 0;
|
|
799
|
+
while (index < segments.length) {
|
|
800
|
+
const segment = segments[index];
|
|
801
|
+
const head = masked.slice(segment.start, segment.end);
|
|
802
|
+
if (!RoutineStatement.test(head)) {
|
|
803
|
+
merged.push(segment);
|
|
804
|
+
index += 1;
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
let end = segment.end;
|
|
808
|
+
let hardEnd = segment.hardEnd;
|
|
809
|
+
let consumed = 0;
|
|
810
|
+
while (!routineBodyComplete(masked.slice(segment.start, end), dialect) && index + 1 < segments.length && consumed < 512) {
|
|
811
|
+
index += 1;
|
|
812
|
+
const next = segments[index];
|
|
813
|
+
end = next.end;
|
|
814
|
+
hardEnd = next.hardEnd;
|
|
815
|
+
consumed += 1;
|
|
816
|
+
if (next.hardEnd) {
|
|
817
|
+
break;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
merged.push({ start: segment.start, end, hardEnd });
|
|
821
|
+
index += 1;
|
|
822
|
+
}
|
|
823
|
+
return merged;
|
|
824
|
+
};
|
|
825
|
+
/**
|
|
826
|
+
* A bare identifier: Unicode letters/digits, `_`, `$`, plus the SQL Server
|
|
827
|
+
* `#`/`@` prefix so a temp table keeps its name.
|
|
828
|
+
*/
|
|
829
|
+
const SqlBareName = /^[#@]?[\p{L}_][\p{L}\p{N}_$]*/u;
|
|
830
|
+
/**
|
|
831
|
+
* A quoted identifier: `"x"`, `` `x` `` or `[x]`, with the doubled closing
|
|
832
|
+
* character as the escape.
|
|
833
|
+
*/
|
|
834
|
+
const SqlQuotedName = /^"(?:[^"]|"")*"|^`(?:[^`]|``)*`|^\[(?:[^\]]|\]\])*\]/u;
|
|
835
|
+
/**
|
|
836
|
+
* Window used when reading one identifier. SQL identifiers are short; the
|
|
837
|
+
* bound keeps a pathological file from allocating a huge slice per lookup.
|
|
838
|
+
*/
|
|
839
|
+
const SqlNameWindow = 512;
|
|
840
|
+
/**
|
|
841
|
+
* Read one identifier segment at an offset, skipping leading spaces.
|
|
842
|
+
*
|
|
843
|
+
* @param text - Raw SQL text.
|
|
844
|
+
* @param offset - Absolute offset.
|
|
845
|
+
* @returns Decoded name and end offset, or `undefined`.
|
|
846
|
+
*/
|
|
847
|
+
const readSqlName = (text, offset) => {
|
|
848
|
+
const cursor = skipSqlSpaces(text, offset);
|
|
849
|
+
const window = text.slice(cursor, cursor + SqlNameWindow);
|
|
850
|
+
const quoted = SqlQuotedName.exec(window);
|
|
851
|
+
if (quoted !== null) {
|
|
852
|
+
const raw = quoted[0];
|
|
853
|
+
const body = raw.slice(1, -1);
|
|
854
|
+
const first = raw[0];
|
|
855
|
+
const name = first === '"' ? body.replace(/""/g, '"') : first === "`" ? body.replace(/``/g, "`") : body.replace(/\]\]/g, "]");
|
|
856
|
+
return name.length === 0 ? (void 0) : { name, end: cursor + raw.length };
|
|
857
|
+
}
|
|
858
|
+
const bare = SqlBareName.exec(window);
|
|
859
|
+
return bare === null ? (void 0) : { name: bare[0], end: cursor + bare[0].length };
|
|
860
|
+
};
|
|
861
|
+
/**
|
|
862
|
+
* Read a dotted, possibly schema-qualified identifier.
|
|
863
|
+
*
|
|
864
|
+
* @param text - Raw SQL text.
|
|
865
|
+
* @param offset - Absolute offset of the first segment.
|
|
866
|
+
* @returns Decoded dotted name and end offset, or `undefined`.
|
|
867
|
+
*/
|
|
868
|
+
const readSqlQualifiedName = (text, offset) => {
|
|
869
|
+
let cursor = offset;
|
|
870
|
+
let name = "";
|
|
871
|
+
let end = offset;
|
|
872
|
+
let segments = 0;
|
|
873
|
+
while (segments < 8) {
|
|
874
|
+
const part = readSqlName(text, cursor);
|
|
875
|
+
if (part === (void 0)) {
|
|
876
|
+
break;
|
|
877
|
+
}
|
|
878
|
+
name = segments === 0 ? part.name : `${name}.${part.name}`;
|
|
879
|
+
end = part.end;
|
|
880
|
+
segments += 1;
|
|
881
|
+
const dot = skipSqlSpaces(text, end);
|
|
882
|
+
if (text[dot] !== ".") {
|
|
883
|
+
break;
|
|
884
|
+
}
|
|
885
|
+
cursor = skipSqlSpaces(text, dot + 1);
|
|
886
|
+
}
|
|
887
|
+
return segments === 0 ? (void 0) : { name, end };
|
|
888
|
+
};
|
|
889
|
+
/**
|
|
890
|
+
* Build one placeholder-range node.
|
|
891
|
+
*
|
|
892
|
+
* @param spec - Node description.
|
|
893
|
+
* @returns Outline node with zero byte range.
|
|
894
|
+
*/
|
|
895
|
+
const sqlNode = (spec) => {
|
|
896
|
+
const startLine = Math.max(1, Math.trunc(spec.startLine));
|
|
897
|
+
const endLine = Math.max(startLine, Math.trunc(spec.endLine));
|
|
898
|
+
return {
|
|
899
|
+
isFile: false,
|
|
900
|
+
isDirectory: false,
|
|
901
|
+
path: "",
|
|
902
|
+
type: spec.kind,
|
|
903
|
+
range: { startByte: 0n, endByte: 0n, startLine: BigInt(startLine), endLine: BigInt(endLine) },
|
|
904
|
+
label: spec.label,
|
|
905
|
+
depth: Math.max(0, Math.trunc(spec.depth)),
|
|
906
|
+
...(spec.modifiers === (void 0) || spec.modifiers.length === 0 ? {} : { modifiers: [...spec.modifiers] }),
|
|
907
|
+
...(spec.detail === (void 0) ? {} : { detail: spec.detail }),
|
|
908
|
+
children: spec.children ?? [],
|
|
909
|
+
};
|
|
910
|
+
};
|
|
911
|
+
/**
|
|
912
|
+
* Max characters kept in a column definition detail. The registry caps the
|
|
913
|
+
* final detail at 200 characters; the tighter bound keeps a generated
|
|
914
|
+
* `DEFAULT` expression from crowding out the type.
|
|
915
|
+
*/
|
|
916
|
+
const MaxSqlDefinitionLength = 120;
|
|
917
|
+
/**
|
|
918
|
+
* Collapse whitespace and cut one column definition.
|
|
919
|
+
*
|
|
920
|
+
* The value is taken from the masked text, so a trailing `-- comment` or the
|
|
921
|
+
* body of a `DEFAULT '...'` string cannot leak into the detail. Opening
|
|
922
|
+
* identifier delimiters survive masking (they keep an item's real start
|
|
923
|
+
* offset), so those are removed as well.
|
|
924
|
+
*
|
|
925
|
+
* @param value - Masked definition text.
|
|
926
|
+
* @returns Normalized definition.
|
|
927
|
+
*/
|
|
928
|
+
const normalizeSqlDefinition = (value) => value.replace(/["`\[]/g, " ").replace(/\s+/g, " ").trim().slice(0, MaxSqlDefinitionLength);
|
|
929
|
+
/**
|
|
930
|
+
* Split a parenthesized body or a comma-separated action list on top-level
|
|
931
|
+
* commas while keeping parentheses balanced.
|
|
932
|
+
*
|
|
933
|
+
* @param masked - Masked SQL text.
|
|
934
|
+
* @param from - Absolute start offset.
|
|
935
|
+
* @param to - Absolute end offset.
|
|
936
|
+
* @returns Trimmed items in source order.
|
|
937
|
+
*/
|
|
938
|
+
const splitSqlItems = (masked, from, to) => {
|
|
939
|
+
const items = [];
|
|
940
|
+
const append = (start, end) => {
|
|
941
|
+
let itemStart = start;
|
|
942
|
+
let itemEnd = end;
|
|
943
|
+
while (itemStart < itemEnd && /\s/.test(masked[itemStart])) {
|
|
944
|
+
itemStart += 1;
|
|
945
|
+
}
|
|
946
|
+
while (itemEnd > itemStart && /\s/.test(masked[itemEnd - 1])) {
|
|
947
|
+
itemEnd -= 1;
|
|
948
|
+
}
|
|
949
|
+
if (itemEnd > itemStart) {
|
|
950
|
+
items.push({ start: itemStart, end: itemEnd });
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
let start = from;
|
|
954
|
+
let depth = 0;
|
|
955
|
+
for (let index = from; index < to; index += 1) {
|
|
956
|
+
const character = masked[index];
|
|
957
|
+
if (character === "(") {
|
|
958
|
+
depth += 1;
|
|
959
|
+
continue;
|
|
960
|
+
}
|
|
961
|
+
if (character === ")") {
|
|
962
|
+
depth = Math.max(0, depth - 1);
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
965
|
+
if (character === "," && depth === 0) {
|
|
966
|
+
append(start, index);
|
|
967
|
+
start = index + 1;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
append(start, to);
|
|
971
|
+
return items;
|
|
972
|
+
};
|
|
973
|
+
/**
|
|
974
|
+
* Index of the `)` matching the `(` at `open`, or `-1` when it never closes.
|
|
975
|
+
*
|
|
976
|
+
* @param text - Masked text.
|
|
977
|
+
* @param open - Absolute offset of `(`.
|
|
978
|
+
* @param limit - Absolute offset that must not be crossed.
|
|
979
|
+
* @returns Absolute offset of the matching `)`.
|
|
980
|
+
*/
|
|
981
|
+
const findMatchingParen = (text, open, limit) => {
|
|
982
|
+
let depth = 0;
|
|
983
|
+
for (let index = open; index < limit; index += 1) {
|
|
984
|
+
const character = text[index];
|
|
985
|
+
if (character === "(") {
|
|
986
|
+
depth += 1;
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
if (character === ")") {
|
|
990
|
+
depth -= 1;
|
|
991
|
+
if (depth === 0) {
|
|
992
|
+
return index;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return -1;
|
|
997
|
+
};
|
|
998
|
+
/**
|
|
999
|
+
* Keywords that start a table-level constraint clause.
|
|
1000
|
+
*/
|
|
1001
|
+
const SqlConstraintKeywords = new Set([
|
|
1002
|
+
"PRIMARY",
|
|
1003
|
+
"FOREIGN",
|
|
1004
|
+
"UNIQUE",
|
|
1005
|
+
"CHECK",
|
|
1006
|
+
"KEY",
|
|
1007
|
+
"INDEX",
|
|
1008
|
+
"FULLTEXT",
|
|
1009
|
+
"SPATIAL",
|
|
1010
|
+
"EXCLUDE",
|
|
1011
|
+
]);
|
|
1012
|
+
/**
|
|
1013
|
+
* Words that can follow a `UNIQUE`/`KEY` clause without being an index name.
|
|
1014
|
+
* They guard `UNIQUE USING INDEX ...` and `UNIQUE NULLS NOT DISTINCT (...)`.
|
|
1015
|
+
*/
|
|
1016
|
+
const SqlConstraintNonNames = new Set([
|
|
1017
|
+
"USING",
|
|
1018
|
+
"INCLUDE",
|
|
1019
|
+
"WITH",
|
|
1020
|
+
"DEFERRABLE",
|
|
1021
|
+
"NULLS",
|
|
1022
|
+
"WHERE",
|
|
1023
|
+
"ON",
|
|
1024
|
+
]);
|
|
1025
|
+
/**
|
|
1026
|
+
* Recognize a table-level constraint item.
|
|
1027
|
+
*
|
|
1028
|
+
* The optional `CONSTRAINT name` prefix is resolved from raw text because a
|
|
1029
|
+
* quoted constraint name is masked to spaces in the structural scan. The clause
|
|
1030
|
+
* keyword itself is read from the masked text so a `CHECK (... 'PRIMARY KEY')`
|
|
1031
|
+
* string can never fake a constraint.
|
|
1032
|
+
*
|
|
1033
|
+
* @param context - Parsing context.
|
|
1034
|
+
* @param start - Absolute item start.
|
|
1035
|
+
* @param end - Absolute item end.
|
|
1036
|
+
* @returns Constraint head, or `undefined` when the item is a column.
|
|
1037
|
+
*/
|
|
1038
|
+
const readSqlConstraintHead = (context, start, end) => {
|
|
1039
|
+
const raw = context.source.text;
|
|
1040
|
+
const masked = context.masked;
|
|
1041
|
+
const lead = /^\s*CONSTRAINT\b/i.exec(masked.slice(start, end));
|
|
1042
|
+
let offset = start;
|
|
1043
|
+
let name = "";
|
|
1044
|
+
if (lead !== null) {
|
|
1045
|
+
const after = start + lead[0].length;
|
|
1046
|
+
const quotedStart = skipSqlSpaces(raw, after);
|
|
1047
|
+
const firstCharacter = raw[quotedStart] ?? "";
|
|
1048
|
+
if (firstCharacter === '"' || firstCharacter === "`" || firstCharacter === "[") {
|
|
1049
|
+
const quoted = readSqlQualifiedName(raw, quotedStart);
|
|
1050
|
+
if (quoted !== (void 0)) {
|
|
1051
|
+
name = quoted.name;
|
|
1052
|
+
offset = quoted.end;
|
|
1053
|
+
}
|
|
1054
|
+
else {
|
|
1055
|
+
offset = quotedStart;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
const token = readSqlQualifiedName(raw, quotedStart);
|
|
1060
|
+
if (token !== (void 0) && !SqlConstraintKeywords.has(token.name.toUpperCase())) {
|
|
1061
|
+
name = token.name;
|
|
1062
|
+
offset = token.end;
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1065
|
+
offset = quotedStart;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
const clause = /^\s*(PRIMARY\s+KEY|FOREIGN\s+KEY|UNIQUE|CHECK|KEY|INDEX|FULLTEXT|SPATIAL|EXCLUDE)\b/i.exec(masked.slice(offset, end));
|
|
1070
|
+
if (clause === null) {
|
|
1071
|
+
return (void 0);
|
|
1072
|
+
}
|
|
1073
|
+
const clauseText = clause[1].replace(/\s+/g, " ").toUpperCase();
|
|
1074
|
+
const clauseStart = offset + clause.index + (clause[0].length - clause[1].length);
|
|
1075
|
+
let indexName = "";
|
|
1076
|
+
if (name.length === 0 &&
|
|
1077
|
+
(clauseText === "UNIQUE" || clauseText === "KEY" || clauseText === "INDEX" || clauseText === "FULLTEXT" || clauseText === "SPATIAL")) {
|
|
1078
|
+
const keywordEnd = offset + clause.index + clause[0].length;
|
|
1079
|
+
const tail = /^\s*(?:KEY|INDEX)\b/i.exec(masked.slice(keywordEnd, end));
|
|
1080
|
+
const afterTail = keywordEnd + (tail === null ? 0 : tail[0].length);
|
|
1081
|
+
const rawStart = skipSqlSpaces(raw, afterTail);
|
|
1082
|
+
if (raw[rawStart] !== "(") {
|
|
1083
|
+
const candidate = readSqlName(raw, rawStart);
|
|
1084
|
+
if (candidate !== (void 0) && !SqlConstraintNonNames.has(candidate.name.toUpperCase())) {
|
|
1085
|
+
indexName = candidate.name;
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
return { clause: clauseText, clauseStart, name: name.length > 0 ? name : indexName };
|
|
1090
|
+
};
|
|
1091
|
+
/**
|
|
1092
|
+
* Read the target table of a `REFERENCES` clause.
|
|
1093
|
+
*
|
|
1094
|
+
* @param context - Parsing context.
|
|
1095
|
+
* @param from - Absolute offset to search from.
|
|
1096
|
+
* @param to - Absolute offset to stop at.
|
|
1097
|
+
* @returns Dotted target name, or an empty string.
|
|
1098
|
+
*/
|
|
1099
|
+
const readSqlReferencesTarget = (context, from, to) => {
|
|
1100
|
+
const references = /\bREFERENCES\b/i.exec(context.masked.slice(from, to));
|
|
1101
|
+
if (references === null) {
|
|
1102
|
+
return "";
|
|
1103
|
+
}
|
|
1104
|
+
const target = readSqlQualifiedName(context.source.text, from + references.index + references[0].length);
|
|
1105
|
+
return target === (void 0) ? "" : target.name;
|
|
1106
|
+
};
|
|
1107
|
+
/**
|
|
1108
|
+
* Read the table a trigger or index attaches to (`... ON <table>`).
|
|
1109
|
+
*
|
|
1110
|
+
* @param context - Parsing context.
|
|
1111
|
+
* @param from - Absolute offset to search from.
|
|
1112
|
+
* @param to - Absolute offset to stop at.
|
|
1113
|
+
* @returns Dotted target name, or an empty string.
|
|
1114
|
+
*/
|
|
1115
|
+
const readSqlOnTarget = (context, from, to) => {
|
|
1116
|
+
const on = /\bON\b/i.exec(context.masked.slice(from, to));
|
|
1117
|
+
if (on === null) {
|
|
1118
|
+
return "";
|
|
1119
|
+
}
|
|
1120
|
+
const target = readSqlQualifiedName(context.source.text, from + on.index + on[0].length);
|
|
1121
|
+
return target === (void 0) ? "" : target.name;
|
|
1122
|
+
};
|
|
1123
|
+
/**
|
|
1124
|
+
* Build one table-level constraint node.
|
|
1125
|
+
*
|
|
1126
|
+
* Kind mapping: `PRIMARY KEY`, `UNIQUE`, `CHECK`, `KEY`, `INDEX`, `FULLTEXT`,
|
|
1127
|
+
* `SPATIAL` and `EXCLUDE` become `key`; `FOREIGN KEY` becomes `dependency`
|
|
1128
|
+
* because it points at another table. A `REFERENCES` clause is only a
|
|
1129
|
+
* column-level `reference` node.
|
|
1130
|
+
*
|
|
1131
|
+
* @param context - Parsing context.
|
|
1132
|
+
* @param start - Absolute item start.
|
|
1133
|
+
* @param end - Absolute item end.
|
|
1134
|
+
* @param depth - Node depth.
|
|
1135
|
+
* @returns Constraint node, or `undefined` when the item is not a constraint.
|
|
1136
|
+
*/
|
|
1137
|
+
const sqlConstraintNode = (context, start, end, depth) => {
|
|
1138
|
+
const head = readSqlConstraintHead(context, start, end);
|
|
1139
|
+
if (head === (void 0)) {
|
|
1140
|
+
return (void 0);
|
|
1141
|
+
}
|
|
1142
|
+
const foreign = head.clause === "FOREIGN KEY";
|
|
1143
|
+
const target = foreign ? readSqlReferencesTarget(context, head.clauseStart, end) : "";
|
|
1144
|
+
const descriptor = foreign && target.length > 0 ? `foreign key -> ${target}` : head.clause.toLowerCase();
|
|
1145
|
+
const label = head.name.length > 0 ? head.name : descriptor;
|
|
1146
|
+
return sqlNode({
|
|
1147
|
+
label,
|
|
1148
|
+
kind: foreign ? "dependency" : "key",
|
|
1149
|
+
startLine: lineAtOffset(context.source.starts, start),
|
|
1150
|
+
endLine: lineAtOffset(context.source.starts, Math.max(start, end - 1)),
|
|
1151
|
+
depth,
|
|
1152
|
+
detail: `${context.dialect}:${descriptor}`,
|
|
1153
|
+
});
|
|
1154
|
+
};
|
|
1155
|
+
/**
|
|
1156
|
+
* Inline column constraints reported as children of a column. `REFERENCES` is
|
|
1157
|
+
* handled separately because it carries a target name.
|
|
1158
|
+
*/
|
|
1159
|
+
const SqlInlineConstraints = [
|
|
1160
|
+
{ pattern: /\bPRIMARY\s+KEY\b/i, label: "primary key" },
|
|
1161
|
+
{ pattern: /\bUNIQUE\b/i, label: "unique" },
|
|
1162
|
+
{ pattern: /\bCHECK\b/i, label: "check" },
|
|
1163
|
+
];
|
|
1164
|
+
/**
|
|
1165
|
+
* Build one column node.
|
|
1166
|
+
*
|
|
1167
|
+
* A column is `property`; its `detail` is the normalized type/definition text
|
|
1168
|
+
* prefixed with the dialect. Inline `PRIMARY KEY`, `UNIQUE`, `CHECK` and
|
|
1169
|
+
* `REFERENCES` clauses become depth-2 children (`key`/`reference`), so a
|
|
1170
|
+
* `REFERENCES other(id)` edge is visible without re-reading the file.
|
|
1171
|
+
*
|
|
1172
|
+
* @param context - Parsing context.
|
|
1173
|
+
* @param start - Absolute item start.
|
|
1174
|
+
* @param end - Absolute item end.
|
|
1175
|
+
* @param depth - Node depth.
|
|
1176
|
+
* @returns Column node, or `undefined` when no identifier can be read.
|
|
1177
|
+
*/
|
|
1178
|
+
const sqlColumnNode = (context, start, end, depth) => {
|
|
1179
|
+
const raw = context.source.text;
|
|
1180
|
+
const masked = context.masked;
|
|
1181
|
+
const starts = context.source.starts;
|
|
1182
|
+
const name = readSqlQualifiedName(raw, start);
|
|
1183
|
+
if (name === (void 0) || name.end > end) {
|
|
1184
|
+
return (void 0);
|
|
1185
|
+
}
|
|
1186
|
+
const definition = normalizeSqlDefinition(masked.slice(name.end, end));
|
|
1187
|
+
const inlineStart = name.end;
|
|
1188
|
+
const inlineMasked = masked.slice(inlineStart, end);
|
|
1189
|
+
const children = [];
|
|
1190
|
+
for (let index = 0; index < SqlInlineConstraints.length; index += 1) {
|
|
1191
|
+
const inline = SqlInlineConstraints[index];
|
|
1192
|
+
const match = inline.pattern.exec(inlineMasked);
|
|
1193
|
+
if (match === null) {
|
|
1194
|
+
continue;
|
|
1195
|
+
}
|
|
1196
|
+
const line = lineAtOffset(starts, inlineStart + match.index);
|
|
1197
|
+
children.push(sqlNode({
|
|
1198
|
+
label: inline.label,
|
|
1199
|
+
kind: "key",
|
|
1200
|
+
startLine: line,
|
|
1201
|
+
endLine: line,
|
|
1202
|
+
depth: depth + 1,
|
|
1203
|
+
detail: `${context.dialect}:${inline.label}`,
|
|
1204
|
+
}));
|
|
1205
|
+
}
|
|
1206
|
+
const references = /\bREFERENCES\b/i.exec(inlineMasked);
|
|
1207
|
+
if (references !== null) {
|
|
1208
|
+
const target = readSqlQualifiedName(raw, inlineStart + references.index + references[0].length);
|
|
1209
|
+
const line = lineAtOffset(starts, inlineStart + references.index);
|
|
1210
|
+
children.push(sqlNode({
|
|
1211
|
+
label: target === (void 0) ? "references" : `references ${target.name}`,
|
|
1212
|
+
kind: "reference",
|
|
1213
|
+
startLine: line,
|
|
1214
|
+
endLine: line,
|
|
1215
|
+
depth: depth + 1,
|
|
1216
|
+
detail: target === (void 0) ? `${context.dialect}:references` : `${context.dialect}:references -> ${target.name}`,
|
|
1217
|
+
}));
|
|
1218
|
+
}
|
|
1219
|
+
return sqlNode({
|
|
1220
|
+
label: name.name,
|
|
1221
|
+
kind: "property",
|
|
1222
|
+
startLine: lineAtOffset(starts, start),
|
|
1223
|
+
endLine: lineAtOffset(starts, Math.max(start, end - 1)),
|
|
1224
|
+
depth,
|
|
1225
|
+
detail: `${context.dialect}:${definition.length === 0 ? "column" : definition}`,
|
|
1226
|
+
children,
|
|
1227
|
+
});
|
|
1228
|
+
};
|
|
1229
|
+
/**
|
|
1230
|
+
* Build one table-body item: a constraint when it starts with a constraint
|
|
1231
|
+
* clause, otherwise a column.
|
|
1232
|
+
*
|
|
1233
|
+
* @param context - Parsing context.
|
|
1234
|
+
* @param start - Absolute item start.
|
|
1235
|
+
* @param end - Absolute item end.
|
|
1236
|
+
* @param depth - Node depth.
|
|
1237
|
+
* @returns Definition node, or `undefined` when the item is malformed.
|
|
1238
|
+
*/
|
|
1239
|
+
const sqlDefinitionNode = (context, start, end, depth) => {
|
|
1240
|
+
const constraint = sqlConstraintNode(context, start, end, depth);
|
|
1241
|
+
if (constraint !== (void 0)) {
|
|
1242
|
+
return constraint;
|
|
1243
|
+
}
|
|
1244
|
+
if (/^\s*(?:CONSTRAINT|LIKE|PERIOD)\b/i.test(context.masked.slice(start, end))) {
|
|
1245
|
+
return (void 0);
|
|
1246
|
+
}
|
|
1247
|
+
return sqlColumnNode(context, start, end, depth);
|
|
1248
|
+
};
|
|
1249
|
+
/**
|
|
1250
|
+
* `CREATE ...` head with its optional modifiers, MySQL definer/algorithm
|
|
1251
|
+
* clauses and the recognized object keyword.
|
|
1252
|
+
*/
|
|
1253
|
+
const SqlCreateObject = /^\s*CREATE\s+(?:(OR\s+(?:REPLACE|ALTER))\s+)?(?:ALGORITHM\s*=\s*\S+\s+)?(?:DEFINER\s*=\s*\S+\s+)?(?:SQL\s+SECURITY\s+\S+\s+)?((?:(?:TEMP|TEMPORARY|UNLOGGED|MATERIALIZED|UNIQUE|NONCLUSTERED|CLUSTERED)\s+)*)(TABLE|VIEW|INDEX|TRIGGER|FUNCTION|PROCEDURE|SCHEMA|DATABASE|SEQUENCE)\b/i;
|
|
1254
|
+
/**
|
|
1255
|
+
* `ALTER TABLE` with its optional `IF EXISTS`/`ONLY` qualifiers.
|
|
1256
|
+
*/
|
|
1257
|
+
const SqlAlterTable = /^\s*ALTER\s+TABLE\s+(?:(?:IF\s+EXISTS|ONLY)\s+)*/i;
|
|
1258
|
+
/**
|
|
1259
|
+
* `ADD` action of an `ALTER TABLE`.
|
|
1260
|
+
*/
|
|
1261
|
+
const SqlAddAction = /^\s*ADD\b/i;
|
|
1262
|
+
/**
|
|
1263
|
+
* `ADD COLUMN` suffix.
|
|
1264
|
+
*/
|
|
1265
|
+
const SqlAddColumn = /^\s*COLUMN\s+/i;
|
|
1266
|
+
/**
|
|
1267
|
+
* `ADD ... IF NOT EXISTS` suffix.
|
|
1268
|
+
*/
|
|
1269
|
+
const SqlAddIfNotExists = /^\s*IF\s+NOT\s+EXISTS\s+/i;
|
|
1270
|
+
/**
|
|
1271
|
+
* `ALTER COLUMN` / `MODIFY [COLUMN]` action.
|
|
1272
|
+
*/
|
|
1273
|
+
const SqlAlterColumnAction = /^\s*(?:ALTER|MODIFY)\s+(?:COLUMN\s+)?/i;
|
|
1274
|
+
/**
|
|
1275
|
+
* Collect the modifiers of one `CREATE` match in source order.
|
|
1276
|
+
*
|
|
1277
|
+
* @param create - Match of `SqlCreateObject`.
|
|
1278
|
+
* @returns Lower-case modifiers such as `or replace`, `materialized`, `unique`.
|
|
1279
|
+
*/
|
|
1280
|
+
const sqlCreateModifiers = (create) => {
|
|
1281
|
+
const modifiers = [];
|
|
1282
|
+
const orReplace = create[1];
|
|
1283
|
+
if (orReplace !== (void 0)) {
|
|
1284
|
+
modifiers.push(orReplace.replace(/\s+/g, " ").toLowerCase());
|
|
1285
|
+
}
|
|
1286
|
+
const declared = (create[2] ?? "").trim().toLowerCase();
|
|
1287
|
+
if (declared.length > 0) {
|
|
1288
|
+
const parts = declared.split(/\s+/);
|
|
1289
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
1290
|
+
if (parts[index].length > 0) {
|
|
1291
|
+
modifiers.push(parts[index]);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
return modifiers;
|
|
1296
|
+
};
|
|
1297
|
+
/**
|
|
1298
|
+
* Build one `ALTER TABLE` node. `ADD` actions become depth-1 children (a column
|
|
1299
|
+
* or a constraint); `ALTER COLUMN`/`MODIFY` produce a `property` child that
|
|
1300
|
+
* marks the column as touched.
|
|
1301
|
+
*
|
|
1302
|
+
* @param context - Parsing context.
|
|
1303
|
+
* @param segment - Statement span.
|
|
1304
|
+
* @param alter - Match of `SqlAlterTable`.
|
|
1305
|
+
* @returns `ALTER TABLE` node, or `undefined` when the table name is missing.
|
|
1306
|
+
*/
|
|
1307
|
+
const sqlAlterNode = (context, segment, alter) => {
|
|
1308
|
+
const name = readSqlQualifiedName(context.source.text, segment.start + alter[0].length);
|
|
1309
|
+
if (name === (void 0)) {
|
|
1310
|
+
return (void 0);
|
|
1311
|
+
}
|
|
1312
|
+
const children = [];
|
|
1313
|
+
const actions = splitSqlItems(context.masked, name.end, segment.end);
|
|
1314
|
+
for (let index = 0; index < actions.length; index += 1) {
|
|
1315
|
+
const action = actions[index];
|
|
1316
|
+
const actionMasked = context.masked.slice(action.start, action.end);
|
|
1317
|
+
const add = SqlAddAction.exec(actionMasked);
|
|
1318
|
+
if (add !== null) {
|
|
1319
|
+
let offset = action.start + add[0].length;
|
|
1320
|
+
const column = SqlAddColumn.exec(context.masked.slice(offset, action.end));
|
|
1321
|
+
if (column !== null) {
|
|
1322
|
+
offset += column[0].length;
|
|
1323
|
+
}
|
|
1324
|
+
const notExists = SqlAddIfNotExists.exec(context.masked.slice(offset, action.end));
|
|
1325
|
+
if (notExists !== null) {
|
|
1326
|
+
offset += notExists[0].length;
|
|
1327
|
+
}
|
|
1328
|
+
const child = sqlDefinitionNode(context, offset, action.end, 1);
|
|
1329
|
+
if (child !== (void 0)) {
|
|
1330
|
+
children.push(child);
|
|
1331
|
+
}
|
|
1332
|
+
continue;
|
|
1333
|
+
}
|
|
1334
|
+
const alterColumn = SqlAlterColumnAction.exec(actionMasked);
|
|
1335
|
+
if (alterColumn !== null) {
|
|
1336
|
+
const columnName = readSqlQualifiedName(context.source.text, action.start + alterColumn[0].length);
|
|
1337
|
+
if (columnName !== (void 0)) {
|
|
1338
|
+
const line = lineAtOffset(context.source.starts, action.start);
|
|
1339
|
+
children.push(sqlNode({
|
|
1340
|
+
label: columnName.name,
|
|
1341
|
+
kind: "property",
|
|
1342
|
+
startLine: line,
|
|
1343
|
+
endLine: line,
|
|
1344
|
+
depth: 1,
|
|
1345
|
+
detail: `${context.dialect}:alter column`,
|
|
1346
|
+
}));
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return sqlNode({
|
|
1351
|
+
label: name.name,
|
|
1352
|
+
kind: "class",
|
|
1353
|
+
startLine: lineAtOffset(context.source.starts, segment.start),
|
|
1354
|
+
endLine: lineAtOffset(context.source.starts, Math.max(segment.start, segment.end - 1)),
|
|
1355
|
+
depth: 0,
|
|
1356
|
+
detail: `${context.dialect}:alter table`,
|
|
1357
|
+
children,
|
|
1358
|
+
});
|
|
1359
|
+
};
|
|
1360
|
+
/**
|
|
1361
|
+
* Build the node of one `CREATE ...` statement.
|
|
1362
|
+
*
|
|
1363
|
+
* Kind mapping is documented in `docs/testing/languages/sqlReport.md`:
|
|
1364
|
+
* table/alter-table `class`, view `type`, index `key`, trigger `method`,
|
|
1365
|
+
* function/procedure `function`, schema/database `namespace`, sequence
|
|
1366
|
+
* `object`, column `property`, constraint `key`/`dependency`, column-level
|
|
1367
|
+
* `REFERENCES` `reference`.
|
|
1368
|
+
*
|
|
1369
|
+
* @param context - Parsing context.
|
|
1370
|
+
* @param segment - Statement span.
|
|
1371
|
+
* @param create - Match of `SqlCreateObject`.
|
|
1372
|
+
* @returns The created node, or `undefined` when no name can be read.
|
|
1373
|
+
*/
|
|
1374
|
+
const sqlCreateNode = (context, segment, create) => {
|
|
1375
|
+
const source = context.source;
|
|
1376
|
+
const masked = context.masked;
|
|
1377
|
+
const dialect = context.dialect;
|
|
1378
|
+
const modifiers = sqlCreateModifiers(create);
|
|
1379
|
+
const object = (create[3] ?? "").toUpperCase();
|
|
1380
|
+
let nameOffset = segment.start + create[0].length;
|
|
1381
|
+
const notExists = /^\s*IF\s+NOT\s+EXISTS\s+/i.exec(masked.slice(nameOffset, segment.end));
|
|
1382
|
+
if (notExists !== null) {
|
|
1383
|
+
modifiers.push("if not exists");
|
|
1384
|
+
nameOffset += notExists[0].length;
|
|
1385
|
+
}
|
|
1386
|
+
const name = readSqlQualifiedName(source.text, nameOffset);
|
|
1387
|
+
if (name === (void 0)) {
|
|
1388
|
+
return (void 0);
|
|
1389
|
+
}
|
|
1390
|
+
const startLine = lineAtOffset(source.starts, segment.start);
|
|
1391
|
+
const endLine = lineAtOffset(source.starts, Math.max(segment.start, segment.end - 1));
|
|
1392
|
+
if (object === "TABLE") {
|
|
1393
|
+
const children = [];
|
|
1394
|
+
const bodyOpen = skipSqlSpaces(masked, name.end);
|
|
1395
|
+
if (masked[bodyOpen] === "(") {
|
|
1396
|
+
const bodyClose = findMatchingParen(masked, bodyOpen, segment.end);
|
|
1397
|
+
const bodyLimit = bodyClose === -1 ? segment.end : bodyClose;
|
|
1398
|
+
const items = splitSqlItems(masked, bodyOpen + 1, bodyLimit);
|
|
1399
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
1400
|
+
const child = sqlDefinitionNode(context, items[index].start, items[index].end, 1);
|
|
1401
|
+
if (child !== (void 0)) {
|
|
1402
|
+
children.push(child);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
if (/\bWITHOUT\s+ROWID\b/i.test(masked.slice(name.end, segment.end))) {
|
|
1407
|
+
modifiers.push("without rowid");
|
|
1408
|
+
}
|
|
1409
|
+
return sqlNode({ label: name.name, kind: "class", startLine, endLine, depth: 0, detail: `${dialect}:table`, modifiers, children });
|
|
1410
|
+
}
|
|
1411
|
+
if (object === "VIEW") {
|
|
1412
|
+
const materialized = modifiers.includes("materialized");
|
|
1413
|
+
return sqlNode({
|
|
1414
|
+
label: name.name,
|
|
1415
|
+
kind: "type",
|
|
1416
|
+
startLine,
|
|
1417
|
+
endLine,
|
|
1418
|
+
depth: 0,
|
|
1419
|
+
detail: `${dialect}:${materialized ? "materialized view" : "view"}`,
|
|
1420
|
+
modifiers,
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
if (object === "INDEX") {
|
|
1424
|
+
const table = readSqlOnTarget(context, name.end, segment.end);
|
|
1425
|
+
const descriptor = `${modifiers.includes("unique") ? "unique index" : "index"}${table.length === 0 ? "" : ` on ${table}`}`;
|
|
1426
|
+
return sqlNode({ label: name.name, kind: "key", startLine, endLine, depth: 0, detail: `${dialect}:${descriptor}`, modifiers });
|
|
1427
|
+
}
|
|
1428
|
+
if (object === "TRIGGER") {
|
|
1429
|
+
const table = readSqlOnTarget(context, name.end, segment.end);
|
|
1430
|
+
return sqlNode({
|
|
1431
|
+
label: name.name,
|
|
1432
|
+
kind: "method",
|
|
1433
|
+
startLine,
|
|
1434
|
+
endLine,
|
|
1435
|
+
depth: 0,
|
|
1436
|
+
detail: `${dialect}:trigger${table.length === 0 ? "" : ` on ${table}`}`,
|
|
1437
|
+
modifiers,
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
if (object === "FUNCTION" || object === "PROCEDURE") {
|
|
1441
|
+
return sqlNode({ label: name.name, kind: "function", startLine, endLine, depth: 0, detail: `${dialect}:${object.toLowerCase()}`, modifiers });
|
|
1442
|
+
}
|
|
1443
|
+
if (object === "SCHEMA" || object === "DATABASE") {
|
|
1444
|
+
return sqlNode({ label: name.name, kind: "namespace", startLine, endLine, depth: 0, detail: `${dialect}:${object.toLowerCase()}`, modifiers });
|
|
1445
|
+
}
|
|
1446
|
+
if (object === "SEQUENCE") {
|
|
1447
|
+
return sqlNode({ label: name.name, kind: "object", startLine, endLine, depth: 0, detail: `${dialect}:sequence`, modifiers });
|
|
1448
|
+
}
|
|
1449
|
+
return (void 0);
|
|
1450
|
+
};
|
|
1451
|
+
/**
|
|
1452
|
+
* Parse the statements of one SQL text.
|
|
1453
|
+
*
|
|
1454
|
+
* @param input - Decoded SQL text and its path.
|
|
1455
|
+
* @param dialect - Explicit dialect, or `undefined` to detect one.
|
|
1456
|
+
* @returns DDL outline tree in source order.
|
|
1457
|
+
*/
|
|
1458
|
+
export const parseSql = (input, dialect) => {
|
|
1459
|
+
const source = readSourceLines(input.text);
|
|
1460
|
+
if (source.totalLines === 0) {
|
|
1461
|
+
return [];
|
|
1462
|
+
}
|
|
1463
|
+
const chosen = dialect ?? detectSqlDialect(input.text);
|
|
1464
|
+
const directives = findSqlDelimiterDirectives(source.lines);
|
|
1465
|
+
const mysqlDialect = chosen === "mysql";
|
|
1466
|
+
const maskedBase = maskSql(input.text, {
|
|
1467
|
+
hashComments: mysqlDialect,
|
|
1468
|
+
backslashEscapes: mysqlDialect,
|
|
1469
|
+
nestedBlockComments: chosen === "postgresql",
|
|
1470
|
+
bracketQuotes: chosen === "sqlserver",
|
|
1471
|
+
dollarQuotes: chosen === "postgresql" && directives.length === 0,
|
|
1472
|
+
});
|
|
1473
|
+
const masked = blankSqlDirectiveLines(maskedBase, input.text, source.starts, directives);
|
|
1474
|
+
const maskedLines = splitLines(masked);
|
|
1475
|
+
const boundaries = sqlBoundaries(source.starts, maskedLines, directives, chosen);
|
|
1476
|
+
const segments = mergeRoutineStatements(splitSqlStatements(masked, source.starts, boundaries), masked, chosen);
|
|
1477
|
+
const context = { source, masked, dialect: chosen };
|
|
1478
|
+
const nodes = [];
|
|
1479
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
1480
|
+
const segment = segments[index];
|
|
1481
|
+
const head = masked.slice(segment.start, segment.end);
|
|
1482
|
+
const create = SqlCreateObject.exec(head);
|
|
1483
|
+
const node = create !== null ? sqlCreateNode(context, segment, create) : undefined;
|
|
1484
|
+
if (node !== (void 0)) {
|
|
1485
|
+
nodes.push(node);
|
|
1486
|
+
continue;
|
|
1487
|
+
}
|
|
1488
|
+
const alter = SqlAlterTable.exec(head);
|
|
1489
|
+
if (alter !== null) {
|
|
1490
|
+
const altered = sqlAlterNode(context, segment, alter);
|
|
1491
|
+
if (altered !== (void 0)) {
|
|
1492
|
+
nodes.push(altered);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
return [...nodes].sort((left, right) => Number(left.range.startLine) - Number(right.range.startLine));
|
|
1497
|
+
};
|
|
1498
|
+
/**
|
|
1499
|
+
* Lowercase extension of a file name, including the dot.
|
|
1500
|
+
*
|
|
1501
|
+
* @param name - Base name.
|
|
1502
|
+
* @returns Extension, or an empty string for a dot file or no extension.
|
|
1503
|
+
*/
|
|
1504
|
+
const extensionOfName = (name) => {
|
|
1505
|
+
const dot = name.lastIndexOf(".");
|
|
1506
|
+
return dot <= 0 || dot === name.length - 1 ? "" : name.slice(dot).toLowerCase();
|
|
1507
|
+
};
|
|
1508
|
+
/**
|
|
1509
|
+
* Whether a base name has one of the given extensions.
|
|
1510
|
+
*
|
|
1511
|
+
* @param name - Base name.
|
|
1512
|
+
* @param extensions - Lower-case extensions including the dot.
|
|
1513
|
+
* @returns `true` when the base name matches.
|
|
1514
|
+
*/
|
|
1515
|
+
const hasSqlExtension = (name, extensions) => extensions.includes(extensionOfName(name));
|
|
1516
|
+
/**
|
|
1517
|
+
* Extensions owned by the SQL strategy.
|
|
1518
|
+
*/
|
|
1519
|
+
const SqlExtensions = [".sql"];
|
|
1520
|
+
/**
|
|
1521
|
+
* The `.sql` strategy.
|
|
1522
|
+
*
|
|
1523
|
+
* The id is the stable tag `sql` because `OutlineStrategy.id` is static and
|
|
1524
|
+
* the registry writes it to `OutlineResult.strategy` before any text is read.
|
|
1525
|
+
* The dialect is therefore reported on every node `detail` as
|
|
1526
|
+
* `<dialect>:<descriptor>`; a host that wants a per-dialect strategy id can
|
|
1527
|
+
* register four thin strategies whose `parse` calls `parseSql(input, dialect)`
|
|
1528
|
+
* (see the report's integration section).
|
|
1529
|
+
*/
|
|
1530
|
+
export const SqlStrategy = {
|
|
1531
|
+
id: "sql",
|
|
1532
|
+
category: "data",
|
|
1533
|
+
language: "sql",
|
|
1534
|
+
matches: (input) => hasSqlExtension(input.name, SqlExtensions),
|
|
1535
|
+
parse: (input) => parseSql(input),
|
|
1536
|
+
};
|