@azure-net/kit 6.0.0-next.1 → 6.0.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +178 -2
- package/dist/delivery/index.d.ts +2 -1
- package/dist/delivery/index.js +2 -1
- package/dist/delivery/injectable-dependencies/AsyncHelpers.d.ts +2 -4
- package/dist/delivery/injectable-dependencies/AsyncHelpers.js +96 -102
- package/dist/delivery/presenter/RegisterProvider.d.ts +9 -0
- package/dist/delivery/presenter/RegisterProvider.js +51 -0
- package/dist/delivery.d.ts +0 -1
- package/dist/delivery.js +0 -1
- package/dist/i18n/Internal.d.ts +13 -0
- package/dist/i18n/Internal.js +10 -0
- package/dist/i18n/Translations.svelte.d.ts +20 -0
- package/dist/i18n/Translations.svelte.js +254 -0
- package/dist/i18n/Types.d.ts +30 -0
- package/dist/i18n/Types.js +1 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.js +1 -0
- package/dist/infra/datasource/BaseDatasource.d.ts +19 -6
- package/dist/infra/datasource/BaseDatasource.js +4 -4
- package/dist/infra/http-service/HttpServiceInstance.d.ts +26 -16
- package/dist/infra/http-service/HttpServiceInstance.js +21 -13
- package/dist/infra/index.d.ts +2 -2
- package/dist/infra/index.js +2 -2
- package/dist/infra/query/QueryBuilder.d.ts +6 -1
- package/dist/infra/query/QueryBuilder.js +14 -12
- package/dist/plugin/AzureNetPlugin.d.ts +1 -4
- package/dist/plugin/AzureNetPlugin.js +16 -64
- package/dist/plugin/index.d.ts +1 -1
- package/dist/server/Context/Runtime.d.ts +2 -0
- package/dist/server/Context/Runtime.js +18 -0
- package/dist/server/Context/index.d.ts +1 -0
- package/dist/server/Context/index.js +1 -0
- package/dist/shared/app/App.d.ts +44 -69
- package/dist/shared/app/App.js +91 -102
- package/dist/shared/app/index.d.ts +2 -1
- package/dist/shared/app/middleware/ClientMiddleware.d.ts +2 -2
- package/dist/shared/app/middleware/ClientMiddleware.js +0 -1
- package/dist/shared/app/middleware/ServerMiddleware.d.ts +1 -1
- package/dist/shared/app/middleware/ServerMiddleware.js +1 -1
- package/dist/shared/app-error/ErrorRuntime.d.ts +1 -1
- package/dist/shared/app-error/ErrorRuntime.js +4 -3
- package/dist/shared/boundary-provider/Provider.d.ts +1 -1
- package/dist/shared/boundary-provider/Provider.js +44 -29
- package/dist/shared/cookie/UniversalCookie.js +1 -1
- package/dist/shared/index.d.ts +1 -3
- package/dist/shared/index.js +1 -3
- package/dist/shared/logger/index.js +1 -1
- package/dist/shared/request-context/Internal.d.ts +16 -0
- package/dist/shared/request-context/Internal.js +24 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +1 -0
- package/dist/svelte/query/Query.svelte.js +4 -2
- package/package.json +151 -134
- package/dist/external/edges/Dev.d.ts +0 -1
- package/dist/external/edges/Dev.js +0 -1
- package/dist/external/edges/Edges.d.ts +0 -1
- package/dist/external/edges/Edges.js +0 -1
- package/dist/external/edges/ServerContext.d.ts +0 -1
- package/dist/external/edges/ServerContext.js +0 -1
- package/dist/external/edges/_edges.d.ts +0 -1
- package/dist/external/edges/_edges.js +0 -1
- package/dist/external/i18n/index.d.ts +0 -1
- package/dist/external/i18n/index.js +0 -1
- package/dist/shared/event-bus/EventBus.d.ts +0 -12
- package/dist/shared/event-bus/EventBus.js +0 -3
- package/dist/shared/event-bus/index.d.ts +0 -1
- package/dist/shared/event-bus/index.js +0 -1
- /package/dist/{shared → svelte}/redirect/index.d.ts +0 -0
- /package/dist/{shared → svelte}/redirect/index.js +0 -0
package/README.md
CHANGED
|
@@ -2,6 +2,106 @@
|
|
|
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 Edges SSR state is hydrated once; 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 = () => createAsyncResource(() => this.useCases.collection());
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const useOrdersPresenter = RegisterProvider(() => {
|
|
75
|
+
const { Orders } = ApplicationProvider();
|
|
76
|
+
return new OrdersPresenter(Orders);
|
|
77
|
+
}, 'scoped');
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Call `useOrdersPresenter()` during component initialization, not at module scope.
|
|
81
|
+
The registration itself is safe at module scope and does not resolve dependencies.
|
|
82
|
+
Arrow fields remain callable after destructuring. `RegisterProvider` returns the
|
|
83
|
+
actual class instance, without a proxy or automatic method binding.
|
|
84
|
+
Classes using Svelte runes such as `$state` must be placed in a `.svelte.ts` module
|
|
85
|
+
(or declared inside a Svelte component), not a regular `.ts` file.
|
|
86
|
+
|
|
87
|
+
| Lifecycle | Server | Browser |
|
|
88
|
+
| ------------------------------------------ | ----------------------------------------- | ----------------------------- |
|
|
89
|
+
| `scoped` (default) | One instance per registration per request | New instance per call |
|
|
90
|
+
| `transient` (`trancient` is also accepted) | New instance per call | New instance per call |
|
|
91
|
+
| `singleton` | One instance per registration per request | One instance per registration |
|
|
92
|
+
|
|
93
|
+
Server caching requires `AzureNetPlugin` request context and fails
|
|
94
|
+
outside it. There is no process-wide server singleton. Request caches have weak
|
|
95
|
+
keys and do not retain completed requests. Registrations do not require names,
|
|
96
|
+
Svelte context, hydration, or state serialization. Factory callbacks must be
|
|
97
|
+
synchronous; recursive construction is rejected.
|
|
98
|
+
|
|
99
|
+
This API does not own effects, subscriptions, timers, or `dispose()`. Keep
|
|
100
|
+
component-owned effects in component-lived instances; a cached browser singleton
|
|
101
|
+
must not rely on effects owned by the first component that requested it. SSR
|
|
102
|
+
isolation does not serialize state to the browser and cannot protect mutable
|
|
103
|
+
module-level state captured by user factories.
|
|
104
|
+
|
|
5
105
|
**@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
106
|
|
|
7
107
|
## ✨ What is it?
|
|
@@ -10,8 +110,8 @@
|
|
|
10
110
|
|
|
11
111
|
## 🚀 What's included?
|
|
12
112
|
|
|
13
|
-
- **
|
|
14
|
-
- **Localization and translation
|
|
113
|
+
- **Reactive state classes** — request-isolated server instances and browser singletons via `RegisterProvider`.
|
|
114
|
+
- **Localization and translation**: built-in rune-based i18n with request-scoped SSR instances. No edges or external translations dependency.
|
|
15
115
|
- **Extensive utilities** — for working with text, objects, dates, and common tasks.
|
|
16
116
|
- **Data validation** — handy rules and validators.
|
|
17
117
|
- **Reusable base abstractions** — `Repositories`, `Services`, `Providers`, and `Datasources` for quick setup and reuse.
|
|
@@ -40,3 +140,79 @@ In this guide, you will find:
|
|
|
40
140
|
## 🚀 Get started
|
|
41
141
|
|
|
42
142
|
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**.
|
|
143
|
+
|
|
144
|
+
## Initial state and `useBoot`
|
|
145
|
+
|
|
146
|
+
`AzureNetPlugin()` owns a kit-local AsyncLocalStorage scope. It does not depend on
|
|
147
|
+
edges, serialize stores, inject response scripts or create virtual route modules.
|
|
148
|
+
There are no edges store/presenter exports or devtools exports in this version.
|
|
149
|
+
`RequestContext.current()` exposes only `event` and `state`, is server-only, and
|
|
150
|
+
throws outside an active request. Internal DI/error/lifecycle caches are private.
|
|
151
|
+
|
|
152
|
+
Declare transferable state in `src/app.d.ts` (prefer optional fields for CSR):
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import type { IUser } from '#contexts/Identity/Domain';
|
|
156
|
+
declare global {
|
|
157
|
+
namespace App {
|
|
158
|
+
interface RequestContextState {
|
|
159
|
+
currentUser?: IUser | null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
export {};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
import { createApp } from '@azure-net/kit';
|
|
168
|
+
import { RequestContext } from '@azure-net/kit/server-context';
|
|
169
|
+
|
|
170
|
+
export const { register } = createApp((app) =>
|
|
171
|
+
app
|
|
172
|
+
.useServer(async ({ event, resolve, useMiddlewares }) => {
|
|
173
|
+
await useMiddlewares([ServerAuth]);
|
|
174
|
+
await TranslationManager().preloadTranslation();
|
|
175
|
+
return resolve(event);
|
|
176
|
+
})
|
|
177
|
+
.useBoot(({ state }) => {
|
|
178
|
+
useCurrentUser().setUser(state.currentUser ?? null);
|
|
179
|
+
TranslationManager().syncFromState(state);
|
|
180
|
+
})
|
|
181
|
+
);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
In the root layout call `register()` before resolving presenters or state classes.
|
|
185
|
+
`useBoot` is synchronous and runs once per server request render and once per
|
|
186
|
+
browser app initialization, before client lifecycle callbacks. Await all server
|
|
187
|
+
state preparation before `resolve`. Do not mutate the transfer snapshot during
|
|
188
|
+
rendering. Client navigations and repeated register calls do not rehydrate state.
|
|
189
|
+
`useClientInit` runs earlier and must not depend on this snapshot. CSR without an
|
|
190
|
+
SSR snapshot receives `{}`; initialize defaults and fetch missing data explicitly.
|
|
191
|
+
Only publish safe DTOs, never tokens or the whole request/locals. Personalized HTML
|
|
192
|
+
must not be publicly cached. Classes, functions and services are not serialized.
|
|
193
|
+
|
|
194
|
+
Svelte currently requires async mode for `hydratable`:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
sveltekit({ compilerOptions: { experimental: { async: true } } });
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Keep the application's CSP correctly configured; Svelte manages the transfer
|
|
201
|
+
script and serialization. No custom HTML parser is used by the kit.
|
|
202
|
+
|
|
203
|
+
## DI naming
|
|
204
|
+
|
|
205
|
+
`createBoundaryProvider` is replaced by `CreateDiContainer` (no compatibility alias):
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
const ApplicationProvider = CreateDiContainer('ApplicationProvider', {
|
|
209
|
+
register: () => ({ Users: () => new UsersUseCases(repository) })
|
|
210
|
+
});
|
|
211
|
+
const { Users } = ApplicationProvider();
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The accessor remains lazy, synchronous and request-scoped on SSR. `RegisterProvider`
|
|
215
|
+
registers an individual class factory; `singleton` means one browser instance but
|
|
216
|
+
one instance per request on the server, never a server process singleton.
|
|
217
|
+
|
|
218
|
+
Built-in i18n: see [I18N.md](./I18N.md). preloadTranslation() fills request state automatically; syncFromState(state) restores it synchronously in useBoot. No separate translation package, writable/derived stores or edges adapter is required.
|
package/dist/delivery/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { createAsyncAction, createAsyncResource, type AsyncActionSettings, type AsyncHelperRetry, type AsyncResourceSettings, type AsyncActionResponse } from './injectable-dependencies/index.js';
|
|
2
|
+
export { RegisterProvider, type ProviderLifecycle } from './presenter/RegisterProvider.js';
|
|
2
3
|
export { createSchemaFactory, schema, type Schema, type RequestErrors, type ISchemaError, type ValidationResult, type ValidationRuleResult, type ValidationParams, type ValidationMessage, type ValidationErrorsMap } from './schema/index.js';
|
|
3
4
|
export { createRules, validationMessagesRu, validationMessagesI18n, validationMessagesEn, type ValidationRuleParams, type BaseValidationMessages } from './schema/rules/index.js';
|
package/dist/delivery/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { createAsyncAction, createAsyncResource } from './injectable-dependencies/index.js';
|
|
2
|
+
export { RegisterProvider } from './presenter/RegisterProvider.js';
|
|
2
3
|
export { createSchemaFactory, schema } from './schema/index.js';
|
|
3
4
|
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
|
|
22
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
|
|
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
|
|
85
|
+
return result;
|
|
110
86
|
};
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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,9 @@
|
|
|
1
|
+
export type ProviderLifecycle = 'scoped' | 'transient' | 'singleton';
|
|
2
|
+
/**
|
|
3
|
+
* Registration is safe at module scope; the factory is called only by the returned accessor.
|
|
4
|
+
* scoped: per request on the server, per call in the browser.
|
|
5
|
+
* singleton: per request on the server, per registration in the browser.
|
|
6
|
+
* transient: a new instance on every call.
|
|
7
|
+
* No hydration, component context, effects or disposal are managed by this function.
|
|
8
|
+
*/
|
|
9
|
+
export declare function RegisterProvider<T extends object>(factory: () => T extends PromiseLike<unknown> ? never : T, lifecycle?: ProviderLifecycle): () => T;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { RequestContext } from '../../shared/request-context/Internal.js';
|
|
2
|
+
import { BROWSER } from '../../external/tools/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Registration is safe at module scope; the factory is called only by the returned accessor.
|
|
5
|
+
* scoped: per request on the server, per call in the browser.
|
|
6
|
+
* singleton: per request on the server, per registration in the browser.
|
|
7
|
+
* transient: a new instance on every call.
|
|
8
|
+
* No hydration, component context, effects or disposal are managed by this function.
|
|
9
|
+
*/
|
|
10
|
+
export function RegisterProvider(factory, lifecycle = 'scoped') {
|
|
11
|
+
if (!['scoped', 'transient', 'singleton'].includes(lifecycle)) {
|
|
12
|
+
throw new TypeError(`[RegisterProvider] Unknown lifecycle: ${lifecycle}`);
|
|
13
|
+
}
|
|
14
|
+
const requests = new WeakMap();
|
|
15
|
+
let clientInstance;
|
|
16
|
+
let constructing = false;
|
|
17
|
+
const create = () => {
|
|
18
|
+
if (constructing)
|
|
19
|
+
throw new Error('[RegisterProvider] Circular presenter dependency.');
|
|
20
|
+
constructing = true;
|
|
21
|
+
try {
|
|
22
|
+
const instance = factory();
|
|
23
|
+
if (!instance || (typeof instance !== 'object' && typeof instance !== 'function')) {
|
|
24
|
+
throw new TypeError('[RegisterProvider] Factory must return an instance.');
|
|
25
|
+
}
|
|
26
|
+
if ('then' in instance && typeof instance.then === 'function') {
|
|
27
|
+
// Consume a rejected async factory without allowing an unhandled rejection.
|
|
28
|
+
void Promise.resolve(instance).catch(() => undefined);
|
|
29
|
+
throw new TypeError('[RegisterProvider] Factory must be synchronous.');
|
|
30
|
+
}
|
|
31
|
+
return instance;
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
constructing = false;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
return () => {
|
|
38
|
+
if (lifecycle === 'transient')
|
|
39
|
+
return create();
|
|
40
|
+
if (BROWSER)
|
|
41
|
+
return lifecycle === 'singleton' ? (clientInstance ??= create()) : create();
|
|
42
|
+
// Weak keys do not retain finished requests or their presenter graphs.
|
|
43
|
+
const context = RequestContext.current();
|
|
44
|
+
let instance = requests.get(context);
|
|
45
|
+
if (!instance) {
|
|
46
|
+
instance = create();
|
|
47
|
+
requests.set(context, instance);
|
|
48
|
+
}
|
|
49
|
+
return instance;
|
|
50
|
+
};
|
|
51
|
+
}
|
package/dist/delivery.d.ts
CHANGED
package/dist/delivery.js
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface TranslationLifecycle {
|
|
2
|
+
preloadTranslation(): Promise<void>;
|
|
3
|
+
initializeClient(): Promise<void>;
|
|
4
|
+
syncFromState(state: App.RequestContextState): void;
|
|
5
|
+
transformHtml(html: string): string;
|
|
6
|
+
}
|
|
7
|
+
declare const translationManager: unique symbol;
|
|
8
|
+
export type TranslationManagerFactory = (() => object) & {
|
|
9
|
+
readonly [translationManager]: true;
|
|
10
|
+
};
|
|
11
|
+
export declare const bindTranslationLifecycle: (manager: object, lifecycle: TranslationLifecycle) => void;
|
|
12
|
+
export declare const getTranslationLifecycle: (manager: object) => TranslationLifecycle;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const lifecycles = new WeakMap();
|
|
2
|
+
export const bindTranslationLifecycle = (manager, lifecycle) => {
|
|
3
|
+
lifecycles.set(manager, lifecycle);
|
|
4
|
+
};
|
|
5
|
+
export const getTranslationLifecycle = (manager) => {
|
|
6
|
+
const lifecycle = lifecycles.get(manager);
|
|
7
|
+
if (!lifecycle)
|
|
8
|
+
throw new Error('[AzureNetKit:i18n] Expected a manager returned by createTranslations().');
|
|
9
|
+
return lifecycle;
|
|
10
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type TranslationManagerFactory } from './Internal.js';
|
|
2
|
+
import type { Path, TranslationCookieOptions, MessageLoader, TranslationVariables } from './Types.js';
|
|
3
|
+
export declare const createTranslations: <const TMessages extends Record<string, MessageLoader>, const TInitLocale extends Extract<keyof TMessages, string>>(params: {
|
|
4
|
+
messages: TMessages;
|
|
5
|
+
initLang: TInitLocale;
|
|
6
|
+
cookieName?: string;
|
|
7
|
+
cookieOptions?: TranslationCookieOptions;
|
|
8
|
+
initLangFromAcceptLanguage?: boolean;
|
|
9
|
+
prefix?: string;
|
|
10
|
+
}) => (() => {
|
|
11
|
+
switchLocale: (requestedLocale: Extract<keyof TMessages, string>) => Promise<void>;
|
|
12
|
+
applyHtmlLocaleAttr: (html: string) => string;
|
|
13
|
+
subscribeLocaleChangeEvent: (listener: (locale: Extract<keyof TMessages, string>) => void) => (() => void);
|
|
14
|
+
t: (translation?: string | {
|
|
15
|
+
key: string | Path<Awaited<ReturnType<TMessages[TInitLocale]>>, "", []>;
|
|
16
|
+
vars?: TranslationVariables;
|
|
17
|
+
} | Path<Awaited<ReturnType<TMessages[TInitLocale]>>, "", []>, vars?: TranslationVariables) => string;
|
|
18
|
+
readonly locale: Extract<keyof TMessages, string>;
|
|
19
|
+
locales: Readonly<Record<Extract<keyof TMessages, string>, Extract<keyof TMessages, string>>>;
|
|
20
|
+
}) & TranslationManagerFactory;
|