every-plugin 2.10.1 → 3.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/every-plugin-serve.mjs +12 -0
- package/bin/every-plugin.mjs +59 -0
- package/dist/_virtual/_rolldown/runtime.cjs +1 -2
- package/dist/build/artifact-names.cjs +119 -0
- package/dist/build/artifact-names.cjs.map +1 -0
- package/dist/build/artifact-names.d.cts +68 -0
- package/dist/build/artifact-names.d.cts.map +1 -0
- package/dist/build/artifact-names.d.mts +68 -0
- package/dist/build/artifact-names.d.mts.map +1 -0
- package/dist/build/artifact-names.mjs +102 -0
- package/dist/build/artifact-names.mjs.map +1 -0
- package/dist/build/contract-types.cjs +89 -0
- package/dist/build/contract-types.cjs.map +1 -0
- package/dist/build/contract-types.mjs +81 -0
- package/dist/build/contract-types.mjs.map +1 -0
- package/dist/build/rspack/build-report-plugin.cjs +37 -0
- package/dist/build/rspack/build-report-plugin.cjs.map +1 -0
- package/dist/build/rspack/build-report-plugin.d.cts +18 -0
- package/dist/build/rspack/build-report-plugin.d.cts.map +1 -0
- package/dist/build/rspack/build-report-plugin.d.mts +18 -0
- package/dist/build/rspack/build-report-plugin.d.mts.map +1 -0
- package/dist/build/rspack/build-report-plugin.mjs +37 -0
- package/dist/build/rspack/build-report-plugin.mjs.map +1 -0
- package/dist/build/rspack/chunk-completeness-plugin.cjs +47 -0
- package/dist/build/rspack/chunk-completeness-plugin.cjs.map +1 -0
- package/dist/build/rspack/chunk-completeness-plugin.mjs +47 -0
- package/dist/build/rspack/chunk-completeness-plugin.mjs.map +1 -0
- package/dist/build/rspack/compose.cjs +69 -0
- package/dist/build/rspack/compose.cjs.map +1 -0
- package/dist/build/rspack/compose.d.cts +20 -0
- package/dist/build/rspack/compose.d.cts.map +1 -0
- package/dist/build/rspack/compose.d.mts +20 -0
- package/dist/build/rspack/compose.d.mts.map +1 -0
- package/dist/build/rspack/compose.mjs +65 -0
- package/dist/build/rspack/compose.mjs.map +1 -0
- package/dist/build/rspack/fix-mf-data-uri-plugin.cjs +11 -11
- package/dist/build/rspack/fix-mf-data-uri-plugin.cjs.map +1 -1
- package/dist/build/rspack/fix-mf-data-uri-plugin.d.cts +2 -0
- package/dist/build/rspack/fix-mf-data-uri-plugin.d.cts.map +1 -1
- package/dist/build/rspack/fix-mf-data-uri-plugin.d.mts +2 -0
- package/dist/build/rspack/fix-mf-data-uri-plugin.d.mts.map +1 -1
- package/dist/build/rspack/fix-mf-data-uri-plugin.mjs +11 -10
- package/dist/build/rspack/fix-mf-data-uri-plugin.mjs.map +1 -1
- package/dist/build/rspack/generated-config.cjs +35 -0
- package/dist/build/rspack/generated-config.cjs.map +1 -0
- package/dist/build/rspack/generated-config.mjs +32 -0
- package/dist/build/rspack/generated-config.mjs.map +1 -0
- package/dist/build/rspack/index.cjs +8 -5
- package/dist/build/rspack/index.d.cts +5 -3
- package/dist/build/rspack/index.d.mts +5 -3
- package/dist/build/rspack/index.mjs +5 -3
- package/dist/build/rspack/module-federation.cjs +2 -2
- package/dist/build/rspack/module-federation.cjs.map +1 -1
- package/dist/build/rspack/module-federation.mjs +1 -1
- package/dist/build/rspack/module-federation.mjs.map +1 -1
- package/dist/build/rspack/plugin.cjs +44 -69
- package/dist/build/rspack/plugin.cjs.map +1 -1
- package/dist/build/rspack/plugin.d.cts +4 -13
- package/dist/build/rspack/plugin.d.cts.map +1 -1
- package/dist/build/rspack/plugin.d.mts +4 -13
- package/dist/build/rspack/plugin.d.mts.map +1 -1
- package/dist/build/rspack/plugin.mjs +44 -69
- package/dist/build/rspack/plugin.mjs.map +1 -1
- package/dist/build/rspack/utils.cjs +7 -5
- package/dist/build/rspack/utils.cjs.map +1 -1
- package/dist/build/rspack/utils.mjs +7 -6
- package/dist/build/rspack/utils.mjs.map +1 -1
- package/dist/build/ui/factory.cjs +88 -0
- package/dist/build/ui/factory.cjs.map +1 -0
- package/dist/build/ui/factory.d.cts +11 -0
- package/dist/build/ui/factory.d.cts.map +1 -0
- package/dist/build/ui/factory.d.mts +11 -0
- package/dist/build/ui/factory.d.mts.map +1 -0
- package/dist/build/ui/factory.mjs +80 -0
- package/dist/build/ui/factory.mjs.map +1 -0
- package/dist/build/ui/generated-config.cjs +118 -0
- package/dist/build/ui/generated-config.cjs.map +1 -0
- package/dist/build/ui/generated-config.d.cts +33 -0
- package/dist/build/ui/generated-config.d.cts.map +1 -0
- package/dist/build/ui/generated-config.d.mts +33 -0
- package/dist/build/ui/generated-config.d.mts.map +1 -0
- package/dist/build/ui/generated-config.mjs +111 -0
- package/dist/build/ui/generated-config.mjs.map +1 -0
- package/dist/build/ui/hash-artifacts-plugin.cjs +61 -0
- package/dist/build/ui/hash-artifacts-plugin.cjs.map +1 -0
- package/dist/build/ui/hash-artifacts-plugin.d.cts +20 -0
- package/dist/build/ui/hash-artifacts-plugin.d.cts.map +1 -0
- package/dist/build/ui/hash-artifacts-plugin.d.mts +20 -0
- package/dist/build/ui/hash-artifacts-plugin.d.mts.map +1 -0
- package/dist/build/ui/hash-artifacts-plugin.mjs +58 -0
- package/dist/build/ui/hash-artifacts-plugin.mjs.map +1 -0
- package/dist/build/ui/index.cjs +90 -0
- package/dist/build/ui/index.cjs.map +1 -0
- package/dist/build/ui/index.d.cts +29 -0
- package/dist/build/ui/index.d.cts.map +1 -0
- package/dist/build/ui/index.d.mts +29 -0
- package/dist/build/ui/index.d.mts.map +1 -0
- package/dist/build/ui/index.mjs +68 -0
- package/dist/build/ui/index.mjs.map +1 -0
- package/dist/build/ui/manifest-plugin.cjs +37 -0
- package/dist/build/ui/manifest-plugin.cjs.map +1 -0
- package/dist/build/ui/manifest-plugin.d.cts +19 -0
- package/dist/build/ui/manifest-plugin.d.cts.map +1 -0
- package/dist/build/ui/manifest-plugin.d.mts +19 -0
- package/dist/build/ui/manifest-plugin.d.mts.map +1 -0
- package/dist/build/ui/manifest-plugin.mjs +37 -0
- package/dist/build/ui/manifest-plugin.mjs.map +1 -0
- package/dist/build/ui/rsbuild-config.cjs +203 -0
- package/dist/build/ui/rsbuild-config.cjs.map +1 -0
- package/dist/build/ui/rsbuild-config.d.cts +45 -0
- package/dist/build/ui/rsbuild-config.d.cts.map +1 -0
- package/dist/build/ui/rsbuild-config.d.mts +45 -0
- package/dist/build/ui/rsbuild-config.d.mts.map +1 -0
- package/dist/build/ui/rsbuild-config.mjs +200 -0
- package/dist/build/ui/rsbuild-config.mjs.map +1 -0
- package/dist/build/ui/stubs.cjs +107 -0
- package/dist/build/ui/stubs.cjs.map +1 -0
- package/dist/build/ui/stubs.d.cts +25 -0
- package/dist/build/ui/stubs.d.cts.map +1 -0
- package/dist/build/ui/stubs.d.mts +25 -0
- package/dist/build/ui/stubs.d.mts.map +1 -0
- package/dist/build/ui/stubs.mjs +106 -0
- package/dist/build/ui/stubs.mjs.map +1 -0
- package/dist/cli.cjs +83 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +6 -0
- package/dist/cli.d.cts.map +1 -0
- package/dist/cli.d.mts +6 -0
- package/dist/cli.d.mts.map +1 -0
- package/dist/cli.mjs +82 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/dev/serve.cjs +390 -0
- package/dist/dev/serve.cjs.map +1 -0
- package/dist/dev/serve.d.cts +23 -0
- package/dist/dev/serve.d.cts.map +1 -0
- package/dist/dev/serve.d.mts +23 -0
- package/dist/dev/serve.d.mts.map +1 -0
- package/dist/dev/serve.mjs +384 -0
- package/dist/dev/serve.mjs.map +1 -0
- package/dist/dev/watch-kill.cjs +39 -0
- package/dist/dev/watch-kill.cjs.map +1 -0
- package/dist/dev/watch-kill.mjs +38 -0
- package/dist/dev/watch-kill.mjs.map +1 -0
- package/dist/effect-helpers.cjs +23 -0
- package/dist/effect-helpers.cjs.map +1 -0
- package/dist/effect-helpers.d.cts +20 -0
- package/dist/effect-helpers.d.cts.map +1 -0
- package/dist/effect-helpers.d.mts +20 -0
- package/dist/effect-helpers.d.mts.map +1 -0
- package/dist/effect-helpers.mjs +22 -0
- package/dist/effect-helpers.mjs.map +1 -0
- package/dist/errors.cjs +49 -69
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +13 -96
- package/dist/errors.d.cts.map +1 -1
- package/dist/errors.d.mts +13 -96
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs +16 -34
- package/dist/errors.mjs.map +1 -1
- package/dist/identity.cjs +19 -0
- package/dist/identity.cjs.map +1 -0
- package/dist/identity.d.cts +33 -0
- package/dist/identity.d.cts.map +1 -0
- package/dist/identity.d.mts +33 -0
- package/dist/identity.d.mts.map +1 -0
- package/dist/identity.mjs +16 -0
- package/dist/identity.mjs.map +1 -0
- package/dist/index.cjs +8 -5
- package/dist/index.d.cts +8 -6
- package/dist/index.d.mts +8 -6
- package/dist/index.mjs +6 -4
- package/dist/plugin.cjs +12 -26
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +44 -25
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +45 -26
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +15 -29
- package/dist/plugin.mjs.map +1 -1
- package/dist/remote-entry.cjs +103 -0
- package/dist/remote-entry.cjs.map +1 -0
- package/dist/remote-entry.d.cts +53 -0
- package/dist/remote-entry.d.cts.map +1 -0
- package/dist/remote-entry.d.mts +53 -0
- package/dist/remote-entry.d.mts.map +1 -0
- package/dist/remote-entry.mjs +98 -0
- package/dist/remote-entry.mjs.map +1 -0
- package/dist/runtime/errors.cjs +140 -34
- package/dist/runtime/errors.cjs.map +1 -1
- package/dist/runtime/errors.d.cts +42 -7
- package/dist/runtime/errors.d.cts.map +1 -1
- package/dist/runtime/errors.d.mts +42 -7
- package/dist/runtime/errors.d.mts.map +1 -1
- package/dist/runtime/errors.mjs +136 -31
- package/dist/runtime/errors.mjs.map +1 -1
- package/dist/runtime/index.cjs +95 -23
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.cts +16 -0
- package/dist/runtime/index.d.cts.map +1 -1
- package/dist/runtime/index.d.mts +16 -0
- package/dist/runtime/index.d.mts.map +1 -1
- package/dist/runtime/index.mjs +98 -26
- package/dist/runtime/index.mjs.map +1 -1
- package/dist/runtime/mf-config.cjs +3 -78
- package/dist/runtime/mf-config.cjs.map +1 -1
- package/dist/runtime/mf-config.d.cts +3 -49
- package/dist/runtime/mf-config.d.cts.map +1 -1
- package/dist/runtime/mf-config.d.mts +3 -49
- package/dist/runtime/mf-config.d.mts.map +1 -1
- package/dist/runtime/mf-config.mjs +5 -78
- package/dist/runtime/mf-config.mjs.map +1 -1
- package/dist/runtime/services/module-federation.service.cjs +63 -12
- package/dist/runtime/services/module-federation.service.cjs.map +1 -1
- package/dist/runtime/services/module-federation.service.d.cts +4 -13
- package/dist/runtime/services/module-federation.service.d.mts +4 -13
- package/dist/runtime/services/module-federation.service.mjs +63 -14
- package/dist/runtime/services/module-federation.service.mjs.map +1 -1
- package/dist/runtime/services/normalize.cjs +2 -22
- package/dist/runtime/services/normalize.d.cts +2 -21
- package/dist/runtime/services/normalize.d.mts +2 -21
- package/dist/runtime/services/normalize.mjs +2 -21
- package/dist/runtime/services/plugin-lifecycle.service.cjs +9 -11
- package/dist/runtime/services/plugin-lifecycle.service.cjs.map +1 -1
- package/dist/runtime/services/plugin-lifecycle.service.d.cts +3 -16
- package/dist/runtime/services/plugin-lifecycle.service.d.mts +3 -16
- package/dist/runtime/services/plugin-lifecycle.service.mjs +10 -13
- package/dist/runtime/services/plugin-lifecycle.service.mjs.map +1 -1
- package/dist/runtime/services/plugin-loader.service.cjs +30 -29
- package/dist/runtime/services/plugin-loader.service.cjs.map +1 -1
- package/dist/runtime/services/plugin-loader.service.d.cts +6 -53
- package/dist/runtime/services/plugin-loader.service.d.mts +6 -53
- package/dist/runtime/services/plugin-loader.service.mjs +29 -30
- package/dist/runtime/services/plugin-loader.service.mjs.map +1 -1
- package/dist/runtime/services/plugin.service.cjs +10 -12
- package/dist/runtime/services/plugin.service.cjs.map +1 -1
- package/dist/runtime/services/plugin.service.d.cts +14 -36
- package/dist/runtime/services/plugin.service.d.cts.map +1 -1
- package/dist/runtime/services/plugin.service.d.mts +14 -36
- package/dist/runtime/services/plugin.service.d.mts.map +1 -1
- package/dist/runtime/services/plugin.service.mjs +14 -18
- package/dist/runtime/services/plugin.service.mjs.map +1 -1
- package/dist/runtime/services/secrets.service.cjs +12 -12
- package/dist/runtime/services/secrets.service.cjs.map +1 -1
- package/dist/runtime/services/secrets.service.d.cts +3 -15
- package/dist/runtime/services/secrets.service.d.mts +3 -15
- package/dist/runtime/services/secrets.service.mjs +12 -13
- package/dist/runtime/services/secrets.service.mjs.map +1 -1
- package/dist/runtime/services/shared-identity.cjs +40 -0
- package/dist/runtime/services/shared-identity.cjs.map +1 -0
- package/dist/runtime/services/shared-identity.mjs +38 -0
- package/dist/runtime/services/shared-identity.mjs.map +1 -0
- package/dist/runtime/validation.cjs +2 -2
- package/dist/runtime/validation.cjs.map +1 -1
- package/dist/runtime/validation.mjs +2 -2
- package/dist/runtime/validation.mjs.map +1 -1
- package/dist/shared-deps-spec.cjs +384 -0
- package/dist/shared-deps-spec.cjs.map +1 -0
- package/dist/shared-deps-spec.d.cts +182 -0
- package/dist/shared-deps-spec.d.cts.map +1 -0
- package/dist/shared-deps-spec.d.mts +182 -0
- package/dist/shared-deps-spec.d.mts.map +1 -0
- package/dist/shared-deps-spec.mjs +368 -0
- package/dist/shared-deps-spec.mjs.map +1 -0
- package/dist/types.d.cts +22 -41
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +22 -41
- package/dist/types.d.mts.map +1 -1
- package/dist/ui/manifest/construct.cjs +268 -0
- package/dist/ui/manifest/construct.cjs.map +1 -0
- package/dist/ui/manifest/construct.d.cts +48 -0
- package/dist/ui/manifest/construct.d.cts.map +1 -0
- package/dist/ui/manifest/construct.d.mts +48 -0
- package/dist/ui/manifest/construct.d.mts.map +1 -0
- package/dist/ui/manifest/construct.mjs +268 -0
- package/dist/ui/manifest/construct.mjs.map +1 -0
- package/dist/ui/manifest/contract.cjs +34 -0
- package/dist/ui/manifest/contract.cjs.map +1 -0
- package/dist/ui/manifest/contract.d.cts +26 -0
- package/dist/ui/manifest/contract.d.cts.map +1 -0
- package/dist/ui/manifest/contract.d.mts +26 -0
- package/dist/ui/manifest/contract.d.mts.map +1 -0
- package/dist/ui/manifest/contract.mjs +26 -0
- package/dist/ui/manifest/contract.mjs.map +1 -0
- package/dist/ui/manifest/descriptor.cjs +64 -0
- package/dist/ui/manifest/descriptor.cjs.map +1 -0
- package/dist/ui/manifest/descriptor.d.cts +60 -0
- package/dist/ui/manifest/descriptor.d.cts.map +1 -0
- package/dist/ui/manifest/descriptor.d.mts +60 -0
- package/dist/ui/manifest/descriptor.d.mts.map +1 -0
- package/dist/ui/manifest/descriptor.mjs +61 -0
- package/dist/ui/manifest/descriptor.mjs.map +1 -0
- package/dist/ui/manifest/generator.cjs +128 -0
- package/dist/ui/manifest/generator.cjs.map +1 -0
- package/dist/ui/manifest/generator.d.cts +41 -0
- package/dist/ui/manifest/generator.d.cts.map +1 -0
- package/dist/ui/manifest/generator.d.mts +41 -0
- package/dist/ui/manifest/generator.d.mts.map +1 -0
- package/dist/ui/manifest/generator.mjs +125 -0
- package/dist/ui/manifest/generator.mjs.map +1 -0
- package/dist/ui/manifest/index.cjs +35 -0
- package/dist/ui/manifest/index.d.cts +9 -0
- package/dist/ui/manifest/index.d.mts +9 -0
- package/dist/ui/manifest/index.mjs +9 -0
- package/dist/ui/manifest/manifest-schema.cjs +97 -0
- package/dist/ui/manifest/manifest-schema.cjs.map +1 -0
- package/dist/ui/manifest/manifest-schema.d.cts +132 -0
- package/dist/ui/manifest/manifest-schema.d.cts.map +1 -0
- package/dist/ui/manifest/manifest-schema.d.mts +132 -0
- package/dist/ui/manifest/manifest-schema.d.mts.map +1 -0
- package/dist/ui/manifest/manifest-schema.mjs +90 -0
- package/dist/ui/manifest/manifest-schema.mjs.map +1 -0
- package/dist/ui/manifest/mount-registry.cjs +52 -0
- package/dist/ui/manifest/mount-registry.cjs.map +1 -0
- package/dist/ui/manifest/mount-registry.d.cts +61 -0
- package/dist/ui/manifest/mount-registry.d.cts.map +1 -0
- package/dist/ui/manifest/mount-registry.d.mts +61 -0
- package/dist/ui/manifest/mount-registry.d.mts.map +1 -0
- package/dist/ui/manifest/mount-registry.mjs +48 -0
- package/dist/ui/manifest/mount-registry.mjs.map +1 -0
- package/dist/ui/manifest/nav.cjs +13 -0
- package/dist/ui/manifest/nav.cjs.map +1 -0
- package/dist/ui/manifest/nav.d.cts +34 -0
- package/dist/ui/manifest/nav.d.cts.map +1 -0
- package/dist/ui/manifest/nav.d.mts +34 -0
- package/dist/ui/manifest/nav.d.mts.map +1 -0
- package/dist/ui/manifest/nav.mjs +13 -0
- package/dist/ui/manifest/nav.mjs.map +1 -0
- package/dist/ui/manifest/route-config.cjs +33 -0
- package/dist/ui/manifest/route-config.cjs.map +1 -0
- package/dist/ui/manifest/route-config.d.cts +40 -0
- package/dist/ui/manifest/route-config.d.cts.map +1 -0
- package/dist/ui/manifest/route-config.d.mts +40 -0
- package/dist/ui/manifest/route-config.d.mts.map +1 -0
- package/dist/ui/manifest/route-config.mjs +33 -0
- package/dist/ui/manifest/route-config.mjs.map +1 -0
- package/dist/version-manifest.cjs +65 -0
- package/dist/version-manifest.cjs.map +1 -0
- package/dist/version-manifest.d.cts +52 -0
- package/dist/version-manifest.d.cts.map +1 -0
- package/dist/version-manifest.d.mts +52 -0
- package/dist/version-manifest.d.mts.map +1 -0
- package/dist/version-manifest.mjs +63 -0
- package/dist/version-manifest.mjs.map +1 -0
- package/package.json +165 -68
- package/skills/plugin-client/SKILL.md +24 -5
- package/skills/plugin-development/SKILL.md +79 -47
- package/skills/plugin-testing/SKILL.md +41 -12
- package/src/build/artifact-names.ts +117 -0
- package/src/build/contract-types.ts +101 -0
- package/src/build/rspack/build-report-plugin.ts +34 -0
- package/src/build/rspack/chunk-completeness-plugin.ts +57 -0
- package/src/build/rspack/compose.ts +87 -0
- package/src/build/rspack/fix-mf-data-uri-plugin.ts +23 -12
- package/src/build/rspack/generated-config.ts +35 -0
- package/src/build/rspack/index.ts +10 -5
- package/src/build/rspack/module-federation.ts +1 -1
- package/src/build/rspack/plugin.ts +103 -105
- package/src/build/rspack/utils.ts +8 -2
- package/src/build/ui/factory.ts +96 -0
- package/src/build/ui/generated-config.ts +133 -0
- package/src/build/ui/hash-artifacts-plugin.ts +79 -0
- package/src/build/ui/index.ts +98 -0
- package/src/build/ui/manifest-plugin.ts +54 -0
- package/src/build/ui/rsbuild-config.ts +256 -0
- package/src/build/ui/stubs.ts +103 -0
- package/src/cli.ts +100 -0
- package/src/dev/serve.ts +477 -0
- package/src/dev/watch-kill.ts +58 -0
- package/src/effect-helpers.ts +25 -0
- package/src/errors.ts +16 -18
- package/src/identity.ts +36 -0
- package/src/index.ts +4 -0
- package/src/plugin.ts +70 -92
- package/src/remote-entry.ts +171 -0
- package/src/runtime/errors.ts +186 -27
- package/src/runtime/index.ts +132 -43
- package/src/runtime/mf-config.ts +12 -107
- package/src/runtime/services/module-federation.service.ts +221 -117
- package/src/runtime/services/normalize.ts +1 -18
- package/src/runtime/services/plugin-lifecycle.service.ts +66 -75
- package/src/runtime/services/plugin-loader.service.ts +239 -212
- package/src/runtime/services/plugin.service.ts +61 -43
- package/src/runtime/services/secrets.service.ts +20 -15
- package/src/runtime/services/shared-identity.ts +68 -0
- package/src/runtime/validation.ts +5 -7
- package/src/shared-deps-spec.ts +478 -0
- package/src/types.ts +22 -48
- package/src/ui/manifest/construct.ts +403 -0
- package/src/ui/manifest/contract.ts +33 -0
- package/src/ui/manifest/descriptor.ts +96 -0
- package/src/ui/manifest/generator.ts +238 -0
- package/src/ui/manifest/index.ts +55 -0
- package/src/ui/manifest/manifest-schema.ts +107 -0
- package/src/ui/manifest/mount-registry.ts +50 -0
- package/src/ui/manifest/nav.ts +43 -0
- package/src/ui/manifest/route-config.ts +82 -0
- package/src/version-manifest.ts +88 -0
- package/dist/build/rspack/dev-server-middleware.cjs +0 -248
- package/dist/build/rspack/dev-server-middleware.cjs.map +0 -1
- package/dist/build/rspack/dev-server-middleware.mjs +0 -247
- package/dist/build/rspack/dev-server-middleware.mjs.map +0 -1
- package/dist/build/shared-deps.cjs +0 -83
- package/dist/build/shared-deps.cjs.map +0 -1
- package/dist/build/shared-deps.d.cts +0 -67
- package/dist/build/shared-deps.d.cts.map +0 -1
- package/dist/build/shared-deps.d.mts +0 -67
- package/dist/build/shared-deps.d.mts.map +0 -1
- package/dist/build/shared-deps.mjs +0 -79
- package/dist/build/shared-deps.mjs.map +0 -1
- package/dist/effect.cjs +0 -9
- package/dist/effect.d.cts +0 -1
- package/dist/effect.d.mts +0 -1
- package/dist/effect.mjs +0 -3
- package/dist/orpc-openapi.cjs +0 -17
- package/dist/orpc-openapi.d.cts +0 -2
- package/dist/orpc-openapi.d.mts +0 -2
- package/dist/orpc-openapi.mjs +0 -5
- package/dist/orpc.cjs +0 -46
- package/dist/orpc.d.cts +0 -7
- package/dist/orpc.d.mts +0 -7
- package/dist/orpc.mjs +0 -11
- package/dist/package.cjs +0 -23
- package/dist/package.cjs.map +0 -1
- package/dist/package.mjs +0 -17
- package/dist/package.mjs.map +0 -1
- package/dist/runtime/services/module-federation.service.d.cts.map +0 -1
- package/dist/runtime/services/module-federation.service.d.mts.map +0 -1
- package/dist/runtime/services/normalize.cjs.map +0 -1
- package/dist/runtime/services/normalize.d.cts.map +0 -1
- package/dist/runtime/services/normalize.d.mts.map +0 -1
- package/dist/runtime/services/normalize.mjs.map +0 -1
- package/dist/runtime/services/plugin-lifecycle.service.d.cts.map +0 -1
- package/dist/runtime/services/plugin-lifecycle.service.d.mts.map +0 -1
- package/dist/runtime/services/plugin-loader.service.d.cts.map +0 -1
- package/dist/runtime/services/plugin-loader.service.d.mts.map +0 -1
- package/dist/runtime/services/secrets.service.d.cts.map +0 -1
- package/dist/runtime/services/secrets.service.d.mts.map +0 -1
- package/dist/testing/index.cjs +0 -57
- package/dist/testing/index.cjs.map +0 -1
- package/dist/testing/index.d.cts +0 -54
- package/dist/testing/index.d.cts.map +0 -1
- package/dist/testing/index.d.mts +0 -54
- package/dist/testing/index.d.mts.map +0 -1
- package/dist/testing/index.mjs +0 -55
- package/dist/testing/index.mjs.map +0 -1
- package/dist/zod-core.cjs +0 -9
- package/dist/zod-core.d.cts +0 -1
- package/dist/zod-core.d.mts +0 -1
- package/dist/zod-core.mjs +0 -3
- package/dist/zod.cjs +0 -11
- package/dist/zod.d.cts +0 -2
- package/dist/zod.d.mts +0 -2
- package/dist/zod.mjs +0 -3
- package/src/build/rspack/dev-server-middleware.ts +0 -324
- package/src/build/shared-deps.ts +0 -102
- package/src/effect.ts +0 -1
- package/src/orpc-openapi.ts +0 -2
- package/src/orpc.ts +0 -9
- package/src/testing/index.ts +0 -87
- package/src/testing/mocks/module-federation.service.ts +0 -41
- package/src/zod-core.ts +0 -1
- package/src/zod.ts +0 -3
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: plugin-development
|
|
3
3
|
description: Build every-plugin modules with oRPC contracts, Effect services, and Module Federation. Use when creating or modifying plugins under plugins/ or the _template scaffold.
|
|
4
4
|
metadata:
|
|
5
|
-
sources: "src/plugin.ts,src/
|
|
5
|
+
sources: "src/plugin.ts,src/errors.ts,src/types.ts,src/runtime/errors.ts,src/runtime/services/module-federation.service.ts,src/runtime/index.ts,plugins/_template/src/index.ts"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# every-plugin Development
|
|
@@ -24,13 +24,13 @@ plugins/your-plugin/
|
|
|
24
24
|
└── tsconfig.json
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Import `zod`, `effect`, and `@orpc/*` directly — the `every-plugin/zod`, `every-plugin/orpc`, and `every-plugin/effect` facade barrels no longer exist. The build pins zod via the workspace catalog, so version skew between plugin and framework is not a concern.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
## Step 1: Define the Contract
|
|
30
30
|
|
|
31
31
|
```typescript
|
|
32
|
-
import { eventIterator, oc } from "
|
|
33
|
-
import { z } from "
|
|
32
|
+
import { eventIterator, oc } from "@orpc/contract";
|
|
33
|
+
import { z } from "zod";
|
|
34
34
|
|
|
35
35
|
const Errors = {
|
|
36
36
|
UNAUTHORIZED: { status: 401, message: "Auth required" },
|
|
@@ -58,17 +58,17 @@ export type ContractType = typeof contract;
|
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
Key points:
|
|
61
|
-
-
|
|
61
|
+
- Import `oc`/`eventIterator` from `@orpc/contract`, `z` from `zod`, `Effect`/`Layer`/`Context` from `effect`
|
|
62
62
|
- Use `eventIterator(schema)` for streaming responses
|
|
63
63
|
- Define error objects with `status` + `message` and pass via `.errors()`
|
|
64
|
-
- Use `
|
|
64
|
+
- Use `PluginErrors` from `every-plugin/errors` for standard UNAUTHORIZED/FORBIDDEN/NOT_FOUND/BAD_REQUEST
|
|
65
65
|
|
|
66
66
|
## Step 2: Create the Service
|
|
67
67
|
|
|
68
68
|
Plain TypeScript class with Effect error handling:
|
|
69
69
|
|
|
70
70
|
```typescript
|
|
71
|
-
import { Effect } from "
|
|
71
|
+
import { Effect } from "effect";
|
|
72
72
|
|
|
73
73
|
export class MyService {
|
|
74
74
|
constructor(private baseUrl: string, private apiKey: string) {}
|
|
@@ -93,10 +93,10 @@ export class MyService {
|
|
|
93
93
|
## Step 3: Wire with createPlugin
|
|
94
94
|
|
|
95
95
|
```typescript
|
|
96
|
+
import { ORPCError } from "@orpc/server";
|
|
97
|
+
import { Effect, Layer } from "effect";
|
|
96
98
|
import { createPlugin } from "every-plugin";
|
|
97
|
-
import {
|
|
98
|
-
import { ORPCError } from "every-plugin/orpc";
|
|
99
|
-
import { z } from "every-plugin/zod";
|
|
99
|
+
import { z } from "zod";
|
|
100
100
|
import { contract } from "./contract";
|
|
101
101
|
import { MyService } from "./service";
|
|
102
102
|
|
|
@@ -109,35 +109,37 @@ export default createPlugin({
|
|
|
109
109
|
}),
|
|
110
110
|
contract,
|
|
111
111
|
|
|
112
|
-
initialize: (config, _plugins
|
|
112
|
+
initialize: (config, _plugins) =>
|
|
113
113
|
Effect.gen(function* () {
|
|
114
114
|
const service = new MyService(config.variables.baseUrl, config.secrets.apiKey);
|
|
115
115
|
yield* service.ping();
|
|
116
|
-
return { service };
|
|
117
|
-
}),
|
|
118
116
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
createRouter: (deps, builder) => {
|
|
122
|
-
const { service } = deps;
|
|
117
|
+
return Layer.succeed(MyServiceTag, service);
|
|
118
|
+
}),
|
|
123
119
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
120
|
+
createRouter: (builder) => ({
|
|
121
|
+
getById: builder.getById.effect(function* ({ input, context, errors }) {
|
|
122
|
+
if (!context.userId) {
|
|
123
|
+
return yield* Effect.fail(errors.UNAUTHORIZED({ message: "Auth required" }));
|
|
124
|
+
}
|
|
125
|
+
const service = yield* MyServiceTag;
|
|
126
|
+
return yield* service.getById(input.id);
|
|
127
|
+
}),
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return await Effect.runPromise(service.ping());
|
|
135
|
-
}),
|
|
136
|
-
};
|
|
137
|
-
},
|
|
129
|
+
ping: builder.ping.effect(function* () {
|
|
130
|
+
const service = yield* MyServiceTag;
|
|
131
|
+
return yield* service.ping();
|
|
132
|
+
}),
|
|
133
|
+
}),
|
|
138
134
|
});
|
|
139
135
|
```
|
|
140
136
|
|
|
137
|
+
Key points:
|
|
138
|
+
- `initialize` returns an `Effect` producing a `Layer` — services built in it are provided to `.effect()` handlers via the plugin's lifecycle scope
|
|
139
|
+
- Teardown lives in Layer finalizers — there is no `shutdown` option
|
|
140
|
+
- Handlers access services with `yield* Tag` (Effect-native generators)
|
|
141
|
+
- For streaming handlers (async generators), use `Context.get(context["effect/context"], Tag)`
|
|
142
|
+
|
|
141
143
|
## Plugin Composition (withPlugins)
|
|
142
144
|
|
|
143
145
|
When an API plugin needs to call other plugins in-process:
|
|
@@ -148,9 +150,9 @@ import type { PluginsClient } from "./lib/plugins-types.gen";
|
|
|
148
150
|
export default createPlugin.withPlugins<PluginsClient>()({
|
|
149
151
|
variables: z.object({ demoMessage: z.string().optional() }),
|
|
150
152
|
contract,
|
|
151
|
-
initialize: (config, plugins
|
|
153
|
+
initialize: (config, plugins) =>
|
|
152
154
|
Effect.sync(() => ({ plugins, demoMessage: config.variables.demoMessage ?? "not configured" })),
|
|
153
|
-
createRouter: (
|
|
155
|
+
createRouter: (builder) => ({
|
|
154
156
|
pluginDemo: builder.pluginDemo.handler(async () => {
|
|
155
157
|
const status = await deps.plugins.registry().getRegistryStatus();
|
|
156
158
|
return { apiVariable: deps.demoMessage, registryStatus: status };
|
|
@@ -165,17 +167,17 @@ export default createPlugin.withPlugins<PluginsClient>()({
|
|
|
165
167
|
|
|
166
168
|
## Long-Lived Scoped Resources
|
|
167
169
|
|
|
168
|
-
For database pools, caches, publisher channels, or any resource that should live for the plugin's lifetime, use `
|
|
170
|
+
For database pools, caches, publisher channels, or any resource that should live for the plugin's lifetime, use `buildScoped(tag, layer)` inside `initialize` (or `buildScopedContext(layer)` for multi-service layers):
|
|
169
171
|
|
|
170
172
|
```typescript
|
|
171
|
-
import {
|
|
172
|
-
import {
|
|
173
|
+
import { Effect, Layer } from "effect";
|
|
174
|
+
import { buildScoped, createPlugin } from "every-plugin";
|
|
173
175
|
|
|
174
176
|
export default createPlugin({
|
|
175
177
|
// ...
|
|
176
|
-
initialize: (config, _plugins
|
|
178
|
+
initialize: (config, _plugins) =>
|
|
177
179
|
Effect.gen(function* () {
|
|
178
|
-
const repo = yield*
|
|
180
|
+
const repo = yield* buildScoped(
|
|
179
181
|
MyRepoTag,
|
|
180
182
|
MyRepoLive.pipe(Layer.provide(DatabaseLive(config.secrets.MY_DATABASE_URL))),
|
|
181
183
|
);
|
|
@@ -188,9 +190,19 @@ export default createPlugin({
|
|
|
188
190
|
});
|
|
189
191
|
```
|
|
190
192
|
|
|
191
|
-
`
|
|
193
|
+
`buildScoped(tag, layer)` builds the layer with the plugin's lifecycle scope and resolves the service from the built context.
|
|
192
194
|
Resources persist until the plugin shuts down and are automatically cleaned up during `runtime.shutdown()`.
|
|
193
195
|
|
|
196
|
+
For layers that provide several services at once, use `buildScopedContext` and resolve each tag:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
const services = yield* buildScopedContext(
|
|
200
|
+
Layer.mergeAll(TenantsLive, NodesLive).pipe(Layer.provide(database)),
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const tenants = Context.get(services, TenantsTag);
|
|
204
|
+
```
|
|
205
|
+
|
|
194
206
|
**Bad — creates a transient scope that closes immediately:**
|
|
195
207
|
```typescript
|
|
196
208
|
const svc = yield* Effect.provide(MyTag, MyLive.pipe(Layer.provide(DatabaseLive(url))))
|
|
@@ -198,15 +210,14 @@ const svc = yield* Effect.provide(MyTag, MyLive.pipe(Layer.provide(DatabaseLive(
|
|
|
198
210
|
|
|
199
211
|
**Good — resources persist for the plugin's lifetime:**
|
|
200
212
|
```typescript
|
|
201
|
-
const svc = yield*
|
|
213
|
+
const svc = yield* buildScoped(MyTag, MyLive.pipe(Layer.provide(DatabaseLive(url))))
|
|
202
214
|
```
|
|
203
215
|
|
|
204
216
|
Key rules:
|
|
205
|
-
- Use `
|
|
217
|
+
- Use `buildScoped(...)` for any `Layer.scoped(...)` resource that should survive initialization
|
|
206
218
|
- Plain class construction (new Service(...)) is still fine directly in `initialize`
|
|
207
|
-
-
|
|
219
|
+
- Handlers access services via the injected oRPC context (`yield* Tag` in `.effect()` handlers), not from initialize's return value
|
|
208
220
|
- Do not use `Effect.provide(Tag, Layer.scoped(...))` for persistent dependencies inside `initialize`
|
|
209
|
-
- `tools` is provided by the framework and does not need to be imported
|
|
210
221
|
|
|
211
222
|
## Dev Server Config (plugin.dev.ts)
|
|
212
223
|
|
|
@@ -215,7 +226,6 @@ import type { PluginConfigInput } from "every-plugin";
|
|
|
215
226
|
import Plugin from "./src/index";
|
|
216
227
|
|
|
217
228
|
export default {
|
|
218
|
-
pluginId: "my-plugin",
|
|
219
229
|
port: 3010,
|
|
220
230
|
config: {
|
|
221
231
|
variables: {
|
|
@@ -228,6 +238,10 @@ export default {
|
|
|
228
238
|
};
|
|
229
239
|
```
|
|
230
240
|
|
|
241
|
+
No `pluginId` field — the dev server derives the plugin id from the workspace
|
|
242
|
+
itself (the `plugins/<key>` layout key and the package.json name); it never
|
|
243
|
+
reads one from this file.
|
|
244
|
+
|
|
231
245
|
Port assignments: host=3000, api=3001, auth=3002, ui=3003, ui-ssr=3004, plugins=3010+.
|
|
232
246
|
|
|
233
247
|
## Build Config (rspack.config.js)
|
|
@@ -254,9 +268,27 @@ export default {
|
|
|
254
268
|
|
|
255
269
|
## Common Mistakes
|
|
256
270
|
|
|
257
|
-
- Importing `oc` from
|
|
258
|
-
- Importing `z` from `zod` instead of `every-plugin/zod` — may cause Vitest CJS/ESM interop issues; always use `every-plugin/zod`
|
|
271
|
+
- Importing `z`/`oc`/`Effect` from `every-plugin/zod`, `every-plugin/orpc`, or `every-plugin/effect` — those facade barrels were deleted; import from `zod`, `@orpc/contract`, and `effect` directly
|
|
259
272
|
- Forgetting `.errors(Errors)` on routes that can throw ORPCError — untyped errors
|
|
260
273
|
- Using `Effect.runPromise` inside `Effect.gen` — use `yield*` instead for proper error channel
|
|
261
274
|
- Putting business logic in `createRouter` — keep it in the service class, router is just glue
|
|
262
|
-
- Using `Effect.provide(Tag, Layer.scoped(...))` inside `initialize` for long-lived resources — creates a transient scope that releases the resource immediately after initialization. Use `
|
|
275
|
+
- Using `Effect.provide(Tag, Layer.scoped(...))` inside `initialize` for long-lived resources — creates a transient scope that releases the resource immediately after initialization. Use `buildScoped(Tag, Layer.scoped(...))` instead
|
|
276
|
+
|
|
277
|
+
## The tasks you will actually be given
|
|
278
|
+
|
|
279
|
+
**"Add a route to a plugin"** — e.g. a `deleteItem` procedure. Add it to `plugins/<your-plugin>/src/contract.ts` (`.route({ method: "DELETE", path: "/items/{id}" }).input(...).errors(Errors)`), add a method on the service class, then wire it in `src/index.ts` as `deleteItem: builder.deleteItem.effect(function* ({ input, context, errors }) { ... })` — check `context.userId` first and fail with `errors.UNAUTHORIZED(...)` / `errors.NOT_FOUND(...)` inside the generator, mirroring `plugins/_template/src/index.ts` (`getById`, `deleteThing`). Verify with `cd plugins/<your-plugin> && pnpm test`, then `pnpm run typecheck` (regenerates `plugins-types.gen.ts` for cross-plugin consumers).
|
|
280
|
+
|
|
281
|
+
**"Ship a new plugin"** — copy `plugins/_template/` to `plugins/<key>/`, set `name` in its package.json (this is the registry id tests use), fill in `plugin.dev.ts` (port; plugins start at 3010), and register it in the authored config with `Plugin("<key>").path("plugins/<key>", { ... })` (see `bos.app.ts`). Run `bos types gen` or restart `pnpm run dev`, then deploy with `cd plugins/<key> && bos plugin publish <key>`.
|
|
282
|
+
|
|
283
|
+
**"A plugin fails to load in the host"** — capture the error words and match them in the table below; shared-identity failures are confirmed with `bos mf check` and fixed by rebuilding/redeploying the one plugin.
|
|
284
|
+
|
|
285
|
+
## What comes back when it fails
|
|
286
|
+
|
|
287
|
+
| Error word / shape you see | Meaning | Action |
|
|
288
|
+
|---|---|---|
|
|
289
|
+
| `ModuleFederationError` with "shared identity mismatch" (or the plugin's `mf-manifest.json` reports an older `pluginVersion` than the host) | plugin bundle built against a different `@module-federation/runtime`, or missing a `shared[]` dep the host requires | rebuild + redeploy that one plugin (`cd plugins/<key> && bos plugin publish <key>`); confirm with `bos mf check`. Stop editing code — this is a build/deploy problem |
|
|
290
|
+
| `No valid plugin constructor found for '<id>'` / `missing the required 'binding' property` | the remote module's export was not created by `createPlugin()` | `export default createPlugin({...})` from `src/index.ts`, rebuild |
|
|
291
|
+
| `PluginRuntimeError` with operation `validate-config` or `validate-secrets` and a Zod cause | runtime config violates the plugin's `variables`/`secrets` schemas (secrets hydrate into variables, then re-validate) | fix `plugin.dev.ts` in dev, or the runtime config entry; read the Zod issue paths in the error |
|
|
292
|
+
| `Plugin ID '<id>' not found in registry.` | registry key mismatch | align the key used with the plugins/ directory key and package.json `name` |
|
|
293
|
+
| 401 `UNAUTHORIZED` with data `{ apiKeyProvided: boolean, ... }` | no session or API key reached the handler (see `every-plugin/errors`) | caller-side problem — sign in or send `x-api-key`; do not catch it in the plugin |
|
|
294
|
+
| 503 `SERVICE_UNAVAILABLE` / 502 `CONNECTION_ERROR` shapes from `every-plugin/errors` | upstream dependency of the service failed | surface them via `.errors(...)` on the contract, not ad-hoc ORPCError codes |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: plugin-testing
|
|
3
3
|
description: Test every-plugin modules with vitest and the plugin runtime. Use when writing or modifying plugin tests under plugins/*/src/__tests__/ or plugins/*/tests/.
|
|
4
4
|
metadata:
|
|
5
|
-
sources: "src/
|
|
5
|
+
sources: "src/index.ts,src/runtime/index.ts,src/runtime/errors.ts,plugins/_template/tests/setup.ts"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# every-plugin Testing
|
|
@@ -23,7 +23,7 @@ Test the service class directly without the plugin runtime:
|
|
|
23
23
|
|
|
24
24
|
```typescript
|
|
25
25
|
import { describe, expect, it } from "vitest";
|
|
26
|
-
import { Effect } from "
|
|
26
|
+
import { Effect } from "effect";
|
|
27
27
|
import { MyService } from "../service";
|
|
28
28
|
|
|
29
29
|
describe("MyService", () => {
|
|
@@ -126,16 +126,27 @@ describe("API with mock registry", () => {
|
|
|
126
126
|
|
|
127
127
|
## Testing Scope Lifecycle
|
|
128
128
|
|
|
129
|
-
When a plugin uses `
|
|
129
|
+
When a plugin uses `buildScoped(...)` inside `initialize`, verify that scoped resources persist after initialization and are released during shutdown:
|
|
130
130
|
|
|
131
131
|
```typescript
|
|
132
132
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
133
133
|
import { createPlugin, createPluginRuntime } from "every-plugin";
|
|
134
|
-
import { Context, Effect, Layer } from "
|
|
134
|
+
import { Context, Effect, Layer } from "effect";
|
|
135
135
|
|
|
136
136
|
let released = false;
|
|
137
137
|
|
|
138
|
-
class TestTag extends Context.
|
|
138
|
+
class TestTag extends Context.Service<TestTag, { value: string }>()("TestTag") {}
|
|
139
|
+
|
|
140
|
+
const TestLive = Layer.effect(
|
|
141
|
+
TestTag,
|
|
142
|
+
Effect.acquireRelease(
|
|
143
|
+
Effect.sync(() => ({ value: "live" })),
|
|
144
|
+
() =>
|
|
145
|
+
Effect.sync(() => {
|
|
146
|
+
released = true;
|
|
147
|
+
}),
|
|
148
|
+
),
|
|
149
|
+
);
|
|
139
150
|
|
|
140
151
|
describe("scope lifecycle", () => {
|
|
141
152
|
let runtime: ReturnType<typeof createPluginRuntime>;
|
|
@@ -143,12 +154,11 @@ describe("scope lifecycle", () => {
|
|
|
143
154
|
beforeAll(async () => {
|
|
144
155
|
const plugin = createPlugin({
|
|
145
156
|
// ... variables, secrets, contract ...
|
|
146
|
-
initialize: (
|
|
147
|
-
Effect.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
createRouter: (deps) => ({
|
|
157
|
+
initialize: () =>
|
|
158
|
+
Effect.succeed(
|
|
159
|
+
TestLive,
|
|
160
|
+
),
|
|
161
|
+
createRouter: (builder) => ({
|
|
152
162
|
// ... routes ...
|
|
153
163
|
}),
|
|
154
164
|
});
|
|
@@ -212,6 +222,25 @@ export default defineConfig({
|
|
|
212
222
|
|
|
213
223
|
- Not calling `runtime.shutdown()` in `afterAll` — leaves plugin scopes/resources running
|
|
214
224
|
- Using `vi.fn()` without `.mockResolvedValue()` — unhandled promise rejections
|
|
215
|
-
- Forgetting `vite-tsconfig-paths` plugin —
|
|
225
|
+
- Forgetting `vite-tsconfig-paths` plugin — package subpath imports like `every-plugin/errors` won't resolve
|
|
216
226
|
- Omitting the `registry` object when calling `createPluginRuntime(...)` — the runtime requires explicit plugin entries
|
|
217
227
|
- Testing only happy paths — always test error channels (Effect failures, ORPCError throws)
|
|
228
|
+
|
|
229
|
+
## The tasks you will actually be given
|
|
230
|
+
|
|
231
|
+
**"Add an integration test for a new route"** — in `plugins/<your-plugin>/tests/integration/plugin.test.ts`, follow the `_template` pattern (`plugins/_template/tests/setup.ts`): call `await getPluginClient({ userId: "user123" })` — it boots `createPluginRuntime` with the registry keyed by the package.json `name`, config from `plugin.dev.ts`, and an HTTP server that maps `x-test-user`/`x-test-session` headers to context. Then drive the route through the client and assert the response. Run with `cd plugins/<your-plugin> && pnpm test` (script `vitest run`, `testTimeout: 30000` in `vitest.config.ts`).
|
|
232
|
+
|
|
233
|
+
**"Prove a service method fails with the right ORPC code"** — follow `plugins/_template/tests/unit/things-service.test.ts`: build a fresh layer with `DatabaseLive('pglite:<mkdtemp dir>')` and `Layer.succeed(PluginIdTag, "<pluginId>")`, run the failing effect through `Effect.runPromiseExit` + `Cause.squash`, then `expect(error).toBeInstanceOf(ORPCError)` and `expect(error.code).toBe("CONFLICT")` (or `NOT_FOUND`). Clean the temp dir in `afterEach`.
|
|
234
|
+
|
|
235
|
+
**"A test fails at `usePlugin` with a validation error"** — the config comes from `plugin.dev.ts` via `tests/setup.ts` (`TEST_CONFIG`). The runtime validates `variables` and `secrets` against the plugin's zod schemas (`validate-config` / `validate-secrets` → `PluginRuntimeError` with a `zodError` cause). Fix the config in `plugin.dev.ts`, not the test.
|
|
236
|
+
|
|
237
|
+
## What comes back when it fails
|
|
238
|
+
|
|
239
|
+
| Error word / shape you see | Meaning | Action |
|
|
240
|
+
|---|---|---|
|
|
241
|
+
| `Plugin ID '<id>' not found in registry.` (`PluginRuntimeError`, operation `validate-plugin-id`) | the `usePlugin` key does not match a registry entry | make the registry key the package.json `name` (as `tests/setup.ts` does), then read again |
|
|
242
|
+
| `PluginRuntimeError` with operation `validate-config` / `validate-secrets` and a Zod cause | `TEST_CONFIG` violates the plugin's schemas | fix `plugin.dev.ts`; do not loosen the schemas to make tests pass |
|
|
243
|
+
| `ModuleFederationError` | only when loading a remote URL — in-process tests (`module: Plugin`) never produce it | if you see it, your registry entry points at a URL instead of the imported module |
|
|
244
|
+
| `Cannot find module` for `every-plugin/...` subpaths | `vite-tsconfig-paths` missing from `vitest.config.ts` plugins | add it (see the Vitest Config section) |
|
|
245
|
+
| Test hangs past 30s | default `testTimeout: 30000`; a streaming handler's `for await` never terminates | pass `signal`/`maxResults` limits like `_template`'s `listenBackground`, or abort the iterator |
|
|
246
|
+
| Scoped resource still alive after tests finish | `runtime.shutdown()` (or `_template`'s `teardown()`) never ran | call it in `afterAll` |
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
/** Dev-mode entry names: the in-memory dev server serves these exact names,
|
|
5
|
+
* and dev consumers (the host's html shell, the compose client payload, the
|
|
6
|
+
* readiness probe) append them — dev has no build report to discover hashed
|
|
7
|
+
* names from. */
|
|
8
|
+
export const DEV_ENTRY_FILENAME = "remoteEntry.js";
|
|
9
|
+
export const DEV_SERVER_ENTRY_FILENAME = "remoteEntry.server.js";
|
|
10
|
+
/** Fixed-name build outputs that get additive immutable hashed copies. */
|
|
11
|
+
export const MF_MANIFEST_FILENAME = "mf-manifest.json";
|
|
12
|
+
export const STYLE_FILENAME = "style.css";
|
|
13
|
+
export const HASHED_ENTRY_PATTERN = "remoteEntry.[contenthash].js";
|
|
14
|
+
export const HASHED_SERVER_ENTRY_PATTERN = "remoteEntry.server.[contenthash].js";
|
|
15
|
+
|
|
16
|
+
/** The dev-stack signal: everything a dev server spawns (watch or no-watch
|
|
17
|
+
* one-shot builds) serves the fixed dev entry names, because every dev
|
|
18
|
+
* consumer appends them and no build report exists to discover hashed names
|
|
19
|
+
* from. Every other invocation — local builds, host-test production builds,
|
|
20
|
+
* regression container builds, deploys — is a build and emits content-hashed
|
|
21
|
+
* entries + build reports. NODE_ENV cannot carry this decision (vitest runs
|
|
22
|
+
* as "test", bundler CLIs default "production" even for dev watches), and
|
|
23
|
+
* DEPLOY=true only marks deploy builds — so the dev server stamps its own
|
|
24
|
+
* children. */
|
|
25
|
+
export function isBuildInvocation(): boolean {
|
|
26
|
+
return process.env.BOS_DEV_SERVER !== "1";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function uiEntryFilename(input: { isBuild: boolean; server?: boolean }): string {
|
|
30
|
+
if (!input.isBuild) return input.server ? DEV_SERVER_ENTRY_FILENAME : DEV_ENTRY_FILENAME;
|
|
31
|
+
return input.server ? HASHED_SERVER_ENTRY_PATTERN : HASHED_ENTRY_PATTERN;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const HASH_SEGMENT_PATTERN = /\.[a-f0-9]{8,}\./;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Serving classification for bundle objects: anything whose name carries a
|
|
38
|
+
* content-hash segment (including hashed entrypoints like
|
|
39
|
+
* `remoteEntry.8f3a….js`) is immutable by construction; fixed-name
|
|
40
|
+
* entrypoints and non-hashed files a redeploy replaces in place must
|
|
41
|
+
* revalidate.
|
|
42
|
+
*/
|
|
43
|
+
export function isImmutableBundlePath(name: string): boolean {
|
|
44
|
+
const base = name.split("/").pop() ?? name;
|
|
45
|
+
return HASH_SEGMENT_PATTERN.test(base);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function cacheControlOf(name: string): string {
|
|
49
|
+
return isImmutableBundlePath(name)
|
|
50
|
+
? "public, max-age=31536000, immutable"
|
|
51
|
+
: "public, max-age=0, must-revalidate";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Short content hash used to mint additive hashed copies of fixed-name artifacts. */
|
|
55
|
+
export function contentHashOf(content: string | Uint8Array): string {
|
|
56
|
+
return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function hashedArtifactName(base: string, hash: string, ext: string): string {
|
|
60
|
+
return `${base}.${hash}.${ext.replace(/^\./, "")}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Find the content-hashed entry asset for a base name ("remoteEntry", "remoteEntry.server"). */
|
|
64
|
+
export function findHashedEntry(assetNames: string[], base: string): string | null {
|
|
65
|
+
const hashed = assetNames.find((name) => {
|
|
66
|
+
const rest = name.startsWith(`${base}.`) ? name.slice(base.length + 1) : null;
|
|
67
|
+
if (rest === null) return false;
|
|
68
|
+
const hash = rest.slice(0, -3);
|
|
69
|
+
return name.endsWith(".js") && /^[a-f0-9]{8,}$/.test(hash);
|
|
70
|
+
});
|
|
71
|
+
return hashed ?? null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const BuildEntryReportSchema = z.object({
|
|
75
|
+
entry: z.string().min(1),
|
|
76
|
+
browserManifest: z.string().min(1).optional(),
|
|
77
|
+
css: z.string().min(1).optional(),
|
|
78
|
+
});
|
|
79
|
+
export type BuildEntryReport = z.infer<typeof BuildEntryReportSchema>;
|
|
80
|
+
|
|
81
|
+
export interface ArtifactCopy {
|
|
82
|
+
from: string;
|
|
83
|
+
to: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The additive-copy plan for one compiled dist root: each supplied fixed-name
|
|
88
|
+
* artifact gets an immutable hashed copy. The hashed entry itself needs no
|
|
89
|
+
* copy — the build report carries its name to the deploy leg. Returns null
|
|
90
|
+
* when the build predates hashed entry names (only fixed names emitted).
|
|
91
|
+
*/
|
|
92
|
+
export function planArtifactCopies(input: {
|
|
93
|
+
assetNames: string[];
|
|
94
|
+
entryBase: string;
|
|
95
|
+
contents?: Record<string, string | Uint8Array | undefined>;
|
|
96
|
+
}): { copies: Array<ArtifactCopy>; report: BuildEntryReport } | null {
|
|
97
|
+
const entry = findHashedEntry(input.assetNames, input.entryBase);
|
|
98
|
+
if (!entry) return null;
|
|
99
|
+
|
|
100
|
+
const copies: Array<ArtifactCopy> = [];
|
|
101
|
+
const report: BuildEntryReport = { entry };
|
|
102
|
+
|
|
103
|
+
const hashedCopy = (fixedName: string, reportField: "browserManifest" | "css") => {
|
|
104
|
+
const content = input.contents?.[fixedName];
|
|
105
|
+
if (content === undefined) return;
|
|
106
|
+
const dot = fixedName.lastIndexOf(".");
|
|
107
|
+
const base = fixedName.slice(0, dot);
|
|
108
|
+
const ext = fixedName.slice(dot + 1);
|
|
109
|
+
const hashed = hashedArtifactName(base, contentHashOf(content), ext);
|
|
110
|
+
copies.push({ from: fixedName, to: hashed });
|
|
111
|
+
report[reportField] = hashed;
|
|
112
|
+
};
|
|
113
|
+
hashedCopy(MF_MANIFEST_FILENAME, "browserManifest");
|
|
114
|
+
hashedCopy(STYLE_FILENAME, "css");
|
|
115
|
+
|
|
116
|
+
return { copies, report };
|
|
117
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
|
|
9
|
+
export type ContractTypesStatus = "generated" | "up-to-date" | "skipped";
|
|
10
|
+
|
|
11
|
+
export const CONTRACT_ENTRY = path.join("src", "contract.ts");
|
|
12
|
+
export const CONTRACT_TYPES_DIR = "types";
|
|
13
|
+
export const CONTRACT_TYPES_FILE = path.join("types", "contract.d.ts");
|
|
14
|
+
|
|
15
|
+
export function contractTypesUpToDate(cwd: string): boolean {
|
|
16
|
+
const entry = path.join(cwd, CONTRACT_ENTRY);
|
|
17
|
+
const outFile = path.join(cwd, CONTRACT_TYPES_FILE);
|
|
18
|
+
if (!fs.existsSync(entry) || !fs.existsSync(outFile)) return false;
|
|
19
|
+
return fs.statSync(outFile).mtimeMs >= fs.statSync(entry).mtimeMs;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function resolveTscBinary(cwd: string): string {
|
|
23
|
+
for (const from of [
|
|
24
|
+
path.join(cwd, "package.json"),
|
|
25
|
+
path.join(import.meta.dirname, "..", "..", "..", "package.json"),
|
|
26
|
+
]) {
|
|
27
|
+
try {
|
|
28
|
+
const req = createRequire(path.resolve(from));
|
|
29
|
+
const pkgJsonPath = req.resolve("typescript/package.json");
|
|
30
|
+
return path.join(path.dirname(pkgJsonPath), "bin", "tsc");
|
|
31
|
+
} catch {
|
|
32
|
+
// try the next resolution root
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return "tsc";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Regenerates types/contract.d.ts for a plugin workspace by invoking the
|
|
40
|
+
* workspace's TypeScript 7 binary with explicit flags — no tsconfig file
|
|
41
|
+
* involved, so emit layout is deterministic and there is nothing to sync.
|
|
42
|
+
*
|
|
43
|
+
* - "skipped": the workspace has no src/contract.ts
|
|
44
|
+
* - "up-to-date": types/contract.d.ts is newer than src/contract.ts
|
|
45
|
+
* - "generated": declarations were re-emitted (types/ is cleaned first, so
|
|
46
|
+
* stale nested layouts never survive)
|
|
47
|
+
*/
|
|
48
|
+
export async function generateContractTypes(
|
|
49
|
+
cwd: string = process.cwd(),
|
|
50
|
+
): Promise<ContractTypesStatus> {
|
|
51
|
+
const entry = path.join(cwd, CONTRACT_ENTRY);
|
|
52
|
+
if (!fs.existsSync(entry)) {
|
|
53
|
+
return "skipped";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (contractTypesUpToDate(cwd)) {
|
|
57
|
+
return "up-to-date";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const outDir = path.join(cwd, CONTRACT_TYPES_DIR);
|
|
61
|
+
fs.rmSync(outDir, { recursive: true, force: true });
|
|
62
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
63
|
+
|
|
64
|
+
const tsc = resolveTscBinary(cwd);
|
|
65
|
+
const args = [
|
|
66
|
+
"--ignoreConfig",
|
|
67
|
+
"--emitDeclarationOnly",
|
|
68
|
+
"--declaration",
|
|
69
|
+
"--rootDir",
|
|
70
|
+
"src",
|
|
71
|
+
"--outDir",
|
|
72
|
+
CONTRACT_TYPES_DIR,
|
|
73
|
+
"--target",
|
|
74
|
+
"esnext",
|
|
75
|
+
"--module",
|
|
76
|
+
"esnext",
|
|
77
|
+
"--moduleResolution",
|
|
78
|
+
"bundler",
|
|
79
|
+
"--strict",
|
|
80
|
+
"--skipLibCheck",
|
|
81
|
+
CONTRACT_ENTRY,
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
await execFileAsync(process.execPath, [tsc, ...args], { cwd });
|
|
86
|
+
} catch (error) {
|
|
87
|
+
const err = error as { message?: string; stdout?: string; stderr?: string };
|
|
88
|
+
const detail = [err.stderr, err.stdout, err.message].filter(Boolean).join("\n").trim();
|
|
89
|
+
throw new Error(
|
|
90
|
+
`Contract type generation failed for ${path.basename(cwd)} — tsc exited non-zero:\n${detail}`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!fs.existsSync(path.join(cwd, CONTRACT_TYPES_FILE))) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Contract type generation produced no output — expected ${path.join(cwd, CONTRACT_TYPES_FILE)}`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return "generated";
|
|
101
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Compiler, RspackPluginInstance } from "@rspack/core";
|
|
2
|
+
import { BuildEntryReportSchema, findHashedEntry } from "../artifact-names";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Emits `build-report.json` for the deploy leg: the container entry is hashed
|
|
6
|
+
* by the build's filename template (`remoteEntry.[contenthash].js`), and the
|
|
7
|
+
* report names it — in-compilation, so watch-mode cleans can't open a window
|
|
8
|
+
* where the report 404s (mirrors the `output.clean` rationale on the build
|
|
9
|
+
* plugin).
|
|
10
|
+
*/
|
|
11
|
+
export class BuildReportPlugin implements RspackPluginInstance {
|
|
12
|
+
name = "BuildReportPlugin";
|
|
13
|
+
|
|
14
|
+
constructor(private entryBase = "remoteEntry") {}
|
|
15
|
+
|
|
16
|
+
apply(compiler: Compiler) {
|
|
17
|
+
compiler.hooks.thisCompilation.tap(this.name, (compilation) => {
|
|
18
|
+
const webpack = (compiler as Compiler & { webpack?: any }).webpack;
|
|
19
|
+
const rawSource = webpack?.sources?.RawSource;
|
|
20
|
+
const stage = webpack?.Compilation?.PROCESS_ASSETS_STAGE_ADDITIONS ?? 1000;
|
|
21
|
+
|
|
22
|
+
compilation.hooks.processAssets.tap({ name: this.name, stage }, (assets) => {
|
|
23
|
+
const entry = findHashedEntry(Object.keys(assets), this.entryBase);
|
|
24
|
+
if (!entry || !rawSource) return;
|
|
25
|
+
|
|
26
|
+
const report = BuildEntryReportSchema.parse({ entry });
|
|
27
|
+
compilation.emitAsset(
|
|
28
|
+
"build-report.json",
|
|
29
|
+
new rawSource(`${JSON.stringify(report, null, 2)}\n`),
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Compiler, RspackPluginInstance } from "@rspack/core";
|
|
2
|
+
import { findHashedEntry } from "../artifact-names";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Fails the build when a container entry's eager chunk references
|
|
6
|
+
* (`Promise.all([__webpack_require__.e(N), …])` in get factories) point at
|
|
7
|
+
* chunk files the compilation never emits. Rspack 2.x splitChunks re-ids the
|
|
8
|
+
* final chunk graph while the federation get-factory codegen can keep
|
|
9
|
+
* pre-split ids — the emitted entry then 404s its own chunks at load time and
|
|
10
|
+
* the failure surfaces downstream as `__webpack_modules__[r] is not a
|
|
11
|
+
* function`. SplitChunks is disabled in the composition for exactly this
|
|
12
|
+
* reason; this gate catches any regression loudly at build time instead.
|
|
13
|
+
*/
|
|
14
|
+
export class ChunkCompletenessPlugin implements RspackPluginInstance {
|
|
15
|
+
name = "ChunkCompletenessPlugin";
|
|
16
|
+
|
|
17
|
+
constructor(private entryBase = "remoteEntry") {}
|
|
18
|
+
|
|
19
|
+
apply(compiler: Compiler) {
|
|
20
|
+
compiler.hooks.thisCompilation.tap(this.name, (compilation) => {
|
|
21
|
+
const webpack = (compiler as Compiler & { webpack?: any }).webpack;
|
|
22
|
+
const stage = webpack?.Compilation?.PROCESS_ASSETS_STAGE_ADDITIONS ?? 1000;
|
|
23
|
+
|
|
24
|
+
compilation.hooks.processAssets.tap({ name: this.name, stage }, (assets) => {
|
|
25
|
+
const names = Object.keys(assets);
|
|
26
|
+
const entry =
|
|
27
|
+
findHashedEntry(names, this.entryBase) ?? names.find((n) => n === `${this.entryBase}.js`);
|
|
28
|
+
if (!entry) return;
|
|
29
|
+
|
|
30
|
+
const jsFiles = new Set(names.filter((n) => n.endsWith(".js")));
|
|
31
|
+
const source = assets[entry].source().toString();
|
|
32
|
+
|
|
33
|
+
const special = new Map<string, string>();
|
|
34
|
+
const u = source.match(/\.u\s*=\s*(\w+)=>([^,;]+)/);
|
|
35
|
+
if (u) {
|
|
36
|
+
for (const m of u[2].matchAll(/(\d+)\s*===\s*\w+\s*\?\s*"([^"]+)"/g)) {
|
|
37
|
+
special.set(m[1], m[2]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
for (const pa of source.matchAll(/Promise\.all\(\[([^\]]*)\]\)/g)) {
|
|
42
|
+
for (const m of pa[1].matchAll(/(?:__webpack_require__|\w+)\.e\((\d+)\)/g)) {
|
|
43
|
+
const id = m[1];
|
|
44
|
+
const file = special.has(id) ? `${special.get(id)}.js` : `${id}.js`;
|
|
45
|
+
if (!jsFiles.has(file)) {
|
|
46
|
+
compilation.errors.push(
|
|
47
|
+
new Error(
|
|
48
|
+
`[${this.name}] entry ${entry} eagerly loads chunk ${id} (${file}) but the compilation never emitted it — the built remote would fail at load time with "__webpack_modules__[r] is not a function".`,
|
|
49
|
+
),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|