@vibesdotdev/applets 0.0.0-stage → 1.0.0-rc.2
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/LICENSE +18 -0
- package/README.md +43 -2
- package/dist/applet-gate.d.ts +47 -0
- package/dist/applet-gate.d.ts.map +1 -0
- package/dist/applet-gate.js +167 -0
- package/dist/applet-gate.js.map +1 -0
- package/dist/applet-root-surface.d.ts +135 -0
- package/dist/applet-root-surface.d.ts.map +1 -0
- package/dist/applet-root-surface.js +222 -0
- package/dist/applet-root-surface.js.map +1 -0
- package/dist/applet-value-bound.d.ts +24 -0
- package/dist/applet-value-bound.d.ts.map +1 -0
- package/dist/applet-value-bound.js +46 -0
- package/dist/applet-value-bound.js.map +1 -0
- package/dist/applets.cloud.plugin.d.ts +4 -0
- package/dist/applets.cloud.plugin.d.ts.map +1 -0
- package/dist/applets.cloud.plugin.js +11 -0
- package/dist/applets.cloud.plugin.js.map +1 -0
- package/dist/applets.consumer.plugin.d.ts +4 -0
- package/dist/applets.consumer.plugin.d.ts.map +1 -0
- package/dist/applets.consumer.plugin.js +11 -0
- package/dist/applets.consumer.plugin.js.map +1 -0
- package/dist/applets.plugin.d.ts +3 -0
- package/dist/applets.plugin.d.ts.map +1 -0
- package/dist/applets.plugin.js +30 -0
- package/dist/applets.plugin.js.map +1 -0
- package/dist/authorization.d.ts +87 -0
- package/dist/authorization.d.ts.map +1 -0
- package/dist/authorization.js +189 -0
- package/dist/authorization.js.map +1 -0
- package/dist/browser-runtime.d.ts +34 -0
- package/dist/browser-runtime.d.ts.map +1 -0
- package/dist/browser-runtime.js +5 -0
- package/dist/browser-runtime.js.map +1 -0
- package/dist/build-definition.d.ts +34 -0
- package/dist/build-definition.d.ts.map +1 -0
- package/dist/build-definition.js +44 -0
- package/dist/build-definition.js.map +1 -0
- package/dist/capabilities/capability.kind.d.ts +44 -0
- package/dist/capabilities/capability.kind.d.ts.map +1 -0
- package/dist/capabilities/capability.kind.js +17 -0
- package/dist/capabilities/capability.kind.js.map +1 -0
- package/dist/capabilities/runtime-dispatch.d.ts +9 -0
- package/dist/capabilities/runtime-dispatch.d.ts.map +1 -0
- package/dist/capabilities/runtime-dispatch.js +62 -0
- package/dist/capabilities/runtime-dispatch.js.map +1 -0
- package/dist/cli/applet-group.descriptor.d.ts +11 -0
- package/dist/cli/applet-group.descriptor.d.ts.map +1 -0
- package/dist/cli/applet-group.descriptor.js +19 -0
- package/dist/cli/applet-group.descriptor.js.map +1 -0
- package/dist/cli/applet-local-store.storage.descriptor.d.ts +66 -0
- package/dist/cli/applet-local-store.storage.descriptor.d.ts.map +1 -0
- package/dist/cli/applet-local-store.storage.descriptor.js +39 -0
- package/dist/cli/applet-local-store.storage.descriptor.js.map +1 -0
- package/dist/cli/applet-serve.process.descriptor.d.ts +37 -0
- package/dist/cli/applet-serve.process.descriptor.d.ts.map +1 -0
- package/dist/cli/applet-serve.process.descriptor.js +41 -0
- package/dist/cli/applet-serve.process.descriptor.js.map +1 -0
- package/dist/cli/commands/build.command.descriptor.d.ts +40 -0
- package/dist/cli/commands/build.command.descriptor.d.ts.map +1 -0
- package/dist/cli/commands/build.command.descriptor.js +52 -0
- package/dist/cli/commands/build.command.descriptor.js.map +1 -0
- package/dist/cli/commands/build.command.impl.d.ts +8 -0
- package/dist/cli/commands/build.command.impl.d.ts.map +1 -0
- package/dist/cli/commands/build.command.impl.js +116 -0
- package/dist/cli/commands/build.command.impl.js.map +1 -0
- package/dist/cli/commands/init.command.descriptor.d.ts +29 -0
- package/dist/cli/commands/init.command.descriptor.d.ts.map +1 -0
- package/dist/cli/commands/init.command.descriptor.js +23 -0
- package/dist/cli/commands/init.command.descriptor.js.map +1 -0
- package/dist/cli/commands/init.command.impl.d.ts +7 -0
- package/dist/cli/commands/init.command.impl.d.ts.map +1 -0
- package/dist/cli/commands/init.command.impl.js +64 -0
- package/dist/cli/commands/init.command.impl.js.map +1 -0
- package/dist/cli/commands/list.command.descriptor.d.ts +26 -0
- package/dist/cli/commands/list.command.descriptor.d.ts.map +1 -0
- package/dist/cli/commands/list.command.descriptor.js +22 -0
- package/dist/cli/commands/list.command.descriptor.js.map +1 -0
- package/dist/cli/commands/list.command.impl.d.ts +13 -0
- package/dist/cli/commands/list.command.impl.d.ts.map +1 -0
- package/dist/cli/commands/list.command.impl.js +47 -0
- package/dist/cli/commands/list.command.impl.js.map +1 -0
- package/dist/cli/commands/serve-stylesheet.d.ts +11 -0
- package/dist/cli/commands/serve-stylesheet.d.ts.map +1 -0
- package/dist/cli/commands/serve-stylesheet.js +50 -0
- package/dist/cli/commands/serve-stylesheet.js.map +1 -0
- package/dist/cli/commands/serve-surface.d.ts +36 -0
- package/dist/cli/commands/serve-surface.d.ts.map +1 -0
- package/dist/cli/commands/serve-surface.js +410 -0
- package/dist/cli/commands/serve-surface.js.map +1 -0
- package/dist/cli/commands/serve.command.descriptor.d.ts +42 -0
- package/dist/cli/commands/serve.command.descriptor.d.ts.map +1 -0
- package/dist/cli/commands/serve.command.descriptor.js +46 -0
- package/dist/cli/commands/serve.command.descriptor.js.map +1 -0
- package/dist/cli/commands/serve.command.impl.d.ts +110 -0
- package/dist/cli/commands/serve.command.impl.d.ts.map +1 -0
- package/dist/cli/commands/serve.command.impl.js +1105 -0
- package/dist/cli/commands/serve.command.impl.js.map +1 -0
- package/dist/cli/commands/serve.schemas.d.ts +127 -0
- package/dist/cli/commands/serve.schemas.d.ts.map +1 -0
- package/dist/cli/commands/serve.schemas.js +85 -0
- package/dist/cli/commands/serve.schemas.js.map +1 -0
- package/dist/cli/commands/served-applet-browser.d.ts +12 -0
- package/dist/cli/commands/served-applet-browser.d.ts.map +1 -0
- package/dist/cli/commands/served-applet-browser.js +48 -0
- package/dist/cli/commands/served-applet-browser.js.map +1 -0
- package/dist/cli/commands/served-applet-connection.d.ts +15 -0
- package/dist/cli/commands/served-applet-connection.d.ts.map +1 -0
- package/dist/cli/commands/served-applet-connection.js +25 -0
- package/dist/cli/commands/served-applet-connection.js.map +1 -0
- package/dist/cli/commands/served-applet-rpc.d.ts +16 -0
- package/dist/cli/commands/served-applet-rpc.d.ts.map +1 -0
- package/dist/cli/commands/served-applet-rpc.js +108 -0
- package/dist/cli/commands/served-applet-rpc.js.map +1 -0
- package/dist/cli/commands/served-applet-surface.d.ts +14 -0
- package/dist/cli/commands/served-applet-surface.d.ts.map +1 -0
- package/dist/cli/commands/served-applet-surface.js +99 -0
- package/dist/cli/commands/served-applet-surface.js.map +1 -0
- package/dist/cli/commands/templates.command.descriptor.d.ts +22 -0
- package/dist/cli/commands/templates.command.descriptor.d.ts.map +1 -0
- package/dist/cli/commands/templates.command.descriptor.js +21 -0
- package/dist/cli/commands/templates.command.descriptor.js.map +1 -0
- package/dist/cli/commands/templates.command.impl.d.ts +35 -0
- package/dist/cli/commands/templates.command.impl.d.ts.map +1 -0
- package/dist/cli/commands/templates.command.impl.js +59 -0
- package/dist/cli/commands/templates.command.impl.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/local-serve.authz-policy.descriptor.d.ts +22 -0
- package/dist/cli/local-serve.authz-policy.descriptor.d.ts.map +1 -0
- package/dist/cli/local-serve.authz-policy.descriptor.js +13 -0
- package/dist/cli/local-serve.authz-policy.descriptor.js.map +1 -0
- package/dist/cli/plugin.d.ts +19 -0
- package/dist/cli/plugin.d.ts.map +1 -0
- package/dist/cli/plugin.js +57 -0
- package/dist/cli/plugin.js.map +1 -0
- package/dist/cli/project-files.d.ts +48 -0
- package/dist/cli/project-files.d.ts.map +1 -0
- package/dist/cli/project-files.js +130 -0
- package/dist/cli/project-files.js.map +1 -0
- package/dist/client/contract.d.ts +113 -0
- package/dist/client/contract.d.ts.map +1 -0
- package/dist/client/contract.js +17 -0
- package/dist/client/contract.js.map +1 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +3 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/repository.context.d.ts +113 -0
- package/dist/client/repository.context.d.ts.map +1 -0
- package/dist/client/repository.context.js +8 -0
- package/dist/client/repository.context.js.map +1 -0
- package/dist/client/runtime-client.d.ts +22 -0
- package/dist/client/runtime-client.d.ts.map +1 -0
- package/dist/client/runtime-client.js +200 -0
- package/dist/client/runtime-client.js.map +1 -0
- package/dist/companion-path.d.ts +25 -0
- package/dist/companion-path.d.ts.map +1 -0
- package/dist/companion-path.js +90 -0
- package/dist/companion-path.js.map +1 -0
- package/dist/component-ref.d.ts +12 -0
- package/dist/component-ref.d.ts.map +1 -0
- package/dist/component-ref.js +45 -0
- package/dist/component-ref.js.map +1 -0
- package/dist/composition-plan.d.ts +92 -0
- package/dist/composition-plan.d.ts.map +1 -0
- package/dist/composition-plan.js +44 -0
- package/dist/composition-plan.js.map +1 -0
- package/dist/composition.d.ts +97 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js +99 -0
- package/dist/composition.js.map +1 -0
- package/dist/config/applets.config.descriptor.d.ts +55 -0
- package/dist/config/applets.config.descriptor.d.ts.map +1 -0
- package/dist/config/applets.config.descriptor.js +37 -0
- package/dist/config/applets.config.descriptor.js.map +1 -0
- package/dist/config/load.d.ts +4 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/load.js +12 -0
- package/dist/config/load.js.map +1 -0
- package/dist/content-path.d.ts +62 -0
- package/dist/content-path.d.ts.map +1 -0
- package/dist/content-path.js +121 -0
- package/dist/content-path.js.map +1 -0
- package/dist/contribution-registry.d.ts +270 -0
- package/dist/contribution-registry.d.ts.map +1 -0
- package/dist/contribution-registry.js +488 -0
- package/dist/contribution-registry.js.map +1 -0
- package/dist/docs/applets.docs.descriptor.d.ts +4 -0
- package/dist/docs/applets.docs.descriptor.d.ts.map +1 -0
- package/dist/docs/applets.docs.descriptor.js +74 -0
- package/dist/docs/applets.docs.descriptor.js.map +1 -0
- package/dist/host/local-host.contract.d.ts +7 -0
- package/dist/host/local-host.contract.d.ts.map +1 -0
- package/dist/host/local-host.contract.js +2 -0
- package/dist/host/local-host.contract.js.map +1 -0
- package/dist/host/local-host.impl.d.ts +9 -0
- package/dist/host/local-host.impl.d.ts.map +1 -0
- package/dist/host/local-host.impl.js +41 -0
- package/dist/host/local-host.impl.js.map +1 -0
- package/dist/host/local-host.plugin.d.ts +3 -0
- package/dist/host/local-host.plugin.d.ts.map +1 -0
- package/dist/host/local-host.plugin.js +16 -0
- package/dist/host/local-host.plugin.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/local-project.d.ts +106 -0
- package/dist/local-project.d.ts.map +1 -0
- package/dist/local-project.js +277 -0
- package/dist/local-project.js.map +1 -0
- package/dist/manifest-hash.d.ts +12 -0
- package/dist/manifest-hash.d.ts.map +1 -0
- package/dist/manifest-hash.js +76 -0
- package/dist/manifest-hash.js.map +1 -0
- package/dist/presentation.d.ts +43 -0
- package/dist/presentation.d.ts.map +1 -0
- package/dist/presentation.js +36 -0
- package/dist/presentation.js.map +1 -0
- package/dist/retention.d.ts +34 -0
- package/dist/retention.d.ts.map +1 -0
- package/dist/retention.js +51 -0
- package/dist/retention.js.map +1 -0
- package/dist/schemas/applet-messages.d.ts +97 -0
- package/dist/schemas/applet-messages.d.ts.map +1 -0
- package/dist/schemas/applet-messages.js +51 -0
- package/dist/schemas/applet-messages.js.map +1 -0
- package/dist/schemas/capabilities.d.ts +37 -0
- package/dist/schemas/capabilities.d.ts.map +1 -0
- package/dist/schemas/capabilities.js +28 -0
- package/dist/schemas/capabilities.js.map +1 -0
- package/dist/schemas/component-release-ref.d.ts +13 -0
- package/dist/schemas/component-release-ref.d.ts.map +1 -0
- package/dist/schemas/component-release-ref.js +21 -0
- package/dist/schemas/component-release-ref.js.map +1 -0
- package/dist/schemas/contributes.d.ts +306 -0
- package/dist/schemas/contributes.d.ts.map +1 -0
- package/dist/schemas/contributes.js +184 -0
- package/dist/schemas/contributes.js.map +1 -0
- package/dist/schemas/definition.d.ts +301 -0
- package/dist/schemas/definition.d.ts.map +1 -0
- package/dist/schemas/definition.js +457 -0
- package/dist/schemas/definition.js.map +1 -0
- package/dist/schemas/dispatch.d.ts +52 -0
- package/dist/schemas/dispatch.d.ts.map +1 -0
- package/dist/schemas/dispatch.js +32 -0
- package/dist/schemas/dispatch.js.map +1 -0
- package/dist/schemas/grants.d.ts +56 -0
- package/dist/schemas/grants.d.ts.map +1 -0
- package/dist/schemas/grants.js +64 -0
- package/dist/schemas/grants.js.map +1 -0
- package/dist/schemas/index.d.ts +16 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +16 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/instance.d.ts +230 -0
- package/dist/schemas/instance.d.ts.map +1 -0
- package/dist/schemas/instance.js +44 -0
- package/dist/schemas/instance.js.map +1 -0
- package/dist/schemas/lineage.d.ts +60 -0
- package/dist/schemas/lineage.d.ts.map +1 -0
- package/dist/schemas/lineage.js +52 -0
- package/dist/schemas/lineage.js.map +1 -0
- package/dist/schemas/observation.d.ts +70 -0
- package/dist/schemas/observation.d.ts.map +1 -0
- package/dist/schemas/observation.js +49 -0
- package/dist/schemas/observation.js.map +1 -0
- package/dist/schemas/patterns.d.ts +24 -0
- package/dist/schemas/patterns.d.ts.map +1 -0
- package/dist/schemas/patterns.js +24 -0
- package/dist/schemas/patterns.js.map +1 -0
- package/dist/schemas/publishing.d.ts +27 -0
- package/dist/schemas/publishing.d.ts.map +1 -0
- package/dist/schemas/publishing.js +20 -0
- package/dist/schemas/publishing.js.map +1 -0
- package/dist/schemas/run.d.ts +35 -0
- package/dist/schemas/run.d.ts.map +1 -0
- package/dist/schemas/run.js +26 -0
- package/dist/schemas/run.js.map +1 -0
- package/dist/schemas/share-link.d.ts +61 -0
- package/dist/schemas/share-link.d.ts.map +1 -0
- package/dist/schemas/share-link.js +68 -0
- package/dist/schemas/share-link.js.map +1 -0
- package/dist/schemas/state-events.d.ts +66 -0
- package/dist/schemas/state-events.d.ts.map +1 -0
- package/dist/schemas/state-events.js +66 -0
- package/dist/schemas/state-events.js.map +1 -0
- package/dist/schemas/subscription.d.ts +47 -0
- package/dist/schemas/subscription.d.ts.map +1 -0
- package/dist/schemas/subscription.js +46 -0
- package/dist/schemas/subscription.js.map +1 -0
- package/dist/schemas/template-revision.d.ts +53 -0
- package/dist/schemas/template-revision.d.ts.map +1 -0
- package/dist/schemas/template-revision.js +30 -0
- package/dist/schemas/template-revision.js.map +1 -0
- package/dist/selection.d.ts +22 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +21 -0
- package/dist/selection.js.map +1 -0
- package/dist/storage/index.d.ts +27 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +27 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/records.d.ts +28 -0
- package/dist/storage/records.d.ts.map +1 -0
- package/dist/storage/records.js +272 -0
- package/dist/storage/records.js.map +1 -0
- package/dist/svelte/applets-svelte.plugin.d.ts +38 -0
- package/dist/svelte/applets-svelte.plugin.d.ts.map +1 -0
- package/dist/svelte/applets-svelte.plugin.js +25 -0
- package/dist/svelte/components/AppletCanvas.svelte +488 -0
- package/dist/svelte/components/AppletCanvas.svelte.d.ts +53 -0
- package/dist/svelte/components/AppletCanvas.svelte.d.ts.map +1 -0
- package/dist/svelte/components/AppletCatalog.svelte +42 -0
- package/dist/svelte/components/AppletCatalog.svelte.d.ts +8 -0
- package/dist/svelte/components/AppletCatalog.svelte.d.ts.map +1 -0
- package/dist/svelte/components/AppletComposition.svelte +128 -0
- package/dist/svelte/components/AppletComposition.svelte.d.ts +30 -0
- package/dist/svelte/components/AppletComposition.svelte.d.ts.map +1 -0
- package/dist/svelte/components/AppletConsent.svelte +143 -0
- package/dist/svelte/components/AppletConsent.svelte.d.ts +12 -0
- package/dist/svelte/components/AppletConsent.svelte.d.ts.map +1 -0
- package/dist/svelte/components/LocalAppletView.svelte +102 -0
- package/dist/svelte/components/LocalAppletView.svelte.d.ts +13 -0
- package/dist/svelte/components/LocalAppletView.svelte.d.ts.map +1 -0
- package/dist/svelte/components/RunnableTracker.svelte +51 -0
- package/dist/svelte/components/RunnableTracker.svelte.d.ts +18 -0
- package/dist/svelte/components/RunnableTracker.svelte.d.ts.map +1 -0
- package/dist/svelte/index.d.ts +11 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +10 -0
- package/dist/svelte/lib/canvas-lifecycle.d.ts +68 -0
- package/dist/svelte/lib/canvas-lifecycle.d.ts.map +1 -0
- package/dist/svelte/lib/canvas-lifecycle.js +99 -0
- package/dist/svelte/lib/capability-review.d.ts +64 -0
- package/dist/svelte/lib/capability-review.d.ts.map +1 -0
- package/dist/svelte/lib/capability-review.js +77 -0
- package/dist/svelte/lib/message-protocol.d.ts +184 -0
- package/dist/svelte/lib/message-protocol.d.ts.map +1 -0
- package/dist/svelte/lib/message-protocol.js +251 -0
- package/dist/svelte/runnable-source.d.ts +5 -0
- package/dist/svelte/runnable-source.d.ts.map +1 -0
- package/dist/svelte/runnable-source.js +92 -0
- package/dist/svelte/serve-index.svelte +39 -0
- package/dist/svelte/serve-index.svelte.d.ts +10 -0
- package/dist/svelte/serve-index.svelte.d.ts.map +1 -0
- package/dist/svelte/state.svelte.d.ts +26 -0
- package/dist/svelte/state.svelte.d.ts.map +1 -0
- package/dist/svelte/state.svelte.js +74 -0
- package/dist/svelte/styles.css +4 -0
- package/dist/svelte/tool-widgets/AppletToolCard.svelte +148 -0
- package/dist/svelte/tool-widgets/AppletToolCard.svelte.d.ts +5 -0
- package/dist/svelte/tool-widgets/AppletToolCard.svelte.d.ts.map +1 -0
- package/dist/svelte/tool-widgets/applet-tool-card.d.ts +41 -0
- package/dist/svelte/tool-widgets/applet-tool-card.d.ts.map +1 -0
- package/dist/svelte/tool-widgets/applet-tool-card.js +167 -0
- package/dist/svelte/tool-widgets/index.d.ts +3 -0
- package/dist/svelte/tool-widgets/index.d.ts.map +1 -0
- package/dist/svelte/tool-widgets/index.js +2 -0
- package/dist/templates/index.d.ts +2 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +2 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/template.kind.d.ts +39 -0
- package/dist/templates/template.kind.d.ts.map +1 -0
- package/dist/templates/template.kind.js +29 -0
- package/dist/templates/template.kind.js.map +1 -0
- package/dist/tools/applet.descriptor.d.ts +196 -0
- package/dist/tools/applet.descriptor.d.ts.map +1 -0
- package/dist/tools/applet.descriptor.js +81 -0
- package/dist/tools/applet.descriptor.js.map +1 -0
- package/dist/tools/applet.impl.d.ts +32 -0
- package/dist/tools/applet.impl.d.ts.map +1 -0
- package/dist/tools/applet.impl.js +485 -0
- package/dist/tools/applet.impl.js.map +1 -0
- package/dist/tools/applet.schemas.d.ts +198 -0
- package/dist/tools/applet.schemas.d.ts.map +1 -0
- package/dist/tools/applet.schemas.js +136 -0
- package/dist/tools/applet.schemas.js.map +1 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/upgrade.d.ts +45 -0
- package/dist/upgrade.d.ts.map +1 -0
- package/dist/upgrade.js +179 -0
- package/dist/upgrade.js.map +1 -0
- package/dist/vite.d.ts +26 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +28 -0
- package/dist/vite.js.map +1 -0
- package/package.json +264 -4
- package/templates/applet-tour/Applet.svelte +95 -0
- package/templates/applet-tour/applet.descriptor.ts +15 -0
- package/templates/assembly-field/Applet.svelte +242 -0
- package/templates/assembly-field/applet.descriptor.ts +7 -0
- package/templates/assembly-field/applet.storage.json +47 -0
- package/templates/assembly-field/schemas/settings.ts +14 -0
- package/templates/assembly-field/settings.ts +32 -0
- package/templates/benchmark-comparison/Applet.svelte +17 -0
- package/templates/benchmark-comparison/applet.descriptor.ts +7 -0
- package/templates/file-diff-viewer/Applet.svelte +91 -0
- package/templates/file-diff-viewer/applet.descriptor.ts +14 -0
- package/templates/file-diff-viewer/diff.ts +63 -0
- package/templates/file-viewer/Applet.svelte +80 -0
- package/templates/file-viewer/applet.descriptor.ts +14 -0
- package/templates/fleet-runs/Applet.svelte +14 -0
- package/templates/fleet-runs/applet.descriptor.ts +7 -0
- package/templates/graph-runbook-tracker/Applet.svelte +13 -0
- package/templates/graph-runbook-tracker/applet.descriptor.ts +7 -0
- package/templates/runbook-tracker/Applet.svelte +12 -0
- package/templates/runbook-tracker/applet.descriptor.ts +7 -0
- package/templates/runnable-tracker/Applet.svelte +16 -0
- package/templates/runnable-tracker/applet.descriptor.ts +10 -0
- package/templates/storage-viewer/Applet.svelte +90 -0
- package/templates/storage-viewer/applet.descriptor.ts +15 -0
- package/templates/workspace-graph-runbook-tracker/Applet.svelte +13 -0
- package/templates/workspace-graph-runbook-tracker/applet.descriptor.ts +7 -0
|
@@ -0,0 +1,1105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vibes applet serve` implementation — ONE local web server for every applet.
|
|
3
|
+
*
|
|
4
|
+
* Single-origin by design: one local port (applets config `serveHostname` /
|
|
5
|
+
* `servePort`, default 127.0.0.1:5190) serves an index at `/`, each
|
|
6
|
+
* applet host page at `/applet/{id}/`, compiled entries, transpiled project
|
|
7
|
+
* sources, the handed file set, and every module the applet reaches. A
|
|
8
|
+
* second invocation probes the health endpoint first and REUSES a running
|
|
9
|
+
* server instead of multiplying ports. Wire bodies are schema-validated
|
|
10
|
+
* (serve.schemas.ts) — schema-first, not stringly-typed.
|
|
11
|
+
*
|
|
12
|
+
* Module serving mirrors the filesystem: a resolved file is served at
|
|
13
|
+
* `/@fs/<absolute path>`, so relative imports inside any package resolve in
|
|
14
|
+
* the browser exactly as they do on disk. Bare specifiers are resolved with
|
|
15
|
+
* the module system (exports maps and conditions), never by regex on the
|
|
16
|
+
* package name; `.svelte` compiles and `.ts` transpiles at request time.
|
|
17
|
+
*
|
|
18
|
+
* Runtime-first: the workspace root resolves through the runtime, and every
|
|
19
|
+
* response is served from disk at request time — new or edited applet
|
|
20
|
+
* projects appear without restarting the server.
|
|
21
|
+
*/
|
|
22
|
+
import { createCLIActionLifetime } from '@vibesdotdev/cli/providers';
|
|
23
|
+
import { writeStdoutLine } from '@vibesdotdev/cli/output';
|
|
24
|
+
import * as z from 'zod/v4';
|
|
25
|
+
import { requireRuntimeCwd, CaughtFailureSchema, isCapabilityError, CapabilityError } from '@vibesdotdev/kernel';
|
|
26
|
+
import { APPLET_SERVE_HOST_KIND } from '@vibesdotdev/kernel/kinds/applet-serve-host.kind';
|
|
27
|
+
import { getLogger } from '@vibesdotdev/logging';
|
|
28
|
+
import { statChecked } from '@vibesdotdev/filesystem';
|
|
29
|
+
import { getRuntimePath } from '@vibesdotdev/paths';
|
|
30
|
+
import { APPLET_SVELTE_RUNTIME_PROP, mediaTypeFor, readAppletLocalProject, resolveAppletCompanion } from '@vibesdotdev/applets';
|
|
31
|
+
import { appletProjectsDir, digestContents, listAppletProjectIds, readAppletProjectEntries, resolveAppletProjectHost, resolveAppletWorkspaceRoot } from "../project-files.js";
|
|
32
|
+
import { serveHealthSchema, servedAppletSchema, servedAppletFilesSchema } from "./serve.schemas.js";
|
|
33
|
+
import { collectServedStyleContents, compileServedStylesheet, prepareServedSvelte } from "./serve-stylesheet.js";
|
|
34
|
+
import { answerServedAppletSurface } from "./serve-surface.js";
|
|
35
|
+
import { createServedAppletRpc } from "./served-applet-rpc.js";
|
|
36
|
+
import { compile, compileModule } from 'svelte/compiler';
|
|
37
|
+
import { loadAppletsConfig } from "../../config/load.js";
|
|
38
|
+
const HEALTH_PATH = '/__vibes/applets/serve/health';
|
|
39
|
+
const FS_PREFIX = '/@fs';
|
|
40
|
+
const JSON_HEADERS = { 'content-type': 'application/json', 'cache-control': 'no-store' };
|
|
41
|
+
const JS_HEADERS = { 'content-type': 'text/javascript', 'cache-control': 'no-store' };
|
|
42
|
+
const CSS_HEADERS = { 'content-type': 'text/css', 'cache-control': 'no-store' };
|
|
43
|
+
const HTML_HEADERS = { 'content-type': 'text/html', 'cache-control': 'no-store' };
|
|
44
|
+
/** Both separators to one, so URL paths and containment checks are host-independent. */
|
|
45
|
+
function toPosix(path) {
|
|
46
|
+
return path.replaceAll('\\', '/');
|
|
47
|
+
}
|
|
48
|
+
/** The host-canonical path, or null when nothing is there. Existence is asked
|
|
49
|
+
* through the checked-stat outcome, not caught from a refusal, so only a
|
|
50
|
+
* confirmed absence answers null and every other failure surfaces. This is
|
|
51
|
+
* the HOST's own view: installed packages and this package's own sources
|
|
52
|
+
* routinely live outside the served workspace, which the workspace view
|
|
53
|
+
* refuses. Project content never uses this one. */
|
|
54
|
+
/** The nearest directory at or above `file` that holds a package.json, or null. */
|
|
55
|
+
async function packageRootOf(host, file) {
|
|
56
|
+
let dir = host.paths.dirname(file);
|
|
57
|
+
while (true) {
|
|
58
|
+
const manifest = await statChecked(host.hostFiles, host.paths.join(dir, 'package.json'));
|
|
59
|
+
if (manifest.found && manifest.stat.kind !== 'directory')
|
|
60
|
+
return dir;
|
|
61
|
+
const parent = host.paths.dirname(dir);
|
|
62
|
+
if (parent === dir)
|
|
63
|
+
return null;
|
|
64
|
+
dir = parent;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async function realpathOrNullHost(host, path) {
|
|
68
|
+
const checked = await statChecked(host.hostFiles, path);
|
|
69
|
+
if (!checked.found)
|
|
70
|
+
return null;
|
|
71
|
+
return (await host.hostFiles.realpath?.(path)) ?? path;
|
|
72
|
+
}
|
|
73
|
+
/** A host answer as text: an unencoded read is bytes; a text read passes through. */
|
|
74
|
+
function textOf(contents) {
|
|
75
|
+
return contents instanceof Uint8Array ? new TextDecoder().decode(contents) : contents;
|
|
76
|
+
}
|
|
77
|
+
/** A host read as a `Response` body: text passes through; bytes copy into an ArrayBuffer-backed view. */
|
|
78
|
+
function asBody(contents) {
|
|
79
|
+
if (!(contents instanceof Uint8Array))
|
|
80
|
+
return contents;
|
|
81
|
+
const copy = new Uint8Array(contents.length);
|
|
82
|
+
copy.set(contents);
|
|
83
|
+
return copy;
|
|
84
|
+
}
|
|
85
|
+
/** A file's bytes, or null when the path is absent or is not a regular file. */
|
|
86
|
+
async function readFileOrNull(host, path) {
|
|
87
|
+
const checked = await statChecked(host.files, path);
|
|
88
|
+
if (!checked.found || checked.stat.kind !== 'file')
|
|
89
|
+
return null;
|
|
90
|
+
return host.files.readFile(path);
|
|
91
|
+
}
|
|
92
|
+
/** `readFileOrNull` through the HOST's own view, for installed-package bytes.
|
|
93
|
+
* Project companions stay on the workspace view (`readFileOrNull`). */
|
|
94
|
+
async function readFileOrNullHost(host, path) {
|
|
95
|
+
const checked = await statChecked(host.hostFiles, path);
|
|
96
|
+
if (!checked.found || checked.stat.kind !== 'file')
|
|
97
|
+
return null;
|
|
98
|
+
return host.hostFiles.readFile(path);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Whether a `--root` is absolute without asking the path kind first: POSIX
|
|
102
|
+
* `/x`, or a Windows drive/UNC root. The capture-free order matters — a
|
|
103
|
+
* RELATIVE root must fail closed on a host with no captured cwd before any
|
|
104
|
+
* other kind is consulted.
|
|
105
|
+
*/
|
|
106
|
+
function looksAbsolute(root) {
|
|
107
|
+
return root.startsWith('/') || root.startsWith('\\\\') || /^[A-Za-z]:[\\/]/.test(root);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* An explicit `--root` wins (a managed process names its workspace; a relative
|
|
111
|
+
* one resolves against where the command ran). Otherwise the runtime's bound
|
|
112
|
+
* workspace answers, so an unbound host fails `workspace-unbound` before any
|
|
113
|
+
* filesystem kind is asked for.
|
|
114
|
+
*/
|
|
115
|
+
async function resolveRootOption(runtime, root) {
|
|
116
|
+
const cwd = looksAbsolute(root) ? undefined : requireRuntimeCwd(runtime);
|
|
117
|
+
const paths = await getRuntimePath(runtime);
|
|
118
|
+
return cwd === undefined ? paths.resolve(root) : paths.resolve(cwd, root);
|
|
119
|
+
}
|
|
120
|
+
/** Modules a plain page cannot resolve, answered with the browser truth:
|
|
121
|
+
* `esm-env` selects its build by export condition, and the kit's `/svelte`
|
|
122
|
+
* barrel carries a few SvelteKit-bound components that import the `$app`
|
|
123
|
+
* and `$env` virtuals. Outside SvelteKit those are inert stand-ins. */
|
|
124
|
+
const shimSpecifierSchema = z.enum(['esm-env', '$app/state', '$app/navigation', '$env/dynamic/public', '$env/dynamic/private']);
|
|
125
|
+
const SHIMS = {
|
|
126
|
+
'esm-env': 'export const BROWSER = true;\nexport const DEV = true;\nexport const NODE = false;\n',
|
|
127
|
+
'$app/state': [
|
|
128
|
+
'export const page = { url: new URL(location.href), params: {}, route: { id: null }, status: 200, error: null, data: {}, form: null, state: {} };',
|
|
129
|
+
'export const navigating = { from: null, to: null, type: null, willUnload: false, delta: null, complete: Promise.resolve() };',
|
|
130
|
+
'export const updated = { current: false, check: async () => false };'
|
|
131
|
+
].join('\n') + '\n',
|
|
132
|
+
'$app/navigation': [
|
|
133
|
+
'export const goto = async (url) => { location.href = String(url); };',
|
|
134
|
+
'export const invalidate = async () => {}; export const invalidateAll = async () => {};',
|
|
135
|
+
'export const preloadData = async () => ({ type: "loaded", status: 200, data: {} }); export const preloadCode = async () => {};',
|
|
136
|
+
'export const beforeNavigate = () => {}; export const afterNavigate = () => {}; export const onNavigate = () => {};',
|
|
137
|
+
'export const pushState = () => {}; export const replaceState = () => {}; export const disableScrollHandling = () => {};'
|
|
138
|
+
].join('\n') + '\n',
|
|
139
|
+
'$env/dynamic/public': 'export const env = {};\n',
|
|
140
|
+
'$env/dynamic/private': 'export const env = {};\n'
|
|
141
|
+
};
|
|
142
|
+
const SHIM_PREFIX = '/@shim/';
|
|
143
|
+
/**
|
|
144
|
+
* A stylesheet a module imports (`import 'pkg/style.css'`, `import './x.css'`): the page cannot load
|
|
145
|
+
* CSS as a module, so the import is rewritten to this prefix plus the sheet's own URL, which answers a
|
|
146
|
+
* module that adds the sheet once as a `<link>`.
|
|
147
|
+
*/
|
|
148
|
+
const CSS_MODULE_PREFIX = '/@css-module';
|
|
149
|
+
/** A `?raw` import (Vite's convention, kept in built packages): the file's text as a module's default export. */
|
|
150
|
+
const RAW_MODULE_PREFIX = '/@raw-module';
|
|
151
|
+
/** A `?worker` import (Vite's convention, kept in built packages): a `Worker` constructor for the resolved module. */
|
|
152
|
+
const WORKER_MODULE_PREFIX = '/@worker-module';
|
|
153
|
+
function cssModule(sheetUrl) {
|
|
154
|
+
const href = JSON.stringify(sheetUrl);
|
|
155
|
+
return `if (!document.querySelector(\`link[data-applet-css=\${JSON.stringify(${href})}]\`)) {\n\tconst link = document.createElement('link');\n\tlink.rel = 'stylesheet';\n\tlink.href = ${href};\n\tlink.dataset.appletCss = ${href};\n\tdocument.head.appendChild(link);\n}\nexport {};\n`;
|
|
156
|
+
}
|
|
157
|
+
function workerModule(workerUrl) {
|
|
158
|
+
const href = JSON.stringify(workerUrl);
|
|
159
|
+
return `export default class ServedModuleWorker extends Worker {\n\tconstructor(options) {\n\t\tsuper(${href}, { type: 'module', name: options?.name, credentials: options?.credentials });\n\t}\n}\n`;
|
|
160
|
+
}
|
|
161
|
+
function shimUrl(specifier) {
|
|
162
|
+
return SHIM_PREFIX + encodeURIComponent(specifier) + '.js';
|
|
163
|
+
}
|
|
164
|
+
/** Export conditions a plain browser page satisfies, most specific first. */
|
|
165
|
+
const BROWSER_CONDITIONS = ['browser', 'svelte', 'import', 'module', 'default'];
|
|
166
|
+
const STYLESHEET_CONDITIONS = ['style', ...BROWSER_CONDITIONS];
|
|
167
|
+
const exportEntrySchema = z.union([
|
|
168
|
+
z.string(),
|
|
169
|
+
z.array(z.lazy(() => exportEntrySchema)),
|
|
170
|
+
z.record(z.string(), z.lazy(() => exportEntrySchema).optional())
|
|
171
|
+
]);
|
|
172
|
+
const exportsMapSchema = z.record(z.string(), exportEntrySchema);
|
|
173
|
+
const packageJsonSchema = z.looseObject({
|
|
174
|
+
exports: z.unknown().optional(),
|
|
175
|
+
module: z.string().optional(),
|
|
176
|
+
browser: z.unknown().optional(),
|
|
177
|
+
main: z.string().optional()
|
|
178
|
+
});
|
|
179
|
+
function isExportString(entry) {
|
|
180
|
+
return typeof entry === 'string';
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Walk an exports map entry to its relative files for the given conditions,
|
|
184
|
+
* most preferred first. A published package can name a condition target it
|
|
185
|
+
* does not ship (Kit's `style` points at `src/`, which only `dist/` publishes),
|
|
186
|
+
* so the caller takes the first candidate that exists rather than the first
|
|
187
|
+
* condition that matches.
|
|
188
|
+
*/
|
|
189
|
+
function exportCandidates(entry, conditions = BROWSER_CONDITIONS) {
|
|
190
|
+
if (entry === undefined)
|
|
191
|
+
return [];
|
|
192
|
+
if (isExportString(entry))
|
|
193
|
+
return [entry];
|
|
194
|
+
if (Array.isArray(entry))
|
|
195
|
+
return entry.flatMap((item) => exportCandidates(item, conditions));
|
|
196
|
+
return conditions.flatMap((condition) => (condition in entry ? exportCandidates(entry[condition], conditions) : []));
|
|
197
|
+
}
|
|
198
|
+
function splitSpecifier(specifier) {
|
|
199
|
+
const parts = specifier.split('/');
|
|
200
|
+
const name = specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
|
|
201
|
+
const subpath = specifier.slice(name.length); // '' or '/x/y'
|
|
202
|
+
return { name, subpath };
|
|
203
|
+
}
|
|
204
|
+
async function findPackageRoot(host, name, fromDir) {
|
|
205
|
+
let dir = fromDir;
|
|
206
|
+
while (true) {
|
|
207
|
+
const candidate = host.paths.join(dir, 'node_modules', name);
|
|
208
|
+
// `node_modules/<name>` is host-owned and routinely sits outside the
|
|
209
|
+
// workspace, so this walk reads the host's own filesystem, not the
|
|
210
|
+
// workspace-rooted view, which would refuse the whole subtree.
|
|
211
|
+
if (await host.hostFiles.exists(host.paths.join(candidate, 'package.json')))
|
|
212
|
+
return candidate;
|
|
213
|
+
const parent = host.paths.dirname(dir);
|
|
214
|
+
if (parent === dir)
|
|
215
|
+
return null;
|
|
216
|
+
dir = parent;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const PackageNameSchema = z.looseObject({ name: z.string().optional() });
|
|
220
|
+
/**
|
|
221
|
+
* The package a directory sits in, when that package is `name` (a package importing its own name,
|
|
222
|
+
* as Node resolves a self-reference): the served kit checkout imports `@vibesdotdev/kit/...` with
|
|
223
|
+
* no `node_modules` link to itself, and must still resolve through its own exports map.
|
|
224
|
+
*/
|
|
225
|
+
async function selfPackageRoot(host, name, fromDir) {
|
|
226
|
+
let dir = fromDir;
|
|
227
|
+
while (true) {
|
|
228
|
+
const manifest = host.paths.join(dir, 'package.json');
|
|
229
|
+
if (await host.hostFiles.exists(manifest)) {
|
|
230
|
+
const parsed = PackageNameSchema.safeParse(JSON.parse(textOf(await host.hostFiles.readFile(manifest, 'utf8'))));
|
|
231
|
+
return parsed.success && parsed.data.name === name ? dir : null;
|
|
232
|
+
}
|
|
233
|
+
const parent = host.paths.dirname(dir);
|
|
234
|
+
if (parent === dir)
|
|
235
|
+
return null;
|
|
236
|
+
dir = parent;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Resolve a bare specifier for a browser: the package's exports map under
|
|
241
|
+
* browser conditions first (so `svelte` yields the client build, not the
|
|
242
|
+
* server one), then the module system's own answer as the fallback.
|
|
243
|
+
*/
|
|
244
|
+
async function resolveForBrowser(host, specifier, fromDir, fallback, conditions = BROWSER_CONDITIONS) {
|
|
245
|
+
const { name, subpath } = splitSpecifier(specifier);
|
|
246
|
+
const root = (await findPackageRoot(host, name, fromDir)) ?? (await selfPackageRoot(host, name, fromDir));
|
|
247
|
+
if (root) {
|
|
248
|
+
// `findPackageRoot` saw this package.json; a read or parse failure is a
|
|
249
|
+
// broken install and surfaces rather than resolving as if it had no exports.
|
|
250
|
+
const pkg = packageJsonSchema.parse(JSON.parse(textOf(await host.hostFiles.readFile(host.paths.join(root, 'package.json'), 'utf8'))));
|
|
251
|
+
const parsedMap = exportsMapSchema.safeParse(pkg.exports);
|
|
252
|
+
if (parsedMap.success) {
|
|
253
|
+
const map = parsedMap.data;
|
|
254
|
+
const key = '.' + subpath;
|
|
255
|
+
const candidates = exportCandidates(map[key], conditions);
|
|
256
|
+
if (candidates.length === 0) {
|
|
257
|
+
// One-level `*` patterns, e.g. "./components/*": "./dist/components/*"
|
|
258
|
+
for (const [pattern, target] of Object.entries(map)) {
|
|
259
|
+
const star = pattern.indexOf('*');
|
|
260
|
+
if (star < 0)
|
|
261
|
+
continue;
|
|
262
|
+
const head = pattern.slice(0, star), tail = pattern.slice(star + 1);
|
|
263
|
+
if (key.startsWith(head) && key.endsWith(tail) && key.length >= head.length + tail.length) {
|
|
264
|
+
const filled = key.slice(head.length, key.length - tail.length);
|
|
265
|
+
const picked = exportCandidates(target, conditions);
|
|
266
|
+
if (picked.length > 0) {
|
|
267
|
+
candidates.push(...picked.map((rel) => rel.replace('*', filled)));
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
for (const rel of candidates) {
|
|
274
|
+
const abs = host.paths.resolve(root, rel);
|
|
275
|
+
if (await host.hostFiles.exists(abs))
|
|
276
|
+
return (await realpathOrNullHost(host, abs)) ?? abs;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else if (!subpath) {
|
|
280
|
+
const rel = z.string().safeParse(pkg.browser).data ?? pkg.module ?? pkg.main;
|
|
281
|
+
if (rel) {
|
|
282
|
+
const abs = host.paths.resolve(root, rel);
|
|
283
|
+
if (await host.hostFiles.exists(abs))
|
|
284
|
+
return (await realpathOrNullHost(host, abs)) ?? abs;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const fallen = fallback(specifier, fromDir);
|
|
289
|
+
if (!fallen)
|
|
290
|
+
return null;
|
|
291
|
+
// Real paths only: a symlinked package must resolve its own dependencies
|
|
292
|
+
// from where it actually lives, not from where it was linked.
|
|
293
|
+
if (!(await host.hostFiles.exists(fallen)))
|
|
294
|
+
return fallen;
|
|
295
|
+
return (await realpathOrNullHost(host, fallen)) ?? fallen;
|
|
296
|
+
}
|
|
297
|
+
/** A relative specifier a browser cannot follow (no extension, or a
|
|
298
|
+
* directory) is pinned to the file it means on disk. */
|
|
299
|
+
async function completeRelative(host, specifier, fromDir) {
|
|
300
|
+
const abs = host.paths.resolve(fromDir, specifier);
|
|
301
|
+
const exact = await statChecked(host.hostFiles, abs);
|
|
302
|
+
if (host.paths.extname(abs) && exact.found && exact.stat.kind !== 'directory')
|
|
303
|
+
return null; // already exact
|
|
304
|
+
// TypeScript sources import their siblings by the `.js` name they compile to (`./utils.js` for utils.ts).
|
|
305
|
+
if (abs.endsWith('.js') && !exact.found) {
|
|
306
|
+
const typed = await statChecked(host.hostFiles, abs.replace(/\.js$/, '.ts'));
|
|
307
|
+
if (typed.found && typed.stat.kind !== 'directory')
|
|
308
|
+
return specifier.replace(/\.js$/, '.ts');
|
|
309
|
+
}
|
|
310
|
+
const candidates = [
|
|
311
|
+
abs + '.js',
|
|
312
|
+
abs + '.ts',
|
|
313
|
+
abs + '.svelte',
|
|
314
|
+
host.paths.join(abs, 'index.js'),
|
|
315
|
+
host.paths.join(abs, 'index.ts')
|
|
316
|
+
];
|
|
317
|
+
for (const candidate of candidates) {
|
|
318
|
+
const checked = await statChecked(host.hostFiles, candidate);
|
|
319
|
+
if (checked.found && checked.stat.kind !== 'directory') {
|
|
320
|
+
const rel = specifier.endsWith('/') ? specifier.slice(0, -1) : specifier;
|
|
321
|
+
return rel + toPosix(candidate.slice(abs.length));
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
function toFsUrl(absolutePath) {
|
|
327
|
+
return FS_PREFIX + absolutePath.split(/[\\/]/).map(encodeURIComponent).join('/');
|
|
328
|
+
}
|
|
329
|
+
function fromFsUrl(host, path) {
|
|
330
|
+
if (!path.startsWith(FS_PREFIX + '/'))
|
|
331
|
+
return null;
|
|
332
|
+
return host.paths.resolve(path.slice(FS_PREFIX.length).split('/').map(decodeURIComponent).join('/'));
|
|
333
|
+
}
|
|
334
|
+
function isRelative(specifier) {
|
|
335
|
+
return specifier.startsWith('./') || specifier.startsWith('../') || specifier.startsWith('/');
|
|
336
|
+
}
|
|
337
|
+
function replaceSpecifier(code, specifier, target) {
|
|
338
|
+
return code.split(`'${specifier}'`).join(`'${target}'`).split(`"${specifier}"`).join(`"${target}"`);
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* The URL one resolved companion is served at.
|
|
342
|
+
*
|
|
343
|
+
* Compiled modules get a `.js` suffix on their SOURCE path (so `x.svelte` is
|
|
344
|
+
* `x.svelte.js`, a rune `x.svelte.ts` is `x.svelte.ts.js`, a rune `x.svelte.js`
|
|
345
|
+
* is `x.svelte.js.js`), which keeps every source unambiguous under one route
|
|
346
|
+
* table. TypeScript is compiled to `x.js`, the convention the route already had.
|
|
347
|
+
* Everything else — css, md, images — is served at its own path as bytes.
|
|
348
|
+
*/
|
|
349
|
+
function companionTarget(projectId, path) {
|
|
350
|
+
if (path.endsWith('.svelte.ts') || path.endsWith('.svelte.js') || path.endsWith('.svelte')) {
|
|
351
|
+
return `/applet/${projectId}/${path}.js`;
|
|
352
|
+
}
|
|
353
|
+
if (path.endsWith('.ts'))
|
|
354
|
+
return `/applet/${projectId}/${path.slice(0, -3)}.js`;
|
|
355
|
+
return `/applet/${projectId}/${path}`;
|
|
356
|
+
}
|
|
357
|
+
/** Rewrite every specifier in a served module to a URL this server answers. */
|
|
358
|
+
export async function rewriteModule(code, ctx) {
|
|
359
|
+
for (const specifier of ctx.tools.scan(code)) {
|
|
360
|
+
let target = null;
|
|
361
|
+
if (specifier.endsWith('?raw') && isRelative(specifier) && !ctx.project) {
|
|
362
|
+
// A vendor module's raw text import: the file beside it, served as a string module.
|
|
363
|
+
code = replaceSpecifier(code, specifier, RAW_MODULE_PREFIX + toFsUrl(ctx.host.paths.resolve(ctx.fromDirs[0], specifier.slice(0, -'?raw'.length))));
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (specifier.endsWith('?worker') && !ctx.project) {
|
|
367
|
+
// A vendor module's web-worker constructor import: resolve the underlying worker script and wrap it.
|
|
368
|
+
const bare = specifier.slice(0, -'?worker'.length);
|
|
369
|
+
let workerTarget = null;
|
|
370
|
+
if (isRelative(bare)) {
|
|
371
|
+
const completed = (await completeRelative(ctx.host, bare, ctx.fromDirs[0])) ?? bare;
|
|
372
|
+
workerTarget = toFsUrl(ctx.host.paths.resolve(ctx.fromDirs[0], completed));
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
for (const dir of ctx.fromDirs) {
|
|
376
|
+
const abs = await resolveForBrowser(ctx.host, bare, dir, ctx.tools.resolve);
|
|
377
|
+
if (abs) {
|
|
378
|
+
await ctx.admitResolved?.(abs);
|
|
379
|
+
workerTarget = toFsUrl(abs);
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (workerTarget) {
|
|
385
|
+
code = replaceSpecifier(code, specifier, WORKER_MODULE_PREFIX + workerTarget);
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (isRelative(specifier)) {
|
|
390
|
+
if (ctx.project) {
|
|
391
|
+
// A2b: companions resolve RELATIVE to the importing file, through the
|
|
392
|
+
// same pure resolver every host uses — extensionless names complete,
|
|
393
|
+
// nested `../` paths work, and binary media is served as bytes.
|
|
394
|
+
const resolved = resolveAppletCompanion({
|
|
395
|
+
importer: ctx.project.importer,
|
|
396
|
+
specifier,
|
|
397
|
+
files: ctx.project.files
|
|
398
|
+
});
|
|
399
|
+
if (!resolved.ok)
|
|
400
|
+
continue;
|
|
401
|
+
target = companionTarget(ctx.project.id, resolved.path);
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
// Vendor modules: the URL mirrors the disk, so relative imports
|
|
405
|
+
// resolve as they do on disk — once they name an actual file.
|
|
406
|
+
target = await completeRelative(ctx.host, specifier, ctx.fromDirs[0]);
|
|
407
|
+
if (!target)
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
else if (shimSpecifierSchema.safeParse(specifier).success) {
|
|
412
|
+
target = shimUrl(specifier);
|
|
413
|
+
}
|
|
414
|
+
else if (specifier === '@vibesdotdev/applets/messages') {
|
|
415
|
+
target = '/applets/messages.js';
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
for (const dir of ctx.fromDirs) {
|
|
419
|
+
const abs = await resolveForBrowser(ctx.host, specifier, dir, ctx.tools.resolve);
|
|
420
|
+
if (abs) {
|
|
421
|
+
await ctx.admitResolved?.(abs);
|
|
422
|
+
target = toFsUrl(abs);
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (!target)
|
|
427
|
+
continue; // leave it; the browser reports the unresolved specifier by name
|
|
428
|
+
}
|
|
429
|
+
// A stylesheet import becomes the module that links it.
|
|
430
|
+
if (target.endsWith('.css'))
|
|
431
|
+
target = CSS_MODULE_PREFIX + target;
|
|
432
|
+
code = replaceSpecifier(code, specifier, target);
|
|
433
|
+
}
|
|
434
|
+
// The selected local host resolves browser build constants with its native compiler.
|
|
435
|
+
return ctx.tools.transpile(code);
|
|
436
|
+
}
|
|
437
|
+
function indexHostPage() {
|
|
438
|
+
return `<!doctype html>
|
|
439
|
+
<html><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
440
|
+
<title>Local applets</title>
|
|
441
|
+
<link rel="stylesheet" href="/kit/styles.css"/>
|
|
442
|
+
<style>html,body{margin:0;height:100%;background:#0d1117;color:#e6edf3}</style>
|
|
443
|
+
</head><body><div id="app"></div>
|
|
444
|
+
<script type="module">
|
|
445
|
+
const health = await fetch('/__vibes/applets/serve/health').then(r => r.json());
|
|
446
|
+
const { mount } = await import('/serve/index.js');
|
|
447
|
+
mount(document.getElementById('app'), { applets: health.applets });
|
|
448
|
+
</script>
|
|
449
|
+
</body></html>`;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* One applet's page. The applet mounts with its root surface as the `vibes`
|
|
453
|
+
* prop (the delivery contract in `browser-runtime.ts`); it used to mount with
|
|
454
|
+
* empty props, so a local applet had no runtime surface at all.
|
|
455
|
+
*/
|
|
456
|
+
export function hostPage(id, surfaceClientUrl) {
|
|
457
|
+
return `<!doctype html>
|
|
458
|
+
<html><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
459
|
+
<title>Applet — ${id}</title>
|
|
460
|
+
<link rel="stylesheet" href="/kit/styles.css?project=${id}"/><style>html,body{margin:0;height:100%;background:#0d1117}#app{height:100vh}</style>
|
|
461
|
+
</head><body><div id="runtime-status" role="status"></div><button id="runtime-reconnect" type="button" hidden>Reconnect runtime</button><p id="runtime-failure" role="alert" hidden></p><p id="runtime-cleanup-failure" role="alert" hidden></p><div id="app"></div>
|
|
462
|
+
<script type="module">
|
|
463
|
+
const status = document.getElementById('runtime-status');
|
|
464
|
+
const retry = document.getElementById('runtime-reconnect');
|
|
465
|
+
const failure = document.getElementById('runtime-failure');
|
|
466
|
+
const cleanupFailure = document.getElementById('runtime-cleanup-failure');
|
|
467
|
+
let connection;
|
|
468
|
+
let mounted;
|
|
469
|
+
let pending;
|
|
470
|
+
let releasing;
|
|
471
|
+
let disposed = false;
|
|
472
|
+
function report(error, target = failure) {
|
|
473
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
474
|
+
const detail = target === cleanupFailure ? 'Previous runtime connection cleanup: ' + message : message;
|
|
475
|
+
target.textContent = [target.textContent, detail].filter(Boolean).join('\\n');
|
|
476
|
+
target.hidden = false;
|
|
477
|
+
}
|
|
478
|
+
function release() {
|
|
479
|
+
if (releasing) return releasing;
|
|
480
|
+
const previousMount = mounted;
|
|
481
|
+
const previousConnection = connection;
|
|
482
|
+
mounted = undefined;
|
|
483
|
+
connection = undefined;
|
|
484
|
+
releasing = Promise.allSettled([
|
|
485
|
+
(async () => { if (previousMount !== undefined) await unmount(previousMount); })(),
|
|
486
|
+
(async () => { await previousConnection?.close(); })()
|
|
487
|
+
]).then(results => {
|
|
488
|
+
for (const result of results) if (result.status === 'rejected') report(result.reason, cleanupFailure);
|
|
489
|
+
}).finally(() => { releasing = undefined; });
|
|
490
|
+
return releasing;
|
|
491
|
+
}
|
|
492
|
+
async function connect() {
|
|
493
|
+
if (pending || disposed) return pending;
|
|
494
|
+
pending = Promise.resolve().then(async () => {
|
|
495
|
+
retry.disabled = true;
|
|
496
|
+
failure.hidden = true;
|
|
497
|
+
failure.textContent = '';
|
|
498
|
+
status.textContent = 'Connecting to the runtime.';
|
|
499
|
+
await release();
|
|
500
|
+
if (disposed) return;
|
|
501
|
+
const opened = await openServedApplet({ projectId: '${id}', origin: location.origin, fetch: (url, init) => fetch(url, init) });
|
|
502
|
+
if (disposed) { await opened.close(); return; }
|
|
503
|
+
connection = opened;
|
|
504
|
+
const props = readAppletLocation(AppletPresentationSchema.parse({ id: '${id}', title: '${id}' }), undefined, new URL(location.href));
|
|
505
|
+
mounted = mount(document.getElementById('app'), { ...props, ${APPLET_SVELTE_RUNTIME_PROP}: opened.surface });
|
|
506
|
+
status.textContent = '';
|
|
507
|
+
retry.hidden = true;
|
|
508
|
+
void opened.closed.then(() => {
|
|
509
|
+
if (connection !== opened || disposed) return;
|
|
510
|
+
const released = release();
|
|
511
|
+
status.textContent = 'Runtime connection closed. Reconnect to read the recorded work.';
|
|
512
|
+
retry.hidden = false;
|
|
513
|
+
return released;
|
|
514
|
+
}).catch(error => report(error, cleanupFailure));
|
|
515
|
+
window.dispatchEvent(new MessageEvent('message', { data: { type: 'files', files: files.files } }));
|
|
516
|
+
}).catch(async error => {
|
|
517
|
+
await release();
|
|
518
|
+
report(error);
|
|
519
|
+
status.textContent = 'Runtime connection unavailable.';
|
|
520
|
+
retry.hidden = false;
|
|
521
|
+
}).finally(() => { retry.disabled = false; pending = undefined; });
|
|
522
|
+
return pending;
|
|
523
|
+
}
|
|
524
|
+
window.addEventListener('pagehide', () => {
|
|
525
|
+
disposed = true;
|
|
526
|
+
void (async () => { await pending; await release(); })().catch(report);
|
|
527
|
+
}, { once: true });
|
|
528
|
+
const files = await fetch('/applet/${id}/files').then(r => r.json());
|
|
529
|
+
const { openServedApplet, readAppletLocation, AppletPresentationSchema } = await import('${surfaceClientUrl}');
|
|
530
|
+
const { mount, unmount } = await import('/applet/${id}/applet.js');
|
|
531
|
+
retry.addEventListener('click', connect);
|
|
532
|
+
await connect();
|
|
533
|
+
</script>
|
|
534
|
+
</body></html>`;
|
|
535
|
+
}
|
|
536
|
+
/** Svelte compiles a component to `export default function <Name>()`. Hosts
|
|
537
|
+
* need mount(element, props); expose Svelte's mount under that shape. */
|
|
538
|
+
function withMountAdapter(code, svelteUrl) {
|
|
539
|
+
const defaultExport = /export default function ([A-Za-z_$][\w$]*)/.exec(code);
|
|
540
|
+
if (!defaultExport)
|
|
541
|
+
return code;
|
|
542
|
+
return (code +
|
|
543
|
+
`\nimport { mount as __svelte_mount, unmount as __svelte_unmount } from '${svelteUrl}';\n` +
|
|
544
|
+
'export function mount(target, props = {}) {\n' +
|
|
545
|
+
`\treturn __svelte_mount(${defaultExport[1]}, { target, props });\n` +
|
|
546
|
+
'}\n' +
|
|
547
|
+
'export function unmount(instance) { return __svelte_unmount(instance); }\n');
|
|
548
|
+
}
|
|
549
|
+
/** Load one project from disk and compile its entry for the browser. */
|
|
550
|
+
export async function loadProject(workspaceRoot, id, ctx, svelteUrl) {
|
|
551
|
+
let entries;
|
|
552
|
+
const projectRead = await (async () => {
|
|
553
|
+
try {
|
|
554
|
+
return { ok: true, entries: await readAppletProjectEntries(workspaceRoot, id, ctx.host) };
|
|
555
|
+
}
|
|
556
|
+
catch (error) {
|
|
557
|
+
return { ok: false, error };
|
|
558
|
+
}
|
|
559
|
+
})();
|
|
560
|
+
if (!projectRead.ok) {
|
|
561
|
+
// No such project: 404 at the route layer. An unreadable one surfaces,
|
|
562
|
+
// and so does a failure no schema can read — the original, never a
|
|
563
|
+
// parse error about it.
|
|
564
|
+
const parsed = CaughtFailureSchema.safeParse(projectRead.error);
|
|
565
|
+
const failure = parsed.success ? parsed.data : null;
|
|
566
|
+
if (failure && isCapabilityError(failure) && failure.reason === 'applet-project-not-found')
|
|
567
|
+
return null;
|
|
568
|
+
throw projectRead.error;
|
|
569
|
+
}
|
|
570
|
+
entries = projectRead.entries;
|
|
571
|
+
const result = await readAppletLocalProject({ id, entries, digest: digestContents });
|
|
572
|
+
if (!result.ok)
|
|
573
|
+
return null;
|
|
574
|
+
const project = result.project;
|
|
575
|
+
const files = project.files.map((file) => file.path);
|
|
576
|
+
const filename = ctx.host.paths.join(appletProjectsDir(workspaceRoot, ctx.host), id, project.entry);
|
|
577
|
+
const prepared = await prepareServedSvelte(project.sources[project.entry], {
|
|
578
|
+
path: filename, base: ctx.host.paths.dirname(filename)
|
|
579
|
+
}, ctx.tools.compileStyle);
|
|
580
|
+
const compiled = compile(prepared, {
|
|
581
|
+
filename: project.entry,
|
|
582
|
+
generate: 'client',
|
|
583
|
+
css: 'injected'
|
|
584
|
+
});
|
|
585
|
+
const code = withMountAdapter(await rewriteModule(compiled.js.code, {
|
|
586
|
+
...ctx,
|
|
587
|
+
project: { id, sources: project.sources, files, importer: project.entry }
|
|
588
|
+
}), svelteUrl);
|
|
589
|
+
return { id, entry: project.entry, sources: project.sources, files, compiledEntry: code };
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* One companion route for one loaded project — the body of the applet-scoped
|
|
593
|
+
* module and asset routes.
|
|
594
|
+
*
|
|
595
|
+
* A2b: companions resolve relative to the importing file. Compiled modules are
|
|
596
|
+
* addressed by their SOURCE path plus a `.js` suffix, so `x.svelte`, rune
|
|
597
|
+
* `x.svelte.ts` and rune `x.svelte.js` never collide under one route table.
|
|
598
|
+
* Everything else — css, md, svg, images — is served as BYTES with its declared
|
|
599
|
+
* media type. Returns null when no branch matches; the caller answers 404.
|
|
600
|
+
*
|
|
601
|
+
* Exported so a test can drive the REAL route pipeline without a socket.
|
|
602
|
+
*/
|
|
603
|
+
export async function serveAppletCompanion(input) {
|
|
604
|
+
const { workspaceRoot, id, project, rest, tools, host, resolveDirs, admitResolved } = input;
|
|
605
|
+
const projectCtx = (importer) => ({
|
|
606
|
+
tools,
|
|
607
|
+
host,
|
|
608
|
+
fromDirs: resolveDirs,
|
|
609
|
+
admitResolved,
|
|
610
|
+
project: { id, sources: project.sources, files: project.files, importer }
|
|
611
|
+
});
|
|
612
|
+
if (rest.endsWith('.svelte.ts.js')) {
|
|
613
|
+
// A rune module written in TypeScript: transpile, then the module compiler.
|
|
614
|
+
const source = rest.slice(0, -3);
|
|
615
|
+
if (source in project.sources) {
|
|
616
|
+
const js = tools.transpile(project.sources[source]);
|
|
617
|
+
const compiled = compileModule(js, { filename: source, generate: 'client' });
|
|
618
|
+
return new Response(await rewriteModule(compiled.js.code, projectCtx(source)), { headers: JS_HEADERS });
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
else if (rest.endsWith('.svelte.js.js')) {
|
|
622
|
+
// A rune module already in JavaScript.
|
|
623
|
+
const source = rest.slice(0, -3);
|
|
624
|
+
if (source in project.sources) {
|
|
625
|
+
const compiled = compileModule(project.sources[source], { filename: source, generate: 'client' });
|
|
626
|
+
return new Response(await rewriteModule(compiled.js.code, projectCtx(source)), { headers: JS_HEADERS });
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
else if (rest.endsWith('.svelte.js')) {
|
|
630
|
+
// A companion component of the project, compiled for the browser.
|
|
631
|
+
const svelteFile = rest.slice(0, -3);
|
|
632
|
+
if (svelteFile in project.sources) {
|
|
633
|
+
const filename = host.paths.join(appletProjectsDir(workspaceRoot, host), id, svelteFile);
|
|
634
|
+
const prepared = await prepareServedSvelte(project.sources[svelteFile], {
|
|
635
|
+
path: filename, base: host.paths.dirname(filename)
|
|
636
|
+
}, tools.compileStyle);
|
|
637
|
+
const compiled = compile(prepared, {
|
|
638
|
+
filename: svelteFile,
|
|
639
|
+
generate: 'client',
|
|
640
|
+
css: 'injected'
|
|
641
|
+
});
|
|
642
|
+
return new Response(await rewriteModule(compiled.js.code, projectCtx(svelteFile)), { headers: JS_HEADERS });
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
else if (rest.endsWith('.js')) {
|
|
646
|
+
// A project TypeScript source, transpiled for the browser; else a project
|
|
647
|
+
// JavaScript source served through the same rewriter.
|
|
648
|
+
const srcPath = rest.replace(/\.js$/, '.ts');
|
|
649
|
+
if (srcPath in project.sources) {
|
|
650
|
+
return new Response(await rewriteModule(tools.transpile(project.sources[srcPath]), projectCtx(srcPath)), {
|
|
651
|
+
headers: JS_HEADERS
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
if (rest in project.sources) {
|
|
655
|
+
return new Response(await rewriteModule(project.sources[rest], projectCtx(rest)), { headers: JS_HEADERS });
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
// A companion of any other type — css, md, svg, images: served as BYTES
|
|
660
|
+
// with its declared media type, straight from the project.
|
|
661
|
+
const mediaType = mediaTypeFor(rest);
|
|
662
|
+
if (mediaType !== undefined && project.files.includes(rest)) {
|
|
663
|
+
const full = host.paths.join(appletProjectsDir(workspaceRoot, host), id, ...rest.split('/'));
|
|
664
|
+
// A file removed since the listing is a confirmed absence (404); any other read failure is rethrown.
|
|
665
|
+
const read = await readFileOrNull(host, full);
|
|
666
|
+
if (read === null)
|
|
667
|
+
return new Response('Not found', { status: 404 });
|
|
668
|
+
return new Response(asBody(read), {
|
|
669
|
+
headers: { 'content-type': mediaType, 'cache-control': 'no-store' }
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return null;
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* A package's installed root, walked through the HOST's own filesystem and
|
|
677
|
+
* path kinds — never `require.resolve`, whose answer belongs to whichever
|
|
678
|
+
* context (a compiled bundle, a linked checkout) happened to call it.
|
|
679
|
+
*/
|
|
680
|
+
async function resolvePackageRoot(host, roots, name) {
|
|
681
|
+
for (const dir of roots) {
|
|
682
|
+
const found = await findPackageRoot(host, name, dir);
|
|
683
|
+
if (found)
|
|
684
|
+
return (await realpathOrNullHost(host, found)) ?? found;
|
|
685
|
+
}
|
|
686
|
+
throw new CapabilityError(`Package "${name}" is not installed in this workspace.`, 'UNAVAILABLE', {
|
|
687
|
+
reason: 'applet-package-unavailable',
|
|
688
|
+
details: { package: name },
|
|
689
|
+
retryable: false
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Everything `run` must resolve before it can listen: the kit, the svelte
|
|
694
|
+
* runtime, the optional animation sheet, and this package's own served
|
|
695
|
+
* sources. Exported so a test can drive the REAL startup resolution without
|
|
696
|
+
* a socket.
|
|
697
|
+
*/
|
|
698
|
+
export async function resolveServePackageRoots(input) {
|
|
699
|
+
const { host, runtime, tools, workspaceRoot, packageDir, packageSourcePath, surfaceClientPath } = input;
|
|
700
|
+
// The kit the workspace links (its source checkout, when present) wins over
|
|
701
|
+
// the copy pinned by this package: local serve exists to iterate on what
|
|
702
|
+
// is being built, and the pinned copy is the fallback for a plain install.
|
|
703
|
+
const kitPath = (await findPackageRoot(host, '@vibesdotdev/kit', workspaceRoot)) ??
|
|
704
|
+
(await resolvePackageRoot(host, [workspaceRoot, packageDir], '@vibesdotdev/kit'));
|
|
705
|
+
const kitRoot = (await realpathOrNullHost(host, kitPath)) ?? kitPath;
|
|
706
|
+
const resolveDirs = [kitRoot, packageDir, workspaceRoot];
|
|
707
|
+
const svelteEntry = await resolveForBrowser(host, 'svelte', kitRoot, tools.resolve);
|
|
708
|
+
if (!svelteEntry)
|
|
709
|
+
throw new CapabilityError('Svelte runtime not found in the workspace node_modules.', 'UNAVAILABLE', {
|
|
710
|
+
reason: 'applet-package-unavailable',
|
|
711
|
+
details: { package: 'svelte' },
|
|
712
|
+
retryable: false
|
|
713
|
+
});
|
|
714
|
+
const svelteUrl = toFsUrl(svelteEntry);
|
|
715
|
+
// The kit owns where its stylesheet builds to; its exports map names it.
|
|
716
|
+
const kitStylesheet = await resolveForBrowser(host, '@vibesdotdev/kit/styles.css', kitRoot, tools.resolve, STYLESHEET_CONDITIONS);
|
|
717
|
+
// Optional: without it the kit sheet drops the import. Absence is asked, not
|
|
718
|
+
// caught — findPackageRoot answers null when the package is not installed,
|
|
719
|
+
// so the one default below rests on a declared fact, not a swallowed refusal.
|
|
720
|
+
const twAnimatePath = (await findPackageRoot(host, 'tw-animate-css', workspaceRoot)) ??
|
|
721
|
+
(await findPackageRoot(host, 'tw-animate-css', packageDir));
|
|
722
|
+
const twAnimateRoot = twAnimatePath === null ? null : ((await realpathOrNullHost(host, twAnimatePath)) ?? twAnimatePath);
|
|
723
|
+
if (twAnimateRoot === null) {
|
|
724
|
+
getLogger('applets:serve', runtime).debug('optional package not installed; the kit sheet drops the import', {
|
|
725
|
+
package: 'tw-animate-css'
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
const packageSourceDir = (await realpathOrNullHost(host, packageSourcePath)) ?? packageSourcePath;
|
|
729
|
+
const surfaceClientUrl = toFsUrl((await realpathOrNullHost(host, surfaceClientPath)) ?? surfaceClientPath);
|
|
730
|
+
return { kitRoot, kitStylesheet, resolveDirs, svelteEntry, svelteUrl, twAnimateRoot, packageSourceDir, surfaceClientUrl };
|
|
731
|
+
}
|
|
732
|
+
export async function run(runtime, id, options = {}) {
|
|
733
|
+
const workspaceRoot = options.root
|
|
734
|
+
? await resolveRootOption(runtime, options.root)
|
|
735
|
+
: await resolveAppletWorkspaceRoot(runtime);
|
|
736
|
+
const host = await resolveAppletProjectHost(runtime);
|
|
737
|
+
const appletsSettings = await loadAppletsConfig(runtime);
|
|
738
|
+
const hostname = appletsSettings.serveHostname;
|
|
739
|
+
const port = options.port ?? appletsSettings.servePort;
|
|
740
|
+
const base = `http://${hostname}:${port}`;
|
|
741
|
+
// Reuse probe: if our server already runs here, attach instead of multiply.
|
|
742
|
+
// Only an unreachable port means "not running"; a server that answers our
|
|
743
|
+
// health path with another shape is reported by the schema parse.
|
|
744
|
+
let probe = null;
|
|
745
|
+
const probeOutcome = await (async () => {
|
|
746
|
+
try {
|
|
747
|
+
return { ok: true, response: await fetch(base + HEALTH_PATH, { signal: AbortSignal.timeout(1500) }) };
|
|
748
|
+
}
|
|
749
|
+
catch (error) {
|
|
750
|
+
return { ok: false, error };
|
|
751
|
+
}
|
|
752
|
+
})();
|
|
753
|
+
if (probeOutcome.ok) {
|
|
754
|
+
probe = probeOutcome.response;
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
// Refused or silent: nothing serves here yet. Any other failure surfaces.
|
|
758
|
+
const probeError = z.object({ code: z.string().optional(), name: z.string().optional() }).safeParse(probeOutcome.error);
|
|
759
|
+
const failure = probeError.success ? probeError.data : {};
|
|
760
|
+
if (failure.code !== 'ConnectionRefused' &&
|
|
761
|
+
failure.code !== 'ECONNREFUSED' &&
|
|
762
|
+
failure.name !== 'TimeoutError') {
|
|
763
|
+
throw probeOutcome.error;
|
|
764
|
+
}
|
|
765
|
+
getLogger('applets:serve', runtime).debug('no server on this port yet; starting one', { base });
|
|
766
|
+
}
|
|
767
|
+
if (probe?.ok) {
|
|
768
|
+
const health = serveHealthSchema.parse(await probe.json());
|
|
769
|
+
await writeStdoutLine(runtime, `Applet server already running at ${base} (${health.applets.length} applet(s)).`);
|
|
770
|
+
await writeStdoutLine(runtime, id ? `Open ${base}/applet/${id}/` : `Open ${base}/`);
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
// The host bindings (serve + transpiler + resolver) resolve from the
|
|
774
|
+
// runtime's `applet/serve-host` kind: no implementation id and no Bun
|
|
775
|
+
// global at this call site. A host without the bindings fails typed
|
|
776
|
+
// (`NOT_FOUND` `not-registered`) instead of crashing on a missing global.
|
|
777
|
+
const serveHost = await runtime.query(APPLET_SERVE_HOST_KIND).enabled().firstOrThrow();
|
|
778
|
+
const tools = {
|
|
779
|
+
scan: serveHost.scanModuleSpecifiers,
|
|
780
|
+
resolve: (specifier, fromDir) => serveHost.resolveModuleSpecifier(specifier, fromDir, runtime),
|
|
781
|
+
transpile: serveHost.transpileTypeScriptSource,
|
|
782
|
+
compileStyle: entry => compileServedStylesheet(serveHost, { entry, sources: [], loadStylesheet })
|
|
783
|
+
};
|
|
784
|
+
// Files may only be served from the workspace or an installed package.
|
|
785
|
+
// This package's own sources are served too: the page imports the applet's
|
|
786
|
+
// surface client from here, wherever the CLI runs this package from.
|
|
787
|
+
const modulePath = host.paths.fileURLToPath(import.meta.url);
|
|
788
|
+
const hereDir = host.paths.dirname(modulePath);
|
|
789
|
+
const moduleExtension = host.paths.extname(modulePath);
|
|
790
|
+
const { kitRoot, kitStylesheet, resolveDirs, svelteUrl, twAnimateRoot, packageSourceDir, surfaceClientUrl } = await resolveServePackageRoots({
|
|
791
|
+
host,
|
|
792
|
+
runtime,
|
|
793
|
+
tools,
|
|
794
|
+
workspaceRoot,
|
|
795
|
+
packageDir: hereDir,
|
|
796
|
+
packageSourcePath: host.paths.fileURLToPath(new URL('../../', import.meta.url)),
|
|
797
|
+
surfaceClientPath: host.paths.fileURLToPath(new URL('./served-applet-browser' + moduleExtension, import.meta.url))
|
|
798
|
+
});
|
|
799
|
+
const workspacePosix = toPosix(workspaceRoot);
|
|
800
|
+
const appletRpc = createServedAppletRpc({ runtime, workspaceRoot });
|
|
801
|
+
const packagePosix = toPosix(packageSourceDir);
|
|
802
|
+
// Package roots a bare specifier resolved into, by real path. Installed
|
|
803
|
+
// packages already pass the `node_modules` check; workspace-linked ones
|
|
804
|
+
// resolve through their link to a real path that would otherwise refuse.
|
|
805
|
+
const admittedPackageRoots = new Set();
|
|
806
|
+
async function admitResolved(absolutePath) {
|
|
807
|
+
const root = await packageRootOf(host, (await realpathOrNullHost(host, absolutePath)) ?? absolutePath);
|
|
808
|
+
if (root)
|
|
809
|
+
admittedPackageRoots.add(toPosix(root));
|
|
810
|
+
}
|
|
811
|
+
const allowed = (absolutePath) => {
|
|
812
|
+
const candidate = toPosix(absolutePath);
|
|
813
|
+
return (candidate.startsWith(workspacePosix + '/') ||
|
|
814
|
+
candidate.startsWith(packagePosix + '/') ||
|
|
815
|
+
candidate.startsWith(toPosix(kitRoot) + '/') ||
|
|
816
|
+
candidate.includes('/node_modules/') ||
|
|
817
|
+
[...admittedPackageRoots].some(root => candidate.startsWith(root + '/')));
|
|
818
|
+
};
|
|
819
|
+
async function loadStylesheet(id, base) {
|
|
820
|
+
const resolved = isRelative(id)
|
|
821
|
+
? host.paths.resolve(base, id)
|
|
822
|
+
: await resolveForBrowser(host, id, base, tools.resolve, STYLESHEET_CONDITIONS);
|
|
823
|
+
const path = resolved === null ? null : await realpathOrNullHost(host, resolved);
|
|
824
|
+
if (path === null) {
|
|
825
|
+
throw new CapabilityError('An imported applet stylesheet is unavailable.', 'UNAVAILABLE', {
|
|
826
|
+
reason: 'applet-stylesheet-import-unavailable', details: { id }
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
if (!allowed(path)) {
|
|
830
|
+
throw new CapabilityError('An imported stylesheet is outside the admitted module roots.', 'PERMISSION_DENIED', {
|
|
831
|
+
reason: 'applet-stylesheet-import-denied'
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
return { path, base: host.paths.dirname(path), content: textOf(await host.hostFiles.readFile(path, 'utf8')) };
|
|
835
|
+
}
|
|
836
|
+
// Actual text identity also catches edits that preserve size or timestamp granularity.
|
|
837
|
+
const moduleCache = new Map();
|
|
838
|
+
async function serveModule(requestedPath) {
|
|
839
|
+
const absolutePath = await realpathOrNullHost(host, requestedPath);
|
|
840
|
+
if (!absolutePath)
|
|
841
|
+
return new Response('Not found', { status: 404 });
|
|
842
|
+
const info = await statChecked(host.hostFiles, absolutePath);
|
|
843
|
+
if (!info.found || info.stat.kind !== 'file')
|
|
844
|
+
return new Response('Not found', { status: 404 });
|
|
845
|
+
const ext = host.paths.extname(absolutePath);
|
|
846
|
+
if (ext === '.css')
|
|
847
|
+
return new Response(asBody(await host.hostFiles.readFile(absolutePath)), { headers: CSS_HEADERS });
|
|
848
|
+
if (ext === '.json')
|
|
849
|
+
return new Response(asBody(await host.hostFiles.readFile(absolutePath)), { headers: JSON_HEADERS });
|
|
850
|
+
const original = textOf(await host.hostFiles.readFile(absolutePath, 'utf8'));
|
|
851
|
+
const source = ext === '.svelte'
|
|
852
|
+
? await prepareServedSvelte(original, { path: absolutePath, base: host.paths.dirname(absolutePath) }, tools.compileStyle)
|
|
853
|
+
: original;
|
|
854
|
+
const cached = moduleCache.get(absolutePath);
|
|
855
|
+
if (cached && cached.source === source)
|
|
856
|
+
return new Response(cached.code, { headers: JS_HEADERS });
|
|
857
|
+
const ctx = { tools, host, fromDirs: [host.paths.dirname(absolutePath), ...resolveDirs], admitResolved };
|
|
858
|
+
let code;
|
|
859
|
+
const runeModule = /\.svelte\.(js|ts|mjs|mts)$/.test(absolutePath);
|
|
860
|
+
if (ext === '.svelte') {
|
|
861
|
+
code = await rewriteModule(compile(source, { filename: absolutePath, generate: 'client', css: 'injected' }).js.code, ctx);
|
|
862
|
+
}
|
|
863
|
+
else if (runeModule) {
|
|
864
|
+
// `.svelte.js` / `.svelte.ts` rune modules need the module compiler,
|
|
865
|
+
// not a plain transpile — `$state` outside a component is an error.
|
|
866
|
+
const js = ext === '.ts' || ext === '.mts' ? tools.transpile(source) : source;
|
|
867
|
+
code = await rewriteModule(compileModule(js, { filename: absolutePath, generate: 'client' }).js.code, ctx);
|
|
868
|
+
}
|
|
869
|
+
else if (ext === '.ts' || ext === '.mts') {
|
|
870
|
+
code = await rewriteModule(tools.transpile(source), ctx);
|
|
871
|
+
}
|
|
872
|
+
else if (ext === '.js' || ext === '.mjs') {
|
|
873
|
+
code = await rewriteModule(source, ctx);
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
return new Response(asBody(await host.hostFiles.readFile(absolutePath)), { headers: { 'cache-control': 'no-store' } });
|
|
877
|
+
}
|
|
878
|
+
moduleCache.set(absolutePath, { source, code });
|
|
879
|
+
return new Response(code, { headers: JS_HEADERS });
|
|
880
|
+
}
|
|
881
|
+
async function serveStylesheet(projectId) {
|
|
882
|
+
const sheet = kitStylesheet === null ? null : await readFileOrNullHost(host, kitStylesheet);
|
|
883
|
+
if (sheet === null || kitStylesheet === null)
|
|
884
|
+
return new Response('Not found', { status: 404 });
|
|
885
|
+
const project = projectId === null ? null : await loadProject(workspaceRoot, projectId, { tools, host, fromDirs: resolveDirs, admitResolved }, svelteUrl);
|
|
886
|
+
if (projectId !== null && project === null)
|
|
887
|
+
return new Response('Not found', { status: 404 });
|
|
888
|
+
const entry = project === null ? '/serve/index.js' : `/applet/${project.id}/applet.js`;
|
|
889
|
+
const sources = await collectServedStyleContents({
|
|
890
|
+
entry,
|
|
891
|
+
scan: tools.scan,
|
|
892
|
+
async load(url) {
|
|
893
|
+
let response = null;
|
|
894
|
+
const diskPath = fromFsUrl(host, url);
|
|
895
|
+
if (diskPath) {
|
|
896
|
+
const real = await realpathOrNullHost(host, diskPath);
|
|
897
|
+
if (real === null || (!allowed(real) && !allowed(diskPath))) {
|
|
898
|
+
throw new CapabilityError('A stylesheet source is outside the admitted module graph.', 'PERMISSION_DENIED', { reason: 'applet-style-source-denied' });
|
|
899
|
+
}
|
|
900
|
+
if (real.endsWith('.css') || real.endsWith('.json'))
|
|
901
|
+
return null;
|
|
902
|
+
response = await serveModule(real);
|
|
903
|
+
}
|
|
904
|
+
else if (project && url === entry) {
|
|
905
|
+
return project.compiledEntry;
|
|
906
|
+
}
|
|
907
|
+
else if (project && url.startsWith(`/applet/${project.id}/`)) {
|
|
908
|
+
response = await serveAppletCompanion({ workspaceRoot, id: project.id, project, rest: url.slice(`/applet/${project.id}/`.length), tools, host, resolveDirs, admitResolved });
|
|
909
|
+
}
|
|
910
|
+
else if (url === '/serve/index.js') {
|
|
911
|
+
response = await serveModule(host.paths.fileURLToPath(new URL('../../svelte/serve-index.svelte', import.meta.url)));
|
|
912
|
+
}
|
|
913
|
+
else if (url === '/applets/messages.js') {
|
|
914
|
+
response = await serveModule(host.paths.fileURLToPath(new URL('../../schemas/applet-messages' + moduleExtension, import.meta.url)));
|
|
915
|
+
}
|
|
916
|
+
// Shims, raw resources and stylesheet-link modules contain no utility sources.
|
|
917
|
+
if (response === null)
|
|
918
|
+
return null;
|
|
919
|
+
if (!response.ok)
|
|
920
|
+
throw new CapabilityError('An admitted stylesheet module could not be read.', 'UNAVAILABLE', { reason: 'applet-style-source-unavailable', details: { url, status: response.status } });
|
|
921
|
+
if (response.headers.get('content-type')?.split(';')[0] !== JS_HEADERS['content-type'])
|
|
922
|
+
return null;
|
|
923
|
+
return response.text();
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
let content = textOf(sheet);
|
|
927
|
+
if (twAnimateRoot === null)
|
|
928
|
+
content = content.replace(/@import\s+['"]tw-animate-css['"];?/, '');
|
|
929
|
+
const css = await compileServedStylesheet(serveHost, {
|
|
930
|
+
entry: { path: kitStylesheet, base: host.paths.dirname(kitStylesheet), content: `@import 'tailwindcss';\n${content}` },
|
|
931
|
+
sources,
|
|
932
|
+
loadStylesheet
|
|
933
|
+
});
|
|
934
|
+
return new Response(css, { headers: CSS_HEADERS });
|
|
935
|
+
}
|
|
936
|
+
const server = serveHost.startAppletServeHost({
|
|
937
|
+
port,
|
|
938
|
+
hostname,
|
|
939
|
+
fetch: async (request) => {
|
|
940
|
+
const url = new URL(request.url);
|
|
941
|
+
const path = url.pathname;
|
|
942
|
+
// Health / registry — the reuse probe and the machine surface.
|
|
943
|
+
if (path === HEALTH_PATH) {
|
|
944
|
+
const ids = await listAppletProjectIds(workspaceRoot, host);
|
|
945
|
+
const applets = await Promise.all(ids.map(async (pid) => ({ id: pid, entry: 'Applet.svelte' })));
|
|
946
|
+
const health = { ok: true, root: workspaceRoot, applets };
|
|
947
|
+
return new Response(JSON.stringify(serveHealthSchema.parse(health)), { headers: JSON_HEADERS });
|
|
948
|
+
}
|
|
949
|
+
// ---- Modules: the filesystem mirror ----
|
|
950
|
+
if (path.startsWith(RAW_MODULE_PREFIX + '/')) {
|
|
951
|
+
const rawPath = fromFsUrl(host, path.slice(RAW_MODULE_PREFIX.length));
|
|
952
|
+
const real = rawPath ? await realpathOrNullHost(host, rawPath) : null;
|
|
953
|
+
if (!rawPath || !real)
|
|
954
|
+
return new Response('Not found', { status: 404 });
|
|
955
|
+
if (!allowed(real) && !allowed(rawPath))
|
|
956
|
+
return new Response('Forbidden', { status: 403 });
|
|
957
|
+
const text = textOf(await host.hostFiles.readFile(real, 'utf8'));
|
|
958
|
+
return new Response(`export default ${JSON.stringify(text)};\n`, { headers: JS_HEADERS });
|
|
959
|
+
}
|
|
960
|
+
if (path.startsWith(WORKER_MODULE_PREFIX + '/')) {
|
|
961
|
+
const workerPath = fromFsUrl(host, path.slice(WORKER_MODULE_PREFIX.length));
|
|
962
|
+
const real = workerPath ? await realpathOrNullHost(host, workerPath) : null;
|
|
963
|
+
if (!workerPath || !real)
|
|
964
|
+
return new Response('Not found', { status: 404 });
|
|
965
|
+
if (!allowed(real) && !allowed(workerPath))
|
|
966
|
+
return new Response('Forbidden', { status: 403 });
|
|
967
|
+
return new Response(workerModule(toFsUrl(real)), { headers: JS_HEADERS });
|
|
968
|
+
}
|
|
969
|
+
if (path.startsWith(CSS_MODULE_PREFIX + '/') && path.endsWith('.css')) {
|
|
970
|
+
return new Response(cssModule(path.slice(CSS_MODULE_PREFIX.length)), { headers: JS_HEADERS });
|
|
971
|
+
}
|
|
972
|
+
if (path.startsWith(SHIM_PREFIX) && path.endsWith('.js')) {
|
|
973
|
+
const specifier = shimSpecifierSchema.safeParse(decodeURIComponent(path.slice(SHIM_PREFIX.length, -3)));
|
|
974
|
+
if (specifier.success)
|
|
975
|
+
return new Response(SHIMS[specifier.data], { headers: JS_HEADERS });
|
|
976
|
+
return new Response('Not found', { status: 404 });
|
|
977
|
+
}
|
|
978
|
+
const fsPath = fromFsUrl(host, path);
|
|
979
|
+
if (fsPath) {
|
|
980
|
+
const real = await realpathOrNullHost(host, fsPath);
|
|
981
|
+
if (!real)
|
|
982
|
+
return new Response('Not found', { status: 404 });
|
|
983
|
+
if (!allowed(real) && !allowed(fsPath))
|
|
984
|
+
return new Response('Forbidden', { status: 403 });
|
|
985
|
+
return serveModule(real);
|
|
986
|
+
}
|
|
987
|
+
// ---- Kit stylesheet and the sheets it imports ----
|
|
988
|
+
if (path === '/kit/styles.css') {
|
|
989
|
+
const project = url.searchParams.get('project');
|
|
990
|
+
return serveStylesheet(project === null ? null : servedAppletSchema.shape.id.parse(project));
|
|
991
|
+
}
|
|
992
|
+
if (path.startsWith('/kit/styles/') && path.endsWith('.css')) {
|
|
993
|
+
const name = path.slice('/kit/styles/'.length);
|
|
994
|
+
if (name.includes('..'))
|
|
995
|
+
return new Response('Not found', { status: 404 });
|
|
996
|
+
// The sheets the kit stylesheet imports sit beside it (`./styles/<name>`).
|
|
997
|
+
const sheet = kitStylesheet === null ? null : await readFileOrNullHost(host, host.paths.join(host.paths.dirname(kitStylesheet), 'styles', name));
|
|
998
|
+
if (sheet === null)
|
|
999
|
+
return new Response('Not found', { status: 404 });
|
|
1000
|
+
return new Response(asBody(sheet), { headers: CSS_HEADERS });
|
|
1001
|
+
}
|
|
1002
|
+
// ---- Shared message schemas, compiled through the same pipeline ----
|
|
1003
|
+
if (path === '/applets/messages.js') {
|
|
1004
|
+
const abs = host.paths.fileURLToPath(new URL('../../schemas/applet-messages' + moduleExtension, import.meta.url));
|
|
1005
|
+
return serveModule(abs);
|
|
1006
|
+
}
|
|
1007
|
+
// Chrome: the kit-composed index, compiled through the same pipeline.
|
|
1008
|
+
if (path === '/serve/index.js') {
|
|
1009
|
+
const abs = host.paths.fileURLToPath(new URL('../../svelte/serve-index.svelte', import.meta.url));
|
|
1010
|
+
const response = await serveModule(abs);
|
|
1011
|
+
if (!response.ok)
|
|
1012
|
+
return response;
|
|
1013
|
+
return new Response(withMountAdapter(await response.text(), svelteUrl), { headers: JS_HEADERS });
|
|
1014
|
+
}
|
|
1015
|
+
// Index — kit-composed chrome, rescan per request.
|
|
1016
|
+
if (path === '/') {
|
|
1017
|
+
return new Response(indexHostPage(), { headers: HTML_HEADERS });
|
|
1018
|
+
}
|
|
1019
|
+
// Applet-scoped routes.
|
|
1020
|
+
const m = /^\/applet\/([a-z0-9-]+)(?:\/(.*))?$/.exec(path);
|
|
1021
|
+
if (m) {
|
|
1022
|
+
const id = m[1];
|
|
1023
|
+
const rest = m[2] ?? '';
|
|
1024
|
+
if (rest.startsWith('rpc/'))
|
|
1025
|
+
return appletRpc.handle(id, request);
|
|
1026
|
+
// The applet's root surface: answered from the project's manifest
|
|
1027
|
+
// alone, so it never compiles the entry.
|
|
1028
|
+
if (rest === 'surface') {
|
|
1029
|
+
return answerServedAppletSurface({ runtime, workspaceRoot, projectId: id, request });
|
|
1030
|
+
}
|
|
1031
|
+
const project = await loadProject(workspaceRoot, id, { tools, host, fromDirs: resolveDirs, admitResolved }, svelteUrl);
|
|
1032
|
+
if (!project) {
|
|
1033
|
+
return new Response(JSON.stringify({ error: `No applet "${id}"` }), { status: 404, headers: JSON_HEADERS });
|
|
1034
|
+
}
|
|
1035
|
+
if (rest === '') {
|
|
1036
|
+
return new Response(hostPage(id, surfaceClientUrl), { headers: HTML_HEADERS });
|
|
1037
|
+
}
|
|
1038
|
+
if (rest === 'applet.js') {
|
|
1039
|
+
return new Response(project.compiledEntry, { headers: JS_HEADERS });
|
|
1040
|
+
}
|
|
1041
|
+
if (rest === 'files') {
|
|
1042
|
+
const body = {
|
|
1043
|
+
id,
|
|
1044
|
+
entry: project.entry,
|
|
1045
|
+
files: Object.entries(project.sources).map(([p, contents]) => ({ path: p, contents }))
|
|
1046
|
+
};
|
|
1047
|
+
return new Response(JSON.stringify(servedAppletFilesSchema.parse(body)), { headers: JSON_HEADERS });
|
|
1048
|
+
}
|
|
1049
|
+
const companion = await serveAppletCompanion({
|
|
1050
|
+
workspaceRoot,
|
|
1051
|
+
id,
|
|
1052
|
+
project,
|
|
1053
|
+
rest,
|
|
1054
|
+
tools,
|
|
1055
|
+
host,
|
|
1056
|
+
resolveDirs,
|
|
1057
|
+
admitResolved
|
|
1058
|
+
});
|
|
1059
|
+
if (companion)
|
|
1060
|
+
return companion;
|
|
1061
|
+
return new Response(JSON.stringify({ error: 'Not found' }), { status: 404, headers: JSON_HEADERS });
|
|
1062
|
+
}
|
|
1063
|
+
return new Response('Not found', { status: 404 });
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
const ids = await listAppletProjectIds(workspaceRoot, host);
|
|
1067
|
+
await writeStdoutLine(runtime, `Applet server serving ${ids.length} project(s) at ${base} (Ctrl+C to stop)`);
|
|
1068
|
+
if (id) {
|
|
1069
|
+
if (!ids.includes(id))
|
|
1070
|
+
await writeStdoutLine(runtime, `(no project "${id}" yet — it will appear once created under .vibes/applets)`);
|
|
1071
|
+
await writeStdoutLine(runtime, `Open ${base}/applet/${id}/`);
|
|
1072
|
+
}
|
|
1073
|
+
else {
|
|
1074
|
+
await writeStdoutLine(runtime, `Open ${base}/`);
|
|
1075
|
+
}
|
|
1076
|
+
// The command owns the server's lifetime: it stays pending until a signal
|
|
1077
|
+
// asks it to stop, so the CLI host never auto-exits underneath Bun.serve.
|
|
1078
|
+
const lifetime = await createCLIActionLifetime(runtime, async () => {
|
|
1079
|
+
await Promise.all([
|
|
1080
|
+
Promise.resolve().then(() => {
|
|
1081
|
+
try {
|
|
1082
|
+
server.stop();
|
|
1083
|
+
}
|
|
1084
|
+
finally {
|
|
1085
|
+
moduleCache.clear();
|
|
1086
|
+
}
|
|
1087
|
+
}),
|
|
1088
|
+
appletRpc.close()
|
|
1089
|
+
]);
|
|
1090
|
+
});
|
|
1091
|
+
lifetime.bindProcessSignals();
|
|
1092
|
+
await lifetime.wait();
|
|
1093
|
+
}
|
|
1094
|
+
const serveArgsSchema = z.object({ id: z.string().min(1).optional() }).passthrough();
|
|
1095
|
+
const serveOptionsSchema = z.object({ port: z.number().optional(), root: z.string().min(1).optional() }).passthrough();
|
|
1096
|
+
export async function execute(args, options, context) {
|
|
1097
|
+
const parsedArgs = serveArgsSchema.safeParse(args);
|
|
1098
|
+
const parsedOptions = serveOptionsSchema.safeParse(options);
|
|
1099
|
+
const id = parsedArgs.success ? parsedArgs.data.id : undefined;
|
|
1100
|
+
const port = parsedOptions.success ? parsedOptions.data.port : undefined;
|
|
1101
|
+
const root = parsedOptions.success ? parsedOptions.data.root : undefined;
|
|
1102
|
+
await run(context.runtime, id, { port, root });
|
|
1103
|
+
}
|
|
1104
|
+
export default { execute };
|
|
1105
|
+
//# sourceMappingURL=serve.command.impl.js.map
|