vultus-core 0.1.0 → 0.1.1

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 (50) hide show
  1. package/README.md +38 -0
  2. package/dist/LinkContext.d.ts +13 -0
  3. package/dist/LinkContext.d.ts.map +1 -0
  4. package/dist/LinkContext.js +8 -0
  5. package/dist/LinkContext.js.map +1 -0
  6. package/dist/MessageSink.d.ts +10 -0
  7. package/dist/MessageSink.d.ts.map +1 -0
  8. package/dist/MessageSink.js +6 -0
  9. package/dist/MessageSink.js.map +1 -0
  10. package/dist/action-core.d.ts +36 -0
  11. package/dist/action-core.d.ts.map +1 -0
  12. package/dist/action-core.js +73 -0
  13. package/dist/action-core.js.map +1 -0
  14. package/dist/decision.d.ts +8 -0
  15. package/dist/decision.d.ts.map +1 -0
  16. package/dist/decision.js +15 -0
  17. package/dist/decision.js.map +1 -0
  18. package/dist/index.d.ts +11 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +11 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/parseApiError.d.ts +3 -0
  23. package/dist/parseApiError.d.ts.map +1 -0
  24. package/dist/parseApiError.js +11 -0
  25. package/dist/parseApiError.js.map +1 -0
  26. package/dist/resolve.d.ts +3 -0
  27. package/dist/resolve.d.ts.map +1 -0
  28. package/dist/resolve.js +6 -0
  29. package/dist/resolve.js.map +1 -0
  30. package/dist/routeApiError.d.ts +3 -0
  31. package/dist/routeApiError.d.ts.map +1 -0
  32. package/dist/routeApiError.js +19 -0
  33. package/dist/routeApiError.js.map +1 -0
  34. package/dist/types.d.ts +80 -0
  35. package/dist/types.d.ts.map +1 -0
  36. package/dist/types.js +2 -0
  37. package/dist/types.js.map +1 -0
  38. package/dist/useAction.d.ts +3 -0
  39. package/dist/useAction.d.ts.map +1 -0
  40. package/dist/useAction.js +47 -0
  41. package/dist/useAction.js.map +1 -0
  42. package/dist/useActionErrorCtx.d.ts +4 -0
  43. package/dist/useActionErrorCtx.d.ts.map +1 -0
  44. package/dist/useActionErrorCtx.js +6 -0
  45. package/dist/useActionErrorCtx.js.map +1 -0
  46. package/dist/useActionList.d.ts +13 -0
  47. package/dist/useActionList.d.ts.map +1 -0
  48. package/dist/useActionList.js +42 -0
  49. package/dist/useActionList.js.map +1 -0
  50. package/package.json +20 -5
package/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # vultus-core
2
+
3
+ Framework-neutral action model, hooks, and error-routing for the vultus UI
4
+ family. It is antd-free by design: no Ant Design import appears anywhere in
5
+ this package. Consumed by `vultus-antd` and, transitively, by excavator and
6
+ optio-conversation-ui.
7
+
8
+ ## Install
9
+
10
+ ```sh
11
+ npm install vultus-core
12
+ ```
13
+
14
+ ## Peer dependencies
15
+
16
+ - `react` >=18
17
+ - `react-i18next` >=15
18
+
19
+ ## Entry points
20
+
21
+ - `.` — the action model, exporting: the core `types`, `decision`, `resolve`,
22
+ `parseApiError`, `routeApiError`, `ActionErrorContext` / `useActionErrorCtx`,
23
+ `useAction`, `useActionList`, `InternalLinkContext` / `useInternalLink` /
24
+ `InternalLinkComponent`, and `MessageSinkContext` / `useMessageSink` /
25
+ `MessageSink`.
26
+ - `./link` — `InternalLinkContext`, `useInternalLink`, and the
27
+ `InternalLinkComponent` type on their own, for consumers that only need the
28
+ link-injection seam.
29
+
30
+ ## Distribution
31
+
32
+ This package ships compiled ESM (`dist/`) with TypeScript declarations to
33
+ npm; the repository's own `exports` field resolves `src/` directly for
34
+ workspace consumers.
35
+
36
+ ## License
37
+
38
+ Apache-2.0
@@ -0,0 +1,13 @@
1
+ import { type ComponentType, type ReactNode } from 'react';
2
+ /**
3
+ * How an internal (`/`-prefixed) markdown link renders. Hosts with a router
4
+ * inject an SPA link; the default is a full-navigation anchor so router-less
5
+ * consumers (optio-conversation-ui) work with zero setup.
6
+ */
7
+ export type InternalLinkComponent = ComponentType<{
8
+ href: string;
9
+ children?: ReactNode;
10
+ }>;
11
+ export declare const InternalLinkContext: import("react").Context<InternalLinkComponent>;
12
+ export declare function useInternalLink(): InternalLinkComponent;
13
+ //# sourceMappingURL=LinkContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LinkContext.d.ts","sourceRoot":"","sources":["../src/LinkContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtF;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAM1F,eAAO,MAAM,mBAAmB,gDAA4D,CAAC;AAE7F,wBAAgB,eAAe,IAAI,qBAAqB,CAEvD"}
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from 'react';
3
+ const DefaultInternalLink = ({ href, children }) => (_jsx("a", { href: href, children: children }));
4
+ export const InternalLinkContext = createContext(DefaultInternalLink);
5
+ export function useInternalLink() {
6
+ return useContext(InternalLinkContext);
7
+ }
8
+ //# sourceMappingURL=LinkContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LinkContext.js","sourceRoot":"","sources":["../src/LinkContext.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAsC,MAAM,OAAO,CAAC;AAStF,MAAM,mBAAmB,GAA0B,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CACzE,YAAG,IAAI,EAAE,IAAI,YAAG,QAAQ,GAAK,CAC9B,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,aAAa,CAAwB,mBAAmB,CAAC,CAAC;AAE7F,MAAM,UAAU,eAAe;IAC7B,OAAO,UAAU,CAAC,mBAAmB,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * How the action framework surfaces an otherwise-unhandled error (no form field,
3
+ * no inline error target). Default is a no-op so vultus-core stays antd-free and
4
+ * framework-neutral; vultus-antd's VultusProvider wires this to antd's
5
+ * message.error. Hosts may inject any toast/notification mechanism.
6
+ */
7
+ export type MessageSink = (text: string) => void;
8
+ export declare const MessageSinkContext: import("react").Context<MessageSink>;
9
+ export declare function useMessageSink(): MessageSink;
10
+ //# sourceMappingURL=MessageSink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageSink.d.ts","sourceRoot":"","sources":["../src/MessageSink.tsx"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;AAEjD,eAAO,MAAM,kBAAkB,sCAAuC,CAAC;AAEvE,wBAAgB,cAAc,IAAI,WAAW,CAE5C"}
@@ -0,0 +1,6 @@
1
+ import { createContext, useContext } from 'react';
2
+ export const MessageSinkContext = createContext(() => { });
3
+ export function useMessageSink() {
4
+ return useContext(MessageSinkContext);
5
+ }
6
+ //# sourceMappingURL=MessageSink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageSink.js","sourceRoot":"","sources":["../src/MessageSink.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAUlD,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAa,CAAc,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAEvE,MAAM,UAAU,cAAc;IAC5B,OAAO,UAAU,CAAC,kBAAkB,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type React from 'react';
2
+ import type { MutableRefObject } from 'react';
3
+ import type { MessageSink } from './MessageSink.js';
4
+ import type { ActionErrorCtx, ActionOptions, ActionStatus, ConfirmationSpec, ErrorRoutesRegistry } from './types.js';
5
+ /**
6
+ * Hookless core of the action framework, shared by useAction (single) and
7
+ * useActionList (dynamic list) so the firing machinery + ActionStatus shape
8
+ * live in exactly one place — neither entry point reimplements the other.
9
+ */
10
+ export interface ResolvedFields {
11
+ label: string;
12
+ icon: React.ReactNode | undefined;
13
+ variant: 'default' | 'primary' | 'danger';
14
+ disabled: boolean;
15
+ reason: string | undefined;
16
+ invisible: boolean;
17
+ confirmation: ConfirmationSpec | undefined;
18
+ }
19
+ /** Resolve the static (hookless) ActionStatus fields from the options. */
20
+ export declare function resolveActionFields(opts: ActionOptions<any, any, any>): ResolvedFields;
21
+ export interface FirePromiseDeps<RouteId extends string> {
22
+ disabled: boolean;
23
+ reason: string | undefined;
24
+ getPending: () => boolean;
25
+ setPending: (value: boolean) => void;
26
+ setErrors: (errors: string[]) => void;
27
+ errCtxRef: MutableRefObject<ActionErrorCtx | null>;
28
+ t: (key: string, opts?: Record<string, unknown>) => string;
29
+ registry?: ErrorRoutesRegistry<RouteId>;
30
+ messageSink?: MessageSink;
31
+ }
32
+ /** Build the async fire (pending guard + error routing) for one action. */
33
+ export declare function makeFirePromise<TArgs, TResult, RouteId extends string>(opts: ActionOptions<TArgs, TResult, RouteId>, deps: FirePromiseDeps<RouteId>): (args: TArgs) => Promise<void>;
34
+ /** Assemble the public ActionStatus from resolved fields + state + fire. */
35
+ export declare function assembleStatus<TArgs>(opts: ActionOptions<TArgs, any, any>, fields: ResolvedFields, pending: boolean, errors: string[], firePromise: (args: TArgs) => Promise<void>): ActionStatus<TArgs>;
36
+ //# sourceMappingURL=action-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-core.d.ts","sourceRoot":"","sources":["../src/action-core.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAK9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,YAAY,EACZ,gBAAgB,EAEhB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AAEH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC;IAClC,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC1C,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,gBAAgB,GAAG,SAAS,CAAC;CAC5C;AAED,0EAA0E;AAC1E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,cAAc,CAWtF;AAED,MAAM,WAAW,eAAe,CAAC,OAAO,SAAS,MAAM;IACrD,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,UAAU,EAAE,MAAM,OAAO,CAAC;IAC1B,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACrC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtC,SAAS,EAAE,gBAAgB,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACnD,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;IAC3D,QAAQ,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACxC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,SAAS,MAAM,EACpE,IAAI,EAAE,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAC5C,IAAI,EAAE,eAAe,CAAC,OAAO,CAAC,GAC7B,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,CAmChC;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,KAAK,EAClC,IAAI,EAAE,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,EACpC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,GAC1C,YAAY,CAAC,KAAK,CAAC,CAkBrB"}
@@ -0,0 +1,73 @@
1
+ import { getVerdict, getReason } from './decision.js';
2
+ import { resolve } from './resolve.js';
3
+ import { routeApiError } from './routeApiError.js';
4
+ import { parseApiError } from './parseApiError.js';
5
+ /** Resolve the static (hookless) ActionStatus fields from the options. */
6
+ export function resolveActionFields(opts) {
7
+ const enabledD = resolve(opts.enabled, true);
8
+ return {
9
+ label: resolve(opts.label, ''),
10
+ icon: resolve(opts.icon, undefined),
11
+ variant: resolve(opts.variant, 'default'),
12
+ disabled: !getVerdict(enabledD, true),
13
+ reason: getReason(enabledD),
14
+ invisible: resolve(opts.invisible, false),
15
+ confirmation: resolve(opts.confirmation, undefined),
16
+ };
17
+ }
18
+ /** Build the async fire (pending guard + error routing) for one action. */
19
+ export function makeFirePromise(opts, deps) {
20
+ return async (args) => {
21
+ if (deps.disabled) {
22
+ console.warn(`Action '${opts.id}': disabled — ${deps.reason ?? '(no reason)'}`);
23
+ return;
24
+ }
25
+ if (deps.getPending()) {
26
+ console.warn(`Action '${opts.id}': already pending — ignoring fire`);
27
+ return;
28
+ }
29
+ deps.setErrors([]);
30
+ deps.setPending(true);
31
+ try {
32
+ const result = await opts.fire(args);
33
+ opts.onSuccess?.(result);
34
+ }
35
+ catch (err) {
36
+ if (opts.errorRoute && deps.registry) {
37
+ routeApiError(err, {
38
+ route: opts.errorRoute,
39
+ form: deps.errCtxRef.current?.form,
40
+ setInlineError: deps.errCtxRef.current?.setInlineError,
41
+ t: deps.t,
42
+ showError: deps.messageSink,
43
+ }, deps.registry);
44
+ }
45
+ const parsed = parseApiError(err);
46
+ deps.setErrors([parsed.message ?? 'Action failed']);
47
+ }
48
+ finally {
49
+ deps.setPending(false);
50
+ }
51
+ };
52
+ }
53
+ /** Assemble the public ActionStatus from resolved fields + state + fire. */
54
+ export function assembleStatus(opts, fields, pending, errors, firePromise) {
55
+ const fire = (args) => {
56
+ void firePromise(args);
57
+ };
58
+ return {
59
+ id: opts.id,
60
+ label: fields.label,
61
+ icon: fields.icon,
62
+ variant: fields.variant,
63
+ pending,
64
+ disabled: fields.disabled,
65
+ reason: fields.reason,
66
+ invisible: fields.invisible,
67
+ confirmation: fields.confirmation,
68
+ errors,
69
+ fire: fire,
70
+ firePromise: firePromise,
71
+ };
72
+ }
73
+ //# sourceMappingURL=action-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-core.js","sourceRoot":"","sources":["../src/action-core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { Decision } from './types.js';
2
+ export declare const allow: (reason?: string) => Decision;
3
+ export declare const deny: (reason?: string) => Decision;
4
+ export declare const denyWithReason: (reason: string) => Decision;
5
+ export declare const getVerdict: (d: Decision | undefined, fallback: boolean) => boolean;
6
+ export declare const getReason: (d: Decision | undefined) => string | undefined;
7
+ export declare const andDecisions: (a: Decision, b: Decision) => Decision;
8
+ //# sourceMappingURL=decision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision.d.ts","sourceRoot":"","sources":["../src/decision.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,KAAK,GAAI,SAAS,MAAM,KAAG,QAAuC,CAAC;AAChF,eAAO,MAAM,IAAI,GAAI,SAAS,MAAM,KAAG,QAAwC,CAAC;AAChF,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,KAAG,QAAwC,CAAC;AAEzF,eAAO,MAAM,UAAU,GAAI,GAAG,QAAQ,GAAG,SAAS,EAAE,UAAU,OAAO,KAAG,OACH,CAAC;AAEtE,eAAO,MAAM,SAAS,GAAI,GAAG,QAAQ,GAAG,SAAS,KAAG,MAAM,GAAG,SACgB,CAAC;AAE9E,eAAO,MAAM,YAAY,GAAI,GAAG,QAAQ,EAAE,GAAG,QAAQ,KAAG,QAQvD,CAAC"}
@@ -0,0 +1,15 @@
1
+ export const allow = (reason) => ({ verdict: true, reason });
2
+ export const deny = (reason) => ({ verdict: false, reason });
3
+ export const denyWithReason = (reason) => ({ verdict: false, reason });
4
+ export const getVerdict = (d, fallback) => d === undefined ? fallback : typeof d === 'boolean' ? d : d.verdict;
5
+ export const getReason = (d) => d === undefined ? undefined : typeof d === 'boolean' ? undefined : d.reason;
6
+ export const andDecisions = (a, b) => {
7
+ const av = getVerdict(a, false);
8
+ const bv = getVerdict(b, false);
9
+ if (av && bv) {
10
+ const reasons = [getReason(a), getReason(b)].filter(Boolean);
11
+ return { verdict: true, reason: reasons.join('; ') || undefined };
12
+ }
13
+ return av ? b : a;
14
+ };
15
+ //# sourceMappingURL=decision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision.js","sourceRoot":"","sources":["../src/decision.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,MAAe,EAAY,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAChF,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,MAAe,EAAY,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;AAChF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAc,EAAY,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;AAEzF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAuB,EAAE,QAAiB,EAAW,EAAE,CAChF,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AAEtE,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAuB,EAAsB,EAAE,CACvE,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAE9E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAW,EAAE,CAAW,EAAY,EAAE;IACjE,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAChC,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAChC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAa,CAAC;QACzE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;IACpE,CAAC;IACD,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ export * from './types.js';
2
+ export * from './decision.js';
3
+ export * from './resolve.js';
4
+ export * from './parseApiError.js';
5
+ export * from './routeApiError.js';
6
+ export { ActionErrorContext, useActionErrorCtx } from './useActionErrorCtx.js';
7
+ export { useAction } from './useAction.js';
8
+ export { useActionList } from './useActionList.js';
9
+ export { InternalLinkContext, useInternalLink, type InternalLinkComponent } from './LinkContext.js';
10
+ export { MessageSinkContext, useMessageSink, type MessageSink } from './MessageSink.js';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACpG,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export * from './types.js';
2
+ export * from './decision.js';
3
+ export * from './resolve.js';
4
+ export * from './parseApiError.js';
5
+ export * from './routeApiError.js';
6
+ export { ActionErrorContext, useActionErrorCtx } from './useActionErrorCtx.js';
7
+ export { useAction } from './useAction.js';
8
+ export { useActionList } from './useActionList.js';
9
+ export { InternalLinkContext, useInternalLink } from './LinkContext.js';
10
+ export { MessageSinkContext, useMessageSink } from './MessageSink.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAA8B,MAAM,kBAAkB,CAAC;AACpG,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAoB,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ParsedApiError } from './types.js';
2
+ export declare function parseApiError(err: unknown): ParsedApiError;
3
+ //# sourceMappingURL=parseApiError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseApiError.d.ts","sourceRoot":"","sources":["../src/parseApiError.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAQ1D"}
@@ -0,0 +1,11 @@
1
+ export function parseApiError(err) {
2
+ if (typeof err !== 'object' || err === null)
3
+ return { status: 0 };
4
+ const e = err;
5
+ return {
6
+ status: e.status ?? 0,
7
+ reason: e.body?.reason,
8
+ message: e.body?.message,
9
+ };
10
+ }
11
+ //# sourceMappingURL=parseApiError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseApiError.js","sourceRoot":"","sources":["../src/parseApiError.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IAClE,MAAM,CAAC,GAAG,GAAwE,CAAC;IACnF,OAAO;QACL,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC;QACrB,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM;QACtB,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO;KACzB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ValueOrFn } from './types.js';
2
+ export declare function resolve<T>(v: ValueOrFn<T> | undefined, fallback: T): T;
3
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAGtE"}
@@ -0,0 +1,6 @@
1
+ export function resolve(v, fallback) {
2
+ if (v === undefined)
3
+ return fallback;
4
+ return typeof v === 'function' ? v() : v;
5
+ }
6
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,OAAO,CAAI,CAA2B,EAAE,QAAW;IACjE,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IACrC,OAAO,OAAO,CAAC,KAAK,UAAU,CAAC,CAAC,CAAE,CAAa,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ApiErrorContext, ErrorRoutesRegistry } from './types.js';
2
+ export declare function routeApiError<RouteId extends string>(err: unknown, ctx: ApiErrorContext<RouteId>, registry: ErrorRoutesRegistry<RouteId>): void;
3
+ //# sourceMappingURL=routeApiError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeApiError.d.ts","sourceRoot":"","sources":["../src/routeApiError.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEvE,wBAAgB,aAAa,CAAC,OAAO,SAAS,MAAM,EAClD,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,eAAe,CAAC,OAAO,CAAC,EAC7B,QAAQ,EAAE,mBAAmB,CAAC,OAAO,CAAC,GACrC,IAAI,CAiBN"}
@@ -0,0 +1,19 @@
1
+ import { parseApiError } from './parseApiError.js';
2
+ export function routeApiError(err, ctx, registry) {
3
+ const parsed = parseApiError(err);
4
+ const routeMap = registry[ctx.route];
5
+ const entry = parsed.reason ? routeMap?.[parsed.reason] : undefined;
6
+ const text = entry?.i18nKey
7
+ ? ctx.t(entry.i18nKey)
8
+ : (parsed.message ?? ctx.t('common.error'));
9
+ if (entry?.field && ctx.form) {
10
+ ctx.form.setFields([{ name: entry.field, errors: [text] }]);
11
+ return;
12
+ }
13
+ if (ctx.setInlineError) {
14
+ ctx.setInlineError(text);
15
+ return;
16
+ }
17
+ (ctx.showError ?? (() => { }))(text);
18
+ }
19
+ //# sourceMappingURL=routeApiError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeApiError.js","sourceRoot":"","sources":["../src/routeApiError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,MAAM,UAAU,aAAa,CAC3B,GAAY,EACZ,GAA6B,EAC7B,QAAsC;IAEtC,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,MAAM,IAAI,GAAG,KAAK,EAAE,OAAO;QACzB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;QACtB,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;IAE9C,IAAI,KAAK,EAAE,KAAK,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QAC7B,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5D,OAAO;IACT,CAAC;IACD,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;QACvB,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QACzB,OAAO;IACT,CAAC;IACD,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC"}
@@ -0,0 +1,80 @@
1
+ import type React from 'react';
2
+ import type { MessageSink } from './MessageSink.js';
3
+ /**
4
+ * Minimal structural form contract used by error-routing (antd's FormInstance is
5
+ * assignable to this). Keeps vultus-core antd-free — see AGENTS.md.
6
+ */
7
+ export interface FormLike {
8
+ setFields(fields: {
9
+ name: string | number | (string | number)[];
10
+ errors: string[];
11
+ }[]): void;
12
+ }
13
+ export type Decision = boolean | {
14
+ verdict: boolean;
15
+ reason?: string;
16
+ };
17
+ export type ValueOrFn<T> = T | (() => T);
18
+ export type ConfirmationSpec = {
19
+ kind: 'popconfirm';
20
+ question: string;
21
+ } | {
22
+ kind: 'cascade-modal';
23
+ title: string;
24
+ content: React.ReactNode;
25
+ } | {
26
+ kind: 'typing';
27
+ title: string;
28
+ entityName: string;
29
+ description: React.ReactNode;
30
+ };
31
+ export interface ActionOptions<TArgs = void, TResult = void, RouteId extends string = string> {
32
+ id: string;
33
+ label: ValueOrFn<string>;
34
+ icon?: ValueOrFn<React.ReactNode>;
35
+ variant?: ValueOrFn<'default' | 'primary' | 'danger'>;
36
+ enabled?: ValueOrFn<Decision>;
37
+ invisible?: ValueOrFn<boolean>;
38
+ confirmation?: ValueOrFn<ConfirmationSpec | undefined>;
39
+ errorRoute?: RouteId;
40
+ fire: (args: TArgs) => Promise<TResult> | TResult;
41
+ onSuccess?: (result: TResult) => void;
42
+ }
43
+ type FireArgs<TArgs> = [TArgs] extends [void] ? [] : [args: TArgs];
44
+ export interface ActionStatus<TArgs = void> {
45
+ id: string;
46
+ label: string;
47
+ icon?: React.ReactNode;
48
+ variant: 'default' | 'primary' | 'danger';
49
+ pending: boolean;
50
+ disabled: boolean;
51
+ reason?: string;
52
+ invisible: boolean;
53
+ confirmation?: ConfirmationSpec;
54
+ errors: string[];
55
+ fire(...args: FireArgs<TArgs>): void;
56
+ firePromise(...args: FireArgs<TArgs>): Promise<void>;
57
+ }
58
+ export type ParsedApiError = {
59
+ status: number;
60
+ reason?: string;
61
+ message?: string;
62
+ };
63
+ export type RegistryEntry = {
64
+ i18nKey: string;
65
+ field?: string;
66
+ };
67
+ export type ErrorRoutesRegistry<RouteId extends string> = Record<RouteId, Record<string, RegistryEntry>>;
68
+ export type ApiErrorContext<RouteId extends string> = {
69
+ route: RouteId;
70
+ form?: FormLike;
71
+ setInlineError?: (text: string | null) => void;
72
+ t: (key: string) => string;
73
+ showError?: MessageSink;
74
+ };
75
+ export type ActionErrorCtx = {
76
+ form: FormLike;
77
+ setInlineError: (text: string | null) => void;
78
+ };
79
+ export {};
80
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ import type { ActionOptions, ActionStatus, ErrorRoutesRegistry } from './types.js';
2
+ export declare function useAction<TArgs = void, TResult = void, RouteId extends string = string>(opts: ActionOptions<TArgs, TResult, RouteId>, registry?: ErrorRoutesRegistry<RouteId>): ActionStatus<TArgs>;
3
+ //# sourceMappingURL=useAction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAction.d.ts","sourceRoot":"","sources":["../src/useAction.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEnF,wBAAgB,SAAS,CAAC,KAAK,GAAG,IAAI,EAAE,OAAO,GAAG,IAAI,EAAE,OAAO,SAAS,MAAM,GAAG,MAAM,EACrF,IAAI,EAAE,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAC5C,QAAQ,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,GACtC,YAAY,CAAC,KAAK,CAAC,CAkDrB"}
@@ -0,0 +1,47 @@
1
+ import { useMemo, useRef, useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { useActionErrorCtx } from './useActionErrorCtx.js';
4
+ import { useMessageSink } from './MessageSink.js';
5
+ import { resolveActionFields, makeFirePromise, assembleStatus } from './action-core.js';
6
+ export function useAction(opts, registry) {
7
+ const errCtx = useActionErrorCtx();
8
+ const errCtxRef = useRef(errCtx);
9
+ errCtxRef.current = errCtx;
10
+ const { t } = useTranslation();
11
+ const messageSink = useMessageSink();
12
+ const [pending, setPending] = useState(false);
13
+ const [errors, setErrors] = useState([]);
14
+ const pendingRef = useRef(pending);
15
+ pendingRef.current = pending;
16
+ const fields = resolveActionFields(opts);
17
+ const firePromise = useMemo(() => makeFirePromise(opts, {
18
+ disabled: fields.disabled,
19
+ reason: fields.reason,
20
+ getPending: () => pendingRef.current,
21
+ setPending,
22
+ setErrors,
23
+ errCtxRef,
24
+ t,
25
+ registry,
26
+ messageSink,
27
+ }), [opts, fields.disabled, fields.reason, registry, t, messageSink]);
28
+ // Granular deps by design: memo invalidates on the specific opts/fields
29
+ // primitives below, not on the whole `opts`/`fields` objects (which are
30
+ // fresh each render and would defeat the memo).
31
+ return useMemo(() => assembleStatus(opts, fields, pending, errors, firePromise),
32
+ // eslint-disable-next-line react-hooks/exhaustive-deps
33
+ [
34
+ opts.id,
35
+ fields.label,
36
+ fields.icon,
37
+ fields.variant,
38
+ pending,
39
+ fields.disabled,
40
+ fields.reason,
41
+ fields.invisible,
42
+ fields.confirmation,
43
+ errors,
44
+ firePromise,
45
+ ]);
46
+ }
47
+ //# sourceMappingURL=useAction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAction.js","sourceRoot":"","sources":["../src/useAction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGxF,MAAM,UAAU,SAAS,CACvB,IAA4C,EAC5C,QAAuC;IAEvC,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;IAC3B,MAAM,EAAE,CAAC,EAAE,GAAG,cAAc,EAAE,CAAC;IAC/B,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAW,EAAE,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAEzC,MAAM,WAAW,GAAG,OAAO,CACzB,GAAG,EAAE,CACH,eAAe,CAAC,IAAI,EAAE;QACpB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,UAAU,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO;QACpC,UAAU;QACV,SAAS;QACT,SAAS;QACT,CAAC;QACD,QAAQ;QACR,WAAW;KACZ,CAAC,EACJ,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,WAAW,CAAC,CACjE,CAAC;IAEF,wEAAwE;IACxE,wEAAwE;IACxE,gDAAgD;IAChD,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC;IAChE,uDAAuD;IACvD;QACE,IAAI,CAAC,EAAE;QACP,MAAM,CAAC,KAAK;QACZ,MAAM,CAAC,IAAI;QACX,MAAM,CAAC,OAAO;QACd,OAAO;QACP,MAAM,CAAC,QAAQ;QACf,MAAM,CAAC,MAAM;QACb,MAAM,CAAC,SAAS;QAChB,MAAM,CAAC,YAAY;QACnB,MAAM;QACN,WAAW;KACZ,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ActionErrorCtx } from './types.js';
2
+ export declare const ActionErrorContext: import("react").Context<ActionErrorCtx | null>;
3
+ export declare function useActionErrorCtx(): ActionErrorCtx | null;
4
+ //# sourceMappingURL=useActionErrorCtx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useActionErrorCtx.d.ts","sourceRoot":"","sources":["../src/useActionErrorCtx.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,eAAO,MAAM,kBAAkB,gDAA6C,CAAC;AAE7E,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,IAAI,CAEzD"}
@@ -0,0 +1,6 @@
1
+ import { createContext, useContext } from 'react';
2
+ export const ActionErrorContext = createContext(null);
3
+ export function useActionErrorCtx() {
4
+ return useContext(ActionErrorContext);
5
+ }
6
+ //# sourceMappingURL=useActionErrorCtx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useActionErrorCtx.js","sourceRoot":"","sources":["../src/useActionErrorCtx.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAGlD,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAa,CAAwB,IAAI,CAAC,CAAC;AAE7E,MAAM,UAAU,iBAAiB;IAC/B,OAAO,UAAU,CAAC,kBAAkB,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ActionOptions, ActionStatus, ErrorRoutesRegistry } from './types.js';
2
+ /**
3
+ * Build a DYNAMIC list of actions from a spec array — the list counterpart to
4
+ * useAction. The hook count is fixed regardless of list length (one shared
5
+ * error context, one pending/errors map, one memo), so it is usable where
6
+ * useAction — a per-call hook — cannot be looped (rules of hooks). Pending and
7
+ * errors are tracked per action id in the shared maps; each action gets real
8
+ * per-item pending. The firing machinery + ActionStatus shape are the exact
9
+ * same shared helpers useAction uses (see action-core.ts) — nothing is
10
+ * reimplemented.
11
+ */
12
+ export declare function useActionList<TArgs = void, TResult = void, RouteId extends string = string>(specs: ActionOptions<TArgs, TResult, RouteId>[], registry?: ErrorRoutesRegistry<RouteId>): ActionStatus<TArgs>[];
13
+ //# sourceMappingURL=useActionList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useActionList.d.ts","sourceRoot":"","sources":["../src/useActionList.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEnF;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAG,IAAI,EAAE,OAAO,GAAG,IAAI,EAAE,OAAO,SAAS,MAAM,GAAG,MAAM,EACzF,KAAK,EAAE,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,EAC/C,QAAQ,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,GACtC,YAAY,CAAC,KAAK,CAAC,EAAE,CAqCvB"}
@@ -0,0 +1,42 @@
1
+ import { useMemo, useRef, useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { useActionErrorCtx } from './useActionErrorCtx.js';
4
+ import { useMessageSink } from './MessageSink.js';
5
+ import { resolveActionFields, makeFirePromise, assembleStatus } from './action-core.js';
6
+ /**
7
+ * Build a DYNAMIC list of actions from a spec array — the list counterpart to
8
+ * useAction. The hook count is fixed regardless of list length (one shared
9
+ * error context, one pending/errors map, one memo), so it is usable where
10
+ * useAction — a per-call hook — cannot be looped (rules of hooks). Pending and
11
+ * errors are tracked per action id in the shared maps; each action gets real
12
+ * per-item pending. The firing machinery + ActionStatus shape are the exact
13
+ * same shared helpers useAction uses (see action-core.ts) — nothing is
14
+ * reimplemented.
15
+ */
16
+ export function useActionList(specs, registry) {
17
+ const errCtx = useActionErrorCtx();
18
+ const errCtxRef = useRef(errCtx);
19
+ errCtxRef.current = errCtx;
20
+ const { t } = useTranslation();
21
+ const messageSink = useMessageSink();
22
+ const [pendingMap, setPendingMap] = useState({});
23
+ const [errorsMap, setErrorsMap] = useState({});
24
+ const pendingRef = useRef(pendingMap);
25
+ pendingRef.current = pendingMap;
26
+ return useMemo(() => specs.map((opts) => {
27
+ const fields = resolveActionFields(opts);
28
+ const firePromise = makeFirePromise(opts, {
29
+ disabled: fields.disabled,
30
+ reason: fields.reason,
31
+ getPending: () => !!pendingRef.current[opts.id],
32
+ setPending: (value) => setPendingMap((m) => ({ ...m, [opts.id]: value })),
33
+ setErrors: (errs) => setErrorsMap((m) => ({ ...m, [opts.id]: errs })),
34
+ errCtxRef,
35
+ t,
36
+ registry,
37
+ messageSink,
38
+ });
39
+ return assembleStatus(opts, fields, !!pendingMap[opts.id], errorsMap[opts.id] ?? [], firePromise);
40
+ }), [specs, pendingMap, errorsMap, registry, t, messageSink]);
41
+ }
42
+ //# sourceMappingURL=useActionList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useActionList.js","sourceRoot":"","sources":["../src/useActionList.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGxF;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAC3B,KAA+C,EAC/C,QAAuC;IAEvC,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;IAC3B,MAAM,EAAE,CAAC,EAAE,GAAG,cAAc,EAAE,CAAC;IAC/B,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAA0B,EAAE,CAAC,CAAC;IAC1E,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAA2B,EAAE,CAAC,CAAC;IACzE,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IACtC,UAAU,CAAC,OAAO,GAAG,UAAU,CAAC;IAEhC,OAAO,OAAO,CACZ,GAAG,EAAE,CACH,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,EAAE;YACxC,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/C,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YACzE,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;YACrE,SAAS;YACT,CAAC;YACD,QAAQ;YACR,WAAW;SACZ,CAAC,CAAC;QACH,OAAO,cAAc,CACnB,IAAI,EACJ,MAAM,EACN,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,EACrB,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,EACxB,WAAW,CACZ,CAAC;IACJ,CAAC,CAAC,EACJ,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,EAAE,WAAW,CAAC,CACzD,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vultus-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Framework-neutral action model + hooks for vultus (antd-free by design)",
6
6
  "repository": {
@@ -11,16 +11,29 @@
11
11
  "author": "Kristof Csillag <kristof.csillag@deai-labs.com>",
12
12
  "type": "module",
13
13
  "exports": {
14
- ".": "./src/index.ts",
15
- "./link": "./src/LinkContext.tsx"
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js",
17
+ "default": "./dist/index.js"
18
+ },
19
+ "./link": {
20
+ "types": "./dist/LinkContext.d.ts",
21
+ "import": "./dist/LinkContext.js",
22
+ "default": "./dist/LinkContext.js"
23
+ },
24
+ "./package.json": "./package.json"
16
25
  },
17
26
  "files": [
27
+ "dist",
18
28
  "src",
19
29
  "!src/**/__tests__",
20
30
  "!src/**/*.test.ts",
21
31
  "!src/**/*.test.tsx",
22
32
  "README.md"
23
33
  ],
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
24
37
  "peerDependencies": {
25
38
  "react": ">=18",
26
39
  "react-i18next": ">=15"
@@ -39,7 +52,9 @@
39
52
  "vitest": "^3.2.4"
40
53
  },
41
54
  "scripts": {
42
- "build": "tsc",
55
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
43
56
  "test": "vitest run"
44
- }
57
+ },
58
+ "main": "./dist/index.js",
59
+ "types": "./dist/index.d.ts"
45
60
  }