0.5 पर माइग्रेट करें
0.5 उन bugs को ठीक करता है जिनसे observable behaviour बदलता था, इसलिए इनमें से कुछ fixes 0.4 का code तोड़ते हैं। यह पेज हर breaking change को पैकेज के हिसाब से, बदलने वाले code के साथ, बताता है। हर tuvix.js और @tuvix.js/* पैकेज को एक साथ ^0.5.0 पर upgrade करें; एक ही पेज पर 0.4 और 0.5 adapters मिलाना supported नहीं है।
pnpm up --latest "tuvix.js" "@tuvix.js/*"0.5 से पहले create-tuvix-app से बने projects ^0.1.0 पर निर्भर हैं, जो कभी 0.4 या 0.5 पर resolve नहीं होता। उन ranges को हाथ से बदलें।
Core
Orchestrator global bus इस्तेमाल करता है
0.4 हर orchestrator को getGlobalBus() से अलग एक private bus देता था। 0.5 में orchestrator का bus getGlobalBus() ही है, जब तक आप eventBus विकल्प न दें, और destroy() global bus को चालू छोड़ता है।
// 0.4: two buses; apps listening on getGlobalBus() missed orchestrator events
const orchestrator = createOrchestrator();
orchestrator.getEventBus() === getGlobalBus(); // false// 0.5
import { createOrchestrator, getGlobalBus } from 'tuvix.js';
const shared = createOrchestrator();
console.log(shared.getEventBus() === getGlobalBus()); // true
const isolated = createOrchestrator({ eventBus: {} }); // private bus, as in 0.4
console.log(isolated.getEventBus() === getGlobalBus()); // falseअगर आपने orchestrator का bus window पर रखा था ताकि ऐप्स उसे ढूँढ सकें, तो वह हटा दें और mount context का bus या getGlobalBus() इस्तेमाल करें।
Route changes manual ऐप्स को नहीं छूते
0.4 अगले route change पर ऐसे ऐप को unmount कर देता था जिसके पास न activeWhen था न कोई router route, और mountWhenVisible ऐप्स को भी। 0.5 में reconciles सिर्फ़ उन ऐप्स को छूते हैं जिन्हें activeWhen या कोई route नियंत्रित करता है। अगर आप हाथ से mount किए ऐप को हटाने के लिए navigation पर निर्भर थे, तो उसे खुद unmount करें:
import { createOrchestrator } from 'tuvix.js';
const orchestrator = createOrchestrator();
await orchestrator.unmountApp('cookie-banner');sandbox: true shadow root में रेंडर करता है
0.4 में register पर sandbox कुछ नहीं करता था। 0.5 में sandbox: true ऐप को shadow root के अंदर mount करता है और उसकी entry styles वहीं load करता है। पेज की CSS ऐप तक पहुँचना बंद कर देती है, और ऐप की entry styles पेज तक पहुँचना बंद कर देती हैं। जो ऐप्स पेज के styles पर निर्भर हैं, उनसे sandbox: true हटाएँ। Sandboxing देखें।
prefetch: 'hover'
0.4 पेज पर कहीं भी पहली mouse movement पर हर ऐप को prefetch करता था। 0.5 किसी ऐप को तब prefetch करता है जब pointer किसी ऐसे same-origin link में प्रवेश करता है, या focus उस पर आता है, जिसका path उस ऐप को activate करता है। सारे ऐप्स पहले ही fetch करने के लिए 'idle' या 'immediate' इस्तेमाल करें।
OrchestratorConfig.shared हटा दिया गया
Orchestrator ने shared कभी पढ़ा ही नहीं। इसे हटा दें, और डिपेंडेंसी @tuvix.js/module-federation या import maps से साझा करें।
activeWhen patterns
activeWhen strings अब matchPath इस्तेमाल करती हैं। Query string या hash से अब ऐप unmount नहीं होता, :param segments काम करते हैं, और . और ( जैसे characters ज्यों के त्यों match होते हैं।
// 0.4: `.` was a regex wildcard and matched '/v1x0'
activeWhen: '/v1.0/*'
// 0.4: query strings broke matching, so people wrote functions
activeWhen: (path) => path.split('?')[0].startsWith('/dashboard')import { createOrchestrator } from 'tuvix.js';
const orchestrator = createOrchestrator();
orchestrator.register({
name: 'dashboard',
entry: () => import('./apps/dashboard'),
container: '#app',
activeWhen: '/dashboard/*', // also active on /dashboard?tab=1
});Router
'/' route और RouteConfig.exact
Route paths हर जगह पूरे path से match होते हैं। '/' route अब सिर्फ़ / पर activate होता है; 0.4 में getActiveApps() इसे हर path मानता था, जबकि match() इसे exact मानता था। RouteConfig.exact हटा दिया गया, क्योंकि उससे कभी कुछ नहीं बदला।
// 0.4
routes: [{ path: '/', app: 'shell-nav' }, { path: '/settings', app: 'settings', exact: true }]// 0.5
import { createRouter } from 'tuvix.js';
const router = createRouter({
routes: [
{ path: '/*', app: 'shell-nav' }, // every path
{ path: '/settings', app: 'settings' }, // /settings only
],
});
void router;register पर activeWhen: '/' अब भी हर path से match होता है, क्योंकि activeWhen strings prefix matching इस्तेमाल करती हैं।
push और replace base जोड़ते हैं
// 0.4 with base: '/app'
router.push('/app/settings'); // you had to add the base yourself// 0.5
import { createRouter } from 'tuvix.js';
const router = createRouter({ base: '/app', routes: [] });
await router.push('/settings'); // URL: /app/settings0.5 में /app/settings देने पर /app/app/settings लिखा जाता है। Back/forward navigation रद्द करने वाला guard भी URL को base के साथ वापस लगाता है।
राउटर के दूसरे बदलाव
baseसिर्फ़ segment boundary पर हटता है।base: '/app'के साथ/application/applicationही रहता है।pathToRegex()एक cachedRegExpलौटाता है; इसे mutate न करें। इसका दूसराexactargument हटा दिया गया।- Patterns में regex characters ज्यों के त्यों match होते हैं। Pattern के बीच में
*एक segment से match होता है। matchRoute()औरisPathActive()path में#hashको नज़रअंदाज़ करते हैं।
Loader
हटाए गए विकल्प
LoaderOptions.fetch और EntryConfig.html हटा दिए गए। Loader ने इन्हें कभी पढ़ा ही नहीं; इन्हें अपने config से हटा दें।
// 0.4
new ModuleLoader({ fetch: myFetch });
orchestrator.register({ name: 'x', entry: { scripts: ['/x.js'], html: '<div></div>' }, container: '#x' });// 0.5
import { ModuleLoader } from '@tuvix.js/loader';
const loader = new ModuleLoader({ timeout: 10000 });
void loader;Module scripts हर URL पर एक बार चलती हैं
unload(name) के बाद, जिस ऐप की type: 'module' script ने खुद को register किया था, वह उसी URL से दोबारा load नहीं होता, क्योंकि ब्राउज़र हर URL के लिए module script एक बार चलाते हैं। जिन ऐप्स को आप unload करके फिर से load करते हैं, उनके लिए loader-function entry इस्तेमाल करें:
import { createOrchestrator } from 'tuvix.js';
const orchestrator = createOrchestrator();
orchestrator.register({
name: 'editor',
entry: () => import('./apps/editor'),
container: '#editor',
});Cached module के लिए load() अब 0 की जगह अपनी असली duration बताता है।
Module Federation
federatedEntry और createFederatedEntry अब { scripts: [remoteEntry.js] } की जगह एक loader function लौटाते हैं। 0.4 की entry container script load करती थी और फिर "no exports found" के साथ fail हो जाती थी।
// 0.4
orchestrator.register({
name: 'dashboard',
entry: federatedEntry({ name: 'dashboard', url: 'https://cdn.example.com/dashboard/remoteEntry.js' }),
container: '#main',
});// 0.5
import { createOrchestrator } from '@tuvix.js/core';
import { createFederatedLoader, federatedEntry } from '@tuvix.js/module-federation';
const orchestrator = createOrchestrator();
orchestrator.register({
name: 'dashboard',
entry: federatedEntry(
{ name: 'dashboard', url: 'https://cdn.example.com/dashboard/remoteEntry.js' },
'./App', // exposed module path
),
container: '#main',
activeWhen: '/dashboard/*',
});
const remotes = createFederatedLoader({
remotes: [{ name: 'settings', url: 'https://cdn.example.com/settings/remoteEntry.js' }],
shareScope: {}, // replaces 0.4 `shared`
});
orchestrator.register({
name: 'settings',
entry: remotes.createFederatedEntry('settings', './App'),
container: '#main',
activeWhen: '/settings/*',
});FederatedLoaderConfig.sharedकी जगह अबshareScopeहै। 0.4 नेsharedकभी नहीं पढ़ा।loadRemoteContainerअबanyकी जगहFederatedContainerलौटाता है।
React
TuvixReactAppके server HTML में अब Suspense comment markers (<!--$-->...<!--/$-->) होते हैं। दोनों तरफ़@tuvix.js/react0.5 से रेंडर और hydrate करें।- पहला render throw करने पर
mountreject होता है। 0.4 में एरर React के root error handler तक पहुँचता था औरmountresolve हो जाता था। अब orchestrator ऐप काfallbackदिखाता है औरonErrorcall करता है। - जिस container को module ने कभी mount नहीं किया, उस पर
unmountकुछ नहीं करता। 0.4 उसे खाली कर देता था।
Vue
update() अब app.config.globalProperties.$tuvixProps नहीं लिखता। Props अब reactive हैं:
<!-- 0.4 -->
<template>{{ $tuvixProps.theme }}</template><!-- 0.5 -->
<script setup lang="ts">
defineProps<{ theme?: string }>();
</script>
<template>{{ theme }}</template>Descendants inject('tuvixProps') कर सकते हैं, जो वही reactive object लौटाता है।
VueSsrMicroAppConfigअबssrस्वीकार नहीं करता।createSsrVueMicroAppइसे set करता है।- जिस container को module ने कभी mount नहीं किया, उस पर
unmountकुछ नहीं करता।
Svelte
ssrके बिनाcreateSvelteMicroAppरेंडर करने से पहले container खाली करता है। 0.4 Svelte 3/4 components को मौजूदा markup के बाद जोड़ता था। Server markup रखने और उसे hydrate करने के लिएcreateSsrSvelteMicroAppइस्तेमाल करें।- जिस container को module ने कभी mount नहीं किया, या जिसका mount fail हुआ, उस पर
unmountकुछ नहीं करता। 0.4 उसे खाली कर देता था। अब चारों adapters ऐसा ही व्यवहार करते हैं। svelte/serverload न होने परrenderSvelteToStringअबApp.renderपर fallback नहीं करता।renderदेने पर यह Svelte 5 इस्तेमाल करता है; वरना जबsveltemountexport करता है तो यह Svelte 5 वाला रास्ता लेता है, औरApp.renderसिर्फ़ Svelte 3/4 के लिए call करता है। Svelte 5 का import या render एरर आप तक पहुँचता है।
Svelte 5 components 0.5 में काम करते हैं; 0.4 में createSvelteMicroApp उन पर fail होता था। Svelte 5 का update() component को remount करता है, जब तक आप config में svelte/reactivity से SvelteMap न दें; उसके साथ props अपनी जगह update होते हैं:
import { SvelteMap } from 'svelte/reactivity';
import { createSvelteMicroApp } from '@tuvix.js/svelte';
import Profile from './Profile.svelte';
export default createSvelteMicroApp({ name: 'profile', App: Profile, SvelteMap });Angular
नया config shape
// 0.4
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
export default createAngularMicroApp({
name: 'admin',
module: AppModule,
platform: platformBrowserDynamic,
selector: 'app-root',
});// 0.5
import { createAngularMicroApp } from '@tuvix.js/angular';
import { AppComponent } from './app.component';
import { AppModule } from './app.module';
export default createAngularMicroApp({
name: 'admin',
component: AppComponent, // required
module: AppModule, // only when AppComponent is declared in an NgModule
});platform और compilerOptions हटा दिए गए। अपनी entry से import '@angular/compiler' हटाएँ; JIT components के लिए adapter compiler खुद load करता है।
Default रूप से zoneless
जब तक आप zone: true न दें, ऐप्स zone.js के बिना चलते हैं। Angular 15-17 पर DOM events अब change detection trigger नहीं करते; handlers में ChangeDetectorRef.detectChanges() call करें, या अपनी entry में zone.js import करें और zone: true दें। Angular 18+ zone.js के बिना change detection schedule करता है। Angular देखें।
Props inputs हैं
update() अब window.__TUVIX_ANGULAR_PROPS__ नहीं लिखता। इसकी जगह @Input() properties declare करें; adapter उन्हें setInput से set करता है।
// 0.4
ngOnInit() {
this.userId = (window as any).__TUVIX_ANGULAR_PROPS__?.admin?.userId;
}// 0.5
import { Component, Input } from '@angular/core';
@Component({ selector: 'app-admin', standalone: true, template: '{{ userId }}' })
export class AdminComponent {
@Input() userId = '';
}Angular के दूसरे बदलाव
createSsrAngularMicroAppअबprovideClientHydration()नहीं जोड़ता। यह server host element में दोबारा रेंडर करता है।- जिस container को module ने कभी mount नहीं किया, उस पर
unmountकुछ नहीं करता।
Sandbox
Sandbox({ css: false }).activate(container)nullलौटाता है और कोई shadow root attach नहीं करता।ISandbox.activateShadowRoot | nullलौटाता है;addStylecall करने से पहलेnullकी जाँच करें।JsSandboxstrict mode blocked write परTypeErrorthrow करता है। 0.4 एक warning log करता था और value फिर भी store कर लेता था। आपका code जिन globals को लिखता है, उन्हेंallowedGlobalsमें जोड़ें।
import { createSandbox } from '@tuvix.js/sandbox';
declare const container: HTMLElement;
const sandbox = createSandbox({ css: true, js: false });
const shadow = sandbox.activate(container);
if (shadow) sandbox.css.addStyle(shadow, ':host { display: block; }');Server
- जब कम से कम एक ऐप path से match हुआ हो और हर fragment fetch fail हुआ हो, तब
createServerRenderer().render()statusCode: 502लौटाता है। जब कोई ऐप match नहीं हुआ या एक fragment सफल रहा, तब यह अब भी 200 लौटाता है।createMiddlewareवही status भेजता है। Streaming renderer हमेशा 200 भेजता है। - Rendering throw करने पर
createMiddlewareऔरcreateStreamingMiddlewarenext()की जगहnext(error)call करते हैं, ताकि Express request को आपके error handler तक भेजे।
जानने लायक non-breaking fixes
require('@tuvix.js/core')और बाकी CommonJS entry points काम करते हैं। 0.4 में पाँच पैकेजrequireको उन files की ओर point करते थे जिन्हें build कभी लिखता ही नहीं था।getGlobalBus()अपना instancewindowपर रखता है, इसलिए अलग-अलग bundle हुए माइक्रो ऐप्स एक ही bus साझा करते हैं।- Adapters और
defineMicroApptuvix:mounted,tuvix:unmountedऔरtuvix:errordispatch करते हैं। Lifecycle Events देखें। - Throw करने वाला
bootstrap()अगले mount पर फिर से चलता है। 0.4 उसके बाद उसे छोड़ देता था। start()के बादregister()ऐप को तुरंत mount करता है, अगर मौजूदा path उसे activate करता हो, औरmountWhenVisibleऐप को observe करना शुरू करता है।- हर lifecycle failure bus पर
{ name, error, phase }के साथtuvix:app:erroremit करता है। 0.4 इसे सिर्फ़ load और mount failures के लिए emit करता था। maxListenersहर event के लिए एक बारconsole.warnसे चेतावनी देता है,debugहो या न हो।orchestrator.connectRouter()setTimeout+reconcile()bridges की जगह लेता है। बाहरी राउटर देखें।