@alloy-js/core 0.23.0-dev.11 → 0.23.0-dev.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev/src/binder.js +488 -0
- package/dist/dev/src/binder.js.map +1 -0
- package/dist/dev/src/code.js +181 -0
- package/dist/dev/src/code.js.map +1 -0
- package/dist/dev/src/components/AccessExpression.js +230 -0
- package/dist/dev/src/components/AccessExpression.js.map +1 -0
- package/dist/dev/src/components/AccessExpression.test.js +237 -0
- package/dist/dev/src/components/AccessExpression.test.js.map +1 -0
- package/dist/dev/src/components/AppendFile.js +246 -0
- package/dist/dev/src/components/AppendFile.js.map +1 -0
- package/dist/dev/src/components/Block.js +66 -0
- package/dist/dev/src/components/Block.js.map +1 -0
- package/dist/dev/src/components/CopyFile.js +16 -0
- package/dist/dev/src/components/CopyFile.js.map +1 -0
- package/dist/dev/src/components/Declaration.js +70 -0
- package/dist/dev/src/components/Declaration.js.map +1 -0
- package/dist/dev/src/components/For.js +40 -0
- package/dist/dev/src/components/For.js.map +1 -0
- package/dist/dev/src/components/Indent.js +37 -0
- package/dist/dev/src/components/Indent.js.map +1 -0
- package/dist/dev/src/components/List.js +29 -0
- package/dist/dev/src/components/List.js.map +1 -0
- package/dist/dev/src/components/MemberDeclaration.js +70 -0
- package/dist/dev/src/components/MemberDeclaration.js.map +1 -0
- package/dist/dev/src/components/MemberName.js +11 -0
- package/dist/dev/src/components/MemberName.js.map +1 -0
- package/dist/dev/src/components/MemberScope.js +57 -0
- package/dist/dev/src/components/MemberScope.js.map +1 -0
- package/dist/dev/src/components/Name.js +11 -0
- package/dist/dev/src/components/Name.js.map +1 -0
- package/dist/dev/src/components/Output.js +72 -0
- package/dist/dev/src/components/Output.js.map +1 -0
- package/dist/dev/src/components/Prose.js +32 -0
- package/dist/dev/src/components/Prose.js.map +1 -0
- package/dist/dev/src/components/ReferenceOrContent.js +12 -0
- package/dist/dev/src/components/ReferenceOrContent.js.map +1 -0
- package/dist/dev/src/components/Scope.js +50 -0
- package/dist/dev/src/components/Scope.js.map +1 -0
- package/dist/dev/src/components/Show.js +4 -0
- package/dist/dev/src/components/Show.js.map +1 -0
- package/dist/dev/src/components/SourceDirectory.js +41 -0
- package/dist/dev/src/components/SourceDirectory.js.map +1 -0
- package/dist/dev/src/components/SourceFile.js +52 -0
- package/dist/dev/src/components/SourceFile.js.map +1 -0
- package/dist/dev/src/components/StatementList.js +20 -0
- package/dist/dev/src/components/StatementList.js.map +1 -0
- package/dist/dev/src/components/Switch.js +42 -0
- package/dist/dev/src/components/Switch.js.map +1 -0
- package/dist/dev/src/components/TemplateFile.js +153 -0
- package/dist/dev/src/components/TemplateFile.js.map +1 -0
- package/dist/dev/src/components/UpdateFile.js +71 -0
- package/dist/dev/src/components/UpdateFile.js.map +1 -0
- package/dist/dev/src/components/Wrap.js +18 -0
- package/dist/dev/src/components/Wrap.js.map +1 -0
- package/dist/dev/src/components/index.js +25 -0
- package/dist/dev/src/components/index.js.map +1 -0
- package/dist/dev/src/components/stc/index.js +27 -0
- package/dist/dev/src/components/stc/index.js.map +1 -0
- package/dist/dev/src/components/stc/sti.js +10 -0
- package/dist/dev/src/components/stc/sti.js.map +1 -0
- package/dist/dev/src/content-slot.js +78 -0
- package/dist/dev/src/content-slot.js.map +1 -0
- package/dist/dev/src/content-slot.test.js +89 -0
- package/dist/dev/src/content-slot.test.js.map +1 -0
- package/dist/dev/src/context/assignment.js +46 -0
- package/dist/dev/src/context/assignment.js.map +1 -0
- package/dist/dev/src/context/binder.js +12 -0
- package/dist/dev/src/context/binder.js.map +1 -0
- package/dist/dev/src/context/declaration.js +3 -0
- package/dist/dev/src/context/declaration.js.map +1 -0
- package/dist/dev/src/context/format-options.js +32 -0
- package/dist/dev/src/context/format-options.js.map +1 -0
- package/dist/dev/src/context/index.js +11 -0
- package/dist/dev/src/context/index.js.map +1 -0
- package/dist/dev/src/context/member-declaration.js +11 -0
- package/dist/dev/src/context/member-declaration.js.map +1 -0
- package/dist/dev/src/context/member-scope.js +12 -0
- package/dist/dev/src/context/member-scope.js.map +1 -0
- package/dist/dev/src/context/name-policy.js +13 -0
- package/dist/dev/src/context/name-policy.js.map +1 -0
- package/dist/dev/src/context/scope.js +13 -0
- package/dist/dev/src/context/scope.js.map +1 -0
- package/dist/dev/src/context/source-directory.js +3 -0
- package/dist/dev/src/context/source-directory.js.map +1 -0
- package/dist/dev/src/context/source-file.js +3 -0
- package/dist/dev/src/context/source-file.js.map +1 -0
- package/dist/dev/src/context.js +47 -0
- package/dist/dev/src/context.js.map +1 -0
- package/dist/dev/src/debug/cli.js +164 -0
- package/dist/dev/src/debug/cli.js.map +1 -0
- package/dist/dev/src/debug/diagnostics.test.js +50 -0
- package/dist/dev/src/debug/diagnostics.test.js.map +1 -0
- package/dist/dev/src/debug/effects.js +293 -0
- package/dist/dev/src/debug/effects.js.map +1 -0
- package/dist/dev/src/debug/effects.test.js +280 -0
- package/dist/dev/src/debug/effects.test.js.map +1 -0
- package/dist/dev/src/debug/files.js +29 -0
- package/dist/dev/src/debug/files.js.map +1 -0
- package/dist/dev/src/debug/files.test.js +78 -0
- package/dist/dev/src/debug/files.test.js.map +1 -0
- package/dist/dev/src/debug/index.js +71 -0
- package/dist/dev/src/debug/index.js.map +1 -0
- package/dist/dev/src/debug/message-format.test.js +836 -0
- package/dist/dev/src/debug/message-format.test.js.map +1 -0
- package/dist/dev/src/debug/render-tree-orphans.test.js +365 -0
- package/dist/dev/src/debug/render-tree-orphans.test.js.map +1 -0
- package/dist/dev/src/debug/render.js +479 -0
- package/dist/dev/src/debug/render.js.map +1 -0
- package/dist/dev/src/debug/render.test.js +363 -0
- package/dist/dev/src/debug/render.test.js.map +1 -0
- package/dist/dev/src/debug/serialize.js +70 -0
- package/dist/dev/src/debug/serialize.js.map +1 -0
- package/dist/dev/src/debug/source-map.browser.js +24 -0
- package/dist/dev/src/debug/source-map.browser.js.map +1 -0
- package/dist/dev/src/debug/source-map.js +111 -0
- package/dist/dev/src/debug/source-map.js.map +1 -0
- package/dist/dev/src/debug/symbols.js +196 -0
- package/dist/dev/src/debug/symbols.js.map +1 -0
- package/dist/dev/src/debug/symbols.test.js +93 -0
- package/dist/dev/src/debug/symbols.test.js.map +1 -0
- package/dist/dev/src/debug/trace-writer.js +658 -0
- package/dist/dev/src/debug/trace-writer.js.map +1 -0
- package/dist/dev/src/debug/trace.js +460 -0
- package/dist/dev/src/debug/trace.js.map +1 -0
- package/dist/dev/src/devtools/devtools-protocol.js +2 -0
- package/dist/dev/src/devtools/devtools-protocol.js.map +1 -0
- package/dist/dev/src/devtools/devtools-server.browser.js +33 -0
- package/dist/dev/src/devtools/devtools-server.browser.js.map +1 -0
- package/dist/dev/src/devtools/devtools-server.js +444 -0
- package/dist/dev/src/devtools/devtools-server.js.map +1 -0
- package/dist/dev/src/devtools/devtools-transport.js +114 -0
- package/dist/dev/src/devtools/devtools-transport.js.map +1 -0
- package/dist/dev/src/devtools-entry.browser.js +2 -0
- package/dist/dev/src/devtools-entry.browser.js.map +1 -0
- package/dist/dev/src/devtools-entry.js +2 -0
- package/dist/dev/src/devtools-entry.js.map +1 -0
- package/dist/dev/src/diagnostics.js +89 -0
- package/dist/dev/src/diagnostics.js.map +1 -0
- package/dist/dev/src/host/alloy-host.browser.js +32 -0
- package/dist/dev/src/host/alloy-host.browser.js.map +1 -0
- package/dist/dev/src/host/alloy-host.js +144 -0
- package/dist/dev/src/host/alloy-host.js.map +1 -0
- package/dist/dev/src/host/interface.js +2 -0
- package/dist/dev/src/host/interface.js.map +1 -0
- package/dist/dev/src/host/node-host.browser.js +21 -0
- package/dist/dev/src/host/node-host.browser.js.map +1 -0
- package/dist/dev/src/host/node-host.js +20 -0
- package/dist/dev/src/host/node-host.js.map +1 -0
- package/dist/dev/src/index.browser.js +3 -0
- package/dist/dev/src/index.browser.js.map +1 -0
- package/dist/dev/src/index.js +27 -0
- package/dist/dev/src/index.js.map +1 -0
- package/dist/dev/src/inspect.browser.js +6 -0
- package/dist/dev/src/inspect.browser.js.map +1 -0
- package/dist/dev/src/inspect.js +2 -0
- package/dist/dev/src/inspect.js.map +1 -0
- package/dist/dev/src/jsx-runtime.js +17 -0
- package/dist/dev/src/jsx-runtime.js.map +1 -0
- package/dist/dev/src/library-symbol-reference.js +5 -0
- package/dist/dev/src/library-symbol-reference.js.map +1 -0
- package/dist/dev/src/name-policy.js +24 -0
- package/dist/dev/src/name-policy.js.map +1 -0
- package/dist/dev/src/pretty-string/pretty-string.js +100 -0
- package/dist/dev/src/pretty-string/pretty-string.js.map +1 -0
- package/dist/dev/src/pretty-string/pretty-string.test.js +38 -0
- package/dist/dev/src/pretty-string/pretty-string.test.js.map +1 -0
- package/dist/dev/src/print-hook.js +10 -0
- package/dist/dev/src/print-hook.js.map +1 -0
- package/dist/dev/src/props-combinators.js +109 -0
- package/dist/dev/src/props-combinators.js.map +1 -0
- package/dist/dev/src/reactive-union-set.js +213 -0
- package/dist/dev/src/reactive-union-set.js.map +1 -0
- package/dist/dev/src/reactivity.js +426 -0
- package/dist/dev/src/reactivity.js.map +1 -0
- package/dist/dev/src/refkey.js +167 -0
- package/dist/dev/src/refkey.js.map +1 -0
- package/dist/dev/src/render-stack.js +252 -0
- package/dist/dev/src/render-stack.js.map +1 -0
- package/dist/dev/src/render.js +872 -0
- package/dist/dev/src/render.js.map +1 -0
- package/dist/dev/src/resource.js +124 -0
- package/dist/dev/src/resource.js.map +1 -0
- package/dist/dev/src/runtime/component.js +41 -0
- package/dist/dev/src/runtime/component.js.map +1 -0
- package/dist/dev/src/runtime/intrinsic.js +12 -0
- package/dist/dev/src/runtime/intrinsic.js.map +1 -0
- package/dist/dev/src/scheduler.js +217 -0
- package/dist/dev/src/scheduler.js.map +1 -0
- package/dist/dev/src/stc.js +40 -0
- package/dist/dev/src/stc.js.map +1 -0
- package/dist/dev/src/sti.js +31 -0
- package/dist/dev/src/sti.js.map +1 -0
- package/dist/dev/src/symbols/basic-scope.js +21 -0
- package/dist/dev/src/symbols/basic-scope.js.map +1 -0
- package/dist/dev/src/symbols/basic-symbol.js +34 -0
- package/dist/dev/src/symbols/basic-symbol.js.map +1 -0
- package/dist/dev/src/symbols/decl.js +26 -0
- package/dist/dev/src/symbols/decl.js.map +1 -0
- package/dist/dev/src/symbols/index.js +10 -0
- package/dist/dev/src/symbols/index.js.map +1 -0
- package/dist/dev/src/symbols/output-scope.js +202 -0
- package/dist/dev/src/symbols/output-scope.js.map +1 -0
- package/dist/dev/src/symbols/output-space.js +36 -0
- package/dist/dev/src/symbols/output-space.js.map +1 -0
- package/dist/dev/src/symbols/output-symbol.js +504 -0
- package/dist/dev/src/symbols/output-symbol.js.map +1 -0
- package/dist/dev/src/symbols/symbol-flow.js +106 -0
- package/dist/dev/src/symbols/symbol-flow.js.map +1 -0
- package/dist/dev/src/symbols/symbol-slot.js +68 -0
- package/dist/dev/src/symbols/symbol-slot.js.map +1 -0
- package/dist/dev/src/symbols/symbol-slot.test.js +43 -0
- package/dist/dev/src/symbols/symbol-slot.test.js.map +1 -0
- package/dist/dev/src/symbols/symbol-table.js +93 -0
- package/dist/dev/src/symbols/symbol-table.js.map +1 -0
- package/dist/dev/src/tap.js +109 -0
- package/dist/dev/src/tap.js.map +1 -0
- package/dist/dev/src/trace.js +2 -0
- package/dist/dev/src/trace.js.map +1 -0
- package/dist/dev/src/tracer.js +180 -0
- package/dist/dev/src/tracer.js.map +1 -0
- package/dist/dev/src/utils.js +487 -0
- package/dist/dev/src/utils.js.map +1 -0
- package/dist/dev/src/write-output.js +48 -0
- package/dist/dev/src/write-output.js.map +1 -0
- package/dist/dev/test/browser-build.test.js +85 -0
- package/dist/dev/test/browser-build.test.js.map +1 -0
- package/dist/dev/test/children.test.js +44 -0
- package/dist/dev/test/children.test.js.map +1 -0
- package/dist/dev/test/components/append-file.test.js +394 -0
- package/dist/dev/test/components/append-file.test.js.map +1 -0
- package/dist/dev/test/components/block.test.js +83 -0
- package/dist/dev/test/components/block.test.js.map +1 -0
- package/dist/dev/test/components/copy-file.test.js +119 -0
- package/dist/dev/test/components/copy-file.test.js.map +1 -0
- package/dist/dev/test/components/declaration.test.js +40 -0
- package/dist/dev/test/components/declaration.test.js.map +1 -0
- package/dist/dev/test/components/list.test.js +250 -0
- package/dist/dev/test/components/list.test.js.map +1 -0
- package/dist/dev/test/components/prose.test.js +42 -0
- package/dist/dev/test/components/prose.test.js.map +1 -0
- package/dist/dev/test/components/reference-or-content.test.js +246 -0
- package/dist/dev/test/components/reference-or-content.test.js.map +1 -0
- package/dist/dev/test/components/source-file.test.js +271 -0
- package/dist/dev/test/components/source-file.test.js.map +1 -0
- package/dist/dev/test/components/template-file.test.js +200 -0
- package/dist/dev/test/components/template-file.test.js.map +1 -0
- package/dist/dev/test/components/update-file.test.js +210 -0
- package/dist/dev/test/components/update-file.test.js.map +1 -0
- package/dist/dev/test/components/wrap.test.js +48 -0
- package/dist/dev/test/components/wrap.test.js.map +1 -0
- package/dist/dev/test/control-flow/for.test.js +318 -0
- package/dist/dev/test/control-flow/for.test.js.map +1 -0
- package/dist/dev/test/control-flow/match.test.js +112 -0
- package/dist/dev/test/control-flow/match.test.js.map +1 -0
- package/dist/dev/test/control-flow/show.test.js +38 -0
- package/dist/dev/test/control-flow/show.test.js.map +1 -0
- package/dist/dev/test/lazy-isempty.test.js +121 -0
- package/dist/dev/test/lazy-isempty.test.js.map +1 -0
- package/dist/dev/test/name-policy.test.js +28 -0
- package/dist/dev/test/name-policy.test.js.map +1 -0
- package/dist/dev/test/props-with-defaults.test.js +94 -0
- package/dist/dev/test/props-with-defaults.test.js.map +1 -0
- package/dist/dev/test/reactive-union-set-disposers.test.js +98 -0
- package/dist/dev/test/reactive-union-set-disposers.test.js.map +1 -0
- package/dist/dev/test/reactive-union-set.test.js +171 -0
- package/dist/dev/test/reactive-union-set.test.js.map +1 -0
- package/dist/dev/test/reactivity/circular-reactives.test.js +62 -0
- package/dist/dev/test/reactivity/circular-reactives.test.js.map +1 -0
- package/dist/dev/test/reactivity/cleanup.test.js +96 -0
- package/dist/dev/test/reactivity/cleanup.test.js.map +1 -0
- package/dist/dev/test/reactivity/memo.test.js +17 -0
- package/dist/dev/test/reactivity/memo.test.js.map +1 -0
- package/dist/dev/test/reactivity/ref-rendering.test.js +38 -0
- package/dist/dev/test/reactivity/ref-rendering.test.js.map +1 -0
- package/dist/dev/test/reactivity/shallow-reactive.test.js +52 -0
- package/dist/dev/test/reactivity/shallow-reactive.test.js.map +1 -0
- package/dist/dev/test/reactivity/test.test.js +74 -0
- package/dist/dev/test/reactivity/test.test.js.map +1 -0
- package/dist/dev/test/reactivity/untrack.test.js +27 -0
- package/dist/dev/test/reactivity/untrack.test.js.map +1 -0
- package/dist/dev/test/refkey.test.js +36 -0
- package/dist/dev/test/refkey.test.js.map +1 -0
- package/dist/dev/test/rendering/basic.test.js +194 -0
- package/dist/dev/test/rendering/basic.test.js.map +1 -0
- package/dist/dev/test/rendering/code.test.js +64 -0
- package/dist/dev/test/rendering/code.test.js.map +1 -0
- package/dist/dev/test/rendering/formatting.test.js +797 -0
- package/dist/dev/test/rendering/formatting.test.js.map +1 -0
- package/dist/dev/test/rendering/indent.test.js +183 -0
- package/dist/dev/test/rendering/indent.test.js.map +1 -0
- package/dist/dev/test/rendering/memoization.test.js +37 -0
- package/dist/dev/test/rendering/memoization.test.js.map +1 -0
- package/dist/dev/test/rendering/print-render-stack.test.js +287 -0
- package/dist/dev/test/rendering/print-render-stack.test.js.map +1 -0
- package/dist/dev/test/rendering/refkeys.test.js +37 -0
- package/dist/dev/test/rendering/refkeys.test.js.map +1 -0
- package/dist/dev/test/scheduler-extended.test.js +96 -0
- package/dist/dev/test/scheduler-extended.test.js.map +1 -0
- package/dist/dev/test/scheduler.test.js +46 -0
- package/dist/dev/test/scheduler.test.js.map +1 -0
- package/dist/dev/test/split-props.test.js +78 -0
- package/dist/dev/test/split-props.test.js.map +1 -0
- package/dist/dev/test/stc.test.js +35 -0
- package/dist/dev/test/stc.test.js.map +1 -0
- package/dist/dev/test/symbols/output-scope.test.js +180 -0
- package/dist/dev/test/symbols/output-scope.test.js.map +1 -0
- package/dist/dev/test/symbols/output-symbol.test.js +202 -0
- package/dist/dev/test/symbols/output-symbol.test.js.map +1 -0
- package/dist/dev/test/symbols/resolution.test.js +487 -0
- package/dist/dev/test/symbols/resolution.test.js.map +1 -0
- package/dist/dev/test/symbols/symbol-table.test.js +15 -0
- package/dist/dev/test/symbols/symbol-table.test.js.map +1 -0
- package/dist/dev/test/symbols/utils.js +26 -0
- package/dist/dev/test/symbols/utils.js.map +1 -0
- package/dist/dev/test/utils.test.js +317 -0
- package/dist/dev/test/utils.test.js.map +1 -0
- package/dist/dev/testing/create-test-wrapper.js +76 -0
- package/dist/dev/testing/create-test-wrapper.js.map +1 -0
- package/dist/dev/testing/devtools-utils.js +162 -0
- package/dist/dev/testing/devtools-utils.js.map +1 -0
- package/dist/dev/testing/extend-expect.js +167 -0
- package/dist/dev/testing/extend-expect.js.map +1 -0
- package/dist/dev/testing/extend-expect.test.js +234 -0
- package/dist/dev/testing/extend-expect.test.js.map +1 -0
- package/dist/dev/testing/index.js +4 -0
- package/dist/dev/testing/index.js.map +1 -0
- package/dist/dev/testing/render.js +23 -0
- package/dist/dev/testing/render.js.map +1 -0
- package/dist/dev/testing/vitest.d.js +2 -0
- package/dist/dev/testing/vitest.d.js.map +1 -0
- package/dist/src/debug/cli.d.ts.map +1 -1
- package/dist/src/debug/cli.js +3 -2
- package/dist/src/debug/cli.js.map +1 -1
- package/dist/src/debug/effects.d.ts.map +1 -1
- package/dist/src/debug/effects.js +2 -67
- package/dist/src/debug/effects.js.map +1 -1
- package/dist/src/debug/render.d.ts.map +1 -1
- package/dist/src/debug/render.js +15 -8
- package/dist/src/debug/render.js.map +1 -1
- package/dist/src/debug/source-map.browser.d.ts +16 -0
- package/dist/src/debug/source-map.browser.d.ts.map +1 -0
- package/dist/src/debug/source-map.browser.js +24 -0
- package/dist/src/debug/source-map.browser.js.map +1 -0
- package/dist/src/debug/source-map.d.ts +22 -0
- package/dist/src/debug/source-map.d.ts.map +1 -0
- package/dist/src/debug/source-map.js +111 -0
- package/dist/src/debug/source-map.js.map +1 -0
- package/dist/src/debug/trace.d.ts.map +1 -1
- package/dist/src/debug/trace.js +19 -5
- package/dist/src/debug/trace.js.map +1 -1
- package/dist/src/host/node-host.browser.d.ts +11 -0
- package/dist/src/host/node-host.browser.d.ts.map +1 -0
- package/dist/src/host/node-host.browser.js +21 -0
- package/dist/src/host/node-host.browser.js.map +1 -0
- package/dist/src/host/node-host.d.ts +11 -0
- package/dist/src/host/node-host.d.ts.map +1 -0
- package/dist/src/host/node-host.js +20 -0
- package/dist/src/host/node-host.js.map +1 -0
- package/dist/src/render-stack.d.ts.map +1 -1
- package/dist/src/render-stack.js +4 -3
- package/dist/src/render-stack.js.map +1 -1
- package/dist/src/write-output.d.ts.map +1 -1
- package/dist/src/write-output.js +6 -5
- package/dist/src/write-output.js.map +1 -1
- package/dist/test/browser-build.test.js +66 -67
- package/dist/test/browser-build.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -3
- package/src/debug/cli.ts +3 -2
- package/src/debug/effects.ts +2 -82
- package/src/debug/render.ts +25 -13
- package/src/debug/source-map.browser.ts +30 -0
- package/src/debug/source-map.ts +135 -0
- package/src/debug/trace.ts +22 -5
- package/src/host/node-host.browser.ts +23 -0
- package/src/host/node-host.ts +22 -0
- package/src/render-stack.ts +4 -3
- package/src/write-output.ts +6 -5
- package/test/browser-build.test.ts +71 -78
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,836 @@
|
|
|
1
|
+
import { createComponent as _$createComponent } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Tests that verify the WebSocket message format produced by the server
|
|
4
|
+
* matches what the devtools frontend expects. Each test renders something,
|
|
5
|
+
* collects the raw JSON messages over the WebSocket, and checks the shape.
|
|
6
|
+
*
|
|
7
|
+
* Tests subscribe to specific channels to validate the subscription mechanism
|
|
8
|
+
* and avoid filtering noise from unrelated channels.
|
|
9
|
+
*/
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
11
|
+
import WebSocket from "ws";
|
|
12
|
+
import { createMessageCollector } from "../../testing/devtools-utils.js";
|
|
13
|
+
import { Declaration } from "../components/Declaration.js";
|
|
14
|
+
import { For } from "../components/For.js";
|
|
15
|
+
import { Output } from "../components/Output.js";
|
|
16
|
+
import { Scope } from "../components/Scope.js";
|
|
17
|
+
import { SourceFile } from "../components/SourceFile.js";
|
|
18
|
+
import { enableDevtools, resetDevtoolsServerForTests } from "../devtools/devtools-server.js";
|
|
19
|
+
import { effect, ref } from "../reactivity.js";
|
|
20
|
+
import { renderAsync } from "../render.js";
|
|
21
|
+
import { flushJobsAsync } from "../scheduler.js";
|
|
22
|
+
import { debug } from "./index.js";
|
|
23
|
+
let socket;
|
|
24
|
+
let port;
|
|
25
|
+
beforeEach(async () => {
|
|
26
|
+
debug.effect.reset();
|
|
27
|
+
const server = await enableDevtools({
|
|
28
|
+
port: 0
|
|
29
|
+
});
|
|
30
|
+
port = server.port;
|
|
31
|
+
socket = new WebSocket(`ws://127.0.0.1:${port}`);
|
|
32
|
+
await new Promise((resolve, reject) => {
|
|
33
|
+
socket?.once("open", resolve);
|
|
34
|
+
socket?.once("error", reject);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
afterEach(async () => {
|
|
38
|
+
if (socket) {
|
|
39
|
+
socket.close();
|
|
40
|
+
socket = undefined;
|
|
41
|
+
}
|
|
42
|
+
await resetDevtoolsServerForTests();
|
|
43
|
+
debug.effect.reset();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Subscriptions
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
describe("subscriptions", () => {
|
|
51
|
+
it("only receives messages for subscribed channels", async () => {
|
|
52
|
+
// Subscribe to render only — should NOT see effects, refs, etc.
|
|
53
|
+
const collector = await createMessageCollector(socket, ["render"]);
|
|
54
|
+
await renderAsync(_$createComponent(Output, {
|
|
55
|
+
children: "hello"
|
|
56
|
+
}, {
|
|
57
|
+
fileName: import.meta.url,
|
|
58
|
+
lineNumber: 60,
|
|
59
|
+
columnNumber: 23
|
|
60
|
+
}));
|
|
61
|
+
const messages = await collector.waitForRender();
|
|
62
|
+
collector.stop();
|
|
63
|
+
const types = new Set(messages.map(m => m.type));
|
|
64
|
+
// Should have render messages
|
|
65
|
+
expect(types.has("render:node_added")).toBe(true);
|
|
66
|
+
// Should NOT have effect/ref/symbol/scope/edge messages
|
|
67
|
+
expect(types.has("effect:added")).toBe(false);
|
|
68
|
+
expect(types.has("ref:added")).toBe(false);
|
|
69
|
+
expect(types.has("symbol:added")).toBe(false);
|
|
70
|
+
expect(types.has("scope:added")).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
it("receives messages from multiple subscribed channels", async () => {
|
|
73
|
+
const collector = await createMessageCollector(socket, ["render", "effects", "refs"]);
|
|
74
|
+
await renderAsync(_$createComponent(Output, {
|
|
75
|
+
children: "hello"
|
|
76
|
+
}, {
|
|
77
|
+
fileName: import.meta.url,
|
|
78
|
+
lineNumber: 80,
|
|
79
|
+
columnNumber: 23
|
|
80
|
+
}));
|
|
81
|
+
const messages = await collector.waitForRender();
|
|
82
|
+
collector.stop();
|
|
83
|
+
const types = new Set(messages.map(m => m.type));
|
|
84
|
+
expect(types.has("render:node_added")).toBe(true);
|
|
85
|
+
expect(types.has("effect:added")).toBe(true);
|
|
86
|
+
expect(types.has("ref:added")).toBe(true);
|
|
87
|
+
// Not subscribed to these
|
|
88
|
+
expect(types.has("symbol:added")).toBe(false);
|
|
89
|
+
expect(types.has("scope:added")).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
it("lifecycle signals are received regardless of subscription", async () => {
|
|
92
|
+
// Subscribe to nothing useful — lifecycle signals bypass subscriptions
|
|
93
|
+
const collector = await createMessageCollector(socket, ["diagnostics"]);
|
|
94
|
+
await renderAsync(_$createComponent(Output, {}, {
|
|
95
|
+
fileName: import.meta.url,
|
|
96
|
+
lineNumber: 96,
|
|
97
|
+
columnNumber: 23
|
|
98
|
+
}));
|
|
99
|
+
const messages = await collector.waitForRender();
|
|
100
|
+
collector.stop();
|
|
101
|
+
expect(messages.some(m => m.type === "render:complete")).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// render channel
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
describe("render:node_added", () => {
|
|
110
|
+
it("root node has null parent_id and kind 'root'", async () => {
|
|
111
|
+
const collector = await createMessageCollector(socket, ["render"]);
|
|
112
|
+
await renderAsync(_$createComponent(Output, {}, {
|
|
113
|
+
fileName: import.meta.url,
|
|
114
|
+
lineNumber: 111,
|
|
115
|
+
columnNumber: 23
|
|
116
|
+
}));
|
|
117
|
+
const messages = await collector.waitForRender();
|
|
118
|
+
collector.stop();
|
|
119
|
+
const root = messages.find(m => m.type === "render:node_added" && m.kind === "root");
|
|
120
|
+
expect(root).toMatchObject({
|
|
121
|
+
type: "render:node_added",
|
|
122
|
+
id: expect.any(Number),
|
|
123
|
+
parent_id: null,
|
|
124
|
+
kind: "root",
|
|
125
|
+
seq: expect.any(Number)
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
it("component node has numeric id, parent_id, and name", async () => {
|
|
129
|
+
const collector = await createMessageCollector(socket, ["render"]);
|
|
130
|
+
await renderAsync(_$createComponent(Output, {}, {
|
|
131
|
+
fileName: import.meta.url,
|
|
132
|
+
lineNumber: 129,
|
|
133
|
+
columnNumber: 23
|
|
134
|
+
}));
|
|
135
|
+
const messages = await collector.waitForRender();
|
|
136
|
+
collector.stop();
|
|
137
|
+
const output = messages.find(m => m.type === "render:node_added" && m.name === "Output");
|
|
138
|
+
expect(output).toMatchObject({
|
|
139
|
+
type: "render:node_added",
|
|
140
|
+
id: expect.any(Number),
|
|
141
|
+
parent_id: expect.any(Number),
|
|
142
|
+
kind: "component",
|
|
143
|
+
name: "Output"
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
it("text node has string value", async () => {
|
|
147
|
+
const collector = await createMessageCollector(socket, ["render"]);
|
|
148
|
+
await renderAsync(_$createComponent(Output, {
|
|
149
|
+
children: "hello"
|
|
150
|
+
}, {
|
|
151
|
+
fileName: import.meta.url,
|
|
152
|
+
lineNumber: 147,
|
|
153
|
+
columnNumber: 23
|
|
154
|
+
}));
|
|
155
|
+
const messages = await collector.waitForRender();
|
|
156
|
+
collector.stop();
|
|
157
|
+
const text = messages.find(m => m.type === "render:node_added" && m.kind === "text");
|
|
158
|
+
expect(text).toBeDefined();
|
|
159
|
+
expect(text.value).toBe("hello");
|
|
160
|
+
expect(text.parent_id).toEqual(expect.any(Number));
|
|
161
|
+
});
|
|
162
|
+
it("non-text node has null value (not undefined)", async () => {
|
|
163
|
+
const collector = await createMessageCollector(socket, ["render"]);
|
|
164
|
+
await renderAsync(_$createComponent(Output, {
|
|
165
|
+
children: "hello"
|
|
166
|
+
}, {
|
|
167
|
+
fileName: import.meta.url,
|
|
168
|
+
lineNumber: 161,
|
|
169
|
+
columnNumber: 23
|
|
170
|
+
}));
|
|
171
|
+
const messages = await collector.waitForRender();
|
|
172
|
+
collector.stop();
|
|
173
|
+
const component = messages.find(m => m.type === "render:node_added" && m.kind === "component");
|
|
174
|
+
expect(component.value).toBeNull();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe("render:node_removed", () => {
|
|
178
|
+
it("is emitted when a reactive component removes children", async () => {
|
|
179
|
+
const show = ref(true);
|
|
180
|
+
const collector = await createMessageCollector(socket, ["render"]);
|
|
181
|
+
function Conditional() {
|
|
182
|
+
return () => show.value ? "visible" : "";
|
|
183
|
+
}
|
|
184
|
+
await renderAsync(_$createComponent(Output, {
|
|
185
|
+
get children() {
|
|
186
|
+
return _$createComponent(Conditional, {}, {
|
|
187
|
+
fileName: import.meta.url,
|
|
188
|
+
lineNumber: 183,
|
|
189
|
+
columnNumber: 9
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}, {
|
|
193
|
+
fileName: import.meta.url,
|
|
194
|
+
lineNumber: 182,
|
|
195
|
+
columnNumber: 7
|
|
196
|
+
}));
|
|
197
|
+
const renderMessages = await collector.waitForRender();
|
|
198
|
+
|
|
199
|
+
// Verify the text node "visible" was added
|
|
200
|
+
const textAdded = renderMessages.find(m => m.type === "render:node_added" && m.value === "visible");
|
|
201
|
+
expect(textAdded).toBeDefined();
|
|
202
|
+
|
|
203
|
+
// Toggle visibility to trigger removal
|
|
204
|
+
show.value = false;
|
|
205
|
+
await flushJobsAsync();
|
|
206
|
+
const flushMessages = await collector.waitForFlush();
|
|
207
|
+
collector.stop();
|
|
208
|
+
const removed = flushMessages.filter(m => m.type === "render:node_removed");
|
|
209
|
+
expect(removed.length).toBeGreaterThan(0);
|
|
210
|
+
expect(removed[0]).toMatchObject({
|
|
211
|
+
type: "render:node_removed",
|
|
212
|
+
id: expect.any(Number)
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
describe("render:node_updated", () => {
|
|
217
|
+
it("emits node_added during reactive list growth", async () => {
|
|
218
|
+
const items = ref(["a"]);
|
|
219
|
+
const collector = await createMessageCollector(socket, ["render"]);
|
|
220
|
+
await renderAsync(_$createComponent(Output, {
|
|
221
|
+
get children() {
|
|
222
|
+
return _$createComponent(For, {
|
|
223
|
+
each: items,
|
|
224
|
+
children: item => [item]
|
|
225
|
+
}, {
|
|
226
|
+
fileName: import.meta.url,
|
|
227
|
+
lineNumber: 217,
|
|
228
|
+
columnNumber: 9
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}, {
|
|
232
|
+
fileName: import.meta.url,
|
|
233
|
+
lineNumber: 216,
|
|
234
|
+
columnNumber: 7
|
|
235
|
+
}));
|
|
236
|
+
await collector.waitForRender();
|
|
237
|
+
|
|
238
|
+
// Add an item to trigger reactive update
|
|
239
|
+
items.value = [...items.value, "b"];
|
|
240
|
+
await flushJobsAsync();
|
|
241
|
+
const flushMessages = await collector.waitForFlush();
|
|
242
|
+
collector.stop();
|
|
243
|
+
|
|
244
|
+
// Reactive updates produce new node_added messages for new content
|
|
245
|
+
const added = flushMessages.filter(m => m.type === "render:node_added");
|
|
246
|
+
expect(added.length).toBeGreaterThan(0);
|
|
247
|
+
// Should contain the new "b" text node
|
|
248
|
+
const bNode = added.find(m => m.value === "b");
|
|
249
|
+
expect(bNode).toBeDefined();
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
describe("render:reset", () => {
|
|
253
|
+
it("is sent before node_added messages", async () => {
|
|
254
|
+
const collector = await createMessageCollector(socket, ["render"]);
|
|
255
|
+
await renderAsync(_$createComponent(Output, {}, {
|
|
256
|
+
fileName: import.meta.url,
|
|
257
|
+
lineNumber: 240,
|
|
258
|
+
columnNumber: 23
|
|
259
|
+
}));
|
|
260
|
+
const messages = await collector.waitForRender();
|
|
261
|
+
collector.stop();
|
|
262
|
+
const renderMessages = messages.filter(m => m.type === "render:reset" || m.type === "render:node_added" || m.type === "render:complete");
|
|
263
|
+
expect(renderMessages[0]).toMatchObject({
|
|
264
|
+
type: "render:reset"
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
describe("render:complete", () => {
|
|
269
|
+
it("is sent after all node_added messages", async () => {
|
|
270
|
+
const collector = await createMessageCollector(socket, ["render"]);
|
|
271
|
+
await renderAsync(_$createComponent(Output, {}, {
|
|
272
|
+
fileName: import.meta.url,
|
|
273
|
+
lineNumber: 257,
|
|
274
|
+
columnNumber: 23
|
|
275
|
+
}));
|
|
276
|
+
const messages = await collector.waitForRender();
|
|
277
|
+
collector.stop();
|
|
278
|
+
const renderMessages = messages.filter(m => m.type === "render:reset" || m.type === "render:node_added" || m.type === "render:complete");
|
|
279
|
+
const last = renderMessages[renderMessages.length - 1];
|
|
280
|
+
expect(last).toMatchObject({
|
|
281
|
+
type: "render:complete"
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
// effects channel
|
|
288
|
+
// ---------------------------------------------------------------------------
|
|
289
|
+
|
|
290
|
+
describe("effect:added", () => {
|
|
291
|
+
it("has id, effect_type, and seq", async () => {
|
|
292
|
+
const collector = await createMessageCollector(socket, ["effects"]);
|
|
293
|
+
await renderAsync(_$createComponent(Output, {
|
|
294
|
+
children: "hello"
|
|
295
|
+
}, {
|
|
296
|
+
fileName: import.meta.url,
|
|
297
|
+
lineNumber: 279,
|
|
298
|
+
columnNumber: 23
|
|
299
|
+
}));
|
|
300
|
+
const messages = await collector.waitForRender();
|
|
301
|
+
collector.stop();
|
|
302
|
+
const effects = messages.filter(m => m.type === "effect:added");
|
|
303
|
+
expect(effects.length).toBeGreaterThan(0);
|
|
304
|
+
const e = effects[0];
|
|
305
|
+
expect(e).toMatchObject({
|
|
306
|
+
type: "effect:added",
|
|
307
|
+
id: expect.any(Number),
|
|
308
|
+
seq: expect.any(Number)
|
|
309
|
+
});
|
|
310
|
+
expect("effect_type" in e).toBe(true);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
describe("effect:updated", () => {
|
|
314
|
+
it("is emitted when an effect's component is set", async () => {
|
|
315
|
+
const collector = await createMessageCollector(socket, ["effects"]);
|
|
316
|
+
await renderAsync(_$createComponent(Output, {
|
|
317
|
+
children: "hello"
|
|
318
|
+
}, {
|
|
319
|
+
fileName: import.meta.url,
|
|
320
|
+
lineNumber: 299,
|
|
321
|
+
columnNumber: 23
|
|
322
|
+
}));
|
|
323
|
+
const messages = await collector.waitForRender();
|
|
324
|
+
collector.stop();
|
|
325
|
+
const updated = messages.filter(m => m.type === "effect:updated");
|
|
326
|
+
// Effects get updated with component info during render
|
|
327
|
+
if (updated.length > 0) {
|
|
328
|
+
expect(updated[0]).toMatchObject({
|
|
329
|
+
type: "effect:updated"
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// ---------------------------------------------------------------------------
|
|
336
|
+
// refs channel
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
|
|
339
|
+
describe("ref:added", () => {
|
|
340
|
+
it("has id, kind, and seq", async () => {
|
|
341
|
+
const collector = await createMessageCollector(socket, ["refs"]);
|
|
342
|
+
await renderAsync(_$createComponent(Output, {
|
|
343
|
+
children: "hello"
|
|
344
|
+
}, {
|
|
345
|
+
fileName: import.meta.url,
|
|
346
|
+
lineNumber: 320,
|
|
347
|
+
columnNumber: 23
|
|
348
|
+
}));
|
|
349
|
+
const messages = await collector.waitForRender();
|
|
350
|
+
collector.stop();
|
|
351
|
+
const refs = messages.filter(m => m.type === "ref:added");
|
|
352
|
+
expect(refs.length).toBeGreaterThan(0);
|
|
353
|
+
const r = refs[0];
|
|
354
|
+
expect(r).toMatchObject({
|
|
355
|
+
type: "ref:added",
|
|
356
|
+
id: expect.any(Number),
|
|
357
|
+
seq: expect.any(Number)
|
|
358
|
+
});
|
|
359
|
+
// kind is nullable
|
|
360
|
+
expect("kind" in r).toBe(true);
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// ---------------------------------------------------------------------------
|
|
365
|
+
// edges channel
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
|
|
368
|
+
describe("edge:track and edge:trigger", () => {
|
|
369
|
+
it("emits edge messages with effect_id and ref_id", async () => {
|
|
370
|
+
const collector = await createMessageCollector(socket, ["edges"]);
|
|
371
|
+
await renderAsync(_$createComponent(Output, {
|
|
372
|
+
children: "hello"
|
|
373
|
+
}, {
|
|
374
|
+
fileName: import.meta.url,
|
|
375
|
+
lineNumber: 345,
|
|
376
|
+
columnNumber: 23
|
|
377
|
+
}));
|
|
378
|
+
const messages = await collector.waitForRender();
|
|
379
|
+
collector.stop();
|
|
380
|
+
const edges = messages.filter(m => m.type === "edge:track" || m.type === "edge:trigger");
|
|
381
|
+
expect(edges.length).toBeGreaterThan(0);
|
|
382
|
+
const edge = edges[0];
|
|
383
|
+
expect(edge).toMatchObject({
|
|
384
|
+
seq: expect.any(Number),
|
|
385
|
+
effect_id: expect.any(Number)
|
|
386
|
+
});
|
|
387
|
+
// ref_id is present (may be null for some edge types)
|
|
388
|
+
expect("ref_id" in edge).toBe(true);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// ---------------------------------------------------------------------------
|
|
393
|
+
// symbols channel
|
|
394
|
+
// ---------------------------------------------------------------------------
|
|
395
|
+
|
|
396
|
+
describe("symbol:added", () => {
|
|
397
|
+
it("has id, name, boolean flags, and seq", async () => {
|
|
398
|
+
const collector = await createMessageCollector(socket, ["symbols"]);
|
|
399
|
+
await renderAsync(_$createComponent(Output, {
|
|
400
|
+
get children() {
|
|
401
|
+
return _$createComponent(Scope, {
|
|
402
|
+
name: "myScope",
|
|
403
|
+
get children() {
|
|
404
|
+
return _$createComponent(Declaration, {
|
|
405
|
+
name: "Foo",
|
|
406
|
+
children: "foo content"
|
|
407
|
+
}, {
|
|
408
|
+
fileName: import.meta.url,
|
|
409
|
+
lineNumber: 374,
|
|
410
|
+
columnNumber: 11
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}, {
|
|
414
|
+
fileName: import.meta.url,
|
|
415
|
+
lineNumber: 373,
|
|
416
|
+
columnNumber: 9
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}, {
|
|
420
|
+
fileName: import.meta.url,
|
|
421
|
+
lineNumber: 372,
|
|
422
|
+
columnNumber: 7
|
|
423
|
+
}));
|
|
424
|
+
const messages = await collector.waitForRender();
|
|
425
|
+
collector.stop();
|
|
426
|
+
const symbols = messages.filter(m => m.type === "symbol:added");
|
|
427
|
+
expect(symbols.length).toBeGreaterThan(0);
|
|
428
|
+
const sym = symbols.find(m => m.name === "Foo");
|
|
429
|
+
expect(sym).toBeDefined();
|
|
430
|
+
expect(sym).toMatchObject({
|
|
431
|
+
type: "symbol:added",
|
|
432
|
+
id: expect.any(Number),
|
|
433
|
+
name: "Foo",
|
|
434
|
+
seq: expect.any(Number)
|
|
435
|
+
});
|
|
436
|
+
// Boolean flags are 0/1 integers
|
|
437
|
+
expect([0, 1]).toContain(sym.is_member);
|
|
438
|
+
expect([0, 1]).toContain(sym.is_transient);
|
|
439
|
+
expect([0, 1]).toContain(sym.is_alias);
|
|
440
|
+
// Nullable fields exist as keys
|
|
441
|
+
expect("scope_id" in sym).toBe(true);
|
|
442
|
+
expect("owner_symbol_id" in sym).toBe(true);
|
|
443
|
+
expect("metadata" in sym).toBe(true);
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// ---------------------------------------------------------------------------
|
|
448
|
+
// scopes channel
|
|
449
|
+
// ---------------------------------------------------------------------------
|
|
450
|
+
|
|
451
|
+
describe("scope:added", () => {
|
|
452
|
+
it("has id, name, is_member_scope, and seq", async () => {
|
|
453
|
+
const collector = await createMessageCollector(socket, ["scopes"]);
|
|
454
|
+
await renderAsync(_$createComponent(Output, {
|
|
455
|
+
get children() {
|
|
456
|
+
return _$createComponent(Scope, {
|
|
457
|
+
name: "TestScope",
|
|
458
|
+
children: "content"
|
|
459
|
+
}, {
|
|
460
|
+
fileName: import.meta.url,
|
|
461
|
+
lineNumber: 412,
|
|
462
|
+
columnNumber: 9
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}, {
|
|
466
|
+
fileName: import.meta.url,
|
|
467
|
+
lineNumber: 411,
|
|
468
|
+
columnNumber: 7
|
|
469
|
+
}));
|
|
470
|
+
const messages = await collector.waitForRender();
|
|
471
|
+
collector.stop();
|
|
472
|
+
const scopes = messages.filter(m => m.type === "scope:added");
|
|
473
|
+
expect(scopes.length).toBeGreaterThan(0);
|
|
474
|
+
const scope = scopes.find(s => s.name === "TestScope");
|
|
475
|
+
expect(scope).toBeDefined();
|
|
476
|
+
expect(scope).toMatchObject({
|
|
477
|
+
type: "scope:added",
|
|
478
|
+
id: expect.any(Number),
|
|
479
|
+
name: "TestScope",
|
|
480
|
+
seq: expect.any(Number)
|
|
481
|
+
});
|
|
482
|
+
expect([0, 1]).toContain(scope.is_member_scope);
|
|
483
|
+
expect("parent_id" in scope).toBe(true);
|
|
484
|
+
expect("metadata" in scope).toBe(true);
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
// ---------------------------------------------------------------------------
|
|
489
|
+
// files channel
|
|
490
|
+
// ---------------------------------------------------------------------------
|
|
491
|
+
|
|
492
|
+
describe("file:added", () => {
|
|
493
|
+
it("has path, filetype, and seq", async () => {
|
|
494
|
+
const collector = await createMessageCollector(socket, ["files"]);
|
|
495
|
+
await renderAsync(_$createComponent(Output, {
|
|
496
|
+
get children() {
|
|
497
|
+
return _$createComponent(SourceFile, {
|
|
498
|
+
path: "test.ts",
|
|
499
|
+
filetype: "typescript",
|
|
500
|
+
children: "content"
|
|
501
|
+
}, {
|
|
502
|
+
fileName: import.meta.url,
|
|
503
|
+
lineNumber: 444,
|
|
504
|
+
columnNumber: 9
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
}, {
|
|
508
|
+
fileName: import.meta.url,
|
|
509
|
+
lineNumber: 443,
|
|
510
|
+
columnNumber: 7
|
|
511
|
+
}));
|
|
512
|
+
const messages = await collector.waitForRender();
|
|
513
|
+
collector.stop();
|
|
514
|
+
const files = messages.filter(m => m.type === "file:added");
|
|
515
|
+
expect(files.length).toBeGreaterThan(0);
|
|
516
|
+
const file = files.find(f => f.path.endsWith("test.ts"));
|
|
517
|
+
expect(file).toBeDefined();
|
|
518
|
+
expect(file).toMatchObject({
|
|
519
|
+
type: "file:added",
|
|
520
|
+
path: expect.stringContaining("test.ts"),
|
|
521
|
+
filetype: "typescript",
|
|
522
|
+
seq: expect.any(Number)
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// ---------------------------------------------------------------------------
|
|
528
|
+
// directories channel
|
|
529
|
+
// ---------------------------------------------------------------------------
|
|
530
|
+
|
|
531
|
+
describe("directory:added", () => {
|
|
532
|
+
it("has path and seq", async () => {
|
|
533
|
+
const collector = await createMessageCollector(socket, ["directories"]);
|
|
534
|
+
await renderAsync(_$createComponent(Output, {
|
|
535
|
+
children: "hello"
|
|
536
|
+
}, {
|
|
537
|
+
fileName: import.meta.url,
|
|
538
|
+
lineNumber: 473,
|
|
539
|
+
columnNumber: 23
|
|
540
|
+
}));
|
|
541
|
+
const messages = await collector.waitForRender();
|
|
542
|
+
collector.stop();
|
|
543
|
+
const dirs = messages.filter(m => m.type === "directory:added");
|
|
544
|
+
expect(dirs.length).toBeGreaterThan(0);
|
|
545
|
+
expect(dirs[0]).toMatchObject({
|
|
546
|
+
type: "directory:added",
|
|
547
|
+
path: expect.any(String),
|
|
548
|
+
seq: expect.any(Number)
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
// ---------------------------------------------------------------------------
|
|
554
|
+
// scheduler channel
|
|
555
|
+
// ---------------------------------------------------------------------------
|
|
556
|
+
|
|
557
|
+
describe("scheduler:job", () => {
|
|
558
|
+
it("is emitted during synchronous flush", async () => {
|
|
559
|
+
// Scheduler events are only traced during synchronous flushJobs(),
|
|
560
|
+
// not flushJobsAsync(). Subscribe to scheduler + render to see them.
|
|
561
|
+
const collector = await createMessageCollector(socket, ["scheduler", "render"]);
|
|
562
|
+
// A simple render triggers flushJobs internally
|
|
563
|
+
await renderAsync(_$createComponent(Output, {
|
|
564
|
+
children: "hello"
|
|
565
|
+
}, {
|
|
566
|
+
fileName: import.meta.url,
|
|
567
|
+
lineNumber: 501,
|
|
568
|
+
columnNumber: 23
|
|
569
|
+
}));
|
|
570
|
+
const messages = await collector.waitForRender();
|
|
571
|
+
collector.stop();
|
|
572
|
+
const jobs = messages.filter(m => m.type === "scheduler:job");
|
|
573
|
+
// Scheduler events occur during the synchronous flush inside renderAsync
|
|
574
|
+
if (jobs.length > 0) {
|
|
575
|
+
expect(jobs[0]).toMatchObject({
|
|
576
|
+
type: "scheduler:job",
|
|
577
|
+
seq: expect.any(Number)
|
|
578
|
+
});
|
|
579
|
+
expect("event" in jobs[0] || "jobs_run" in jobs[0]).toBe(true);
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
// ---------------------------------------------------------------------------
|
|
585
|
+
// lifecycle channel
|
|
586
|
+
// ---------------------------------------------------------------------------
|
|
587
|
+
|
|
588
|
+
describe("effect:lifecycle", () => {
|
|
589
|
+
it("is emitted for effect lifecycle events", async () => {
|
|
590
|
+
const collector = await createMessageCollector(socket, ["lifecycle"]);
|
|
591
|
+
const r = ref(0);
|
|
592
|
+
effect(() => {
|
|
593
|
+
void r.value;
|
|
594
|
+
});
|
|
595
|
+
await renderAsync(_$createComponent(Output, {
|
|
596
|
+
children: "hello"
|
|
597
|
+
}, {
|
|
598
|
+
fileName: import.meta.url,
|
|
599
|
+
lineNumber: 529,
|
|
600
|
+
columnNumber: 23
|
|
601
|
+
}));
|
|
602
|
+
const messages = await collector.waitForRender();
|
|
603
|
+
|
|
604
|
+
// Trigger a lifecycle event
|
|
605
|
+
r.value = 1;
|
|
606
|
+
await flushJobsAsync();
|
|
607
|
+
const flushMessages = await collector.waitForFlush();
|
|
608
|
+
collector.stop();
|
|
609
|
+
const all = [...messages, ...flushMessages];
|
|
610
|
+
const lifecycle = all.filter(m => m.type === "effect:lifecycle");
|
|
611
|
+
if (lifecycle.length > 0) {
|
|
612
|
+
expect(lifecycle[0]).toMatchObject({
|
|
613
|
+
type: "effect:lifecycle",
|
|
614
|
+
seq: expect.any(Number)
|
|
615
|
+
});
|
|
616
|
+
expect("effect_id" in lifecycle[0]).toBe(true);
|
|
617
|
+
expect("event" in lifecycle[0]).toBe(true);
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
// ---------------------------------------------------------------------------
|
|
623
|
+
// diagnostics channel
|
|
624
|
+
// ---------------------------------------------------------------------------
|
|
625
|
+
|
|
626
|
+
describe("diagnostics:report", () => {
|
|
627
|
+
it("has message, severity, and seq", async () => {
|
|
628
|
+
const {
|
|
629
|
+
insertDiagnostic
|
|
630
|
+
} = await import("./trace-writer.js");
|
|
631
|
+
const collector = await createMessageCollector(socket, ["diagnostics"]);
|
|
632
|
+
|
|
633
|
+
// Insert a diagnostic directly through the trace writer
|
|
634
|
+
insertDiagnostic("test warning", "warning", undefined, undefined, undefined, undefined);
|
|
635
|
+
|
|
636
|
+
// Render to get a completion signal for the collector
|
|
637
|
+
await renderAsync(_$createComponent(Output, {}, {
|
|
638
|
+
fileName: import.meta.url,
|
|
639
|
+
lineNumber: 571,
|
|
640
|
+
columnNumber: 23
|
|
641
|
+
}));
|
|
642
|
+
const messages = await collector.waitForRender();
|
|
643
|
+
collector.stop();
|
|
644
|
+
const diags = messages.filter(m => m.type === "diagnostics:report");
|
|
645
|
+
expect(diags.length).toBeGreaterThan(0);
|
|
646
|
+
expect(diags[0]).toMatchObject({
|
|
647
|
+
type: "diagnostics:report",
|
|
648
|
+
message: "test warning",
|
|
649
|
+
severity: "warning",
|
|
650
|
+
seq: expect.any(Number)
|
|
651
|
+
});
|
|
652
|
+
// Nullable source location fields exist
|
|
653
|
+
expect("source_file" in diags[0]).toBe(true);
|
|
654
|
+
expect("source_line" in diags[0]).toBe(true);
|
|
655
|
+
});
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
// ---------------------------------------------------------------------------
|
|
659
|
+
// errors channel
|
|
660
|
+
// ---------------------------------------------------------------------------
|
|
661
|
+
|
|
662
|
+
describe("render:error", () => {
|
|
663
|
+
it("has name, message, and seq", async () => {
|
|
664
|
+
const {
|
|
665
|
+
insertRenderError
|
|
666
|
+
} = await import("./trace-writer.js");
|
|
667
|
+
const collector = await createMessageCollector(socket, ["errors"]);
|
|
668
|
+
|
|
669
|
+
// Insert an error directly through the trace writer
|
|
670
|
+
insertRenderError("Error", "kaboom", "Error: kaboom\n at ...", undefined);
|
|
671
|
+
|
|
672
|
+
// Render to get a completion signal
|
|
673
|
+
await renderAsync(_$createComponent(Output, {}, {
|
|
674
|
+
fileName: import.meta.url,
|
|
675
|
+
lineNumber: 603,
|
|
676
|
+
columnNumber: 23
|
|
677
|
+
}));
|
|
678
|
+
const messages = await collector.waitForRender();
|
|
679
|
+
collector.stop();
|
|
680
|
+
const errors = messages.filter(m => m.type === "render:error");
|
|
681
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
682
|
+
expect(errors[0]).toMatchObject({
|
|
683
|
+
type: "render:error",
|
|
684
|
+
name: "Error",
|
|
685
|
+
message: "kaboom",
|
|
686
|
+
seq: expect.any(Number)
|
|
687
|
+
});
|
|
688
|
+
expect("stack" in errors[0]).toBe(true);
|
|
689
|
+
expect("component_stack" in errors[0]).toBe(true);
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
// ---------------------------------------------------------------------------
|
|
694
|
+
// flushJobs:complete lifecycle signal
|
|
695
|
+
// ---------------------------------------------------------------------------
|
|
696
|
+
|
|
697
|
+
describe("flushJobs:complete", () => {
|
|
698
|
+
it("is emitted after a reactive flush", async () => {
|
|
699
|
+
const items = ref(["a"]);
|
|
700
|
+
const collector = await createMessageCollector(socket, ["render"]);
|
|
701
|
+
await renderAsync(_$createComponent(Output, {
|
|
702
|
+
get children() {
|
|
703
|
+
return _$createComponent(For, {
|
|
704
|
+
each: items,
|
|
705
|
+
children: item => [item]
|
|
706
|
+
}, {
|
|
707
|
+
fileName: import.meta.url,
|
|
708
|
+
lineNumber: 632,
|
|
709
|
+
columnNumber: 9
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
}, {
|
|
713
|
+
fileName: import.meta.url,
|
|
714
|
+
lineNumber: 631,
|
|
715
|
+
columnNumber: 7
|
|
716
|
+
}));
|
|
717
|
+
await collector.waitForRender();
|
|
718
|
+
items.value = ["a", "b"];
|
|
719
|
+
await flushJobsAsync();
|
|
720
|
+
const flushMessages = await collector.waitForFlush();
|
|
721
|
+
collector.stop();
|
|
722
|
+
|
|
723
|
+
// waitForFlush resolves on flushJobs:complete, so if we get here it was received
|
|
724
|
+
expect(flushMessages.some(m => m.type === "flushJobs:complete")).toBe(true);
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
// ---------------------------------------------------------------------------
|
|
729
|
+
// debugger:info (connection handshake)
|
|
730
|
+
// ---------------------------------------------------------------------------
|
|
731
|
+
|
|
732
|
+
describe("debugger:info", () => {
|
|
733
|
+
it("is sent on connection before any other messages", async () => {
|
|
734
|
+
// Close the existing socket so the server accepts a new one
|
|
735
|
+
socket.close();
|
|
736
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
737
|
+
const freshSocket = new WebSocket(`ws://127.0.0.1:${port}`);
|
|
738
|
+
const firstMessage = new Promise((resolve, reject) => {
|
|
739
|
+
const timeout = setTimeout(() => reject(new Error("No debugger:info received")), 3000);
|
|
740
|
+
freshSocket.once("message", data => {
|
|
741
|
+
clearTimeout(timeout);
|
|
742
|
+
resolve(JSON.parse(String(data)));
|
|
743
|
+
});
|
|
744
|
+
});
|
|
745
|
+
await new Promise((resolve, reject) => {
|
|
746
|
+
freshSocket.once("open", resolve);
|
|
747
|
+
freshSocket.once("error", reject);
|
|
748
|
+
});
|
|
749
|
+
const first = await firstMessage;
|
|
750
|
+
freshSocket.close();
|
|
751
|
+
// Replace socket so afterEach doesn't close an already-closed socket
|
|
752
|
+
socket = undefined;
|
|
753
|
+
expect(first).toMatchObject({
|
|
754
|
+
type: "debugger:info",
|
|
755
|
+
version: expect.any(String)
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
// ---------------------------------------------------------------------------
|
|
761
|
+
// null vs undefined contract
|
|
762
|
+
// ---------------------------------------------------------------------------
|
|
763
|
+
|
|
764
|
+
describe("null vs undefined contract", () => {
|
|
765
|
+
it("nullable fields arrive as null (not undefined or missing)", async () => {
|
|
766
|
+
const collector = await createMessageCollector(socket, ["render"]);
|
|
767
|
+
await renderAsync(_$createComponent(Output, {
|
|
768
|
+
children: "hello"
|
|
769
|
+
}, {
|
|
770
|
+
fileName: import.meta.url,
|
|
771
|
+
lineNumber: 695,
|
|
772
|
+
columnNumber: 23
|
|
773
|
+
}));
|
|
774
|
+
const messages = await collector.waitForRender();
|
|
775
|
+
collector.stop();
|
|
776
|
+
|
|
777
|
+
// Root node: parent_id should be explicit null
|
|
778
|
+
const root = messages.find(m => m.type === "render:node_added" && m.kind === "root");
|
|
779
|
+
expect(root).toBeDefined();
|
|
780
|
+
expect(root.parent_id).toBeNull();
|
|
781
|
+
expect("parent_id" in root).toBe(true);
|
|
782
|
+
|
|
783
|
+
// Component node: value should be null, not undefined
|
|
784
|
+
const component = messages.find(m => m.type === "render:node_added" && m.kind === "component");
|
|
785
|
+
expect(component).toBeDefined();
|
|
786
|
+
expect(component.value).toBeNull();
|
|
787
|
+
expect("value" in component).toBe(true);
|
|
788
|
+
|
|
789
|
+
// source_file is nullable
|
|
790
|
+
expect("source_file" in root).toBe(true);
|
|
791
|
+
});
|
|
792
|
+
it("non-null fields have correct types", async () => {
|
|
793
|
+
const collector = await createMessageCollector(socket, ["render"]);
|
|
794
|
+
await renderAsync(_$createComponent(Output, {
|
|
795
|
+
children: "hello"
|
|
796
|
+
}, {
|
|
797
|
+
fileName: import.meta.url,
|
|
798
|
+
lineNumber: 723,
|
|
799
|
+
columnNumber: 23
|
|
800
|
+
}));
|
|
801
|
+
const messages = await collector.waitForRender();
|
|
802
|
+
collector.stop();
|
|
803
|
+
const text = messages.find(m => m.type === "render:node_added" && m.kind === "text");
|
|
804
|
+
expect(text).toBeDefined();
|
|
805
|
+
// value is a string for text nodes
|
|
806
|
+
expect(typeof text.value).toBe("string");
|
|
807
|
+
// id is always a number
|
|
808
|
+
expect(typeof text.id).toBe("number");
|
|
809
|
+
// parent_id is a number for non-root nodes
|
|
810
|
+
expect(typeof text.parent_id).toBe("number");
|
|
811
|
+
});
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
// ---------------------------------------------------------------------------
|
|
815
|
+
// seq ordering
|
|
816
|
+
// ---------------------------------------------------------------------------
|
|
817
|
+
|
|
818
|
+
describe("seq ordering", () => {
|
|
819
|
+
it("messages have monotonically increasing seq", async () => {
|
|
820
|
+
const collector = await createMessageCollector(socket, ["render"]);
|
|
821
|
+
await renderAsync(_$createComponent(Output, {
|
|
822
|
+
children: "hello"
|
|
823
|
+
}, {
|
|
824
|
+
fileName: import.meta.url,
|
|
825
|
+
lineNumber: 748,
|
|
826
|
+
columnNumber: 23
|
|
827
|
+
}));
|
|
828
|
+
const messages = await collector.waitForRender();
|
|
829
|
+
collector.stop();
|
|
830
|
+
const seqs = messages.filter(m => typeof m.seq === "number").map(m => m.seq);
|
|
831
|
+
for (let i = 1; i < seqs.length; i++) {
|
|
832
|
+
expect(seqs[i]).toBeGreaterThan(seqs[i - 1]);
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
});
|
|
836
|
+
//# sourceMappingURL=message-format.test.js.map
|