@azure-net/kit 6.0.0-next.1 → 6.0.0-next.10

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.
Files changed (85) hide show
  1. package/README.md +335 -2
  2. package/dist/delivery/index.d.ts +3 -1
  3. package/dist/delivery/index.js +3 -1
  4. package/dist/delivery/injectable-dependencies/AsyncHelpers.d.ts +2 -4
  5. package/dist/delivery/injectable-dependencies/AsyncHelpers.js +96 -102
  6. package/dist/delivery/presenter/Bound.d.ts +1 -0
  7. package/dist/delivery/presenter/Bound.js +9 -0
  8. package/dist/delivery/presenter/RegisterProvider.d.ts +16 -0
  9. package/dist/delivery/presenter/RegisterProvider.js +99 -0
  10. package/dist/delivery/schema/rules/Rules.js +2 -1
  11. package/dist/delivery.d.ts +0 -1
  12. package/dist/delivery.js +0 -1
  13. package/dist/i18n/Internal.d.ts +15 -0
  14. package/dist/i18n/Internal.js +11 -0
  15. package/dist/i18n/TranslationManager.d.ts +19 -0
  16. package/dist/i18n/TranslationManager.js +26 -0
  17. package/dist/i18n/Translations.svelte.d.ts +20 -0
  18. package/dist/i18n/Translations.svelte.js +254 -0
  19. package/dist/i18n/Types.d.ts +30 -0
  20. package/dist/i18n/Types.js +1 -0
  21. package/dist/i18n/index.d.ts +3 -0
  22. package/dist/i18n/index.js +2 -0
  23. package/dist/infra/datasource/BaseDatasource.d.ts +19 -6
  24. package/dist/infra/datasource/BaseDatasource.js +4 -4
  25. package/dist/infra/http-service/HttpServiceInstance.d.ts +26 -16
  26. package/dist/infra/http-service/HttpServiceInstance.js +21 -13
  27. package/dist/infra/index.d.ts +2 -2
  28. package/dist/infra/index.js +2 -2
  29. package/dist/infra/query/QueryBuilder.d.ts +6 -1
  30. package/dist/infra/query/QueryBuilder.js +14 -12
  31. package/dist/plugin/AzureNetPlugin.d.ts +1 -4
  32. package/dist/plugin/AzureNetPlugin.js +21 -64
  33. package/dist/plugin/index.d.ts +1 -1
  34. package/dist/server/Context/Runtime.d.ts +3 -0
  35. package/dist/server/Context/Runtime.js +109 -0
  36. package/dist/shared/app/App.d.ts +47 -77
  37. package/dist/shared/app/App.js +80 -106
  38. package/dist/shared/app/index.d.ts +5 -3
  39. package/dist/shared/app/middleware/ClientMiddleware.d.ts +14 -8
  40. package/dist/shared/app/middleware/ClientMiddleware.js +32 -11
  41. package/dist/shared/app/middleware/ServerMiddleware.d.ts +10 -5
  42. package/dist/shared/app/middleware/ServerMiddleware.js +3 -3
  43. package/dist/shared/app/middleware/Types.d.ts +4 -0
  44. package/dist/shared/app/middleware/Types.js +1 -0
  45. package/dist/shared/app-error/ErrorRuntime.d.ts +1 -1
  46. package/dist/shared/app-error/ErrorRuntime.js +4 -3
  47. package/dist/shared/boundary-provider/CompositionRoot.d.ts +21 -0
  48. package/dist/shared/boundary-provider/CompositionRoot.js +11 -0
  49. package/dist/shared/boundary-provider/Provider.d.ts +3 -2
  50. package/dist/shared/boundary-provider/Provider.js +56 -40
  51. package/dist/shared/boundary-provider/index.d.ts +1 -0
  52. package/dist/shared/boundary-provider/index.js +1 -0
  53. package/dist/shared/cookie/UniversalCookie.js +1 -1
  54. package/dist/shared/index.d.ts +2 -3
  55. package/dist/shared/index.js +2 -3
  56. package/dist/shared/logger/index.js +1 -1
  57. package/dist/shared/request-context/Internal.d.ts +16 -0
  58. package/dist/shared/request-context/Internal.js +24 -0
  59. package/dist/shared/request-context/index.d.ts +1 -0
  60. package/dist/shared/request-context/index.js +2 -0
  61. package/dist/svelte/async-signal/AsyncSignal.svelte.d.ts +20 -4
  62. package/dist/svelte/async-signal/AsyncSignal.svelte.js +53 -13
  63. package/dist/svelte/async-signal/README.md +89 -0
  64. package/dist/svelte/index.d.ts +1 -0
  65. package/dist/svelte/index.js +1 -0
  66. package/dist/svelte/query/Query.svelte.d.ts +1 -1
  67. package/dist/svelte/query/Query.svelte.js +14 -6
  68. package/dist/svelte/query/SearchParamsSync.js +11 -3
  69. package/dist/{shared → svelte}/redirect/index.d.ts +2 -8
  70. package/package.json +35 -26
  71. package/dist/external/edges/Dev.d.ts +0 -1
  72. package/dist/external/edges/Dev.js +0 -1
  73. package/dist/external/edges/Edges.d.ts +0 -1
  74. package/dist/external/edges/Edges.js +0 -1
  75. package/dist/external/edges/ServerContext.d.ts +0 -1
  76. package/dist/external/edges/ServerContext.js +0 -1
  77. package/dist/external/edges/_edges.d.ts +0 -1
  78. package/dist/external/edges/_edges.js +0 -1
  79. package/dist/external/i18n/index.d.ts +0 -1
  80. package/dist/external/i18n/index.js +0 -1
  81. package/dist/shared/event-bus/EventBus.d.ts +0 -12
  82. package/dist/shared/event-bus/EventBus.js +0 -3
  83. package/dist/shared/event-bus/index.d.ts +0 -1
  84. package/dist/shared/event-bus/index.js +0 -1
  85. /package/dist/{shared → svelte}/redirect/index.js +0 -0
package/README.md CHANGED
@@ -2,6 +2,130 @@
2
2
 
3
3
  #### [docs](https://svelte.kit.azure-net.ru)
4
4
 
5
+ ## Version 6 integration
6
+
7
+ Configure `AzureNetPlugin()` from `@azure-net/kit/plugin` alongside SvelteKit in Vite,
8
+ then export `{ register }` from `createApp(...)` in `src/program.ts`.
9
+ Call `register()` synchronously during root layout initialization. `useClient` may
10
+ be asynchronous: the navigation subscription is installed synchronously, and
11
+ middleware supplied after an `await` applies only from that point onward. Handle
12
+ the returned Promise if client initialization can fail. Client middleware itself
13
+ must remain synchronous.
14
+
15
+ The plugin provides virtual hooks only. It does not create or rewrite page/layout
16
+ modules. Manual `hooks.server`, `hooks.client` and `hooks` files are forbidden.
17
+ Initial request state is hydrated once through Svelte hydratable; later navigation is client-owned.
18
+
19
+ `program.ts` is universal: a static server-only import does not become safe merely
20
+ because it is used inside `useServer`. Use an explicit build-time browser guard
21
+ with dynamic server imports when needed, and retain SvelteKit's build checks.
22
+
23
+ ### Application structure
24
+
25
+ ```text
26
+ src/app/
27
+ Contexts/{Orders,Identity,SharedKernel}/
28
+ Domain/{Entities,Ports,Enums}
29
+ Infrastructure/Repositories
30
+ Application/UseCases
31
+ Presentation/{Admin,Public,Shared}/
32
+ Ui/
33
+ Scopes/
34
+ Shell/
35
+ Platform/
36
+ Foundation/{Datasource,Response,Schema,DI}
37
+ Bootstrap/{Routes,Localization}
38
+ ```
39
+
40
+ Without workspaces, `Ui`, `Scopes` and `Shell` sit directly under `Presentation`.
41
+ Central DI lives in `Platform/Foundation/DI/ApplicationProvider.ts`. Contexts may
42
+ import `SharedKernel`, but not each other. Presentation scopes may compose any
43
+ contexts. Workspaces may import `Shared`, but not each other.
44
+
45
+ Boundary providers cache their proxy, registration and services per SSR request,
46
+ not globally. Browser instances are cached until cleanup. Factories and `boot`
47
+ are synchronous. `await cleanupProvider(name)` explicitly disposes instantiated
48
+ services and resets the cache; request completion does not invoke it automatically.
49
+
50
+ Install the CLI separately with `npm i -D @azure-net/cli`. Its commit-tools command
51
+ configures Prettier without installing commit packages. `azure-net commit` commits
52
+ staged changes; `azure-net push` checks, stages, commits and pushes. `--no-check`
53
+ skips checks. Review `.gitignore` before staging.
54
+
55
+ ## Class Presenters And Async Helpers
56
+
57
+ Import `createAsyncResource` and `createAsyncAction` directly from
58
+ `@azure-net/kit/delivery`. `createAsyncHelpers()` has been removed. The error hook
59
+ configured by `createApp().useAzureNetKitError` is still resolved per execution;
60
+ there is no per-presenter parser or shared server error state. A custom error
61
+ payload can be typed as the third generic: `createAsyncAction<Response, Request, CustomError>()`.
62
+
63
+ ```ts
64
+ import { RegisterProvider, createAsyncResource } from '@azure-net/kit/delivery';
65
+ import { ApplicationProvider } from '#platform/Foundation/DI';
66
+ import type { OrdersUseCases } from '#contexts/Orders/Application/UseCases/OrdersUseCases';
67
+
68
+ export class OrdersPresenter {
69
+ constructor(private readonly useCases: OrdersUseCases) {}
70
+
71
+ collection() {
72
+ return createAsyncResource(() => this.useCases.collection());
73
+ }
74
+ }
75
+
76
+ export const useOrdersPresenter = RegisterProvider(
77
+ () => {
78
+ const { Orders } = ApplicationProvider();
79
+ return new OrdersPresenter(Orders);
80
+ },
81
+ { lifecycle: 'scoped', bindMethods: true }
82
+ );
83
+ ```
84
+
85
+ Call `useOrdersPresenter()` during component initialization, not at module scope.
86
+ The registration itself is safe at module scope and does not resolve dependencies.
87
+ `RegisterProvider` returns the actual class instance, without a proxy. The optional
88
+ second argument is an options object: `{ lifecycle?: ..., bindMethods?: ... }`.
89
+ Defaults are `lifecycle: 'scoped'` and `bindMethods: false`. The old string argument
90
+ is no longer supported. With binding disabled, methods and property descriptors are
91
+ left untouched.
92
+
93
+ Use `bindMethods: true` to bind function-valued data properties, including inherited
94
+ methods, or `bindMethods: ['collection']` to select typed method names. Binding runs
95
+ only after the factory returns, once for each newly created instance. Accessors are
96
+ never evaluated or replaced, including Svelte rune accessors. Constructors and
97
+ Object.prototype methods are excluded. A selected accessor (even one returning a
98
+ function), missing method, or protected property throws instead of silently failing.
99
+ TypeScript cannot distinguish a function-returning getter from a method, so the
100
+ accessor check also happens at runtime.
101
+
102
+ Method references are stable after binding. Frozen/non-extensible instances cannot
103
+ receive inherited bound methods. Arbitrary proxies and exotic built-in objects are
104
+ not supported binding targets. Function-valued fields are also bound with `true`;
105
+ use an explicit list if the instance holds component constructors or other callable
106
+ dependencies. No global cache retains instances or bound functions. Binding does not
107
+ apply retroactively to callbacks captured during construction or methods replaced later.
108
+ Classes using Svelte runes such as `$state` must be placed in a `.svelte.ts` module
109
+ (or declared inside a Svelte component), not a regular `.ts` file.
110
+
111
+ | Lifecycle | Server | Browser |
112
+ | ------------------ | ----------------------------------------- | ----------------------------- |
113
+ | `scoped` (default) | One instance per registration per request | New instance per call |
114
+ | `transient` | New instance per call | New instance per call |
115
+ | `singleton` | One instance per registration per request | One instance per registration |
116
+
117
+ Server caching requires `AzureNetPlugin` request context and fails
118
+ outside it. There is no process-wide server singleton. Request caches have weak
119
+ keys and do not retain completed requests. Registrations do not require names,
120
+ Svelte context, hydration, or state serialization. Factory callbacks must be
121
+ synchronous; recursive construction is rejected.
122
+
123
+ This API does not own effects, subscriptions, timers, or `dispose()`. Keep
124
+ component-owned effects in component-lived instances; a cached browser singleton
125
+ must not rely on effects owned by the first component that requested it. SSR
126
+ isolation does not serialize state to the browser and cannot protect mutable
127
+ module-level state captured by user factories.
128
+
5
129
  **@azure-net/kit** is a universal toolkit and library collection, developed as part of the **Azure-net** ecosystem. It is designed to speed up modern application development, reduce boilerplate code, and maintain a unified project architecture.
6
130
 
7
131
  ## ✨ What is it?
@@ -10,8 +134,8 @@
10
134
 
11
135
  ## 🚀 What's included?
12
136
 
13
- - **Built-in store** — flexible and SSR-friendly state management powered by [@azure-net/edges](https://github.com/Pixel1917/edge-s).
14
- - **Localization and translation** — ready-to-use i18n solution ([@azure-net/edges-translations](https://github.com/Pixel1917/edges-svelte-translations)) for multilingual applications.
137
+ - **Reactive state classes** — request-isolated server instances and browser singletons via `RegisterProvider`.
138
+ - **Localization and translation**: built-in rune-based i18n with request-scoped SSR instances. No edges or external translations dependency.
15
139
  - **Extensive utilities** — for working with text, objects, dates, and common tasks.
16
140
  - **Data validation** — handy rules and validators.
17
141
  - **Reusable base abstractions** — `Repositories`, `Services`, `Providers`, and `Datasources` for quick setup and reuse.
@@ -40,3 +164,212 @@ In this guide, you will find:
40
164
  ## 🚀 Get started
41
165
 
42
166
  Ready to dive in? Check out the [docs](https://svelte.kit.azure-net.ru) and start building your app faster and more reliably with **Azure-net**.
167
+
168
+ ## Initial state and `useBoot`
169
+
170
+ `AzureNetPlugin()` owns a kit-local AsyncLocalStorage scope. It does not depend on
171
+ edges, serialize stores, inject response scripts or create virtual route modules.
172
+ There are no edges store/presenter exports or devtools exports in this version.
173
+ `RequestContext.current()` exposes only `event` and `state`, is server-only, and
174
+ throws outside an active request. Internal DI/error/lifecycle caches are private.
175
+
176
+ Declare transferable state in `src/app.d.ts` (prefer optional fields for CSR):
177
+
178
+ ```ts
179
+ import type { IUser } from '#contexts/Identity/Domain';
180
+ declare global {
181
+ namespace App {
182
+ interface RequestContextState {
183
+ currentUser?: IUser | null;
184
+ }
185
+ }
186
+ }
187
+ export {};
188
+ ```
189
+
190
+ ```ts
191
+ import { createApp } from '@azure-net/kit';
192
+ import { RequestContext } from '@azure-net/kit/server-context';
193
+
194
+ export const { register } = createApp((app) =>
195
+ app
196
+ .useServer(async ({ event, resolve, useMiddlewares }) => {
197
+ await useMiddlewares([ServerAuth]);
198
+ return resolve(event);
199
+ })
200
+ .useBoot(({ state }) => {
201
+ useCurrentUser().setUser(state.currentUser ?? null);
202
+ })
203
+ );
204
+ ```
205
+
206
+ In the root layout call `register()` before resolving presenters or state classes.
207
+ `useBoot` is synchronous and runs once per server request render and once per
208
+ browser app initialization, before client lifecycle callbacks. Await all server
209
+ state preparation before `resolve`. Do not mutate the transfer snapshot during
210
+ rendering. Client navigations and repeated register calls do not rehydrate state.
211
+ `useClientInit` runs earlier and must not depend on this snapshot. CSR without an
212
+ SSR snapshot receives `{}`; initialize defaults and fetch missing data explicitly.
213
+ Only publish safe DTOs, never tokens or the whole request/locals. Personalized HTML
214
+ must not be publicly cached. Classes, functions and services are not serialized.
215
+
216
+ Svelte currently requires async mode for `hydratable`:
217
+
218
+ ```ts
219
+ sveltekit({ compilerOptions: { experimental: { async: true } } });
220
+ ```
221
+
222
+ Keep the application's CSP correctly configured; Svelte manages the transfer
223
+ script and serialization. No custom HTML parser is used by the kit.
224
+
225
+ ## DI naming
226
+
227
+ `createBoundaryProvider` is replaced by `CreateServiceContainer` (no compatibility alias):
228
+
229
+ ```ts
230
+ const ApplicationProvider = CreateServiceContainer('ApplicationProvider', {
231
+ register: () => ({ Users: () => new UsersUseCases(repository) })
232
+ });
233
+ const { Users } = ApplicationProvider();
234
+ ```
235
+
236
+ The accessor remains lazy, synchronous and request-scoped on SSR. `RegisterProvider`
237
+ registers an individual class factory; `singleton` means one browser instance but
238
+ one instance per request on the server, never a server process singleton.
239
+
240
+ Built-in i18n: see [I18N.md](./I18N.md). Register a `new TranslationManager(options)` through `app.useTranslations(manager)`. Server preparation, locale hydration and client dictionary loading run automatically before the corresponding user hooks. No separate translation package or manual lifecycle calls are required.
241
+
242
+ ## AsyncSignal SSR Hydration
243
+
244
+ Client-only signals remain synchronous. Opt into SSR with a stable, non-empty key:
245
+
246
+ ```svelte
247
+ <script lang="ts">
248
+ import { createAsyncSignal } from '@azure-net/kit';
249
+
250
+ const result = await createAsyncSignal(() => loadProfile(), {
251
+ server: true,
252
+ key: 'profile:current-user'
253
+ });
254
+ </script>
255
+
256
+ <p>{result.response?.name}</p>
257
+ <button onclick={() => result.refresh()}>Refresh</button>
258
+ ```
259
+
260
+ Call this during component initialization with Svelte experimental async enabled.
261
+ Svelte's `hydratable` transfers the initial serializable result to the browser,
262
+ which reuses it without a duplicate request; later refreshes execute normally.
263
+ There is no module-global server request state. The handler must work in both
264
+ environments. Keys must match across SSR/hydration and distinguish request inputs.
265
+
266
+ `beforeSend` runs only for actual requests; success/error callbacks and `ready`
267
+ settlement apply on both sides. Request errors produce an error-state signal.
268
+ `immediate: false` keeps the signal idle and disables initial hydration. Batch
269
+ factories stay synchronous and explicitly reject `server: true` at type-check and
270
+ runtime; use individually awaited server signals instead.
271
+
272
+ See [AsyncSignal details](./src/lib/svelte/async-signal/README.md) for lifecycle,
273
+ serialization, errors, cancellation, CSP, and batch behavior.
274
+
275
+ ## SvelteKit 3 hooks and class middleware
276
+
277
+ Error callbacks use the native discriminated `CaughtError` contract from
278
+ `@sveltejs/kit/hooks`. `status` and `message` are no longer added at the top level.
279
+ For `kind: 'app'`, `'framework'` or `'validation'`, read `error.status` and
280
+ `error.message`. For `kind: 'unknown'`, `error` remains `unknown`: narrow it before
281
+ logging and do not return its potentially private message to clients. Validation
282
+ issues are available only in the `'validation'` branch. Return `{ status, message }`
283
+ to override the public response, or return nothing to retain SvelteKit defaults.
284
+ The removed `handleValidationError` hook is not generated.
285
+
286
+ ```ts
287
+ app.useServerError(({ kind, error, useLogger }) => {
288
+ if (kind === 'unknown') {
289
+ useLogger(error, {});
290
+ return { status: 500, message: 'Internal Error' };
291
+ }
292
+ return error;
293
+ });
294
+ ```
295
+
296
+ Middleware arrays now contain **constructors**, not functions or existing instances.
297
+ Constructors must accept no required arguments. A fresh instance is constructed
298
+ only when its turn is reached, on every request/navigation. Resolve dependencies
299
+ inside that instance, not into module-level request-specific variables.
300
+
301
+ ```ts
302
+ import type { IServerMiddleware, ServerMiddlewareContext, IClientMiddleware, ClientMiddlewareContext } from '@azure-net/kit';
303
+
304
+ class ServerAuth implements IServerMiddleware {
305
+ async execute({ event, next }: ServerMiddlewareContext) {
306
+ if (!event.cookies.get('session')) return next('/login', 303);
307
+ next();
308
+ }
309
+ }
310
+
311
+ class ClientGuard implements IClientMiddleware {
312
+ execute({ next, to, navigation }: ClientMiddlewareContext): undefined {
313
+ if (navigation?.shallow) return next();
314
+ if (to.pathname === '/private') return next('/login', { replace: true });
315
+ return next();
316
+ }
317
+ }
318
+
319
+ app
320
+ .useServer(async ({ useMiddlewares, event, resolve }) => {
321
+ await useMiddlewares([ServerAuth]);
322
+ return resolve(event);
323
+ })
324
+ .useClient(({ useMiddlewares }) => {
325
+ useMiddlewares([ClientGuard]);
326
+ });
327
+ ```
328
+
329
+ `IMiddleware<Context, Result>` is the common interface. Client `execute` is strictly
330
+ synchronous (`undefined` return, normally `return next()`); server `execute` can
331
+ return `void` or `Promise<void>`. Omitting `next()` blocks the chain. Neither the
332
+ chain nor the container automatically disposes subscriptions owned by middleware:
333
+ keep middleware short-lived and do not attach long-lived listeners there.
334
+
335
+ Client `next(location, options)` and `useRedirect(...).clientRedirectSettings`
336
+ use SvelteKit's `GotoOptions` directly: `replace`, `reset`, `refreshAll`, `shallow`,
337
+ `persistState`, `state` and `invalidate`. Client navigation data includes the native
338
+ `navigation` event. In Kit 3 shallow changes also trigger navigation hooks; choose
339
+ explicitly whether a guard should skip them. `goto` is for app routes, not external
340
+ URLs; external navigation should use browser location APIs.
341
+
342
+ ## CompositionRoot
343
+
344
+ `CreateServiceContainer` remains unchanged. `CompositionRoot` is a lazy class
345
+ facade over it, with a unique symbol as its internal container identity. It can be created at module
346
+ scope; `resolve()` resolves against the current SSR request or browser
347
+ cache, never a request captured by the constructor.
348
+
349
+ ```ts
350
+ import { CompositionRoot } from '@azure-net/kit';
351
+
352
+ export const Http = new CompositionRoot({
353
+ register: () => ({ Api: () => new ApiDatasource() })
354
+ });
355
+
356
+ export const Identity = new CompositionRoot({
357
+ dependsOn: { Http },
358
+ register: ({ Http }) => ({
359
+ Account: () => new AccountUseCases(new AccountRepository(Http.Api))
360
+ })
361
+ });
362
+
363
+ // Inside a presenter factory or an active request:
364
+ const { Account } = Identity.resolve();
365
+ ```
366
+
367
+ Dependencies accept both other `CompositionRoot` instances and existing
368
+ `CreateServiceContainer` accessors. Service types are inferred through the chain.
369
+ Only registration metadata is wrapped; there is no extra proxy around services
370
+ and no new caching layer. Repeated resolution preserves existing container/service
371
+ identity. If explicit cleanup is needed, use `await cleanupProvider(Identity.name)`.
372
+ Declare roots once, rather than creating new roots per component render.
373
+ `name` is now a symbol, not a serializable string. Its description is for diagnostics
374
+ only; equal descriptions never share a cache. Existing string keys passed directly
375
+ to `CreateServiceContainer` and `cleanupProvider` remain supported.
@@ -1,3 +1,5 @@
1
- export { createAsyncHelpers, type AsyncHelperRetry, type AsyncResourceSettings, type AsyncActionResponse } from './injectable-dependencies/index.js';
1
+ export { createAsyncAction, createAsyncResource, type AsyncActionSettings, type AsyncHelperRetry, type AsyncResourceSettings, type AsyncActionResponse } from './injectable-dependencies/index.js';
2
+ export { RegisterProvider, type ProviderLifecycle, type RegisterProviderOptions, type ProviderMethodKey } from './presenter/RegisterProvider.js';
3
+ export { Bound } from './presenter/Bound.js';
2
4
  export { createSchemaFactory, schema, type Schema, type RequestErrors, type ISchemaError, type ValidationResult, type ValidationRuleResult, type ValidationParams, type ValidationMessage, type ValidationErrorsMap } from './schema/index.js';
3
5
  export { createRules, validationMessagesRu, validationMessagesI18n, validationMessagesEn, type ValidationRuleParams, type BaseValidationMessages } from './schema/rules/index.js';
@@ -1,3 +1,5 @@
1
- export { createAsyncHelpers } from './injectable-dependencies/index.js';
1
+ export { createAsyncAction, createAsyncResource } from './injectable-dependencies/index.js';
2
+ export { RegisterProvider } from './presenter/RegisterProvider.js';
3
+ export { Bound } from './presenter/Bound.js';
2
4
  export { createSchemaFactory, schema } from './schema/index.js';
3
5
  export { createRules, validationMessagesRu, validationMessagesI18n, validationMessagesEn } from './schema/rules/index.js';
@@ -18,7 +18,5 @@ export interface AsyncActionSettings<Res = never, Req = never, ErrorResult = App
18
18
  export interface AsyncResourceSettings<Res = never, Req = never, ErrorResult = AppError<Error>> extends AsyncActionSettings<Res, Req, ErrorResult> {
19
19
  reject?: boolean;
20
20
  }
21
- export declare const createAsyncHelpers: <ErrorResult extends object>() => {
22
- createAsyncAction: <Res = never, Req = never>(action: Action<Res>, args?: AsyncActionSettings<Res, Req, ErrorResult>) => Promise<AsyncActionResponse<Res, Req, ErrorResult>>;
23
- createAsyncResource: <Res = never, Req_1 = never>(action: Action<Res>, args?: AsyncResourceSettings<Res, Req_1, ErrorResult>) => Promise<Res>;
24
- };
21
+ export declare const createAsyncAction: <Res = never, Req = never, ErrorResult extends object = Record<never, never>>(action: Action<Res>, args?: AsyncActionSettings<Res, Req, ErrorResult>) => Promise<AsyncActionResponse<Res, Req, ErrorResult>>;
22
+ export declare const createAsyncResource: <Res = never, Req = never, ErrorResult extends object = Record<never, never>>(action: Action<Res>, args?: AsyncResourceSettings<Res, Req, ErrorResult>) => Promise<Res>;
@@ -1,115 +1,109 @@
1
1
  import { AsyncHelperError } from '../../shared/app-error/AppError.js';
2
2
  import { resolveAzureNetKitError } from '../../shared/app-error/ErrorRuntime.js';
3
- export const createAsyncHelpers = () => {
4
- const normalizeError = (err) => (err instanceof Error ? err : new Error(String(err)));
5
- const prepareErrors = async (error, retry) => {
6
- let retryResult = undefined;
7
- const retryFunc = async () => {
8
- if (retry) {
9
- return await retry()
10
- .then((res) => {
11
- retryResult = res;
12
- return undefined;
13
- })
14
- .catch((err) => normalizeError(err));
15
- }
16
- };
17
- const parsedError = await resolveAzureNetKitError(normalizeError(error), {
18
- can: !!retry,
19
- call: retry ? () => retryFunc() : undefined
20
- });
21
- return { error: parsedError, retryResult };
22
- };
23
- const beforeSendResolver = async (settings) => {
24
- if (!settings?.beforeSend)
25
- return undefined;
26
- let abortReason;
27
- let aborted = false;
28
- await new Promise((resolve) => {
29
- let settled = false;
30
- const beforeSend = settings.beforeSend;
31
- const guardTimer = setTimeout(() => {
32
- settle(true);
33
- }, 30000);
34
- const settle = (isAbort, reason) => {
35
- if (settled)
36
- return;
37
- settled = true;
38
- clearTimeout(guardTimer);
39
- aborted = isAbort;
40
- abortReason = reason;
41
- resolve();
42
- };
43
- const next = () => settle(false);
44
- const abort = (reason) => settle(true, reason);
45
- try {
46
- const maybePromise = beforeSend({ next, abort });
47
- if (maybePromise && typeof maybePromise.then === 'function') {
48
- maybePromise.then(() => settle(false), (err) => settle(true, normalizeError(err)));
49
- return;
50
- }
51
- settle(false);
52
- }
53
- catch (err) {
54
- settle(true, normalizeError(err));
55
- }
56
- });
57
- if (!aborted)
58
- return undefined;
59
- const { error } = await prepareErrors(abortReason ?? new AsyncHelperError('Aborted in beforeSend'));
60
- return {
61
- success: false,
62
- error,
63
- response: undefined
64
- };
3
+ const normalizeError = (err) => (err instanceof Error ? err : new Error(String(err)));
4
+ const prepareErrors = async (error, retry) => {
5
+ let retryResult = undefined;
6
+ const retryFunc = async () => {
7
+ if (retry) {
8
+ return await retry()
9
+ .then((res) => {
10
+ retryResult = res;
11
+ return undefined;
12
+ })
13
+ .catch((err) => normalizeError(err));
14
+ }
65
15
  };
66
- const createAsyncAction = async (action, args) => {
67
- const beforeSendResult = await beforeSendResolver(args);
68
- if (beforeSendResult)
69
- return beforeSendResult;
70
- const executeAction = async () => {
71
- const response = await Promise.resolve(action());
72
- return { response, success: true };
73
- };
74
- const handleFailure = async (err, retry) => {
75
- const { error, retryResult } = await prepareErrors(normalizeError(err), retry);
76
- if (retryResult) {
77
- return completeSuccess(retryResult);
78
- }
79
- const result = { error, response: args?.fallbackResponse, success: false };
80
- try {
81
- await args?.onError?.(result);
82
- }
83
- catch (onErrorException) {
84
- throw await resolveAzureNetKitError(new AsyncHelperError('onError caught exception', { cause: normalizeError(onErrorException) }));
85
- }
86
- return result;
87
- };
88
- const completeSuccess = async (result) => {
89
- try {
90
- await args?.onSuccess?.(result);
91
- return result;
92
- }
93
- catch (err) {
94
- return handleFailure(err);
95
- }
16
+ const parsedError = await resolveAzureNetKitError(normalizeError(error), {
17
+ can: !!retry,
18
+ call: retry ? () => retryFunc() : undefined
19
+ });
20
+ return { error: parsedError, retryResult };
21
+ };
22
+ const beforeSendResolver = async (settings) => {
23
+ if (!settings?.beforeSend)
24
+ return undefined;
25
+ let abortReason;
26
+ let aborted = false;
27
+ await new Promise((resolve) => {
28
+ let settled = false;
29
+ const beforeSend = settings.beforeSend;
30
+ const guardTimer = setTimeout(() => {
31
+ settle(true);
32
+ }, 30000);
33
+ const settle = (isAbort, reason) => {
34
+ if (settled)
35
+ return;
36
+ settled = true;
37
+ clearTimeout(guardTimer);
38
+ aborted = isAbort;
39
+ abortReason = reason;
40
+ resolve();
96
41
  };
42
+ const next = () => settle(false);
43
+ const abort = (reason) => settle(true, reason);
97
44
  try {
98
- return completeSuccess(await executeAction());
45
+ const maybePromise = beforeSend({ next, abort });
46
+ if (maybePromise && typeof maybePromise.then === 'function') {
47
+ maybePromise.then(() => settle(false), (err) => settle(true, normalizeError(err)));
48
+ return;
49
+ }
50
+ settle(false);
99
51
  }
100
52
  catch (err) {
101
- return handleFailure(err, executeAction);
53
+ settle(true, normalizeError(err));
102
54
  }
55
+ });
56
+ if (!aborted)
57
+ return undefined;
58
+ const { error } = await prepareErrors(abortReason ?? new AsyncHelperError('Aborted in beforeSend'));
59
+ return {
60
+ success: false,
61
+ error,
62
+ response: undefined
63
+ };
64
+ };
65
+ export const createAsyncAction = async (action, args) => {
66
+ const beforeSendResult = await beforeSendResolver(args);
67
+ if (beforeSendResult)
68
+ return beforeSendResult;
69
+ const executeAction = async () => {
70
+ const response = await Promise.resolve(action());
71
+ return { response, success: true };
103
72
  };
104
- const createAsyncResource = async (action, args) => {
105
- const result = await createAsyncAction(action, args);
106
- if (!result.success && args?.reject) {
107
- throw result.error;
73
+ const handleFailure = async (err, retry) => {
74
+ const { error, retryResult } = await prepareErrors(normalizeError(err), retry);
75
+ if (retryResult) {
76
+ return completeSuccess(retryResult);
77
+ }
78
+ const result = { error, response: args?.fallbackResponse, success: false };
79
+ try {
80
+ await args?.onError?.(result);
81
+ }
82
+ catch (onErrorException) {
83
+ throw await resolveAzureNetKitError(new AsyncHelperError('onError caught exception', { cause: normalizeError(onErrorException) }));
108
84
  }
109
- return result.response;
85
+ return result;
110
86
  };
111
- return {
112
- createAsyncAction,
113
- createAsyncResource
87
+ const completeSuccess = async (result) => {
88
+ try {
89
+ await args?.onSuccess?.(result);
90
+ return result;
91
+ }
92
+ catch (err) {
93
+ return handleFailure(err);
94
+ }
114
95
  };
96
+ try {
97
+ return completeSuccess(await executeAction());
98
+ }
99
+ catch (err) {
100
+ return handleFailure(err, executeAction);
101
+ }
102
+ };
103
+ export const createAsyncResource = async (action, args) => {
104
+ const result = await createAsyncAction(action, args);
105
+ if (!result.success && args?.reject) {
106
+ throw result.error;
107
+ }
108
+ return result.response;
115
109
  };
@@ -0,0 +1 @@
1
+ export declare function Bound<This, Args extends unknown[], Return>(method: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>): void;
@@ -0,0 +1,9 @@
1
+ export function Bound(method, context) {
2
+ context.addInitializer(function () {
3
+ Object.defineProperty(this, context.name, {
4
+ value: method.bind(this),
5
+ configurable: true,
6
+ writable: true
7
+ });
8
+ });
9
+ }
@@ -0,0 +1,16 @@
1
+ export type ProviderLifecycle = 'scoped' | 'transient' | 'singleton';
2
+ export type ProviderMethodKey<T> = Exclude<{
3
+ [K in keyof T]-?: T[K] extends (...args: never[]) => unknown ? K : never;
4
+ }[keyof T], 'constructor'>;
5
+ export interface RegisterProviderOptions<T> {
6
+ lifecycle?: ProviderLifecycle;
7
+ bindMethods?: boolean | readonly ProviderMethodKey<T>[];
8
+ }
9
+ /**
10
+ * Registration is safe at module scope; the factory is called only by the returned accessor.
11
+ * scoped: per request on the server, per call in the browser.
12
+ * singleton: per request on the server, per registration in the browser.
13
+ * transient: a new instance on every call.
14
+ * No hydration, component context, effects or disposal are managed by this function.
15
+ */
16
+ export declare function RegisterProvider<T extends object>(factory: () => T extends PromiseLike<unknown> ? never : T, options?: RegisterProviderOptions<NoInfer<T>>): () => T;