@azure-net/kit 5.0.4 → 5.1.0
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/dist/delivery/injectable-dependencies/AsyncHelpers.js +5 -3
- package/dist/delivery/schema/rules/Rules.js +9 -35
- package/dist/external/edges/_edges.d.ts +1 -1
- package/dist/external/edges/_edges.js +1 -1
- package/dist/infra/http-service/HttpServiceInstance.js +16 -8
- package/dist/plugin/AzureNetPlugin.d.ts +0 -2
- package/dist/plugin/AzureNetPlugin.js +43 -244
- package/dist/shared/app/App.d.ts +3 -5
- package/dist/shared/app/App.js +6 -16
- package/dist/shared/app/middleware/ClientMiddleware.js +12 -11
- package/dist/shared/boundary-provider/Provider.js +0 -8
- package/dist/svelte/active-form/ActiveForm.svelte.js +43 -23
- package/dist/svelte/async-signal/AsyncSignal.svelte.js +22 -14
- package/package.json +5 -14
|
@@ -2,17 +2,18 @@ import { createErrorHandler } from './ErrorHandler.js';
|
|
|
2
2
|
import {} from '../../shared/app-error/AppError.js';
|
|
3
3
|
import { BROWSER } from '../../external/tools/index.js';
|
|
4
4
|
import { RequestContext } from '../../external/edges/ServerContext.js';
|
|
5
|
-
const HANDLER_CONTEXT_KEY = '__azureNetAsyncHelperErrorParser__';
|
|
6
5
|
export class AsyncHelperError extends Error {
|
|
7
6
|
}
|
|
8
7
|
export const createAsyncHelpers = () => {
|
|
9
8
|
const defaultErrorParser = createErrorHandler();
|
|
9
|
+
const handlerContextKey = Symbol('azureNetAsyncHelperErrorParser');
|
|
10
10
|
let clientErrorParser;
|
|
11
11
|
const getErrorParser = () => {
|
|
12
12
|
if (BROWSER)
|
|
13
13
|
return clientErrorParser ?? defaultErrorParser;
|
|
14
14
|
try {
|
|
15
|
-
|
|
15
|
+
const data = RequestContext.current().data;
|
|
16
|
+
return data[handlerContextKey] ?? defaultErrorParser;
|
|
16
17
|
}
|
|
17
18
|
catch {
|
|
18
19
|
return defaultErrorParser;
|
|
@@ -24,7 +25,8 @@ export const createAsyncHelpers = () => {
|
|
|
24
25
|
clientErrorParser = parser;
|
|
25
26
|
return;
|
|
26
27
|
}
|
|
27
|
-
RequestContext.current().data
|
|
28
|
+
const data = RequestContext.current().data;
|
|
29
|
+
data[handlerContextKey] = parser;
|
|
28
30
|
};
|
|
29
31
|
const normalizeError = (err) => (err instanceof Error ? err : new Error(String(err)));
|
|
30
32
|
const prepareErrors = async (error, retry) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DateUtil } from '../../../external/tools/index.js';
|
|
1
|
+
import { DateUtil, ObjectUtil } from '../../../external/tools/index.js';
|
|
2
2
|
/**
|
|
3
3
|
*
|
|
4
4
|
* @param validationMessages - BaseValidationMessages
|
|
@@ -295,23 +295,10 @@ export const createRules = (validationMessages) => {
|
|
|
295
295
|
const sameAs = (params) => {
|
|
296
296
|
const { message, key } = { ...params, message: params?.message ?? validationMessages.sameAs };
|
|
297
297
|
return ({ val, listValues }) => {
|
|
298
|
-
if (checkVal(val))
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return message(String(key));
|
|
303
|
-
}
|
|
304
|
-
try {
|
|
305
|
-
return JSON.stringify(val) === JSON.stringify(listValues[key]) ? undefined : message(String(key));
|
|
306
|
-
}
|
|
307
|
-
catch {
|
|
308
|
-
return 'Parsing error';
|
|
309
|
-
}
|
|
310
|
-
default:
|
|
311
|
-
return String(val ?? '') === String(listValues?.[key] ?? '') ? undefined : message(String(key));
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
return undefined;
|
|
298
|
+
if (!checkVal(val))
|
|
299
|
+
return undefined;
|
|
300
|
+
const comparedValue = listValues?.[key];
|
|
301
|
+
return ObjectUtil.equals(val, comparedValue) ? undefined : message(String(key));
|
|
315
302
|
};
|
|
316
303
|
};
|
|
317
304
|
/**
|
|
@@ -323,23 +310,10 @@ export const createRules = (validationMessages) => {
|
|
|
323
310
|
const notSameAs = (params) => {
|
|
324
311
|
const { message, key } = { ...params, message: params?.message ?? validationMessages.notSameAs };
|
|
325
312
|
return ({ val, listValues }) => {
|
|
326
|
-
if (checkVal(val))
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
return message(String(key));
|
|
331
|
-
}
|
|
332
|
-
try {
|
|
333
|
-
return JSON.stringify(val) !== JSON.stringify(listValues[key]) ? undefined : message(String(key));
|
|
334
|
-
}
|
|
335
|
-
catch {
|
|
336
|
-
return 'Parsing error';
|
|
337
|
-
}
|
|
338
|
-
default:
|
|
339
|
-
return String(val) === String(listValues?.[key]) ? undefined : message(String(key));
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
return undefined;
|
|
313
|
+
if (!checkVal(val))
|
|
314
|
+
return undefined;
|
|
315
|
+
const comparedValue = listValues?.[key];
|
|
316
|
+
return ObjectUtil.equals(val, comparedValue) ? message(String(key)) : undefined;
|
|
343
317
|
};
|
|
344
318
|
};
|
|
345
319
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { edgesHandle,
|
|
1
|
+
export { edgesHandle, edgesHandleRaw } from '@azure-net/edges/server';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { edgesHandle,
|
|
1
|
+
export { edgesHandle, edgesHandleRaw } from '@azure-net/edges/server';
|
|
@@ -193,7 +193,13 @@ const withTimeout = (signal, timeout) => {
|
|
|
193
193
|
};
|
|
194
194
|
};
|
|
195
195
|
const shouldRetryError = (error) => {
|
|
196
|
-
|
|
196
|
+
if (error instanceof Error && error.name === 'AbortError')
|
|
197
|
+
return false;
|
|
198
|
+
if (error instanceof TypeError)
|
|
199
|
+
return true;
|
|
200
|
+
if (!(error instanceof HttpServiceError) || error.type !== HttpErrorTypes.External)
|
|
201
|
+
return false;
|
|
202
|
+
return error.status === 408 || error.status === 425 || error.status === 429 || error.status >= 500;
|
|
197
203
|
};
|
|
198
204
|
const normalizeUnknownError = (error) => {
|
|
199
205
|
if (error instanceof Error)
|
|
@@ -202,14 +208,16 @@ const normalizeUnknownError = (error) => {
|
|
|
202
208
|
};
|
|
203
209
|
const parseResponse = async (response, request) => {
|
|
204
210
|
const raw = request.includeRaw && typeof response?.clone === 'function' ? response?.clone() : undefined;
|
|
205
|
-
|
|
211
|
+
const hasNoBody = request.method === HttpInstanceFetchMethods.HEAD || response.status === 204 || response.status === 205;
|
|
212
|
+
let data = undefined;
|
|
206
213
|
let parseError;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
214
|
+
if (!hasNoBody) {
|
|
215
|
+
try {
|
|
216
|
+
data = await parseBodyByFormat(response, request.responseFormat);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
parseError = normalizeUnknownError(error);
|
|
220
|
+
}
|
|
213
221
|
}
|
|
214
222
|
if (!response.ok || parseError) {
|
|
215
223
|
const message = !response.ok
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
2
|
export interface AzureNetPluginOptions {
|
|
3
3
|
silentChromeDevtools?: boolean;
|
|
4
|
-
syncFromServer?: boolean;
|
|
5
|
-
syncTransformMode?: 'ast' | 'regex' | 'hybrid';
|
|
6
4
|
}
|
|
7
5
|
export declare const AzureNetPlugin: (options?: AzureNetPluginOptions) => Plugin;
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
|
-
import ts from 'typescript';
|
|
4
3
|
const VIRTUAL_SERVER_UTILS_ID = 'virtual:azure-net-kit/server-utils';
|
|
5
|
-
const VIRTUAL_UNIVERSAL_UTILS_ID = 'virtual:azure-net-kit/universal-utils';
|
|
6
4
|
const RESOLVED_VIRTUAL_SERVER_UTILS_ID = `\0${VIRTUAL_SERVER_UTILS_ID}`;
|
|
7
|
-
const RESOLVED_VIRTUAL_UNIVERSAL_UTILS_ID = `\0${VIRTUAL_UNIVERSAL_UTILS_ID}`;
|
|
8
5
|
const VIRTUAL_SERVER_HOOKS_ID = 'virtual:azure-net-kit/hooks.server';
|
|
9
6
|
const VIRTUAL_CLIENT_HOOKS_ID = 'virtual:azure-net-kit/hooks.client';
|
|
10
7
|
const RESOLVED_VIRTUAL_SERVER_HOOKS_ID = `\0${VIRTUAL_SERVER_HOOKS_ID}`;
|
|
@@ -12,33 +9,6 @@ const RESOLVED_VIRTUAL_CLIENT_HOOKS_ID = `\0${VIRTUAL_CLIENT_HOOKS_ID}`;
|
|
|
12
9
|
const VIRTUAL_PROGRAM_ID = 'virtual:azure-net-kit/program';
|
|
13
10
|
const RESOLVED_VIRTUAL_PROGRAM_ID = `\0${VIRTUAL_PROGRAM_ID}`;
|
|
14
11
|
const GENERATED_MARKER = '__AZURE_NET_KIT_VIRTUAL_HOOK__';
|
|
15
|
-
const SYNC_MARKER = "void '__EDGES_SYNC_WRAPPED__';";
|
|
16
|
-
const LOAD_EXPORT_PATTERN = /export\s+const\s+load\s*(?::\s*[^=]+)?=/;
|
|
17
|
-
const ACTIONS_EXPORT_PATTERN = /export\s+const\s+actions\s*(?::\s*[^=]+)?=/;
|
|
18
|
-
const SERVER_ROUTE_PATTERN = /[\\/]\+((page|layout)\.server)\.(t|j)s$/;
|
|
19
|
-
const UNIVERSAL_ROUTE_PATTERN = /[\\/]\+((page|layout))\.(t|j)s$/;
|
|
20
|
-
const AST_SERVER_LOAD_ALIAS = '__edgesWrappedServerLoad';
|
|
21
|
-
const AST_ACTIONS_ALIAS = '__edgesWrappedActions';
|
|
22
|
-
const AST_UNIVERSAL_LOAD_ALIAS = '__edgesWrappedUniversalLoad';
|
|
23
|
-
const applyEdits = (sourceCode, edits) => {
|
|
24
|
-
if (edits.length === 0)
|
|
25
|
-
return sourceCode;
|
|
26
|
-
const sorted = edits.sort((a, b) => b.start - a.start);
|
|
27
|
-
let result = sourceCode;
|
|
28
|
-
for (const edit of sorted) {
|
|
29
|
-
result = result.slice(0, edit.start) + edit.text + result.slice(edit.end);
|
|
30
|
-
}
|
|
31
|
-
return result;
|
|
32
|
-
};
|
|
33
|
-
const findImportInsertPosition = (sourceCode) => {
|
|
34
|
-
const importRegex = /(?:^|\n)((?:import|export)\s+(?:type\s+)?(?:\{[^}]*\}|\*|\w+)(?:\s+from)?\s+['"][^'"]+['"];?)/gm;
|
|
35
|
-
let lastMatch = null;
|
|
36
|
-
let match;
|
|
37
|
-
while ((match = importRegex.exec(sourceCode)) !== null) {
|
|
38
|
-
lastMatch = match;
|
|
39
|
-
}
|
|
40
|
-
return lastMatch ? lastMatch.index + lastMatch[0].length : 0;
|
|
41
|
-
};
|
|
42
12
|
const findStaticImportInsertPosition = (sourceCode) => {
|
|
43
13
|
const importRegex = /(?:^|\n)(import\s+(?:type\s+)?(?:\{[^}]*\}|\*|\w+)(?:\s+from)?\s+['"][^'"]+['"];?)/gm;
|
|
44
14
|
let lastMatch = null;
|
|
@@ -48,145 +18,15 @@ const findStaticImportInsertPosition = (sourceCode) => {
|
|
|
48
18
|
}
|
|
49
19
|
return lastMatch ? lastMatch.index + lastMatch[0].length : 0;
|
|
50
20
|
};
|
|
51
|
-
const ensureSyncImport = (sourceCode, importLine) => {
|
|
52
|
-
if (sourceCode.includes('__EDGES_SYNC_WRAPPED__'))
|
|
53
|
-
return sourceCode;
|
|
54
|
-
const insertPos = findImportInsertPosition(sourceCode);
|
|
55
|
-
const beforeImports = sourceCode.slice(0, insertPos);
|
|
56
|
-
const afterImports = sourceCode.slice(insertPos);
|
|
57
|
-
return `${beforeImports}\n${SYNC_MARKER}\n${importLine}\n${afterImports}`;
|
|
58
|
-
};
|
|
59
|
-
const ensureAstServerImport = (sourceCode) => ensureSyncImport(sourceCode, `import { __withEdgesServerLoad as ${AST_SERVER_LOAD_ALIAS}, __withEdgesActions as ${AST_ACTIONS_ALIAS} } from '${VIRTUAL_SERVER_UTILS_ID}';`);
|
|
60
|
-
const ensureAstUniversalImport = (sourceCode) => ensureSyncImport(sourceCode, `import { __withEdgesUniversalLoad as ${AST_UNIVERSAL_LOAD_ALIAS} } from '${VIRTUAL_UNIVERSAL_UTILS_ID}';`);
|
|
61
|
-
const ensureRegexServerImport = (sourceCode) => ensureSyncImport(sourceCode, `import { __withEdgesServerLoad, __withEdgesActions } from '${VIRTUAL_SERVER_UTILS_ID}';`);
|
|
62
|
-
const ensureRegexUniversalImport = (sourceCode) => ensureSyncImport(sourceCode, `import { __withEdgesUniversalLoad } from '${VIRTUAL_UNIVERSAL_UTILS_ID}';`);
|
|
63
|
-
const findExportedLocal = (sourceFile, code, exportedName) => {
|
|
64
|
-
const edits = [];
|
|
65
|
-
let localName = null;
|
|
66
|
-
let found = false;
|
|
67
|
-
for (const stmt of sourceFile.statements) {
|
|
68
|
-
if (ts.isVariableStatement(stmt)) {
|
|
69
|
-
const exportModifier = stmt.modifiers?.find((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
|
|
70
|
-
if (!exportModifier)
|
|
71
|
-
continue;
|
|
72
|
-
for (const declaration of stmt.declarationList.declarations) {
|
|
73
|
-
if (!ts.isIdentifier(declaration.name) || declaration.name.text !== exportedName)
|
|
74
|
-
continue;
|
|
75
|
-
localName = declaration.name.text;
|
|
76
|
-
found = true;
|
|
77
|
-
const modifierStart = exportModifier.getStart(sourceFile);
|
|
78
|
-
let modifierEnd = exportModifier.end;
|
|
79
|
-
while (modifierEnd < code.length && /\s/.test(code[modifierEnd]))
|
|
80
|
-
modifierEnd += 1;
|
|
81
|
-
edits.push({ start: modifierStart, end: modifierEnd, text: '' });
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
if (!ts.isExportDeclaration(stmt) || !stmt.exportClause || !ts.isNamedExports(stmt.exportClause) || stmt.moduleSpecifier)
|
|
86
|
-
continue;
|
|
87
|
-
const keepSpecs = [];
|
|
88
|
-
let statementHasTarget = false;
|
|
89
|
-
for (const element of stmt.exportClause.elements) {
|
|
90
|
-
const exportName = element.name.text;
|
|
91
|
-
const sourceName = element.propertyName?.text ?? element.name.text;
|
|
92
|
-
if (exportName === exportedName) {
|
|
93
|
-
statementHasTarget = true;
|
|
94
|
-
found = true;
|
|
95
|
-
localName = sourceName;
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
keepSpecs.push(code.slice(element.getStart(sourceFile), element.end).trim());
|
|
99
|
-
}
|
|
100
|
-
if (!statementHasTarget)
|
|
101
|
-
continue;
|
|
102
|
-
if (keepSpecs.length === 0) {
|
|
103
|
-
edits.push({ start: stmt.getStart(sourceFile), end: stmt.end, text: '' });
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
edits.push({ start: stmt.getStart(sourceFile), end: stmt.end, text: `export { ${keepSpecs.join(', ')} };` });
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return { localName, edits, found };
|
|
110
|
-
};
|
|
111
|
-
const wrapServerRouteModuleRegex = (sourceCode) => {
|
|
112
|
-
if (!LOAD_EXPORT_PATTERN.test(sourceCode) && !ACTIONS_EXPORT_PATTERN.test(sourceCode))
|
|
113
|
-
return null;
|
|
114
|
-
let wrapped = ensureRegexServerImport(sourceCode);
|
|
115
|
-
if (LOAD_EXPORT_PATTERN.test(wrapped)) {
|
|
116
|
-
wrapped = wrapped
|
|
117
|
-
.replace(LOAD_EXPORT_PATTERN, (match) => match.replace('export const load', 'const __userLoad'))
|
|
118
|
-
.concat('\n\nexport const load = __withEdgesServerLoad(__userLoad);');
|
|
119
|
-
}
|
|
120
|
-
if (ACTIONS_EXPORT_PATTERN.test(wrapped)) {
|
|
121
|
-
wrapped = wrapped
|
|
122
|
-
.replace(ACTIONS_EXPORT_PATTERN, (match) => match.replace('export const actions', 'const __userActions'))
|
|
123
|
-
.concat('\n\nexport const actions = __withEdgesActions(__userActions);');
|
|
124
|
-
}
|
|
125
|
-
return wrapped;
|
|
126
|
-
};
|
|
127
|
-
const wrapUniversalRouteModuleRegex = (sourceCode) => {
|
|
128
|
-
if (!LOAD_EXPORT_PATTERN.test(sourceCode))
|
|
129
|
-
return null;
|
|
130
|
-
return ensureRegexUniversalImport(sourceCode)
|
|
131
|
-
.replace(LOAD_EXPORT_PATTERN, (match) => match.replace('export const load', 'const __userUniversalLoad'))
|
|
132
|
-
.concat('\n\nexport const load = __withEdgesUniversalLoad(__userUniversalLoad);');
|
|
133
|
-
};
|
|
134
|
-
const wrapServerRouteModuleAst = (sourceCode) => {
|
|
135
|
-
if (sourceCode.includes('__EDGES_SYNC_WRAPPED__'))
|
|
136
|
-
return null;
|
|
137
|
-
let sourceFile;
|
|
138
|
-
try {
|
|
139
|
-
sourceFile = ts.createSourceFile('route.ts', sourceCode, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
140
|
-
}
|
|
141
|
-
catch {
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
const loadInfo = findExportedLocal(sourceFile, sourceCode, 'load');
|
|
145
|
-
const actionsInfo = findExportedLocal(sourceFile, sourceCode, 'actions');
|
|
146
|
-
if (!loadInfo.found && !actionsInfo.found)
|
|
147
|
-
return null;
|
|
148
|
-
if ((loadInfo.found && !loadInfo.localName) || (actionsInfo.found && !actionsInfo.localName))
|
|
149
|
-
return null;
|
|
150
|
-
let nextCode = applyEdits(sourceCode, [...loadInfo.edits, ...actionsInfo.edits]);
|
|
151
|
-
nextCode = ensureAstServerImport(nextCode);
|
|
152
|
-
const append = [];
|
|
153
|
-
if (loadInfo.localName) {
|
|
154
|
-
append.push(`const __edgesServerLoad = ${AST_SERVER_LOAD_ALIAS}(${loadInfo.localName});`);
|
|
155
|
-
append.push('export { __edgesServerLoad as load };');
|
|
156
|
-
}
|
|
157
|
-
if (actionsInfo.localName) {
|
|
158
|
-
append.push(`const __edgesServerActions = ${AST_ACTIONS_ALIAS}(${actionsInfo.localName});`);
|
|
159
|
-
append.push('export { __edgesServerActions as actions };');
|
|
160
|
-
}
|
|
161
|
-
return `${nextCode}\n\n${append.join('\n')}`;
|
|
162
|
-
};
|
|
163
|
-
const wrapUniversalRouteModuleAst = (sourceCode) => {
|
|
164
|
-
if (sourceCode.includes('__EDGES_SYNC_WRAPPED__'))
|
|
165
|
-
return null;
|
|
166
|
-
let sourceFile;
|
|
167
|
-
try {
|
|
168
|
-
sourceFile = ts.createSourceFile('route.ts', sourceCode, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
const loadInfo = findExportedLocal(sourceFile, sourceCode, 'load');
|
|
174
|
-
if (!loadInfo.found || !loadInfo.localName)
|
|
175
|
-
return null;
|
|
176
|
-
let nextCode = applyEdits(sourceCode, [...loadInfo.edits]);
|
|
177
|
-
nextCode = ensureAstUniversalImport(nextCode);
|
|
178
|
-
return `${nextCode}\n\nconst __edgesUniversalLoad = ${AST_UNIVERSAL_LOAD_ALIAS}(${loadInfo.localName});\nexport { __edgesUniversalLoad as load };`;
|
|
179
|
-
};
|
|
180
21
|
const createVirtualServerHooks = (silentChromeDevtools) => `// ${GENERATED_MARKER}
|
|
181
22
|
import { edgesHandle, edgesHandleRaw } from '${VIRTUAL_SERVER_UTILS_ID}';
|
|
182
23
|
|
|
183
24
|
const getRegister = async () => (await import('${VIRTUAL_PROGRAM_ID}')).register;
|
|
184
25
|
|
|
185
|
-
export const init = () =>
|
|
26
|
+
export const init = async () => (await getRegister()).serverInit();
|
|
186
27
|
|
|
187
28
|
export const handle = edgesHandle(async ({ serialize, edgesEvent, resolve }) => {
|
|
188
29
|
const register = await getRegister();
|
|
189
|
-
await register.serverInit?.();
|
|
190
30
|
|
|
191
31
|
return register.handle({
|
|
192
32
|
event: edgesEvent,
|
|
@@ -203,48 +43,19 @@ export const handle = edgesHandle(async ({ serialize, edgesEvent, resolve }) =>
|
|
|
203
43
|
${silentChromeDevtools ? ')' : ', false)'};
|
|
204
44
|
|
|
205
45
|
export const handleError = async ({ error, event, status, message }) => {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
return register.serverError({ error, event, status, message });
|
|
209
|
-
};
|
|
210
|
-
`;
|
|
211
|
-
const createVirtualServerUtils = () => "export { edgesHandle, edgesHandleRaw, __autoWrapHandle, __withEdgesServerLoad, __withEdgesActions } from '@azure-net/kit/edges/internal-package/utils';";
|
|
212
|
-
const createVirtualUniversalUtils = () => `const EDGES_STATE_FIELD = '__edges_state__';
|
|
213
|
-
const EDGES_REV_FIELD = '__edges_rev__';
|
|
214
|
-
|
|
215
|
-
const isObjectRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
216
|
-
|
|
217
|
-
export const __withEdgesUniversalLoad = (load) => {
|
|
218
|
-
\tconst wrapped = async (...args) => {
|
|
219
|
-
\t\tconst event = args[0];
|
|
220
|
-
\t\tconst result = await load(...args);
|
|
221
|
-
\t\tif (!isObjectRecord(event?.data)) return result;
|
|
222
|
-
|
|
223
|
-
\t\tconst inheritedState = event.data[EDGES_STATE_FIELD];
|
|
224
|
-
\t\tconst inheritedRev = event.data[EDGES_REV_FIELD];
|
|
225
|
-
\t\tif (!inheritedState) return result;
|
|
46
|
+
let result;
|
|
226
47
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
\t\t\t\t[EDGES_STATE_FIELD]: inheritedState,
|
|
231
|
-
\t\t\t\t[EDGES_REV_FIELD]: inheritedRev
|
|
232
|
-
\t\t\t};
|
|
233
|
-
\t\t}
|
|
48
|
+
await edgesHandleRaw(event, async () => {
|
|
49
|
+
const register = await getRegister();
|
|
50
|
+
result = await register.serverError({ error, event, status, message });
|
|
234
51
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
\t\t\t\t[EDGES_STATE_FIELD]: inheritedState,
|
|
238
|
-
\t\t\t\t[EDGES_REV_FIELD]: inheritedRev
|
|
239
|
-
\t\t\t};
|
|
240
|
-
\t\t}
|
|
241
|
-
|
|
242
|
-
\t\treturn result;
|
|
243
|
-
\t};
|
|
52
|
+
return new Response(null, { status: 204 });
|
|
53
|
+
});
|
|
244
54
|
|
|
245
|
-
|
|
55
|
+
return result;
|
|
246
56
|
};
|
|
247
57
|
`;
|
|
58
|
+
const createVirtualServerUtils = () => "export { edgesHandle, edgesHandleRaw } from '@azure-net/kit/edges/internal-package/utils';";
|
|
248
59
|
const createVirtualClientHooks = () => `// ${GENERATED_MARKER}
|
|
249
60
|
const getRegister = async () => (await import('${VIRTUAL_PROGRAM_ID}')).register;
|
|
250
61
|
|
|
@@ -258,10 +69,14 @@ const createVirtualProgram = (root) => {
|
|
|
258
69
|
return `export { register } from '/src/program.ts';`;
|
|
259
70
|
if (fs.existsSync(jsProgram))
|
|
260
71
|
return `export { register } from '/src/program.js';`;
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
72
|
+
throw new Error('[AzureNetPlugin] src/program.ts is required. Create it and export register from createApp().');
|
|
73
|
+
};
|
|
74
|
+
const assertProgramExists = (root) => {
|
|
75
|
+
const tsProgram = path.join(root, 'src', 'program.ts');
|
|
76
|
+
const jsProgram = path.join(root, 'src', 'program.js');
|
|
77
|
+
if (fs.existsSync(tsProgram) || fs.existsSync(jsProgram))
|
|
78
|
+
return;
|
|
79
|
+
throw new Error('[AzureNetPlugin] src/program.ts is required. Create it and export register from createApp().');
|
|
265
80
|
};
|
|
266
81
|
const hasUserHook = (root, hookName) => {
|
|
267
82
|
const srcPath = path.join(root, 'src');
|
|
@@ -290,6 +105,13 @@ const ensureVirtualImport = (sourceCode, importLine) => {
|
|
|
290
105
|
const transformGeneratedServerInternal = (sourceCode) => {
|
|
291
106
|
if (sourceCode.includes(GENERATED_MARKER))
|
|
292
107
|
return null;
|
|
108
|
+
if (/hooks\.server\.(?:ts|js)/.test(sourceCode)) {
|
|
109
|
+
throw new Error('[AzureNetPlugin] SvelteKit detected a user-managed hooks.server file. Move its lifecycle code to src/program.ts via createApp().');
|
|
110
|
+
}
|
|
111
|
+
const getHooksPattern = /export\s+async\s+function\s+get_hooks\s*\(\)\s*\{[\s\S]*?\n\}/m;
|
|
112
|
+
if (!getHooksPattern.test(sourceCode)) {
|
|
113
|
+
throw new Error('[AzureNetPlugin] Unsupported SvelteKit generated server internals: get_hooks() was not found.');
|
|
114
|
+
}
|
|
293
115
|
const universalImportMatch = sourceCode.match(/\(\{\s*reroute,\s*transport\s*\}\s*=\s*await\s*import\([^)]+\)\);/);
|
|
294
116
|
const universalImport = universalImportMatch?.[0] ? `\n\t${universalImportMatch[0]}` : '';
|
|
295
117
|
const importLine = `import * as __azureNetServerHooks from '${VIRTUAL_SERVER_HOOKS_ID}';`;
|
|
@@ -307,15 +129,21 @@ const transformGeneratedServerInternal = (sourceCode) => {
|
|
|
307
129
|
\t\ttransport
|
|
308
130
|
\t};
|
|
309
131
|
}`;
|
|
310
|
-
const transformed = ensureVirtualImport(sourceCode, `// ${GENERATED_MARKER}\n${importLine}`).replace(
|
|
132
|
+
const transformed = ensureVirtualImport(sourceCode, `// ${GENERATED_MARKER}\n${importLine}`).replace(getHooksPattern, getHooksReplacement);
|
|
311
133
|
return transformed;
|
|
312
134
|
};
|
|
313
135
|
const transformGeneratedClientApp = (sourceCode) => {
|
|
314
136
|
if (sourceCode.includes(GENERATED_MARKER))
|
|
315
137
|
return null;
|
|
138
|
+
if (/hooks\.client\.(?:ts|js)/.test(sourceCode)) {
|
|
139
|
+
throw new Error('[AzureNetPlugin] SvelteKit detected a user-managed hooks.client file. Move its lifecycle code to src/program.ts via createApp().');
|
|
140
|
+
}
|
|
141
|
+
const handleErrorPattern = /handleError:\s*\(\(\{\s*error\s*\}\)\s*=>\s*\{\s*console\.error\(error\)\s*\}\)/m;
|
|
142
|
+
if (!handleErrorPattern.test(sourceCode)) {
|
|
143
|
+
throw new Error('[AzureNetPlugin] Unsupported SvelteKit generated client app: default handleError was not found.');
|
|
144
|
+
}
|
|
316
145
|
let transformed = ensureVirtualImport(sourceCode, `// ${GENERATED_MARKER}\nimport * as __azureNetClientHooks from '${VIRTUAL_CLIENT_HOOKS_ID}';`);
|
|
317
|
-
transformed = transformed.replace(
|
|
318
|
-
transformed = transformed.replace(/handleError:\s*(?:client_hooks\.handleError\s*\|\|\s*)?\(\(\{\s*error\s*\}\)\s*=>\s*\{\s*console\.error\(error\)\s*\}\)/m, 'handleError: __azureNetClientHooks.handleError');
|
|
146
|
+
transformed = transformed.replace(handleErrorPattern, 'handleError: __azureNetClientHooks.handleError');
|
|
319
147
|
if (/^\s*init:\s*[^,\n]+,?/m.test(transformed)) {
|
|
320
148
|
transformed = transformed.replace(/^\s*init:\s*[^,\n]+,?/m, '\tinit: __azureNetClientHooks.init,');
|
|
321
149
|
}
|
|
@@ -325,30 +153,28 @@ const transformGeneratedClientApp = (sourceCode) => {
|
|
|
325
153
|
return transformed;
|
|
326
154
|
};
|
|
327
155
|
export const AzureNetPlugin = (options) => {
|
|
328
|
-
const { silentChromeDevtools = true
|
|
156
|
+
const { silentChromeDevtools = true } = options ?? {};
|
|
329
157
|
let root = process.cwd();
|
|
330
|
-
let isTestMode = false;
|
|
331
158
|
return {
|
|
332
159
|
name: 'azure-net-kit-plugin',
|
|
333
160
|
enforce: 'pre',
|
|
334
|
-
config(config
|
|
161
|
+
config(config) {
|
|
335
162
|
root = path.resolve(config.root ?? process.cwd());
|
|
336
|
-
isTestMode = env.mode === 'test';
|
|
337
163
|
assertNoUserHooks(root);
|
|
164
|
+
assertProgramExists(root);
|
|
338
165
|
},
|
|
339
166
|
configResolved(config) {
|
|
340
167
|
root = config.root;
|
|
341
|
-
isTestMode ||= config.mode === 'test';
|
|
342
168
|
assertNoUserHooks(root);
|
|
169
|
+
assertProgramExists(root);
|
|
343
170
|
},
|
|
344
171
|
buildStart() {
|
|
345
172
|
assertNoUserHooks(root);
|
|
173
|
+
assertProgramExists(root);
|
|
346
174
|
},
|
|
347
175
|
resolveId(id) {
|
|
348
176
|
if (id === VIRTUAL_SERVER_UTILS_ID)
|
|
349
177
|
return RESOLVED_VIRTUAL_SERVER_UTILS_ID;
|
|
350
|
-
if (id === VIRTUAL_UNIVERSAL_UTILS_ID)
|
|
351
|
-
return RESOLVED_VIRTUAL_UNIVERSAL_UTILS_ID;
|
|
352
178
|
if (id === VIRTUAL_SERVER_HOOKS_ID)
|
|
353
179
|
return RESOLVED_VIRTUAL_SERVER_HOOKS_ID;
|
|
354
180
|
if (id === VIRTUAL_CLIENT_HOOKS_ID)
|
|
@@ -360,8 +186,6 @@ export const AzureNetPlugin = (options) => {
|
|
|
360
186
|
load(id) {
|
|
361
187
|
if (id === RESOLVED_VIRTUAL_SERVER_UTILS_ID)
|
|
362
188
|
return createVirtualServerUtils();
|
|
363
|
-
if (id === RESOLVED_VIRTUAL_UNIVERSAL_UTILS_ID)
|
|
364
|
-
return createVirtualUniversalUtils();
|
|
365
189
|
if (id === RESOLVED_VIRTUAL_SERVER_HOOKS_ID)
|
|
366
190
|
return createVirtualServerHooks(silentChromeDevtools);
|
|
367
191
|
if (id === RESOLVED_VIRTUAL_CLIENT_HOOKS_ID)
|
|
@@ -371,39 +195,14 @@ export const AzureNetPlugin = (options) => {
|
|
|
371
195
|
return null;
|
|
372
196
|
},
|
|
373
197
|
transform(code, id) {
|
|
374
|
-
const normalizedId = id.split(path.sep).join('/');
|
|
375
|
-
if (
|
|
198
|
+
const normalizedId = id.split('?')[0].split(path.sep).join('/');
|
|
199
|
+
if (normalizedId.endsWith('/.svelte-kit/generated/server/internal.js')) {
|
|
376
200
|
const transformed = transformGeneratedServerInternal(code);
|
|
377
|
-
|
|
378
|
-
return { code: transformed, map: null };
|
|
379
|
-
return null;
|
|
201
|
+
return transformed ? { code: transformed, map: null } : null;
|
|
380
202
|
}
|
|
381
|
-
if (
|
|
382
|
-
(normalizedId.endsWith('/.svelte-kit/generated/client/app.js') || normalizedId.endsWith('/.svelte-kit/generated/client-optimized/app.js'))) {
|
|
203
|
+
if (normalizedId.endsWith('/.svelte-kit/generated/client/app.js') || normalizedId.endsWith('/.svelte-kit/generated/client-optimized/app.js')) {
|
|
383
204
|
const transformed = transformGeneratedClientApp(code);
|
|
384
|
-
|
|
385
|
-
return { code: transformed, map: null };
|
|
386
|
-
return null;
|
|
387
|
-
}
|
|
388
|
-
if (code.includes('__EDGES_SYNC_WRAPPED__'))
|
|
389
|
-
return null;
|
|
390
|
-
if (syncFromServer && SERVER_ROUTE_PATTERN.test(id) && !id.includes('hooks.server')) {
|
|
391
|
-
const wrapped = syncTransformMode === 'regex'
|
|
392
|
-
? wrapServerRouteModuleRegex(code)
|
|
393
|
-
: syncTransformMode === 'ast'
|
|
394
|
-
? wrapServerRouteModuleAst(code)
|
|
395
|
-
: (wrapServerRouteModuleAst(code) ?? wrapServerRouteModuleRegex(code));
|
|
396
|
-
if (wrapped)
|
|
397
|
-
return { code: wrapped, map: null };
|
|
398
|
-
}
|
|
399
|
-
if (syncFromServer && UNIVERSAL_ROUTE_PATTERN.test(id) && !id.includes('.server.')) {
|
|
400
|
-
const wrapped = syncTransformMode === 'regex'
|
|
401
|
-
? wrapUniversalRouteModuleRegex(code)
|
|
402
|
-
: syncTransformMode === 'ast'
|
|
403
|
-
? wrapUniversalRouteModuleAst(code)
|
|
404
|
-
: (wrapUniversalRouteModuleAst(code) ?? wrapUniversalRouteModuleRegex(code));
|
|
405
|
-
if (wrapped)
|
|
406
|
-
return { code: wrapped, map: null };
|
|
205
|
+
return transformed ? { code: transformed, map: null } : null;
|
|
407
206
|
}
|
|
408
207
|
return null;
|
|
409
208
|
}
|
package/dist/shared/app/App.d.ts
CHANGED
|
@@ -38,9 +38,7 @@ export interface AppClientErrorContext<TDependencies extends Record<string, unkn
|
|
|
38
38
|
status: number;
|
|
39
39
|
message: string;
|
|
40
40
|
}
|
|
41
|
-
export
|
|
42
|
-
Container: AppContainer<TDependencies>;
|
|
43
|
-
}
|
|
41
|
+
export type AppServerInitLifecycleContext = void;
|
|
44
42
|
export interface AppServerLifecycleContext<TDependencies extends Record<string, unknown>> {
|
|
45
43
|
Container: AppContainer<TDependencies>;
|
|
46
44
|
requestContext: ContextData;
|
|
@@ -61,7 +59,7 @@ export type AppUniversalLifecycleCallback<TDependencies extends Record<string, u
|
|
|
61
59
|
export type AppClientLifecycleCallback<TDependencies extends Record<string, unknown>> = (context: AppClientLifecycleContext<TDependencies>) => MaybePromise<void>;
|
|
62
60
|
export type AppClientInitLifecycleCallback<TDependencies extends Record<string, unknown>> = (context: AppClientInitLifecycleContext<TDependencies>) => MaybePromise<void>;
|
|
63
61
|
export type AppClientErrorCallback<TDependencies extends Record<string, unknown>> = (context: AppClientErrorContext<TDependencies>) => ReturnType<HandleClientError>;
|
|
64
|
-
export type AppServerInitLifecycleCallback
|
|
62
|
+
export type AppServerInitLifecycleCallback = ServerInit;
|
|
65
63
|
export type AppServerLifecycleCallback<TDependencies extends Record<string, unknown>> = (context: AppServerLifecycleContext<TDependencies>) => MaybePromise<Response | void>;
|
|
66
64
|
export type AppServerErrorCallback<TDependencies extends Record<string, unknown>> = (context: AppServerErrorContext<TDependencies>) => ReturnType<HandleServerError>;
|
|
67
65
|
export type CreateAppBuilder<TDependencies extends Record<string, unknown>, TFlags extends BuilderFlags = object> = {
|
|
@@ -76,7 +74,7 @@ export type CreateAppBuilder<TDependencies extends Record<string, unknown>, TFla
|
|
|
76
74
|
}> & OnceMethod<TFlags, 'useClientError', {
|
|
77
75
|
useClientError(callback: AppClientErrorCallback<TDependencies>): CreateAppBuilder<TDependencies, WithFlag<TFlags, 'useClientError'>>;
|
|
78
76
|
}> & OnceMethod<TFlags, 'useServerInit', {
|
|
79
|
-
useServerInit(callback: AppServerInitLifecycleCallback
|
|
77
|
+
useServerInit(callback: AppServerInitLifecycleCallback): CreateAppBuilder<TDependencies, WithFlag<TFlags, 'useServerInit'>>;
|
|
80
78
|
}> & OnceMethod<TFlags, 'useServer', {
|
|
81
79
|
useServer(callback: AppServerLifecycleCallback<TDependencies>): CreateAppBuilder<TDependencies, WithFlag<TFlags, 'useServer'>>;
|
|
82
80
|
}> & OnceMethod<TFlags, 'useServerError', {
|
package/dist/shared/app/App.js
CHANGED
|
@@ -8,7 +8,6 @@ import { executeClientMiddlewares } from './middleware/ClientMiddleware.js';
|
|
|
8
8
|
import { useLogger } from '../logger/index.js';
|
|
9
9
|
const DEFAULT_APP_NAME = '__AzureNetKitGlobalAppContainer__';
|
|
10
10
|
const clientLifecycleFlags = new WeakMap();
|
|
11
|
-
const serverInitLifecycleFlags = new WeakMap();
|
|
12
11
|
const getRequestContext = () => {
|
|
13
12
|
try {
|
|
14
13
|
return RequestContext.current();
|
|
@@ -17,13 +16,10 @@ const getRequestContext = () => {
|
|
|
17
16
|
return undefined;
|
|
18
17
|
}
|
|
19
18
|
};
|
|
20
|
-
const getServerRequestContext = (
|
|
19
|
+
const getServerRequestContext = () => {
|
|
21
20
|
const requestContext = getRequestContext();
|
|
22
21
|
if (requestContext)
|
|
23
22
|
return requestContext;
|
|
24
|
-
if (event) {
|
|
25
|
-
return { data: {}, event };
|
|
26
|
-
}
|
|
27
23
|
throw new AzureNetKitInternalError('[createApp] RequestContext is not initialized on server.');
|
|
28
24
|
};
|
|
29
25
|
const getLifecycleFlags = (appKey, requestContext) => {
|
|
@@ -97,6 +93,7 @@ export const createApp = (callback, name = DEFAULT_APP_NAME) => {
|
|
|
97
93
|
const dependencies = new Map();
|
|
98
94
|
const callbacks = {};
|
|
99
95
|
let clientInitialized = false;
|
|
96
|
+
let serverInitPromise;
|
|
100
97
|
callback(createBuilder(dependencies, callbacks));
|
|
101
98
|
const AppProvider = createBoundaryProvider(name, {
|
|
102
99
|
register: () => {
|
|
@@ -171,22 +168,15 @@ export const createApp = (callback, name = DEFAULT_APP_NAME) => {
|
|
|
171
168
|
const runServerInit = () => {
|
|
172
169
|
if (BROWSER)
|
|
173
170
|
return undefined;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
flags = {};
|
|
177
|
-
serverInitLifecycleFlags.set(appKey, flags);
|
|
178
|
-
}
|
|
179
|
-
if (flags.serverInit)
|
|
180
|
-
return undefined;
|
|
181
|
-
flags.serverInit = true;
|
|
182
|
-
return callbacks.useServerInit?.({ Container });
|
|
171
|
+
serverInitPromise ??= Promise.resolve().then(() => callbacks.useServerInit?.());
|
|
172
|
+
return serverInitPromise;
|
|
183
173
|
};
|
|
184
174
|
const runServerError = ({ error, event, status, message }) => {
|
|
185
175
|
if (BROWSER)
|
|
186
176
|
return undefined;
|
|
187
177
|
return callbacks.useServerError?.({
|
|
188
178
|
Container,
|
|
189
|
-
requestContext: getServerRequestContext(
|
|
179
|
+
requestContext: getServerRequestContext(),
|
|
190
180
|
error,
|
|
191
181
|
event,
|
|
192
182
|
status,
|
|
@@ -206,7 +196,7 @@ export const createApp = (callback, name = DEFAULT_APP_NAME) => {
|
|
|
206
196
|
});
|
|
207
197
|
};
|
|
208
198
|
const handle = async ({ event, resolve }) => {
|
|
209
|
-
const requestContext = getServerRequestContext(
|
|
199
|
+
const requestContext = getServerRequestContext();
|
|
210
200
|
const flags = getLifecycleFlags(appKey, requestContext);
|
|
211
201
|
if (!flags.server) {
|
|
212
202
|
flags.server = true;
|
|
@@ -7,27 +7,28 @@ export const executeClientMiddlewares = async (middlewares, navigation) => {
|
|
|
7
7
|
const to = navigation?.to?.url ?? page?.url;
|
|
8
8
|
for (const middleware of middlewares) {
|
|
9
9
|
let shouldContinue = false;
|
|
10
|
+
let redirectTo;
|
|
10
11
|
const next = (location) => {
|
|
11
12
|
shouldContinue = true;
|
|
12
|
-
|
|
13
|
-
if (navigation) {
|
|
14
|
-
navigation?.cancel();
|
|
15
|
-
}
|
|
16
|
-
return goto(location);
|
|
17
|
-
}
|
|
13
|
+
redirectTo = location;
|
|
18
14
|
};
|
|
19
|
-
|
|
15
|
+
const result = middleware({
|
|
20
16
|
to,
|
|
21
17
|
from,
|
|
22
18
|
next,
|
|
23
19
|
ensureRoute
|
|
24
20
|
});
|
|
21
|
+
if (result && typeof result.then === 'function')
|
|
22
|
+
await result;
|
|
23
|
+
if (redirectTo) {
|
|
24
|
+
navigation?.cancel();
|
|
25
|
+
await goto(redirectTo);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
25
28
|
if (!shouldContinue) {
|
|
26
29
|
navigation?.cancel();
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
console.warn('Navigation blocked: middleware chain stopped (next() not called).');
|
|
30
|
-
}
|
|
30
|
+
console.warn('Navigation blocked: middleware chain stopped (next() not called).');
|
|
31
|
+
return;
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
};
|
|
@@ -2,7 +2,6 @@ import { RequestContext } from '../../external/edges/ServerContext.js';
|
|
|
2
2
|
import { BROWSER } from '../../external/tools/index.js';
|
|
3
3
|
import { AzureNetKitInternalError } from '../app-error/AppError.js';
|
|
4
4
|
const clientCache = new Map();
|
|
5
|
-
const factoriesCache = new WeakMap();
|
|
6
5
|
const providerProxyCache = new Map();
|
|
7
6
|
const clientConstructionStack = [];
|
|
8
7
|
const clientBootFlags = new Map();
|
|
@@ -89,10 +88,6 @@ export const createBoundaryProvider = (name, settings) => {
|
|
|
89
88
|
const getFactories = () => {
|
|
90
89
|
if (factories)
|
|
91
90
|
return factories;
|
|
92
|
-
if (factoriesCache.has(register)) {
|
|
93
|
-
factories = factoriesCache.get(register);
|
|
94
|
-
return factories;
|
|
95
|
-
}
|
|
96
91
|
const deps = {};
|
|
97
92
|
const depInstances = new Map();
|
|
98
93
|
for (const [depName, depProvider] of Object.entries(dependsOn)) {
|
|
@@ -107,9 +102,6 @@ export const createBoundaryProvider = (name, settings) => {
|
|
|
107
102
|
});
|
|
108
103
|
}
|
|
109
104
|
factories = register(deps);
|
|
110
|
-
if (BROWSER) {
|
|
111
|
-
factoriesCache.set(register, factories);
|
|
112
|
-
}
|
|
113
105
|
return factories;
|
|
114
106
|
};
|
|
115
107
|
const providerProxy = new Proxy({}, {
|
|
@@ -7,8 +7,11 @@ export const createActiveForm = (onSubmit, config) => {
|
|
|
7
7
|
let formData = $state(ObjectUtil.deepClone(initial));
|
|
8
8
|
let formErrors = $state({});
|
|
9
9
|
let pending = $state(false);
|
|
10
|
+
let submitRunId = 0;
|
|
11
|
+
let initialLoadRunId = 0;
|
|
10
12
|
const dirty = $derived(!ObjectUtil.equals(formData, initial));
|
|
11
13
|
const loadInitialData = async () => {
|
|
14
|
+
const runId = ++initialLoadRunId;
|
|
12
15
|
const source = config?.initialData;
|
|
13
16
|
if (!source) {
|
|
14
17
|
initial = {};
|
|
@@ -18,33 +21,63 @@ export const createActiveForm = (onSubmit, config) => {
|
|
|
18
21
|
}
|
|
19
22
|
const value = source();
|
|
20
23
|
const nextInitial = isPromise(value) ? await value : value;
|
|
24
|
+
if (runId !== initialLoadRunId)
|
|
25
|
+
return initial;
|
|
21
26
|
initial = (nextInitial ?? {});
|
|
22
27
|
formData = ObjectUtil.deepClone(initial);
|
|
23
28
|
formErrors = {};
|
|
24
29
|
return initial;
|
|
25
30
|
};
|
|
26
31
|
const ready = loadInitialData();
|
|
32
|
+
const abortedResponse = () => ({
|
|
33
|
+
success: false,
|
|
34
|
+
response: undefined
|
|
35
|
+
});
|
|
36
|
+
const resetForm = async (behavior = 'clear', invalidateSubmission = true) => {
|
|
37
|
+
if (invalidateSubmission) {
|
|
38
|
+
submitRunId += 1;
|
|
39
|
+
pending = false;
|
|
40
|
+
}
|
|
41
|
+
if (behavior !== 'reloadInitial')
|
|
42
|
+
initialLoadRunId += 1;
|
|
43
|
+
switch (behavior) {
|
|
44
|
+
case 'clear':
|
|
45
|
+
formData = {};
|
|
46
|
+
break;
|
|
47
|
+
case 'initial':
|
|
48
|
+
formData = ObjectUtil.deepClone(initial);
|
|
49
|
+
break;
|
|
50
|
+
case 'reloadInitial':
|
|
51
|
+
await loadInitialData();
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
formErrors = {};
|
|
55
|
+
};
|
|
56
|
+
const reset = async (behavior = 'clear') => resetForm(behavior, true);
|
|
27
57
|
const submit = async () => {
|
|
58
|
+
const runId = ++submitRunId;
|
|
28
59
|
pending = true;
|
|
29
60
|
try {
|
|
61
|
+
await ready;
|
|
62
|
+
if (runId !== submitRunId)
|
|
63
|
+
return abortedResponse();
|
|
30
64
|
if (config?.beforeSubmit) {
|
|
31
65
|
let aborted = false;
|
|
32
66
|
const abort = () => {
|
|
33
67
|
aborted = true;
|
|
34
|
-
pending = false;
|
|
35
68
|
};
|
|
36
69
|
await config.beforeSubmit({ form: formApi, abort: () => abort() });
|
|
37
|
-
if (aborted)
|
|
38
|
-
return
|
|
39
|
-
success: false,
|
|
40
|
-
response: undefined
|
|
41
|
-
};
|
|
42
|
-
}
|
|
70
|
+
if (aborted || runId !== submitRunId)
|
|
71
|
+
return abortedResponse();
|
|
43
72
|
}
|
|
44
73
|
const result = await onSubmit($state.snapshot(formData));
|
|
74
|
+
if (runId !== submitRunId)
|
|
75
|
+
return result;
|
|
45
76
|
if (result.success) {
|
|
46
77
|
await config?.onSuccess?.(result.response);
|
|
47
|
-
|
|
78
|
+
if (runId !== submitRunId)
|
|
79
|
+
return result;
|
|
80
|
+
await resetForm(config?.successBehavior ?? 'default', false);
|
|
48
81
|
}
|
|
49
82
|
else {
|
|
50
83
|
if (result.error?.validation) {
|
|
@@ -55,23 +88,10 @@ export const createActiveForm = (onSubmit, config) => {
|
|
|
55
88
|
return result;
|
|
56
89
|
}
|
|
57
90
|
finally {
|
|
58
|
-
|
|
91
|
+
if (runId === submitRunId)
|
|
92
|
+
pending = false;
|
|
59
93
|
}
|
|
60
94
|
};
|
|
61
|
-
const reset = async (behavior = 'clear') => {
|
|
62
|
-
switch (behavior) {
|
|
63
|
-
case 'clear':
|
|
64
|
-
formData = {};
|
|
65
|
-
break;
|
|
66
|
-
case 'initial':
|
|
67
|
-
formData = ObjectUtil.deepClone(initial);
|
|
68
|
-
break;
|
|
69
|
-
case 'reloadInitial':
|
|
70
|
-
await loadInitialData();
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
formErrors = {};
|
|
74
|
-
};
|
|
75
95
|
const formApi = {
|
|
76
96
|
get data() {
|
|
77
97
|
return formData;
|
|
@@ -65,15 +65,17 @@ export const createAsyncSignal = (handler, options = {}) => {
|
|
|
65
65
|
}
|
|
66
66
|
const localController = new AbortController();
|
|
67
67
|
abortController = localController;
|
|
68
|
-
if (options.beforeSend) {
|
|
69
|
-
await options.beforeSend({ initial, source });
|
|
70
|
-
}
|
|
71
|
-
if (runId !== currentRunId || abortController !== localController || localController.signal.aborted) {
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
68
|
status = 'pending';
|
|
75
69
|
error = undefined;
|
|
76
70
|
try {
|
|
71
|
+
if (options.beforeSend) {
|
|
72
|
+
const beforeSendResult = options.beforeSend({ initial, source });
|
|
73
|
+
if (beforeSendResult && typeof beforeSendResult.then === 'function')
|
|
74
|
+
await beforeSendResult;
|
|
75
|
+
}
|
|
76
|
+
if (runId !== currentRunId || abortController !== localController || localController.signal.aborted) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
77
79
|
const result = await handler(localController.signal);
|
|
78
80
|
if (runId !== currentRunId || abortController !== localController || localController.signal.aborted) {
|
|
79
81
|
return undefined;
|
|
@@ -87,6 +89,8 @@ export const createAsyncSignal = (handler, options = {}) => {
|
|
|
87
89
|
}
|
|
88
90
|
catch (err) {
|
|
89
91
|
if (err instanceof Error && err.name === 'AbortError') {
|
|
92
|
+
if (runId === currentRunId)
|
|
93
|
+
status = 'idle';
|
|
90
94
|
return undefined;
|
|
91
95
|
}
|
|
92
96
|
if (runId !== currentRunId || abortController !== localController || localController.signal.aborted) {
|
|
@@ -102,6 +106,8 @@ export const createAsyncSignal = (handler, options = {}) => {
|
|
|
102
106
|
finally {
|
|
103
107
|
if (currentRunId === runId) {
|
|
104
108
|
currentPromise = null;
|
|
109
|
+
if (abortController === localController)
|
|
110
|
+
abortController = null;
|
|
105
111
|
}
|
|
106
112
|
}
|
|
107
113
|
};
|
|
@@ -159,19 +165,21 @@ export const createAsyncSignal = (handler, options = {}) => {
|
|
|
159
165
|
execute,
|
|
160
166
|
refresh: execute,
|
|
161
167
|
reset: () => {
|
|
168
|
+
currentRunId += 1;
|
|
169
|
+
abortController?.abort();
|
|
170
|
+
abortController = null;
|
|
171
|
+
currentPromise = null;
|
|
162
172
|
data = undefined;
|
|
163
173
|
error = undefined;
|
|
164
174
|
status = 'idle';
|
|
165
|
-
currentPromise = null;
|
|
166
|
-
if (abortController) {
|
|
167
|
-
abortController.abort();
|
|
168
|
-
abortController = null;
|
|
169
|
-
}
|
|
170
175
|
},
|
|
171
176
|
abort: () => {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
177
|
+
currentRunId += 1;
|
|
178
|
+
abortController?.abort();
|
|
179
|
+
abortController = null;
|
|
180
|
+
currentPromise = null;
|
|
181
|
+
error = undefined;
|
|
182
|
+
status = 'idle';
|
|
175
183
|
}
|
|
176
184
|
};
|
|
177
185
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azure-net/kit",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Pixel1917",
|
|
6
6
|
"description": "Universal toolkit and library collection, developed as part of the Azure-net ecosystem",
|
|
@@ -77,7 +77,6 @@
|
|
|
77
77
|
"svelte": ">=5.0.0"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"@commitlint/cli": "^20.5.0",
|
|
81
80
|
"@eslint/compat": "^2.0.5",
|
|
82
81
|
"@eslint/js": "^10.0.1",
|
|
83
82
|
"@playwright/test": "^1.59.1",
|
|
@@ -87,14 +86,11 @@
|
|
|
87
86
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
88
87
|
"@testing-library/jest-dom": "^6.9.1",
|
|
89
88
|
"@testing-library/svelte": "^5.3.1",
|
|
90
|
-
"commitizen": "^4.3.1",
|
|
91
89
|
"env-cmd": "^11.0.0",
|
|
92
90
|
"eslint": "^10.2.1",
|
|
93
91
|
"eslint-config-prettier": "^10.1.8",
|
|
94
92
|
"eslint-plugin-svelte": "^3.17.0",
|
|
95
|
-
"git-cz": "^4.9.0",
|
|
96
93
|
"globals": "^17.5.0",
|
|
97
|
-
"husky": "^9.1.7",
|
|
98
94
|
"jsdom": "^29.0.2",
|
|
99
95
|
"prettier": "^3.8.3",
|
|
100
96
|
"prettier-plugin-svelte": "^3.5.1",
|
|
@@ -106,11 +102,6 @@
|
|
|
106
102
|
"vite": "^8.0.8",
|
|
107
103
|
"vitest": "^4.1.4"
|
|
108
104
|
},
|
|
109
|
-
"config": {
|
|
110
|
-
"commitizen": {
|
|
111
|
-
"path": "git-cz"
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
105
|
"keywords": [
|
|
115
106
|
"sveltekit",
|
|
116
107
|
"svelte",
|
|
@@ -122,9 +113,9 @@
|
|
|
122
113
|
"utils"
|
|
123
114
|
],
|
|
124
115
|
"dependencies": {
|
|
125
|
-
"@azure-net/edges": "^
|
|
126
|
-
"@azure-net/edges-translations": "^
|
|
127
|
-
"@azure-net/tools": "^
|
|
116
|
+
"@azure-net/edges": "^2.0.0",
|
|
117
|
+
"@azure-net/edges-translations": "^2.0.0",
|
|
118
|
+
"@azure-net/tools": "^2.0.0"
|
|
128
119
|
},
|
|
129
120
|
"scripts": {
|
|
130
121
|
"dev": "vite dev",
|
|
@@ -138,6 +129,6 @@
|
|
|
138
129
|
"test:integration": "vitest --config vitest.integration.config.ts --run",
|
|
139
130
|
"test": "npm run test:unit -- --run && npm run test:e2e",
|
|
140
131
|
"test:e2e": "playwright test --pass-with-no-tests",
|
|
141
|
-
"commit": "pnpm format && git add .
|
|
132
|
+
"commit": "pnpm format && git add ."
|
|
142
133
|
}
|
|
143
134
|
}
|