@hydranium/core 1.0.0-next.11 → 1.0.0-next.111
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/lib/documents/ast-document-manager.d.ts +75 -16
- package/lib/documents/ast-document-manager.d.ts.map +1 -1
- package/lib/documents/ast-document-manager.js +57 -19
- package/lib/documents/ast-document-manager.js.map +1 -1
- package/lib/documents/client-ids.d.ts +5 -19
- package/lib/documents/client-ids.d.ts.map +1 -1
- package/lib/documents/client-ids.js +5 -19
- package/lib/documents/client-ids.js.map +1 -1
- package/lib/documents/hydranium-text-documents.d.ts +124 -36
- package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
- package/lib/documents/hydranium-text-documents.js +191 -51
- package/lib/documents/hydranium-text-documents.js.map +1 -1
- package/lib/documents/language-client-text-shadow.d.ts +41 -9
- package/lib/documents/language-client-text-shadow.d.ts.map +1 -1
- package/lib/documents/language-client-text-shadow.js +56 -11
- package/lib/documents/language-client-text-shadow.js.map +1 -1
- package/lib/documents/self-save-registry.d.ts +37 -6
- package/lib/documents/self-save-registry.d.ts.map +1 -1
- package/lib/documents/self-save-registry.js +28 -15
- package/lib/documents/self-save-registry.js.map +1 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +6 -0
- package/lib/index.js.map +1 -1
- package/lib/langium/ast-extension/ast-node-builder.d.ts +32 -9
- package/lib/langium/ast-extension/ast-node-builder.d.ts.map +1 -1
- package/lib/langium/ast-extension/ast-node-builder.js +60 -22
- package/lib/langium/ast-extension/ast-node-builder.js.map +1 -1
- package/lib/langium/bootstrap.d.ts.map +1 -1
- package/lib/langium/bootstrap.js +16 -9
- package/lib/langium/bootstrap.js.map +1 -1
- package/lib/langium/config/configuration-provider.d.ts +81 -0
- package/lib/langium/config/configuration-provider.d.ts.map +1 -0
- package/lib/langium/config/configuration-provider.js +112 -0
- package/lib/langium/config/configuration-provider.js.map +1 -0
- package/lib/langium/config/index.d.ts +1 -0
- package/lib/langium/config/index.d.ts.map +1 -1
- package/lib/langium/config/index.js +1 -0
- package/lib/langium/config/index.js.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.d.ts +17 -0
- package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.js +40 -6
- package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
- package/lib/langium/document-builder/build-pipeline-integration.d.ts +21 -1
- package/lib/langium/document-builder/build-pipeline-integration.d.ts.map +1 -1
- package/lib/langium/document-builder/build-pipeline-integration.js +32 -5
- package/lib/langium/document-builder/build-pipeline-integration.js.map +1 -1
- package/lib/langium/document-builder/build-session.d.ts +93 -0
- package/lib/langium/document-builder/build-session.d.ts.map +1 -0
- package/lib/langium/document-builder/build-session.js +72 -0
- package/lib/langium/document-builder/build-session.js.map +1 -0
- package/lib/langium/document-builder/document-builder.d.ts +150 -7
- package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
- package/lib/langium/document-builder/document-builder.js +298 -11
- package/lib/langium/document-builder/document-builder.js.map +1 -1
- package/lib/langium/document-builder/index.d.ts +1 -0
- package/lib/langium/document-builder/index.d.ts.map +1 -1
- package/lib/langium/document-builder/index.js +1 -0
- package/lib/langium/document-builder/index.js.map +1 -1
- package/lib/langium/integration-services.d.ts +45 -1
- package/lib/langium/integration-services.d.ts.map +1 -1
- package/lib/langium/integration-services.js +10 -5
- package/lib/langium/integration-services.js.map +1 -1
- package/lib/langium/integrity/integrity-service.d.ts +16 -0
- package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
- package/lib/langium/integrity/integrity-service.js +110 -2
- package/lib/langium/integrity/integrity-service.js.map +1 -1
- package/lib/langium/keys/containment.d.ts +64 -0
- package/lib/langium/keys/containment.d.ts.map +1 -0
- package/lib/langium/keys/containment.js +53 -0
- package/lib/langium/keys/containment.js.map +1 -0
- package/lib/langium/keys/index.d.ts +1 -0
- package/lib/langium/keys/index.d.ts.map +1 -1
- package/lib/langium/keys/index.js +1 -0
- package/lib/langium/keys/index.js.map +1 -1
- package/lib/langium/keys/name-based-key-provider.d.ts +4 -0
- package/lib/langium/keys/name-based-key-provider.d.ts.map +1 -1
- package/lib/langium/keys/name-based-key-provider.js +4 -0
- package/lib/langium/keys/name-based-key-provider.js.map +1 -1
- package/lib/langium/language-module.d.ts +25 -0
- package/lib/langium/language-module.d.ts.map +1 -1
- package/lib/langium/language-module.js +14 -0
- package/lib/langium/language-module.js.map +1 -1
- package/lib/langium/model-service/model-service.d.ts +161 -10
- package/lib/langium/model-service/model-service.d.ts.map +1 -1
- package/lib/langium/model-service/model-service.js +174 -95
- package/lib/langium/model-service/model-service.js.map +1 -1
- package/lib/langium/module.d.ts +84 -15
- package/lib/langium/module.d.ts.map +1 -1
- package/lib/langium/module.js +26 -12
- package/lib/langium/module.js.map +1 -1
- package/lib/langium/naming/name-provider.d.ts +25 -4
- package/lib/langium/naming/name-provider.d.ts.map +1 -1
- package/lib/langium/naming/name-provider.js +2 -1
- package/lib/langium/naming/name-provider.js.map +1 -1
- package/lib/langium/naming/name-separator-validation.d.ts +23 -0
- package/lib/langium/naming/name-separator-validation.d.ts.map +1 -1
- package/lib/langium/naming/name-separator-validation.js +30 -1
- package/lib/langium/naming/name-separator-validation.js.map +1 -1
- package/lib/langium/residency/cst-residency-service.d.ts +31 -11
- package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
- package/lib/langium/residency/cst-residency-service.js +13 -56
- package/lib/langium/residency/cst-residency-service.js.map +1 -1
- package/lib/langium/scope/hydranium-scope-provider.d.ts +48 -13
- package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
- package/lib/langium/scope/hydranium-scope-provider.js +56 -20
- package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
- package/lib/langium/serialization/abstract-serializer.d.ts +11 -20
- package/lib/langium/serialization/abstract-serializer.d.ts.map +1 -1
- package/lib/langium/serialization/abstract-serializer.js +12 -21
- package/lib/langium/serialization/abstract-serializer.js.map +1 -1
- package/lib/langium/service-registry.d.ts +15 -1
- package/lib/langium/service-registry.d.ts.map +1 -1
- package/lib/langium/service-registry.js +16 -0
- package/lib/langium/service-registry.js.map +1 -1
- package/lib/langium/shared-services.d.ts +15 -2
- package/lib/langium/shared-services.d.ts.map +1 -1
- package/lib/langium/shared-services.js.map +1 -1
- package/lib/langium/transfer/transfer-encoder.d.ts +60 -17
- package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
- package/lib/langium/transfer/transfer-encoder.js +38 -16
- package/lib/langium/transfer/transfer-encoder.js.map +1 -1
- package/lib/langium/trivia/comment-preserver.d.ts +423 -0
- package/lib/langium/trivia/comment-preserver.d.ts.map +1 -0
- package/lib/langium/trivia/comment-preserver.js +906 -0
- package/lib/langium/trivia/comment-preserver.js.map +1 -0
- package/lib/langium/trivia/document-ending-preserver.d.ts +43 -0
- package/lib/langium/trivia/document-ending-preserver.d.ts.map +1 -0
- package/lib/langium/trivia/document-ending-preserver.js +48 -0
- package/lib/langium/trivia/document-ending-preserver.js.map +1 -0
- package/lib/langium/trivia/index.d.ts +14 -0
- package/lib/langium/trivia/index.d.ts.map +1 -0
- package/lib/langium/trivia/index.js +14 -0
- package/lib/langium/trivia/index.js.map +1 -0
- package/lib/langium/trivia/trivia-contribution.d.ts +37 -0
- package/lib/langium/trivia/trivia-contribution.d.ts.map +1 -0
- package/lib/langium/trivia/trivia-contribution.js +10 -0
- package/lib/langium/trivia/trivia-contribution.js.map +1 -0
- package/lib/langium/trivia/trivia-preserver.d.ts +50 -0
- package/lib/langium/trivia/trivia-preserver.d.ts.map +1 -0
- package/lib/langium/trivia/trivia-preserver.js +10 -0
- package/lib/langium/trivia/trivia-preserver.js.map +1 -0
- package/lib/langium/trivia/trivia-service.d.ts +70 -0
- package/lib/langium/trivia/trivia-service.d.ts.map +1 -0
- package/lib/langium/trivia/trivia-service.js +56 -0
- package/lib/langium/trivia/trivia-service.js.map +1 -0
- package/lib/langium/validation/document-validator.d.ts +294 -10
- package/lib/langium/validation/document-validator.d.ts.map +1 -1
- package/lib/langium/validation/document-validator.js +448 -5
- package/lib/langium/validation/document-validator.js.map +1 -1
- package/lib/langium/workspace/document-uri-policy.d.ts +3 -4
- package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
- package/lib/langium/workspace/document-uri-policy.js +3 -4
- package/lib/langium/workspace/document-uri-policy.js.map +1 -1
- package/lib/langium/workspace/file-system-provider.d.ts +9 -1
- package/lib/langium/workspace/file-system-provider.d.ts.map +1 -1
- package/lib/langium/workspace/file-system-provider.js +40 -7
- package/lib/langium/workspace/file-system-provider.js.map +1 -1
- package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +25 -0
- package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-langium-document-factory.js +25 -0
- package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts +21 -1
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.js +28 -0
- package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
- package/lib/langium/workspace/in-memory-file-system-provider.d.ts +23 -0
- package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
- package/lib/langium/workspace/in-memory-file-system-provider.js +47 -9
- package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
- package/lib/langium/workspace/initialize-workspace.d.ts +23 -2
- package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
- package/lib/langium/workspace/initialize-workspace.js +13 -5
- package/lib/langium/workspace/initialize-workspace.js.map +1 -1
- package/lib/langium/workspace/langium-documents.d.ts +79 -17
- package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
- package/lib/langium/workspace/langium-documents.js +83 -28
- package/lib/langium/workspace/langium-documents.js.map +1 -1
- package/lib/langium/workspace/virtual-document.d.ts +18 -3
- package/lib/langium/workspace/virtual-document.d.ts.map +1 -1
- package/lib/langium/workspace/virtual-document.js +19 -2
- package/lib/langium/workspace/virtual-document.js.map +1 -1
- package/lib/locale/index.d.ts +10 -0
- package/lib/locale/index.d.ts.map +1 -0
- package/lib/locale/index.js +10 -0
- package/lib/locale/index.js.map +1 -0
- package/lib/locale/server-locale.d.ts +73 -0
- package/lib/locale/server-locale.d.ts.map +1 -0
- package/lib/locale/server-locale.js +61 -0
- package/lib/locale/server-locale.js.map +1 -0
- package/lib/lsp/completion/hydranium-completion-provider.d.ts +44 -2
- package/lib/lsp/completion/hydranium-completion-provider.d.ts.map +1 -1
- package/lib/lsp/completion/hydranium-completion-provider.js +68 -0
- package/lib/lsp/completion/hydranium-completion-provider.js.map +1 -1
- package/lib/lsp/connection-features.d.ts +23 -0
- package/lib/lsp/connection-features.d.ts.map +1 -0
- package/lib/lsp/connection-features.js +60 -0
- package/lib/lsp/connection-features.js.map +1 -0
- package/lib/lsp/diagnostics-connection.d.ts +27 -0
- package/lib/lsp/diagnostics-connection.d.ts.map +1 -0
- package/lib/lsp/diagnostics-connection.js +53 -0
- package/lib/lsp/diagnostics-connection.js.map +1 -0
- package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
- package/lib/lsp/hydranium-document-update-handler.js +10 -1
- package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
- package/lib/lsp/index.d.ts +3 -1
- package/lib/lsp/index.d.ts.map +1 -1
- package/lib/lsp/index.js +3 -1
- package/lib/lsp/index.js.map +1 -1
- package/lib/lsp/lsp-latency.d.ts +39 -0
- package/lib/lsp/lsp-latency.d.ts.map +1 -0
- package/lib/lsp/lsp-latency.js +58 -0
- package/lib/lsp/lsp-latency.js.map +1 -0
- package/lib/lsp/semantic-token-provider.d.ts +34 -1
- package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
- package/lib/lsp/semantic-token-provider.js +55 -8
- package/lib/lsp/semantic-token-provider.js.map +1 -1
- package/lib/lsp/start-language-server.d.ts +5 -3
- package/lib/lsp/start-language-server.d.ts.map +1 -1
- package/lib/lsp/start-language-server.js +10 -3
- package/lib/lsp/start-language-server.js.map +1 -1
- package/lib/messages/carriers.d.ts +54 -0
- package/lib/messages/carriers.d.ts.map +1 -0
- package/lib/messages/carriers.js +62 -0
- package/lib/messages/carriers.js.map +1 -0
- package/lib/messages/index.d.ts +25 -0
- package/lib/messages/index.d.ts.map +1 -0
- package/lib/messages/index.js +25 -0
- package/lib/messages/index.js.map +1 -0
- package/lib/messages/renderer.d.ts +136 -0
- package/lib/messages/renderer.d.ts.map +1 -0
- package/lib/messages/renderer.js +159 -0
- package/lib/messages/renderer.js.map +1 -0
- package/lib/node/heap-ceiling.d.ts +61 -0
- package/lib/node/heap-ceiling.d.ts.map +1 -0
- package/lib/node/heap-ceiling.js +72 -0
- package/lib/node/heap-ceiling.js.map +1 -0
- package/lib/node/index.d.ts +1 -0
- package/lib/node/index.d.ts.map +1 -1
- package/lib/node/index.js +1 -0
- package/lib/node/index.js.map +1 -1
- package/lib/node/latency-from-env.d.ts +1 -1
- package/lib/node/latency-from-env.js +1 -1
- package/lib/node/node-file-system-provider.d.ts +35 -1
- package/lib/node/node-file-system-provider.d.ts.map +1 -1
- package/lib/node/node-file-system-provider.js +140 -45
- package/lib/node/node-file-system-provider.js.map +1 -1
- package/lib/node/profile-capture.d.ts +8 -2
- package/lib/node/profile-capture.d.ts.map +1 -1
- package/lib/node/profile-capture.js.map +1 -1
- package/lib/node/profiling-run.d.ts +2 -2
- package/lib/node/profiling-run.js +1 -1
- package/lib/node/profiling-run.js.map +1 -1
- package/lib/node/rename-over-open-readers.d.ts +52 -0
- package/lib/node/rename-over-open-readers.d.ts.map +1 -0
- package/lib/node/rename-over-open-readers.js +66 -0
- package/lib/node/rename-over-open-readers.js.map +1 -0
- package/lib/node/socket-launcher.d.ts +15 -0
- package/lib/node/socket-launcher.d.ts.map +1 -1
- package/lib/node/socket-launcher.js +3 -2
- package/lib/node/socket-launcher.js.map +1 -1
- package/lib/testing/fake-document.d.ts +16 -4
- package/lib/testing/fake-document.d.ts.map +1 -1
- package/lib/testing/fake-document.js +18 -3
- package/lib/testing/fake-document.js.map +1 -1
- package/lib/testing/make-noop-shared-services.d.ts +14 -0
- package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
- package/lib/testing/make-noop-shared-services.js +28 -2
- package/lib/testing/make-noop-shared-services.js.map +1 -1
- package/lib/testing/make-test-services.d.ts +45 -9
- package/lib/testing/make-test-services.d.ts.map +1 -1
- package/lib/testing/make-test-services.js +19 -3
- package/lib/testing/make-test-services.js.map +1 -1
- package/lib/testing/node/index.d.ts +1 -0
- package/lib/testing/node/index.d.ts.map +1 -1
- package/lib/testing/node/index.js +1 -0
- package/lib/testing/node/index.js.map +1 -1
- package/lib/testing/node/lsp-server-connection.d.ts.map +1 -1
- package/lib/testing/node/lsp-server-connection.js +5 -6
- package/lib/testing/node/lsp-server-connection.js.map +1 -1
- package/lib/testing/node/scratch-workspace.d.ts.map +1 -1
- package/lib/testing/node/scratch-workspace.js +9 -4
- package/lib/testing/node/scratch-workspace.js.map +1 -1
- package/lib/testing/node/spawned-server.d.ts +14 -0
- package/lib/testing/node/spawned-server.d.ts.map +1 -1
- package/lib/testing/node/spawned-server.js +33 -1
- package/lib/testing/node/spawned-server.js.map +1 -1
- package/lib/testing/node/unhandled-rejections.d.ts +20 -0
- package/lib/testing/node/unhandled-rejections.d.ts.map +1 -0
- package/lib/testing/node/unhandled-rejections.js +32 -0
- package/lib/testing/node/unhandled-rejections.js.map +1 -0
- package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
- package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
- package/lib/testing/playwright/flaky-network-proxy.js +164 -0
- package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
- package/lib/testing/playwright/index.d.ts +1 -0
- package/lib/testing/playwright/index.d.ts.map +1 -1
- package/lib/testing/playwright/index.js +1 -0
- package/lib/testing/playwright/index.js.map +1 -1
- package/lib/testing/playwright/server-log-capture.d.ts +14 -4
- package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-capture.js +21 -4
- package/lib/testing/playwright/server-log-capture.js.map +1 -1
- package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-rename-reporter.js +10 -1
- package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
- package/lib/testing/stub-ast-document-manager.d.ts +5 -5
- package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
- package/lib/testing/stub-ast-document-manager.js +4 -6
- package/lib/testing/stub-ast-document-manager.js.map +1 -1
- package/lib/testing/stub-document-builder.d.ts +8 -0
- package/lib/testing/stub-document-builder.d.ts.map +1 -1
- package/lib/testing/stub-document-builder.js +22 -3
- package/lib/testing/stub-document-builder.js.map +1 -1
- package/lib/testing/stub-langium-documents.d.ts +3 -2
- package/lib/testing/stub-langium-documents.d.ts.map +1 -1
- package/lib/testing/stub-langium-documents.js.map +1 -1
- package/lib/testing/stub-model-service.d.ts +5 -4
- package/lib/testing/stub-model-service.d.ts.map +1 -1
- package/lib/testing/stub-model-service.js +2 -2
- package/lib/testing/stub-model-service.js.map +1 -1
- package/lib/util/connection-liveness.d.ts +7 -6
- package/lib/util/connection-liveness.d.ts.map +1 -1
- package/lib/util/connection-liveness.js +35 -14
- package/lib/util/connection-liveness.js.map +1 -1
- package/package.json +17 -8
- package/src/documents/ast-document-manager.ts +129 -36
- package/src/documents/client-ids.ts +5 -22
- package/src/documents/hydranium-text-documents.ts +248 -75
- package/src/documents/language-client-text-shadow.ts +59 -11
- package/src/documents/self-save-registry.ts +52 -13
- package/src/index.ts +6 -0
- package/src/langium/ast-extension/ast-node-builder.ts +66 -22
- package/src/langium/bootstrap.ts +16 -9
- package/src/langium/config/configuration-provider.ts +119 -0
- package/src/langium/config/index.ts +1 -0
- package/src/langium/diagnostics/lsp-logger.ts +41 -6
- package/src/langium/document-builder/build-pipeline-integration.ts +50 -5
- package/src/langium/document-builder/build-session.ts +87 -0
- package/src/langium/document-builder/document-builder.ts +339 -11
- package/src/langium/document-builder/index.ts +1 -0
- package/src/langium/integration-services.ts +67 -6
- package/src/langium/integrity/integrity-service.ts +116 -2
- package/src/langium/keys/containment.ts +87 -0
- package/src/langium/keys/index.ts +1 -0
- package/src/langium/keys/name-based-key-provider.ts +4 -0
- package/src/langium/language-module.ts +38 -0
- package/src/langium/model-service/model-service.ts +264 -32
- package/src/langium/module.ts +106 -21
- package/src/langium/naming/name-provider.ts +27 -5
- package/src/langium/naming/name-separator-validation.ts +34 -5
- package/src/langium/residency/cst-residency-service.ts +36 -14
- package/src/langium/scope/hydranium-scope-provider.ts +60 -22
- package/src/langium/serialization/abstract-serializer.ts +13 -32
- package/src/langium/service-registry.ts +19 -2
- package/src/langium/shared-services.ts +19 -2
- package/src/langium/transfer/transfer-encoder.ts +85 -24
- package/src/langium/trivia/comment-preserver.ts +1100 -0
- package/src/langium/trivia/document-ending-preserver.ts +55 -0
- package/src/langium/trivia/index.ts +14 -0
- package/src/langium/trivia/trivia-contribution.ts +39 -0
- package/src/langium/trivia/trivia-preserver.ts +54 -0
- package/src/langium/trivia/trivia-service.ts +99 -0
- package/src/langium/validation/document-validator.ts +537 -11
- package/src/langium/workspace/document-uri-policy.ts +3 -4
- package/src/langium/workspace/file-system-provider.ts +48 -8
- package/src/langium/workspace/hydranium-langium-document-factory.ts +34 -0
- package/src/langium/workspace/hydranium-workspace-manager.ts +30 -1
- package/src/langium/workspace/in-memory-file-system-provider.ts +53 -10
- package/src/langium/workspace/initialize-workspace.ts +35 -5
- package/src/langium/workspace/langium-documents.ts +106 -33
- package/src/langium/workspace/virtual-document.ts +21 -3
- package/src/locale/index.ts +10 -0
- package/src/locale/server-locale.ts +86 -0
- package/src/lsp/completion/hydranium-completion-provider.ts +82 -2
- package/src/lsp/connection-features.ts +67 -0
- package/src/lsp/diagnostics-connection.ts +56 -0
- package/src/lsp/hydranium-document-update-handler.ts +10 -1
- package/src/lsp/index.ts +3 -1
- package/src/lsp/lsp-latency.ts +62 -0
- package/src/lsp/semantic-token-provider.ts +74 -7
- package/src/lsp/start-language-server.ts +10 -3
- package/src/messages/carriers.ts +78 -0
- package/src/messages/index.ts +36 -0
- package/src/messages/renderer.ts +206 -0
- package/src/node/heap-ceiling.ts +108 -0
- package/src/node/index.ts +1 -0
- package/src/node/latency-from-env.ts +1 -1
- package/src/node/node-file-system-provider.ts +154 -47
- package/src/node/profile-capture.ts +8 -2
- package/src/node/profiling-run.ts +3 -3
- package/src/node/rename-over-open-readers.ts +78 -0
- package/src/node/socket-launcher.ts +18 -2
- package/src/testing/fake-document.ts +24 -5
- package/src/testing/make-noop-shared-services.ts +50 -2
- package/src/testing/make-test-services.ts +74 -18
- package/src/testing/node/index.ts +1 -0
- package/src/testing/node/lsp-server-connection.ts +5 -6
- package/src/testing/node/scratch-workspace.ts +9 -4
- package/src/testing/node/spawned-server.ts +35 -1
- package/src/testing/node/unhandled-rejections.ts +31 -0
- package/src/testing/playwright/flaky-network-proxy.ts +240 -0
- package/src/testing/playwright/index.ts +1 -0
- package/src/testing/playwright/server-log-capture.ts +24 -5
- package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
- package/src/testing/stub-ast-document-manager.ts +10 -14
- package/src/testing/stub-document-builder.ts +32 -3
- package/src/testing/stub-langium-documents.ts +3 -2
- package/src/testing/stub-model-service.ts +7 -6
- package/src/util/connection-liveness.ts +37 -14
- package/lib/lsp/instrument-connection.d.ts +0 -36
- package/lib/lsp/instrument-connection.d.ts.map +0 -1
- package/lib/lsp/instrument-connection.js +0 -62
- package/lib/lsp/instrument-connection.js.map +0 -1
- package/src/lsp/instrument-connection.ts +0 -66
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import * as fsp from 'node:fs/promises';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Errnos Windows raises when a replacing `rename` meets a handle already on the
|
|
14
|
+
* destination open. POSIX renames are unaffected by open handles, so these
|
|
15
|
+
* cannot fire there and the retry below costs nothing.
|
|
16
|
+
*/
|
|
17
|
+
export const RENAME_CONTENTION_CODES = new Set(['EPERM', 'EACCES', 'EBUSY']);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* How long a replacing rename may keep retrying before it gives up.
|
|
21
|
+
*
|
|
22
|
+
* BOUNDED, not open-ended: a genuine permission fault raises the same errno as
|
|
23
|
+
* contention and is distinguishable from it only by never clearing, so an
|
|
24
|
+
* unbounded wait would convert a hard error into a hang.
|
|
25
|
+
*
|
|
26
|
+
* The figure is a judgement, not a measurement, and the honest bound on it is
|
|
27
|
+
* that the previous 2s was observed EXHAUSTED on a loaded Windows CI runner
|
|
28
|
+
* while no upper bound has been established. What would settle it is recording
|
|
29
|
+
* how long a real contended rename takes to clear. graceful-fs tolerates 60s
|
|
30
|
+
* for Windows rename contention, but that is weaker support than it looks: it
|
|
31
|
+
* retries only where the destination has GONE, which is the opposite of
|
|
32
|
+
* replacing one a reader holds open.
|
|
33
|
+
*/
|
|
34
|
+
export const RENAME_BUDGET_MS = 10_000;
|
|
35
|
+
|
|
36
|
+
/** Seams for the test, which cannot make a real rename fail on demand. */
|
|
37
|
+
export interface RenameOverOpenReadersOptions {
|
|
38
|
+
readonly rename?: (from: string, to: string) => Promise<void>;
|
|
39
|
+
readonly budgetMs?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* `rename` the staged content over `destination`, retrying while Windows
|
|
44
|
+
* reports the destination as held open.
|
|
45
|
+
*
|
|
46
|
+
* The staging file exists so a reader never sees a half-written file, and on
|
|
47
|
+
* Windows the very presence of that reader is what makes the replacing rename
|
|
48
|
+
* fail — so the indivisible write breaks under exactly the contention it was
|
|
49
|
+
* built for, and it breaks intermittently, which is worse than never working.
|
|
50
|
+
* A reader's handle is released in milliseconds, so waiting turns a spurious
|
|
51
|
+
* failure into a slightly later success.
|
|
52
|
+
*
|
|
53
|
+
* Exhausting the budget rethrows the last failure, leaving the caller's cleanup
|
|
54
|
+
* and error contract unchanged. An errno OUTSIDE the contention set is rethrown
|
|
55
|
+
* without waiting at all: retrying a fault that will not clear only delays it.
|
|
56
|
+
*/
|
|
57
|
+
export async function renameOverOpenReaders(
|
|
58
|
+
staging: string,
|
|
59
|
+
destination: string,
|
|
60
|
+
options: RenameOverOpenReadersOptions = {}
|
|
61
|
+
): Promise<void> {
|
|
62
|
+
const rename = options.rename ?? fsp.rename;
|
|
63
|
+
const deadline = Date.now() + (options.budgetMs ?? RENAME_BUDGET_MS);
|
|
64
|
+
for (let attempt = 0; ; attempt++) {
|
|
65
|
+
try {
|
|
66
|
+
await rename(staging, destination);
|
|
67
|
+
return;
|
|
68
|
+
} catch (err: unknown) {
|
|
69
|
+
const code = err instanceof Error && 'code' in err ? String((err as NodeJS.ErrnoException).code) : undefined;
|
|
70
|
+
if (code === undefined || !RENAME_CONTENTION_CODES.has(code) || Date.now() >= deadline) {
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
// Backs off to keep a long contention window from spinning, capped so
|
|
74
|
+
// a late attempt still lands promptly once the handle is released.
|
|
75
|
+
await new Promise(resolve => setTimeout(resolve, Math.min(2 ** attempt, 50)));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -24,6 +24,21 @@ export interface SocketServerOptions {
|
|
|
24
24
|
* resolves (only then is the address info available).
|
|
25
25
|
*/
|
|
26
26
|
readonly port?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Interface to bind. Default `'127.0.0.1'` (LOCAL ONLY) — a head whose
|
|
29
|
+
* port is OS-assigned and announced over the LSP connection is
|
|
30
|
+
* reachable only by a client on the same machine anyway, so a wider
|
|
31
|
+
* binding exposes a port nothing off-box can address. Matches
|
|
32
|
+
* `startGlspServer`'s default, so the two socket heads agree.
|
|
33
|
+
*
|
|
34
|
+
* Pass `'::'` for every interface: that is the dual-stack any-address,
|
|
35
|
+
* so both IPv4 and IPv6 clients connect. `'0.0.0.0'` is NOT the same —
|
|
36
|
+
* it takes IPv4 only and refuses `::1`.
|
|
37
|
+
*
|
|
38
|
+
* An address no local interface holds fails the bind, so `started`
|
|
39
|
+
* rejects with `EADDRNOTAVAIL` rather than falling back to a wider one.
|
|
40
|
+
*/
|
|
41
|
+
readonly host?: string;
|
|
27
42
|
/**
|
|
28
43
|
* Optional logger. If omitted, the launcher runs silently — the
|
|
29
44
|
* adopter is expected to surface lifecycle events through their own
|
|
@@ -88,6 +103,7 @@ export interface StartedSocketServer extends IntegratedServer {
|
|
|
88
103
|
*/
|
|
89
104
|
export function startSocketServer(options: SocketServerOptions, onClientConnection: OnClientConnection): StartedSocketServer {
|
|
90
105
|
const port = options.port ?? 0;
|
|
106
|
+
const host = options.host ?? '127.0.0.1';
|
|
91
107
|
const logger = options.logger;
|
|
92
108
|
const tag = options.logTag ?? 'SocketServer';
|
|
93
109
|
const started = new Deferred<void>();
|
|
@@ -128,7 +144,7 @@ export function startSocketServer(options: SocketServerOptions, onClientConnecti
|
|
|
128
144
|
connection.listen();
|
|
129
145
|
});
|
|
130
146
|
|
|
131
|
-
netServer.listen(port);
|
|
147
|
+
netServer.listen(port, host);
|
|
132
148
|
netServer.on('listening', () => {
|
|
133
149
|
const addressInfo = netServer.address();
|
|
134
150
|
if (!addressInfo) {
|
|
@@ -144,7 +160,7 @@ export function startSocketServer(options: SocketServerOptions, onClientConnecti
|
|
|
144
160
|
return;
|
|
145
161
|
}
|
|
146
162
|
handle.port = addressInfo.port;
|
|
147
|
-
logger?.info(`[${tag}] Ready to accept new client requests on port: ${addressInfo.port}`);
|
|
163
|
+
logger?.info(`[${tag}] Ready to accept new client requests on ${addressInfo.address} port: ${addressInfo.port}`);
|
|
148
164
|
started.resolve();
|
|
149
165
|
});
|
|
150
166
|
netServer.on('error', err => {
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
|
-
import { DocumentState, type AstNode, type LangiumDocument, UriUtils, type URI } from '@hydranium/langium';
|
|
10
|
+
import { DocumentState, type AstNode, type AstReflection, type LangiumDocument, UriUtils, type URI } from '@hydranium/langium';
|
|
11
|
+
import { buildAstNode } from '../langium/ast-extension/ast-node-builder.js';
|
|
12
|
+
import { type AstDiagnostic } from '../langium/validation/document-validator.js';
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Build a typed AST-node fixture for unit tests.
|
|
@@ -23,12 +25,29 @@ import { DocumentState, type AstNode, type LangiumDocument, UriUtils, type URI }
|
|
|
23
25
|
* framework synthetic node, and marking it would change scope/index behaviour.
|
|
24
26
|
* A test that wants a genuine synthetic node calls `markSynthetic` explicitly.
|
|
25
27
|
*
|
|
28
|
+
* **Without a `reflection`, the fixture carries only what the test spells out**,
|
|
29
|
+
* which is a shape production never produces: a parsed or built node always has
|
|
30
|
+
* its grammar-declared containment arrays, so a fixture that omits one lets a
|
|
31
|
+
* test pass against an impossible node, and lets code that walks a list crash
|
|
32
|
+
* only under test. Pass the grammar's reflection whenever the type is a real
|
|
33
|
+
* one and the code under test reads a collection off it. Fixtures typed against
|
|
34
|
+
* ad-hoc names have no reflection to pass and keep the plain form.
|
|
35
|
+
*
|
|
26
36
|
* @param node the node shape — `$type` is required; every other property is a
|
|
27
37
|
* value the test cares about (`$container`, cross-reference values, internal
|
|
28
|
-
* `_id` fields, …).
|
|
38
|
+
* `_id` fields, …).
|
|
39
|
+
* @param reflection when given, pre-fills the grammar's declared defaults the
|
|
40
|
+
* same way {@link buildAstNode} does, and the result is a fresh object rather
|
|
41
|
+
* than `node` itself. Omitted, `node` is returned by reference.
|
|
29
42
|
*/
|
|
30
|
-
export function makeFakeAstNode<TAst extends AstNode = AstNode>(
|
|
31
|
-
|
|
43
|
+
export function makeFakeAstNode<TAst extends AstNode = AstNode>(
|
|
44
|
+
node: { $type: string } & Record<string, unknown>,
|
|
45
|
+
reflection?: AstReflection
|
|
46
|
+
): TAst {
|
|
47
|
+
if (!reflection) {
|
|
48
|
+
return node as unknown as TAst;
|
|
49
|
+
}
|
|
50
|
+
return buildAstNode<TAst>(reflection, node.$type, node);
|
|
32
51
|
}
|
|
33
52
|
|
|
34
53
|
/**
|
|
@@ -86,7 +105,7 @@ function stringifyFakeRoot(root: unknown): string {
|
|
|
86
105
|
* staging. Override via `options.state` for tests that gate on earlier
|
|
87
106
|
* phases.
|
|
88
107
|
*/
|
|
89
|
-
export function makeFakeDocument<TAst extends AstNode = AstNode, TDiagnostic =
|
|
108
|
+
export function makeFakeDocument<TAst extends AstNode = AstNode, TDiagnostic extends AstDiagnostic = AstDiagnostic>(
|
|
90
109
|
uri: string | URI,
|
|
91
110
|
root: TAst,
|
|
92
111
|
options: FakeDocumentOptions<TAst, TDiagnostic> = {}
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
import { type Clock, DefaultTracer, type Logger, NoopLogger, SystemClock, type Tracer } from '@hydranium/protocol';
|
|
11
11
|
import type { ServerSharedServicesMinimal } from '../langium/shared-services.js';
|
|
12
|
+
import { DefaultServerLocale, type ServerLocale } from '../locale/server-locale.js';
|
|
13
|
+
import { DefaultMessageRenderer, type MessageRenderer } from '../messages/renderer.js';
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Overrides for {@link makeNoopSharedServices}. The three observability slots
|
|
@@ -29,6 +31,18 @@ export interface NoopSharedServicesOverrides {
|
|
|
29
31
|
Logger?: Logger;
|
|
30
32
|
/** Bound on the top-level `Tracer` slot. Default: a `DefaultTracer` over `Logger` + `Clock`. */
|
|
31
33
|
Tracer?: Tracer;
|
|
34
|
+
/** Factory for the `ServerLocale` slot. Default: a real one, reporting no locale. */
|
|
35
|
+
ServerLocale?: (services: ServerSharedServicesMinimal) => ServerLocale;
|
|
36
|
+
/**
|
|
37
|
+
* Factory for the `MessageRenderer` slot. Default: the real
|
|
38
|
+
* {@link DefaultMessageRenderer}, whose no-catalogue behaviour is a
|
|
39
|
+
* pass-through — so the default is the framework's own behaviour, not a stub.
|
|
40
|
+
*
|
|
41
|
+
* A factory rather than an instance, because a renderer reads the tree it is
|
|
42
|
+
* bound into. Both slots are resolved lazily, so an override is honoured
|
|
43
|
+
* however late the caller reads them.
|
|
44
|
+
*/
|
|
45
|
+
MessageRenderer?: (services: ServerSharedServicesMinimal) => MessageRenderer;
|
|
32
46
|
/** Per-slot `workspace` overrides. Slots left out resolve to `undefined`. */
|
|
33
47
|
workspace?: Record<string, unknown>;
|
|
34
48
|
/** Any other minimal slot (`ServiceRegistry`, `AstReflection`, `additionalDocuments`, …). */
|
|
@@ -63,11 +77,21 @@ export interface NoopSharedServicesOverrides {
|
|
|
63
77
|
export function makeNoopSharedServices<T extends ServerSharedServicesMinimal = ServerSharedServicesMinimal>(
|
|
64
78
|
overrides: NoopSharedServicesOverrides = {}
|
|
65
79
|
): T {
|
|
66
|
-
const {
|
|
80
|
+
const {
|
|
81
|
+
Clock: clock,
|
|
82
|
+
Logger: logger,
|
|
83
|
+
Tracer: tracer,
|
|
84
|
+
ServerLocale: serverLocale,
|
|
85
|
+
MessageRenderer: messageRenderer,
|
|
86
|
+
workspace,
|
|
87
|
+
additionalDocuments,
|
|
88
|
+
...restTop
|
|
89
|
+
} = overrides;
|
|
67
90
|
const resolvedClock = clock ?? new SystemClock();
|
|
68
91
|
const resolvedLogger = logger ?? new NoopLogger();
|
|
69
92
|
const resolvedTracer = tracer ?? new DefaultTracer(resolvedLogger, resolvedClock);
|
|
70
|
-
|
|
93
|
+
// Assembled in two steps, because both defaults read the tree they belong to.
|
|
94
|
+
const services = {
|
|
71
95
|
Clock: resolvedClock,
|
|
72
96
|
Logger: resolvedLogger,
|
|
73
97
|
Tracer: resolvedTracer,
|
|
@@ -75,4 +99,28 @@ export function makeNoopSharedServices<T extends ServerSharedServicesMinimal = S
|
|
|
75
99
|
...restTop,
|
|
76
100
|
workspace: { ...(workspace ?? {}) }
|
|
77
101
|
} as unknown as T;
|
|
102
|
+
// Lazy, like Langium's own per-slot construction — and load-bearing here:
|
|
103
|
+
// both services emit an `instantiated` trace, so building them eagerly puts
|
|
104
|
+
// two lines into the capture of every test that passes a capturing logger
|
|
105
|
+
// and asserts on emptiness.
|
|
106
|
+
defineLazySlot(services, 'ServerLocale', () => serverLocale?.(services) ?? new DefaultServerLocale(services));
|
|
107
|
+
defineLazySlot(services, 'MessageRenderer', () => messageRenderer?.(services) ?? new DefaultMessageRenderer(services));
|
|
108
|
+
return services;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Install `name` as a memoized getter, so nothing is constructed until it is read. */
|
|
112
|
+
function defineLazySlot(target: object, name: string, create: () => unknown): void {
|
|
113
|
+
let resolved: unknown;
|
|
114
|
+
let built = false;
|
|
115
|
+
Object.defineProperty(target, name, {
|
|
116
|
+
configurable: true,
|
|
117
|
+
enumerable: true,
|
|
118
|
+
get: () => {
|
|
119
|
+
if (!built) {
|
|
120
|
+
resolved = create();
|
|
121
|
+
built = true;
|
|
122
|
+
}
|
|
123
|
+
return resolved;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
78
126
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import {
|
|
11
11
|
type Clock,
|
|
12
12
|
DefaultTracer,
|
|
13
|
+
type Locale,
|
|
13
14
|
type Logger,
|
|
14
15
|
NoopLogger,
|
|
15
16
|
type Project,
|
|
@@ -18,11 +19,14 @@ import {
|
|
|
18
19
|
type TransferDiagnostic,
|
|
19
20
|
type TransferElement
|
|
20
21
|
} from '@hydranium/protocol';
|
|
22
|
+
import { DefaultServerLocale, type ServerLocale } from '../locale/server-locale.js';
|
|
23
|
+
import { DefaultMessageRenderer, type MessageRenderer } from '../messages/renderer.js';
|
|
21
24
|
import type { Harness } from '@hydranium/protocol/testing';
|
|
22
25
|
import { type AstNode, type AstNodeDescription, type WorkspaceLock } from '@hydranium/langium';
|
|
23
26
|
import type { ModelService, ModelServiceOptions } from '../langium/model-service/model-service.js';
|
|
24
27
|
import type { ServerSharedServices } from '../langium/module.js';
|
|
25
|
-
import {
|
|
28
|
+
import type { AstDiagnostic } from '../langium/validation/document-validator.js';
|
|
29
|
+
import { DefaultTransferEncoder, type TransferEncoder } from '../langium/transfer/transfer-encoder.js';
|
|
26
30
|
import { DefaultDocumentUriPolicy, type DocumentUriPolicy } from '../langium/workspace/document-uri-policy.js';
|
|
27
31
|
import { HydraniumWorkspaceLock } from '../langium/workspace/hydranium-workspace-lock.js';
|
|
28
32
|
import type { FakeDocumentOptions } from './fake-document.js';
|
|
@@ -46,12 +50,21 @@ import { makeStubWritableFileSystem, type StubWritableFileSystem } from './stub-
|
|
|
46
50
|
* to `ServerSharedServices` in {@link makeTestServices} is therefore the
|
|
47
51
|
* single named place where the "stub stands in for the full service tree"
|
|
48
52
|
* assertion happens.
|
|
53
|
+
*
|
|
54
|
+
* **`TDiagnostic` and `TTransferDiagnostic` are the two ends of a conversion
|
|
55
|
+
* and must stay separate.** The first is what the build left on
|
|
56
|
+
* `LangiumDocument.diagnostics` and what `ModelService` hands back on an
|
|
57
|
+
* `AstDocument`; the second is what `TransferEncoder.toTransferDiagnostic`
|
|
58
|
+
* produces for the wire. One parameter serving both makes the tree
|
|
59
|
+
* unbindable for an adopter who extends either shape, since extending one
|
|
60
|
+
* says nothing about the other.
|
|
49
61
|
*/
|
|
50
62
|
export interface TestSharedServices<
|
|
51
63
|
TAst extends AstNode = AstNode,
|
|
52
|
-
TDiagnostic extends
|
|
64
|
+
TDiagnostic extends AstDiagnostic = AstDiagnostic,
|
|
53
65
|
TTransfer extends TransferElement = TransferElement,
|
|
54
|
-
TProject extends Project = Project
|
|
66
|
+
TProject extends Project = Project,
|
|
67
|
+
TTransferDiagnostic extends TransferDiagnostic = TransferDiagnostic
|
|
55
68
|
> {
|
|
56
69
|
readonly Clock: Clock;
|
|
57
70
|
readonly Logger: Logger;
|
|
@@ -90,17 +103,27 @@ export interface TestSharedServices<
|
|
|
90
103
|
WorkspaceLock: WorkspaceLock;
|
|
91
104
|
};
|
|
92
105
|
readonly model: {
|
|
93
|
-
TransferEncoder: TransferEncoder<
|
|
106
|
+
TransferEncoder: TransferEncoder<TTransferDiagnostic>;
|
|
94
107
|
ModelService: ModelService<TAst, TDiagnostic, TTransfer>;
|
|
95
108
|
};
|
|
109
|
+
/**
|
|
110
|
+
* The REAL services, not stubs — the framework renderer's no-catalogue
|
|
111
|
+
* behaviour is a pass-through, so the stub tree agrees with a production one
|
|
112
|
+
* unless a test installs a catalogue. Bound rather than omitted because
|
|
113
|
+
* `HydraniumDocumentBuilder` renders through the renderer on every
|
|
114
|
+
* `Validated` phase, where an omitted slot is a `TypeError`.
|
|
115
|
+
*/
|
|
116
|
+
readonly ServerLocale: ServerLocale;
|
|
117
|
+
readonly MessageRenderer: MessageRenderer;
|
|
96
118
|
}
|
|
97
119
|
|
|
98
120
|
/** Optional configuration for {@link makeTestServices}. */
|
|
99
121
|
export interface MakeTestServicesOptions<
|
|
100
122
|
TAst extends AstNode = AstNode,
|
|
101
|
-
TDiagnostic extends
|
|
123
|
+
TDiagnostic extends AstDiagnostic = AstDiagnostic,
|
|
102
124
|
TTransfer extends TransferElement = TransferElement,
|
|
103
|
-
TProject extends Project = Project
|
|
125
|
+
TProject extends Project = Project,
|
|
126
|
+
TTransferDiagnostic extends TransferDiagnostic = TransferDiagnostic
|
|
104
127
|
> {
|
|
105
128
|
/**
|
|
106
129
|
* Serialiser used by the bundled `StubModelService`. The stub tree
|
|
@@ -112,6 +135,14 @@ export interface MakeTestServicesOptions<
|
|
|
112
135
|
* for tests that don't assert on the produced text.
|
|
113
136
|
*/
|
|
114
137
|
serialize?: (uri: string, root: TTransfer) => string;
|
|
138
|
+
/**
|
|
139
|
+
* Renderer bound on `MessageRenderer`. Default: the framework's own, which
|
|
140
|
+
* passes every sentence through unchanged. Supply one to install a catalogue
|
|
141
|
+
* or to drive the throwing path.
|
|
142
|
+
*/
|
|
143
|
+
messageRenderer?: (services: ServerSharedServices<TProject>) => MessageRenderer;
|
|
144
|
+
/** Locale handed to the bundle's {@link ServerLocale}. Default: none, i.e. the framework's English. */
|
|
145
|
+
locale?: Locale;
|
|
115
146
|
/**
|
|
116
147
|
* Languages to register on a {@link StubServiceRegistry} bound on the
|
|
117
148
|
* `ServiceRegistry` slot. This is how a test gets multi-language routing:
|
|
@@ -187,7 +218,7 @@ export interface MakeTestServicesOptions<
|
|
|
187
218
|
* Override the {@link TransferEncoder} factory. Default: framework
|
|
188
219
|
* {@link TransferEncoder} with no overrides.
|
|
189
220
|
*/
|
|
190
|
-
transferEncoder?: (services: ServerSharedServices<TProject>) => TransferEncoder<
|
|
221
|
+
transferEncoder?: (services: ServerSharedServices<TProject>) => TransferEncoder<TTransferDiagnostic>;
|
|
191
222
|
}
|
|
192
223
|
|
|
193
224
|
/**
|
|
@@ -205,9 +236,10 @@ export interface MakeTestServicesOptions<
|
|
|
205
236
|
*/
|
|
206
237
|
export interface TestServicesBundle<
|
|
207
238
|
TAst extends AstNode = AstNode,
|
|
208
|
-
TDiagnostic extends
|
|
239
|
+
TDiagnostic extends AstDiagnostic = AstDiagnostic,
|
|
209
240
|
TTransfer extends TransferElement = TransferElement,
|
|
210
|
-
TProject extends Project = Project
|
|
241
|
+
TProject extends Project = Project,
|
|
242
|
+
TTransferDiagnostic extends TransferDiagnostic = TransferDiagnostic
|
|
211
243
|
> extends Harness {
|
|
212
244
|
readonly services: ServerSharedServices<TProject>;
|
|
213
245
|
readonly documents: StubLangiumDocuments<TAst, TDiagnostic>;
|
|
@@ -229,10 +261,18 @@ export interface TestServicesBundle<
|
|
|
229
261
|
*/
|
|
230
262
|
readonly indexManager: StubIndexManager | undefined;
|
|
231
263
|
readonly modelService: ModelService<TAst, TDiagnostic, TTransfer>;
|
|
232
|
-
readonly transferEncoder: TransferEncoder<
|
|
264
|
+
readonly transferEncoder: TransferEncoder<TTransferDiagnostic>;
|
|
233
265
|
readonly logger: Logger;
|
|
234
266
|
/** The clock bound on the `Clock` slot — a `makeFakeClock()` if one was passed. */
|
|
235
267
|
readonly clock: Clock;
|
|
268
|
+
/**
|
|
269
|
+
* The service bound on `ServerLocale`, so a test can hand over a locale
|
|
270
|
+
* mid-run — which is also how it verifies the renderer reads the locale per
|
|
271
|
+
* render rather than caching it at construction.
|
|
272
|
+
*/
|
|
273
|
+
readonly serverLocale: ServerLocale;
|
|
274
|
+
/** The renderer bound on `MessageRenderer` — the framework's own unless one was supplied. */
|
|
275
|
+
readonly messageRenderer: MessageRenderer;
|
|
236
276
|
}
|
|
237
277
|
|
|
238
278
|
/**
|
|
@@ -247,12 +287,13 @@ export interface TestServicesBundle<
|
|
|
247
287
|
*/
|
|
248
288
|
export function makeTestServices<
|
|
249
289
|
TAst extends AstNode = AstNode,
|
|
250
|
-
TDiagnostic extends
|
|
290
|
+
TDiagnostic extends AstDiagnostic = AstDiagnostic,
|
|
251
291
|
TTransfer extends TransferElement = TransferElement,
|
|
252
|
-
TProject extends Project = Project
|
|
292
|
+
TProject extends Project = Project,
|
|
293
|
+
TTransferDiagnostic extends TransferDiagnostic = TransferDiagnostic
|
|
253
294
|
>(
|
|
254
|
-
options: MakeTestServicesOptions<TAst, TDiagnostic, TTransfer, TProject> = {}
|
|
255
|
-
): TestServicesBundle<TAst, TDiagnostic, TTransfer, TProject> {
|
|
295
|
+
options: MakeTestServicesOptions<TAst, TDiagnostic, TTransfer, TProject, TTransferDiagnostic> = {}
|
|
296
|
+
): TestServicesBundle<TAst, TDiagnostic, TTransfer, TProject, TTransferDiagnostic> {
|
|
256
297
|
const documents = makeStubLangiumDocuments<TAst, TDiagnostic>(options.seedDocuments);
|
|
257
298
|
const textDocuments = makeStubHydraniumTextDocuments();
|
|
258
299
|
const documentBuilder = makeStubDocumentBuilder();
|
|
@@ -289,7 +330,7 @@ export function makeTestServices<
|
|
|
289
330
|
// (after the factories run); the literal uses unsafe casts to
|
|
290
331
|
// partially-built objects to satisfy TestSharedServices, then patches
|
|
291
332
|
// them in.
|
|
292
|
-
const services: TestSharedServices<TAst, TDiagnostic, TTransfer, TProject> = {
|
|
333
|
+
const services: TestSharedServices<TAst, TDiagnostic, TTransfer, TProject, TTransferDiagnostic> = {
|
|
293
334
|
Clock: clock,
|
|
294
335
|
Logger: logger,
|
|
295
336
|
Tracer: tracer,
|
|
@@ -311,20 +352,33 @@ export function makeTestServices<
|
|
|
311
352
|
...(indexManager ? { IndexManager: indexManager } : {}),
|
|
312
353
|
WorkspaceLock: new HydraniumWorkspaceLock()
|
|
313
354
|
},
|
|
314
|
-
model: {} as TestSharedServices<TAst, TDiagnostic, TTransfer, TProject>['model']
|
|
355
|
+
model: {} as TestSharedServices<TAst, TDiagnostic, TTransfer, TProject, TTransferDiagnostic>['model'],
|
|
356
|
+
ServerLocale: {} as ServerLocale,
|
|
357
|
+
MessageRenderer: {} as DefaultMessageRenderer
|
|
315
358
|
};
|
|
316
359
|
const sharedServices = services as unknown as ServerSharedServices<TProject>;
|
|
317
360
|
|
|
361
|
+
// Patched in after the literal, like `model` below: both read the tree they
|
|
362
|
+
// belong to, and the renderer reads the locale service.
|
|
363
|
+
const mutableMessages = services as { ServerLocale: ServerLocale; MessageRenderer: MessageRenderer };
|
|
364
|
+
const serverLocale = new DefaultServerLocale(sharedServices);
|
|
365
|
+
if (options.locale) {
|
|
366
|
+
serverLocale.accept(options.locale);
|
|
367
|
+
}
|
|
368
|
+
mutableMessages.ServerLocale = serverLocale;
|
|
369
|
+
const messageRenderer = options.messageRenderer?.(sharedServices) ?? new DefaultMessageRenderer(sharedServices);
|
|
370
|
+
mutableMessages.MessageRenderer = messageRenderer;
|
|
371
|
+
|
|
318
372
|
const serialize = options.serialize ?? ((_uri: string, root: TTransfer) => JSON.stringify(root));
|
|
319
373
|
const transferEncoder = options.transferEncoder
|
|
320
374
|
? options.transferEncoder(sharedServices)
|
|
321
|
-
: new
|
|
375
|
+
: new DefaultTransferEncoder<unknown, TTransferDiagnostic>(sharedServices);
|
|
322
376
|
const modelService = options.modelService
|
|
323
377
|
? options.modelService(sharedServices)
|
|
324
378
|
: makeStubModelService<TAst, TDiagnostic, TTransfer>(sharedServices, serialize, options.modelServiceOptions);
|
|
325
379
|
|
|
326
380
|
const mutableModel = services.model as {
|
|
327
|
-
TransferEncoder: TransferEncoder<
|
|
381
|
+
TransferEncoder: TransferEncoder<TTransferDiagnostic>;
|
|
328
382
|
ModelService: ModelService<TAst, TDiagnostic, TTransfer>;
|
|
329
383
|
};
|
|
330
384
|
mutableModel.TransferEncoder = transferEncoder;
|
|
@@ -346,6 +400,8 @@ export function makeTestServices<
|
|
|
346
400
|
transferEncoder,
|
|
347
401
|
logger,
|
|
348
402
|
clock,
|
|
403
|
+
serverLocale,
|
|
404
|
+
messageRenderer,
|
|
349
405
|
dispose: () => {
|
|
350
406
|
// No-op: the bundled stubs hold only in-memory state (maps, arrays),
|
|
351
407
|
// released with the bundle when it goes out of scope. The hook exists
|
|
@@ -11,14 +11,13 @@ import { type Harness } from '@hydranium/protocol/testing';
|
|
|
11
11
|
import { makeDuplexStreamPair } from '@hydranium/protocol/testing/node';
|
|
12
12
|
import { StreamMessageReader, StreamMessageWriter } from 'vscode-jsonrpc/node';
|
|
13
13
|
import { type Connection, ProposedFeatures } from 'vscode-languageserver/node';
|
|
14
|
+
import { withHydraniumLspFeatures } from '../../lsp/connection-features.js';
|
|
14
15
|
// The node `createConnection` from `vscode-languageserver/node` is built for a
|
|
15
16
|
// standalone server PROCESS: it wires `process.exit()` onto the input stream's
|
|
16
17
|
// `end`/`close` events, onto the `exit` notification (via its watchDog), and onto
|
|
17
|
-
// a parent-pid liveness timer
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
// stream (an uncatchable synchronous `ERR_STREAM_DESTROYED`) and/or exits the host
|
|
21
|
-
// test runner. The lower-level common `createConnection(connectionFactory,
|
|
18
|
+
// a parent-pid liveness timer. In-process every one of those is fatal —
|
|
19
|
+
// destroying the streams at teardown trips the `close` handler, which exits the
|
|
20
|
+
// host test runner. The lower-level common `createConnection(connectionFactory,
|
|
22
21
|
// watchDog, features)` form does none of that: it lets us inject a no-op-`exit`
|
|
23
22
|
// WatchDog and build the connection over the in-memory streams, so teardown is the
|
|
24
23
|
// primitive's responsibility alone.
|
|
@@ -347,7 +346,7 @@ export function makeLspServerConnection(): LspServerConnection {
|
|
|
347
346
|
logger =>
|
|
348
347
|
createProtocolConnection(new StreamMessageReader(pair.clientToServer), new StreamMessageWriter(pair.serverToClient), logger),
|
|
349
348
|
watchDog,
|
|
350
|
-
ProposedFeatures.all
|
|
349
|
+
withHydraniumLspFeatures(ProposedFeatures.all)
|
|
351
350
|
);
|
|
352
351
|
|
|
353
352
|
// Client side mirrors the directions: reads serverToClient, writes clientToServer.
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
* committed sample workspace can **rewrite the sample files**, after which
|
|
22
22
|
* every later run asserts against already-repaired input: a suite that passes
|
|
23
23
|
* while testing nothing. The same applies to a fixture built in place, which
|
|
24
|
-
* additionally
|
|
25
|
-
* through
|
|
24
|
+
* additionally comes back in the serializer's layout, because the write-back
|
|
25
|
+
* goes through it.
|
|
26
26
|
*
|
|
27
27
|
* The second reason is cleanup. Hand-rolled `mkdtempSync` without a matching
|
|
28
28
|
* `rmSync` leaks a directory per test run into the OS temp dir, which nobody
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
*/
|
|
50
50
|
|
|
51
51
|
import { URI } from '@hydranium/langium';
|
|
52
|
-
import { cpSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
52
|
+
import { cpSync, mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
|
|
53
53
|
import { tmpdir } from 'node:os';
|
|
54
54
|
import * as path from 'node:path';
|
|
55
55
|
|
|
@@ -106,7 +106,12 @@ export interface ScratchWorkspace {
|
|
|
106
106
|
* returned {@link ScratchWorkspace.dispose} with an `afterEach`.
|
|
107
107
|
*/
|
|
108
108
|
export function makeScratchWorkspace(options: ScratchWorkspaceOptions = {}): ScratchWorkspace {
|
|
109
|
-
|
|
109
|
+
// RESOLVED, because `tmpdir()` can itself be a symlink — on macOS it answers
|
|
110
|
+
// `/tmp`, which links to `/private/tmp`. A root the server canonicalises to a
|
|
111
|
+
// different path than the one it was handed registers documents under one
|
|
112
|
+
// spelling and reads them under the other, failing initialization with an
|
|
113
|
+
// ENOENT that names neither the link nor this call.
|
|
114
|
+
const root = realpathSync(mkdtempSync(path.join(tmpdir(), options.prefix ?? 'hydranium-scratch-')));
|
|
110
115
|
if (options.seed) {
|
|
111
116
|
cpSync(options.seed, root, { recursive: true });
|
|
112
117
|
}
|
|
@@ -203,6 +203,37 @@ export interface SpawnedServer extends Harness {
|
|
|
203
203
|
dispose(): Promise<number | null>;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Every server started and not yet disposed, so {@link disposeSpawnedServers}
|
|
208
|
+
* can reach one a test forgot.
|
|
209
|
+
*
|
|
210
|
+
* Holds the server — and through it the `ChildProcess` — rather than a pid. A
|
|
211
|
+
* pid identifies a process only while that process is alive: once it exits the
|
|
212
|
+
* OS may reissue the number, and Windows does so within seconds. A teardown
|
|
213
|
+
* that signalled a recorded pid would therefore reach whatever holds it now,
|
|
214
|
+
* which on a concurrent build is another task's compiler or test runner.
|
|
215
|
+
*/
|
|
216
|
+
const liveServers = new Set<SpawnedServer>();
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Terminate every server this module started that has not been disposed, and
|
|
220
|
+
* forget them.
|
|
221
|
+
*
|
|
222
|
+
* Safe to call when none are outstanding, and safe to call twice. Nothing here
|
|
223
|
+
* can reach a process this module did not spawn, because every kill goes
|
|
224
|
+
* through the `ChildProcess` handle libuv holds and libuv will not signal a
|
|
225
|
+
* child it has already reaped.
|
|
226
|
+
*
|
|
227
|
+
* A suite that disposes each server as it finishes needs this only as a net; a
|
|
228
|
+
* suite that does not needs it, because an orphan holding a port or a pipe
|
|
229
|
+
* makes the NEXT run's verdict meaningless.
|
|
230
|
+
*/
|
|
231
|
+
export async function disposeSpawnedServers(): Promise<void> {
|
|
232
|
+
const outstanding = [...liveServers];
|
|
233
|
+
liveServers.clear();
|
|
234
|
+
await Promise.all(outstanding.map(server => server.dispose()));
|
|
235
|
+
}
|
|
236
|
+
|
|
206
237
|
/** The captured streams, quoted into a boot-failure message. */
|
|
207
238
|
function bootDiagnosis(stdoutPrefix: string, stderr: string): string {
|
|
208
239
|
return (
|
|
@@ -390,7 +421,7 @@ export async function startSpawnedServer(options: SpawnedServerOptions): Promise
|
|
|
390
421
|
return exitCode;
|
|
391
422
|
}
|
|
392
423
|
|
|
393
|
-
|
|
424
|
+
const server: SpawnedServer = {
|
|
394
425
|
connection,
|
|
395
426
|
pid: child.pid,
|
|
396
427
|
initializeResult,
|
|
@@ -440,8 +471,11 @@ export async function startSpawnedServer(options: SpawnedServerOptions): Promise
|
|
|
440
471
|
});
|
|
441
472
|
},
|
|
442
473
|
dispose(): Promise<number | null> {
|
|
474
|
+
liveServers.delete(server);
|
|
443
475
|
disposal ??= terminate();
|
|
444
476
|
return disposal;
|
|
445
477
|
}
|
|
446
478
|
};
|
|
479
|
+
liveServers.add(server);
|
|
480
|
+
return server;
|
|
447
481
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Run a Node-only assertion while collecting process-level unhandled rejection
|
|
12
|
+
* events. The callback owns the event-loop drain and can therefore choose the
|
|
13
|
+
* right observation window for the operation under test.
|
|
14
|
+
*
|
|
15
|
+
* Inside the window a rejection reaches only the array: while any listener is
|
|
16
|
+
* registered, Node hands none to its default handler and Vitest reports none as
|
|
17
|
+
* an error. A callback that never asserts on the array therefore hides every
|
|
18
|
+
* rejection it provokes.
|
|
19
|
+
*/
|
|
20
|
+
export async function captureUnhandledRejections<T>(callback: (rejections: unknown[]) => Promise<T>): Promise<T> {
|
|
21
|
+
const rejections: unknown[] = [];
|
|
22
|
+
const listener = (reason: unknown): void => {
|
|
23
|
+
rejections.push(reason);
|
|
24
|
+
};
|
|
25
|
+
process.on('unhandledRejection', listener);
|
|
26
|
+
try {
|
|
27
|
+
return await callback(rejections);
|
|
28
|
+
} finally {
|
|
29
|
+
process.off('unhandledRejection', listener);
|
|
30
|
+
}
|
|
31
|
+
}
|