Evaluating the 1.0 release candidate
This guide is for 1.0.0-rc.1, not a stable 1.0 migration guarantee.
Some corrections listed below describe source changes after RC publication. The source still carries RC.1 metadata; it is not proof those changes exist in an already published archive. Check installed declarations, the exact release commit and source/release evidence.
- Save a known-good application commit and lockfile. Use a staging environment.
- Upgrade to Node >=22.17.0 and npm >=9; use ESM and compatible TypeScript settings.
- Install the exact RC:
npm install --save-exact @tagna/udiot@1.0.0-rc.1. - Replace unpublished deep imports with public entry points.
- Compile with strict checking, then run application/service/browser tests.
- Validate a staged rollout and retain a rollback version/lockfile.
Changes to review explicitly
- Public npm installation needs no GitHub Packages scope mapping or token.
- Root
Actionis a decorator; useStoreActionfor action-object types. - Root
Middlewareis a legacy state type, not the internal pipeline decorator. - Root
Componentis an interface; the DI decorator isComponentDecorator. - Legacy module/schema/reflection declarations may be type-only exports.
/ssr,/isr,/middleware,/typesand similar source-oriented subpaths are not exported. Follow current web/server and validation guides.- JWT validation uses an explicit algorithm allowlist (HMAC, or RSA/ECDSA with a
public key) and requires
expby default. API-key/session strategies require validator callbacks; mere credential presence is insufficient. - Shared rate-limit stores must provide atomic
increment(key, windowMs). - Preserve request/tenant isolation and review cache behavior for private data.
- Review generated templates; unsupported internal imports and business-logic placeholders are not fixed merely by installing the package.
Upgrading from 1.0.0-rc.1
The next candidate fixes defects found in review and tightens several defaults. Each row says what you would notice and what to change. Where a stricter default blocks a request, the server logs a one-time hint naming the option involved.
| Area | Change | What you would notice | What to do |
|---|---|---|---|
| DI | Singletons registered on a parent container are shared by request and tenant scopes; a singleton that depends on a SCOPED provider is built once per scope |
Services that were silently rebuilt per request are now built once | Mark services that hold per-request state ServiceScope.SCOPED |
| Controllers | A handler that returns nothing and takes no @Res() answers statusCode, or 204 |
Requests that used to hang now complete | None; use @Res() to write the response yourself |
| Web errors | The final error handler keeps 4xx statuses and serialises HttpError |
Malformed JSON returns 400, not 500 |
Update tests that expected 500 |
| Diagnostics | Route manifest, diagnostics, config dump and probe check messages are shown only when NODE_ENV is development or test |
404 on /__udiot/* and /diagnostics/config where NODE_ENV is unset or production |
Set NODE_ENV locally, or exposeDiagnostics: true or a per-request function |
| Proxies | New trustProxy setting |
Behind a TLS proxy, sessions did not persist and IP limits lumped clients together | Set trustProxy: 1 (or your hop count) |
| Rate limits | user and userOrIp limits run after @RequireAuth |
Per-user limits now count per user | Use afterAuth to override |
| CSRF | Unsafe requests with a foreign Origin/Referer, or Origin: null, get 403 |
Cross-origin request blocked |
Add legitimate origins to trustedOrigins; call csrf.rotate(req) after login |
| Sessions | 7 days of inactivity (rolling), table udiot_sessions, X-Powered-By removed |
Existing cms_sessions rows are ignored, so users sign in again |
Set tableName: 'cms_sessions' to keep them |
| JWT | requireExp defaults to true; RS*/ES* algorithms with publicKey |
401 for tokens without exp |
Issue tokens with exp, or set requireExp: false |
| Uploads | Stored extension follows the validated MIME type; content checks on by default | Spoofed files rejected; unknown risky extensions saved as .bin |
Set verifyMagicBytes: false only for trusted sources |
| Templates | nl2br escapes its input and returns safe HTML |
Line breaks render without | safe |
Remove | safe after nl2br; it is no longer needed |
| Component templates | Expressions are parsed, not evaluated; only context values and Math, JSON, Number, String, Boolean, Date and a few helpers are reachable; no new or assignment |
A binding that used another global renders empty and logs an error | Pass the value or helper in the render context |
| SSE | Events carry ids for Last-Event-ID replay; slow clients are disconnected |
New id: lines in the stream |
replayBufferSize: 0 turns ids off |
| Agents | runWithTools keeps tool calls on assistant turns and uses retries and telemetry |
Multi-turn tool use works against OpenAI and Anthropic | Set toolCalls on assistant messages when building tool conversations by hand |
| Agents | No built-in vendor prices; costUSD is undefined unless you pass priceTable |
costUSD missing from responses and run events |
Pass your own priceTable if you need dollar figures |
| Agents | A generic apiKey goes to one vendor only |
Anthropic calls fail if they relied on a shared OpenAI-style key | Use anthropicApiKey / openaiApiKey |
| Agents | Tool schemas close objects (additionalProperties: false) and reject types with no JSON Schema form |
describe() throws for z.function() and similar |
Use JSON-representable input schemas |
| Agents | AgentLoop checks oscillation per iteration |
Loops with pass-through steps no longer stop early | None |
| Pipeline | Recovery runs once at the failing middleware; retry only before next(); a second next() call throws |
Fewer duplicate retries | Remove double next() calls |
| Tenancy | A signed-in user without a tenant claim needs authorizeTenant to pick a tenant by header or subdomain |
403 Tenant selection requires authorization |
Pass authorizeTenant(user, tenantId) or put tenant_id in the token |
| Sandbox | Children get an allow-listed environment; spawn failures reject; output capped at 1 MiB per stream; process trees are killed | Commands miss host variables; truncated: true on long output |
Pass env, envAllowList or inheritEnv: true; raise maxOutputBytes |
| Remote sandbox | A token over plain http:// to a remote host is refused |
Constructor throws | Use https://, or allowInsecureHttp: true |
| HMR | The dev server binds localhost and checks Origin |
Other devices or origins cannot connect | Set host and allowedOrigins |
| Packaging | Integrations are optional peer dependencies: typescript, openai, @anthropic-ai/sdk, firebase, bullmq, ioredis, @mikro-orm/*, pg, connect-pg-simple, nunjucks (server templates) and inquirer (interactive CLI prompts); chalk, ora and boxen are no longer installed |
Installing @tagna/udiot no longer pulls them in; using a feature without its peer fails with code UDIOT_OPTIONAL_PEER_MISSING naming the package |
Install the peers you use (see the README table) |
| Database | Firebase helpers moved from @tagna/udiot/database and @tagna/udiot/database/browser to @tagna/udiot/database/firebase; @tagna/udiot/database no longer re-exports the MikroORM MySQL/PostgreSQL drivers or Migrator |
Import errors for those names | Import Firebase helpers from the new entry point and driver APIs from @mikro-orm/postgresql, @mikro-orm/mysql or @mikro-orm/migrations |
| DI | Factories default to SINGLETON like classes; request and tenant containers are disposed when the response ends (onDestroy(), dispose(), Symbol.dispose) |
A factory that ran per resolve() now runs once; per-request services are cleaned up |
Register per-call factories with scope: ServiceScope.TRANSIENT |
| Web server | Request ids (X-Request-Id, req.requestId), a bodyLimit (default '100kb') and graceful stop({ timeoutMs }) |
New response header; bodies over the limit get 413 |
Raise bodyLimit for larger payloads |
| State store | The reducer runs at the end of the middleware pipeline; dispatch() rejects when a middleware or the reducer throws, or when a non-action reaches the reducer |
Middleware errors no longer let the action through; code after await next() sees the new state |
Catch dispatch errors where you relied on them being swallowed; add thunkMiddleware() to dispatch functions |
| State middleware | A legacy (store, action) middleware that returns nothing consumes the action, as documented; loggerMiddleware() and performanceLogger() are pipeline middleware (collapsed/colors removed; performanceLogger().getStats() replaces random log output) |
Actions vanish after a middleware that forgot return action |
Return the action; drop collapsed/colors |
| Thunks | thunkMiddleware() and legacyAdapters.thunk() await the thunk; createAsyncThunk failures are reported once, as /rejected |
await store.dispatch(thunk) waits for the thunk |
None |
| Selectors | createSelector(fn) recomputes when the state changes (it returned its first result forever); @Selector() methods too |
Derived values update | None |
| Store | asObservable() emits each change once (it emitted twice); undo/redo restore frozen state and report the previous state; persisted partial state is merged into the current state |
Fewer duplicate emissions | None |
| Persistence | flush() writes the pending state (it discarded it), overlapping saves queue and keep the newest, remove() drops pending saves |
Saves that were silently lost are written | None |
| Store decorators | @Persist, @EnableDevTools and @AIPowered set up in the constructor and observe actions after the reducer; @Action debounce and throttle are per instance and every debounced caller settles; batchMutations() is async and applies one @@BATCH change |
DevTools and AI see the post-reducer state | await batchMutations(...) |
| Component decorators | Setting a @State property invalidates @Computed values; invalidateComputed() works; subclass metadata no longer leaks into the parent; @Component registers props by name; @AIComponent registers with AIComponentRegistry and no longer logs unimplemented features as enabled |
Computed values update | None |
| Component templates | The parser handles sibling elements, text mixed with elements, void and self-closing tags; several top-level nodes are wrapped in a div |
Templates with siblings render correctly; inner text keeps its spacing | Check templates that relied on the old single-element parse |
| Route validation | Array item errors are reported as body.tags[1] (was body.tags[1].item); UUID versions 1 to 8 are accepted |
Field paths in 422 responses change |
Update clients that match on field paths |
| HTTP caching | shouldReturn304 lets If-None-Match decide alone (weak comparison) and compares dates to the second |
Fewer wrong 304s and more correct ones |
None |
| Pipeline retry | The retry recovery strategy only retries middleware marked idempotent: true |
A failing middleware that was retried now fails at once | Set idempotent: true on middleware that is safe to run twice, such as reads |
| HMR bundler plugins | The webpack and esbuild HMR plugins (never exported) are removed with their optional peers; the Vite plugin's trackDependencies option is removed and it no longer makes modules self-accept |
Vite propagates updates to importers instead of leaving stale bindings | Drop trackDependencies from udiotHMRPlugin() options |
| Dev tools | AIPerformanceOptimizer.autoOptimize() and the codeChanges of its recommendations are removed; @PreserveState ids default to the class name and take an optional hot handle |
Saved component state is restored after a reload | Treat recommendations as advice; pass componentId if two classes share a name |
| CLI generators | Generated code imports from @tagna/udiot and compiles with "module": "NodeNext"; generate and create ask before writing (--yes skips the question, a non-interactive shell needs it); --with-stories is removed |
Scripts that ran udiot generate stop with a request for --yes |
Add --yes to scripted generation |
CLI wizards and create |
Wizards write the files they preview (they wrote nothing before); create calls the configured model through the openai peer (it returned canned output) and supports components, services, controllers, events and handlers |
Real files and real provider calls | Install openai and set OPENAI_API_KEY for create; use wizard ... --force to overwrite |
| Component props | @Prop() no longer infers Object from design metadata (TypeScript emits it for any, unknown, unions and interfaces); PropsValidator accepts primitives for String/Number/Boolean props and accepts a class; ComponentFactory.create() throws on invalid props instead of logging |
@Prop() value!: string | number accepts strings; invalid factory props fail fast |
Pass type explicitly where you relied on the inferred Object check |
| Components | BaseComponent.unmount() removes what mountTo() rendered; ComponentFactory.generateFromIntent() and generateFromPattern() (which always threw) are removed; fromTemplate() reads selectors with several hyphens and single-quoted attributes; once-listeners that throw run once |
Unmounted components leave the page | Use udiot create for AI generation |
| AI helpers | AIComponentGenerator, GenerationStats, ComponentTestGenerator and testGenerator are removed (no model calls, placeholder output, tests that did not parse); AutoOptimizer now runs the analysis on its timer; AIPredictor and StateAnalyzer report the anomalies and cycles they used to miss |
Import errors for the removed names; AutoOptimizer subscribers receive suggestions |
Use udiot create and udiot generate --with-tests |
| UI components | Button children, Card titles and Input labels, values, placeholders and errors are HTML-escaped; mounted buttons call onClick and inputs call onChange; Modal closes with its button or Escape; DragDropManager.destroy() removes its listeners |
Markup in a button label shows as text | Put markup in Card content, which stays HTML |
| Enterprise helpers | HttpClient refuses endpoints on another origin than its base URL; Router evaluates route guards and matches on path segments; Link renders a plain anchor followed by the router (no inline onclick); required form fields accept 0 and false; GlobalErrorHandler passes rejection reasons through unchanged |
/user no longer matches /users; cross-origin endpoint strings throw |
Use a second client for another origin |
| Services | ConfigService refuses __proto__, prototype and constructor key segments; ValidationService min, max and oneOf check 0; RouterService ignores query strings and decodes params; StateService.delete() notifies subscribers and keeps them; AnalyticsService no longer logs event properties; RedisAdapter loads ioredis as an optional peer |
min(1) rejects 0 |
None |
| File-based routing | AutoRouter.handler() serves the discovered routes (they were registered but never served, and API routes lost their HTTP method); route files load by file URL; hot reload imports fresh modules instead of using require.cache; files starting with _ are never routes; only the first directory decides an API route (pages/capital.ts is a page) |
Routes answer requests | Mount router.handler(); rename _-prefixed files that should be routes |
| Background jobs | 'immediate' schedules run once (they re-enqueued themselves forever); an interval with a future startAt first runs at startAt; retry.jitter is applied; dead-letter records keep the first failure time across retries |
Fewer duplicate runs | None |
| Middleware hot reload | New files are loaded and deleted files removed (every watch event was treated as a change); modules reload through ESM imports instead of require.cache; a middleware class is instantiated once; plain middleware functions are accepted |
Hot reload works in ESM projects | None |
| SSR renderer | documentShell stylesheets, links, meta and scripts are added to every page (they were dropped whenever a page had its own head config, as every createWebServer page with SEO does); inlineStyle is rendered; clearCache() imports a fresh module in ESM builds (it threw); a class component without render() fails with a clear error; the unused RenderDocumentOverrides type is removed; OpenAPI output declares controller-path parameters, the default bearer scheme and optional auth |
Shell assets appear on pages that lacked them | Remove shell assets you added to page heads by hand |
| Validators | Validators.string.url() accepts only http: and https: URLs unless given a protocol list (javascript: URLs passed); pattern() resets global regexes between values; number.multipleOf() tolerates floating point error; object.hasProperty() and hasProperties() report non-objects instead of throwing |
Other URL schemes are rejected | Pass the schemes you accept, e.g. url(['https:', 'mailto:']) |
| Cache analyzer | AICacheAnalyzer frequency is accesses per minute since a key was first seen (it was the lifetime count); trackUpdate() measures time between updates in seconds (it measured time since the last read, in milliseconds, against TTLs in seconds) and works for keys not read yet; the new trackSet(key, ttlSeconds) feeds the report's oversizedTTLs and undersizedTTLs, which were always empty; AccessPattern gains firstAccess and lastUpdate |
Different strategy and TTL recommendations | Call trackSet() where entries are stored |
| SSR context | SSRContext keeps = inside cookie and query values (base64 session cookies were cut short), decodes + in queries, keeps an empty first value of a repeated key, splits the URL at the first ? only and no longer throws on malformed percent-escapes; setCookie(..., { maxAge: 0 }) emits Max-Age=0; cookie names, paths and domains with separators throw; directly set Set-Cookie headers are kept |
Correct values where they were truncated | None |
| Test client | createUdiotTestClient keeps cookies the app sets and sends them back (as its docs described; cookies: false opts out), no longer follows redirects, and treats only http:// and https:// paths as absolute URLs |
Tests see 3xx responses and carry sessions between requests |
Follow Location yourself where a test relied on redirects |
| CLI misc | udiot --version prints the package version; sdk publish --version is now --package-version; enhance cannot apply advice-only suggestions and its "safe" auto-fix is whitespace only; telemetry setup keeps existing alert rules |
--version after sdk publish printed the CLI version |
Use --package-version |
This checklist does not replace application-specific compatibility testing or the changelog. Read support status, testing and deployment.
From the canonical UDIOT public documentation.Try it in the playground ↗