@azure-net/kit 3.0.3 → 3.0.5

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.
@@ -1,15 +1,23 @@
1
1
  import type { RequestErrors } from '../../delivery/schema/index.js';
2
2
  import type { AsyncActionResponse } from '../../index.js';
3
- type InitialData<FormData> = Partial<FormData> | Promise<Partial<FormData>> | (() => Partial<FormData> | Promise<Partial<FormData>>);
4
- export interface FormConfig<FormData, Response> {
5
- initialData?: InitialData<FormData>;
3
+ type InitialData<FormData, Initial extends Partial<FormData>> = Initial | Promise<Initial> | (() => Initial | Promise<Initial>);
4
+ type PathRequiredShape<T, P extends string> = P extends `${infer Head}.${infer Tail}` ? Head extends keyof T ? {
5
+ [K in Head]-?: PathRequiredShape<NonNullable<T[K]>, Tail>;
6
+ } : unknown : P extends keyof T ? {
7
+ [K in P]-?: NonNullable<T[K]>;
8
+ } : unknown;
9
+ type UnionToIntersection<U> = (U extends unknown ? (arg: U) => void : never) extends (arg: infer I) => void ? I : never;
10
+ type RequiredByPaths<T, P extends string> = [P] extends [never] ? {} : UnionToIntersection<PathRequiredShape<T, P>>;
11
+ export interface FormConfig<FormData, Response, Initial extends Partial<FormData> = Partial<FormData>, RequiredPath extends string = never> {
12
+ initialData?: InitialData<FormData, Initial>;
13
+ required?: readonly RequiredPath[];
6
14
  onSuccess?: (response: Response) => Promise<void> | void;
7
15
  onError?: () => Promise<void> | void;
8
- beforeSubmit?: (form: ActiveFormController<FormData>, abort: () => void) => Promise<void> | void;
16
+ beforeSubmit?: (form: ActiveFormController<FormData, RequiredPath>, abort: () => void) => Promise<void> | void;
9
17
  waitForInitialData?: boolean;
10
18
  }
11
- export interface ActiveForm<FormData, Response, Custom> {
12
- data: Partial<FormData>;
19
+ export interface ActiveForm<FormData, Response, Custom, RequiredPath extends string = never> {
20
+ data: Partial<FormData> & RequiredByPaths<FormData, RequiredPath>;
13
21
  errors: RequestErrors<FormData>;
14
22
  submit: () => Promise<AsyncActionResponse<Response, FormData, Custom>>;
15
23
  reset: (toInitial?: boolean) => void;
@@ -17,8 +25,8 @@ export interface ActiveForm<FormData, Response, Custom> {
17
25
  dirty: boolean;
18
26
  ready: Promise<Partial<FormData>>;
19
27
  }
20
- export interface ActiveFormController<FormData> {
21
- data: Partial<FormData>;
28
+ export interface ActiveFormController<FormData, RequiredPath extends string = never> {
29
+ data: Partial<FormData> & RequiredByPaths<FormData, RequiredPath>;
22
30
  errors: RequestErrors<FormData>;
23
31
  reset: (toInitial?: boolean) => void;
24
32
  }
@@ -31,5 +39,5 @@ type ExtractFromSubmit<T> = {
31
39
  formData: ExtractFormData<UnwrapPromise<T>>;
32
40
  custom: ExtractCustom<UnwrapPromise<T>>;
33
41
  };
34
- export declare const createActiveForm: <SubmitReturn extends Promise<AsyncActionResponse<unknown, unknown, unknown>>>(onSubmit: (formData: Partial<ExtractFromSubmit<SubmitReturn>["formData"]>) => SubmitReturn, config?: FormConfig<ExtractFromSubmit<SubmitReturn>["formData"], ExtractFromSubmit<SubmitReturn>["response"]>) => ActiveForm<ExtractFromSubmit<SubmitReturn>["formData"], ExtractFromSubmit<SubmitReturn>["response"], ExtractFromSubmit<SubmitReturn>["custom"]>;
42
+ export declare const createActiveForm: <SubmitReturn extends Promise<AsyncActionResponse<unknown, unknown, unknown>>, RequiredPath extends string = never>(onSubmit: (formData: Partial<ExtractFromSubmit<SubmitReturn>["formData"]>) => SubmitReturn, config?: FormConfig<ExtractFromSubmit<SubmitReturn>["formData"], ExtractFromSubmit<SubmitReturn>["response"], Partial<ExtractFromSubmit<SubmitReturn>["formData"]>, RequiredPath>) => ActiveForm<ExtractFromSubmit<SubmitReturn>["formData"], ExtractFromSubmit<SubmitReturn>["response"], ExtractFromSubmit<SubmitReturn>["custom"], RequiredPath>;
35
43
  export {};
@@ -1,9 +1,14 @@
1
1
  export type AsyncStatus = 'idle' | 'pending' | 'success' | 'error';
2
+ export type AsyncSignalSource = 'auto' | 'manual' | 'global';
2
3
  export interface AsyncSignalOptions<TData, TError = Error> {
3
4
  server?: boolean;
4
5
  immediate?: boolean;
5
6
  watch?: (() => unknown)[];
6
- initialData?: TData;
7
+ initialData?: TData | (() => TData);
8
+ beforeSend?: (meta: {
9
+ initial: boolean;
10
+ source: AsyncSignalSource;
11
+ }) => void | Promise<void>;
7
12
  onSuccess?: (data: TData) => void | Promise<void>;
8
13
  onError?: (error: TError) => void | Promise<void>;
9
14
  key?: string;
@@ -25,7 +25,7 @@ const createAsyncSignalManager = () => {
25
25
  if (instances) {
26
26
  const instance = instances.get(key);
27
27
  try {
28
- await instance?.();
28
+ await instance?.('global');
29
29
  }
30
30
  catch {
31
31
  return;
@@ -35,7 +35,7 @@ const createAsyncSignalManager = () => {
35
35
  const refreshAll = async () => {
36
36
  if (instances) {
37
37
  try {
38
- await Promise.all(instances.values().map((val) => val()));
38
+ await Promise.all(instances.values().map((val) => val('global')));
39
39
  }
40
40
  catch {
41
41
  return;
@@ -47,18 +47,25 @@ const createAsyncSignalManager = () => {
47
47
  const asyncSignalManager = createAsyncSignalManager();
48
48
  export const createAsyncSignal = (handler, options = {}) => {
49
49
  const { server = false, immediate = true, watch = [], initialData = undefined, key } = options;
50
- let data = $state(initialData);
50
+ const resolvedInitialData = typeof initialData === 'function' ? initialData() : initialData;
51
+ let data = $state(resolvedInitialData);
51
52
  let error = $state();
52
53
  let status = $state('idle');
53
54
  const pending = $derived(status === 'pending');
54
55
  let abortController = null;
55
56
  let currentPromise = null;
56
57
  let currentRunId = 0;
57
- const run = async (runId) => {
58
+ let started = false;
59
+ const run = async (runId, source) => {
60
+ const initial = !started;
61
+ started = true;
58
62
  if (abortController) {
59
63
  abortController.abort();
60
64
  }
61
65
  abortController = new AbortController();
66
+ if (options.beforeSend) {
67
+ await options.beforeSend({ initial, source });
68
+ }
62
69
  status = 'pending';
63
70
  error = undefined;
64
71
  try {
@@ -90,22 +97,27 @@ export const createAsyncSignal = (handler, options = {}) => {
90
97
  }
91
98
  }
92
99
  };
93
- const start = () => {
100
+ const start = (source) => {
101
+ if (currentPromise) {
102
+ return currentPromise;
103
+ }
94
104
  const runId = ++currentRunId;
95
- const localPromise = run(runId);
105
+ const localPromise = run(runId, source);
96
106
  currentPromise = localPromise;
97
107
  return localPromise;
98
108
  };
99
109
  const execute = async () => {
100
- if (status === 'pending' && currentPromise) {
110
+ if (currentPromise) {
101
111
  await currentPromise;
102
112
  return;
103
113
  }
104
- await start();
114
+ await start('manual');
105
115
  };
106
116
  if (EnvironmentUtil.isBrowser) {
107
117
  const signalKey = key ?? asyncSignalManager.generateKey();
108
- asyncSignalManager.register(signalKey, () => execute());
118
+ asyncSignalManager.register(signalKey, (source) => {
119
+ return start(source);
120
+ });
109
121
  $effect(() => {
110
122
  return () => {
111
123
  asyncSignalManager.unregister(signalKey);
@@ -120,7 +132,7 @@ export const createAsyncSignal = (handler, options = {}) => {
120
132
  return;
121
133
  }
122
134
  if (!isFirst) {
123
- void execute();
135
+ void start('auto');
124
136
  }
125
137
  isFirst = false;
126
138
  });
@@ -129,11 +141,11 @@ export const createAsyncSignal = (handler, options = {}) => {
129
141
  if (immediate) {
130
142
  if (EnvironmentUtil.isServer && server) {
131
143
  untrack(() => {
132
- void execute();
144
+ void start('auto');
133
145
  });
134
146
  }
135
147
  else if (EnvironmentUtil.isBrowser) {
136
- void execute();
148
+ void start('auto');
137
149
  }
138
150
  }
139
151
  return {
@@ -152,7 +164,7 @@ export const createAsyncSignal = (handler, options = {}) => {
152
164
  get ready() {
153
165
  if (currentPromise)
154
166
  return currentPromise;
155
- return start();
167
+ return start('auto');
156
168
  },
157
169
  execute,
158
170
  refresh: execute,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-net/kit",
3
- "version": "3.0.3",
3
+ "version": "3.0.5",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",