svelte-reflector 2.0.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/dist/core/generators/ApiCallStrategy.js +6 -0
- package/dist/core/generators/CallMethodGenerator.js +56 -56
- package/dist/core/generators/ModuleCallStrategy.d.ts +1 -0
- package/dist/core/generators/ModuleCallStrategy.js +21 -11
- package/dist/core/generators/queryOverride.d.ts +12 -0
- package/dist/core/generators/queryOverride.js +22 -0
- package/dist/runtime/reflector.svelte.ts +4 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -265,6 +265,28 @@ The auto-injected `setQueryGroup([...])` constructor on the generated
|
|
|
265
265
|
`setQueryGroup` manually from `$reflector/reflector.svelte` if you still
|
|
266
266
|
need batch URL writes.
|
|
267
267
|
|
|
268
|
+
#### Ephemeral pagination (sidebar / widget)
|
|
269
|
+
|
|
270
|
+
Sometimes a list lives outside the canonical route — a global sidebar, a
|
|
271
|
+
widget, a paginated combobox in a modal — and should NOT mutate the
|
|
272
|
+
current URL. For those cases, every generated `call()` with query params
|
|
273
|
+
accepts an optional `queryOverride`:
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
const sidebar = new UserService();
|
|
277
|
+
|
|
278
|
+
// Current URL stays put. Request goes out with ?page=2&limit=10.
|
|
279
|
+
await sidebar.listAll({
|
|
280
|
+
queryOverride: { page: "2", limit: "10" },
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
When `queryOverride` is passed, the method skips `this.querys.bundle()`
|
|
285
|
+
entirely and uses the override as `queryData`. Without it, the method
|
|
286
|
+
reads from `QueryBuilder.value` (the URL) as usual — so you can mix and
|
|
287
|
+
match on a per-call basis. Omit a key to drop it from the request; pass
|
|
288
|
+
`null` to send a literal `null`.
|
|
289
|
+
|
|
268
290
|
## Configuration
|
|
269
291
|
|
|
270
292
|
### Environment Variables
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { queryOverrideTypeLiteral } from "./queryOverride.js";
|
|
1
2
|
export class ApiCallStrategy {
|
|
2
3
|
listStateAccess(_method) {
|
|
3
4
|
return "this.data";
|
|
@@ -15,6 +16,11 @@ export class ApiCallStrategy {
|
|
|
15
16
|
buildParamsType(method) {
|
|
16
17
|
const responseType = method.responseTypeInterface;
|
|
17
18
|
const pathsBlock = this.buildPathsInfo(method);
|
|
19
|
+
const queryBlock = queryOverrideTypeLiteral(method.analyzers.props.querys);
|
|
20
|
+
if (queryBlock) {
|
|
21
|
+
const pathsArg = pathsBlock ?? "void";
|
|
22
|
+
return `ApiCallParams<${responseType}, ${pathsArg}, ${queryBlock}>`;
|
|
23
|
+
}
|
|
18
24
|
if (pathsBlock) {
|
|
19
25
|
return `ApiCallParams<${responseType}, ${pathsBlock}>`;
|
|
20
26
|
}
|
|
@@ -8,43 +8,43 @@ export class CallMethodGenerator {
|
|
|
8
8
|
const { inside, outside } = this.buildApiCall(method, strategy);
|
|
9
9
|
const methodReturn = this.buildMethodReturn(method, strategy);
|
|
10
10
|
const signature = strategy.buildSignature(method);
|
|
11
|
-
return `
|
|
12
|
-
${description}
|
|
13
|
-
${signature} {
|
|
14
|
-
|
|
15
|
-
const behavior = params?.behavior ?? new Behavior();
|
|
16
|
-
const { onError, onSuccess } = behavior;
|
|
17
|
-
|
|
18
|
-
this.loading = true;
|
|
19
|
-
${props}
|
|
20
|
-
const endpoint = ${endpoint}
|
|
21
|
-
|
|
22
|
-
${outside}
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
${inside}
|
|
26
|
-
await onSuccess?.(response);
|
|
27
|
-
|
|
28
|
-
return ${methodReturn};
|
|
29
|
-
} catch (e) {
|
|
30
|
-
let parsedError: ApiErrorResponse;
|
|
31
|
-
try {
|
|
32
|
-
parsedError = JSON.parse((e as Error).message) as ApiErrorResponse;
|
|
33
|
-
} catch {
|
|
34
|
-
parsedError = { error: 'unknown', message: (e as Error).message ?? String(e) };
|
|
35
|
-
}
|
|
36
|
-
return await onError?.(parsedError);
|
|
37
|
-
} finally {
|
|
38
|
-
this.loading = false;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
11
|
+
return `
|
|
12
|
+
${description}
|
|
13
|
+
${signature} {
|
|
14
|
+
|
|
15
|
+
const behavior = params?.behavior ?? new Behavior();
|
|
16
|
+
const { onError, onSuccess } = behavior;
|
|
17
|
+
|
|
18
|
+
this.loading = true;
|
|
19
|
+
${props}
|
|
20
|
+
const endpoint = ${endpoint}
|
|
21
|
+
|
|
22
|
+
${outside}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
${inside}
|
|
26
|
+
await onSuccess?.(response);
|
|
27
|
+
|
|
28
|
+
return ${methodReturn};
|
|
29
|
+
} catch (e) {
|
|
30
|
+
let parsedError: ApiErrorResponse;
|
|
31
|
+
try {
|
|
32
|
+
parsedError = JSON.parse((e as Error).message) as ApiErrorResponse;
|
|
33
|
+
} catch {
|
|
34
|
+
parsedError = { error: 'unknown', message: (e as Error).message ?? String(e) };
|
|
35
|
+
}
|
|
36
|
+
return await onError?.(parsedError);
|
|
37
|
+
} finally {
|
|
38
|
+
this.loading = false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
41
|
`;
|
|
42
42
|
}
|
|
43
43
|
buildProps(method) {
|
|
44
44
|
const { querys, paths, cookies } = method.analyzers.props;
|
|
45
45
|
const lines = [];
|
|
46
46
|
if (querys.length > 0) {
|
|
47
|
-
lines.push(`const { ${this.joinNames(querys)} } = this.querys.bundle()`);
|
|
47
|
+
lines.push(`const { ${this.joinNames(querys)} } = params?.queryOverride ?? this.querys.bundle()`);
|
|
48
48
|
}
|
|
49
49
|
if (paths.length > 0) {
|
|
50
50
|
lines.push(`const { ${this.joinNames(paths)} } = params?.paths ?? this.paths`);
|
|
@@ -73,13 +73,13 @@ export class CallMethodGenerator {
|
|
|
73
73
|
}
|
|
74
74
|
buildListCall(method, responseType, strategy) {
|
|
75
75
|
const querys = this.joinNames(method.analyzers.props.querys);
|
|
76
|
-
const inside = `
|
|
77
|
-
const response = await api.get<${responseType}, unknown>({
|
|
78
|
-
endpoint,
|
|
79
|
-
queryData: { ${querys} }
|
|
80
|
-
})
|
|
81
|
-
${strategy.listStateAccess(method)} = ${method.analyzers.request.responseType}.from(response.data);
|
|
82
|
-
this.totalPages = response.totalPages;
|
|
76
|
+
const inside = `
|
|
77
|
+
const response = await api.get<${responseType}, unknown>({
|
|
78
|
+
endpoint,
|
|
79
|
+
queryData: { ${querys} }
|
|
80
|
+
})
|
|
81
|
+
${strategy.listStateAccess(method)} = ${method.analyzers.request.responseType}.from(response.data);
|
|
82
|
+
this.totalPages = response.totalPages;
|
|
83
83
|
`;
|
|
84
84
|
return { inside, outside: "" };
|
|
85
85
|
}
|
|
@@ -90,13 +90,13 @@ export class CallMethodGenerator {
|
|
|
90
90
|
const assignment = rType && !isPrimitive
|
|
91
91
|
? `${strategy.entityStateAccess(method)} = new ${rType}({ data: response })`
|
|
92
92
|
: "";
|
|
93
|
-
const inside = `
|
|
94
|
-
const response = await api.get<${responseType}, unknown>({
|
|
95
|
-
endpoint,
|
|
96
|
-
${querys}
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
${assignment}
|
|
93
|
+
const inside = `
|
|
94
|
+
const response = await api.get<${responseType}, unknown>({
|
|
95
|
+
endpoint,
|
|
96
|
+
${querys}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
${assignment}
|
|
100
100
|
`;
|
|
101
101
|
return { inside, outside: "" };
|
|
102
102
|
}
|
|
@@ -111,23 +111,23 @@ export class CallMethodGenerator {
|
|
|
111
111
|
if (hasHeaders) {
|
|
112
112
|
outside.push(`const headers = this.headers.bundle()`);
|
|
113
113
|
}
|
|
114
|
-
const inside = `
|
|
115
|
-
const response = await api.${apiType}<${responseType}>({
|
|
116
|
-
endpoint,
|
|
117
|
-
${hasData ? "data," : ""}
|
|
118
|
-
${hasHeaders ? "headers," : ""}
|
|
119
|
-
})
|
|
114
|
+
const inside = `
|
|
115
|
+
const response = await api.${apiType}<${responseType}>({
|
|
116
|
+
endpoint,
|
|
117
|
+
${hasData ? "data," : ""}
|
|
118
|
+
${hasHeaders ? "headers," : ""}
|
|
119
|
+
})
|
|
120
120
|
`;
|
|
121
121
|
return { inside, outside: outside.join("\n") };
|
|
122
122
|
}
|
|
123
123
|
buildDeleteCall(method, responseType, bodyType, strategy) {
|
|
124
124
|
const hasData = !!bodyType;
|
|
125
125
|
const outside = hasData ? `const data = ${strategy.formStateAccess(method)}.bundle()` : "";
|
|
126
|
-
const inside = `
|
|
127
|
-
const response = await api.delete<${responseType}, unknown>({
|
|
128
|
-
endpoint,
|
|
129
|
-
${hasData ? "data," : ""}
|
|
130
|
-
})
|
|
126
|
+
const inside = `
|
|
127
|
+
const response = await api.delete<${responseType}, unknown>({
|
|
128
|
+
endpoint,
|
|
129
|
+
${hasData ? "data," : ""}
|
|
130
|
+
})
|
|
131
131
|
`;
|
|
132
132
|
return { inside, outside };
|
|
133
133
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { treatByUppercase } from "../../helpers/helpers.js";
|
|
2
|
+
import { queryOverrideEntryType } from "./queryOverride.js";
|
|
2
3
|
export class ModuleCallStrategy {
|
|
3
4
|
listStateAccess(method) {
|
|
4
5
|
return `this.list${method.stateSuffix}`;
|
|
@@ -17,27 +18,36 @@ export class ModuleCallStrategy {
|
|
|
17
18
|
buildParamsType(method) {
|
|
18
19
|
const behaviorType = `Behavior<${method.responseTypeInterface}, ApiErrorResponse>`;
|
|
19
20
|
const pathsBlock = this.buildPathsBlock(method);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
behavior?: ${behaviorType};
|
|
21
|
+
const queryBlock = this.buildQueryOverrideBlock(method);
|
|
22
|
+
const blocks = [`behavior?: ${behaviorType};`, pathsBlock, queryBlock]
|
|
23
|
+
.filter((b) => !!b)
|
|
24
|
+
.join("\n");
|
|
25
|
+
return `{
|
|
26
|
+
${blocks}
|
|
27
27
|
}`;
|
|
28
28
|
}
|
|
29
29
|
buildPathsBlock(method) {
|
|
30
30
|
const paths = method.analyzers.props.paths;
|
|
31
31
|
if (paths.length === 0)
|
|
32
32
|
return undefined;
|
|
33
|
-
return `
|
|
34
|
-
paths?: {
|
|
33
|
+
return `paths?: {
|
|
35
34
|
${paths
|
|
36
35
|
.map((p) => {
|
|
37
36
|
const type = p.rawType ?? p.type;
|
|
38
37
|
return `${p.name}: ${type}`;
|
|
39
38
|
})
|
|
40
|
-
.join("\n")}
|
|
41
|
-
}
|
|
39
|
+
.join("\n")}
|
|
40
|
+
};`;
|
|
41
|
+
}
|
|
42
|
+
buildQueryOverrideBlock(method) {
|
|
43
|
+
const querys = method.analyzers.props.querys;
|
|
44
|
+
if (querys.length === 0)
|
|
45
|
+
return undefined;
|
|
46
|
+
const fields = querys
|
|
47
|
+
.map((q) => `${q.name}?: ${queryOverrideEntryType(q)}`)
|
|
48
|
+
.join("\n");
|
|
49
|
+
return `queryOverride?: {
|
|
50
|
+
${fields}
|
|
51
|
+
};`;
|
|
42
52
|
}
|
|
43
53
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AttributeProp } from "../../types/types.js";
|
|
2
|
+
/** TS type literal for a single query override entry. Mirrors the
|
|
3
|
+
* shape produced by each Prop's bundle entry:
|
|
4
|
+
* PrimitiveProp / EnumProp → `string | null`
|
|
5
|
+
* ArrayProp (enum) → `${type}[]`
|
|
6
|
+
* ArrayProp (non-enum, raro) → `string[]`
|
|
7
|
+
*/
|
|
8
|
+
export declare function queryOverrideEntryType(q: AttributeProp): string;
|
|
9
|
+
/** Inline TS object type literal for the full queryOverride bag,
|
|
10
|
+
* derived from the method's query props. Returns undefined when there
|
|
11
|
+
* are no query params (caller should skip emitting the slot). */
|
|
12
|
+
export declare function queryOverrideTypeLiteral(querys: AttributeProp[]): string | undefined;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** TS type literal for a single query override entry. Mirrors the
|
|
2
|
+
* shape produced by each Prop's bundle entry:
|
|
3
|
+
* PrimitiveProp / EnumProp → `string | null`
|
|
4
|
+
* ArrayProp (enum) → `${type}[]`
|
|
5
|
+
* ArrayProp (non-enum, raro) → `string[]`
|
|
6
|
+
*/
|
|
7
|
+
export function queryOverrideEntryType(q) {
|
|
8
|
+
if ("isEnum" in q && q.isEnum)
|
|
9
|
+
return `${q.type}[]`;
|
|
10
|
+
if (!("rawType" in q) && !("enumName" in q))
|
|
11
|
+
return "string[]";
|
|
12
|
+
return "string | null";
|
|
13
|
+
}
|
|
14
|
+
/** Inline TS object type literal for the full queryOverride bag,
|
|
15
|
+
* derived from the method's query props. Returns undefined when there
|
|
16
|
+
* are no query params (caller should skip emitting the slot). */
|
|
17
|
+
export function queryOverrideTypeLiteral(querys) {
|
|
18
|
+
if (querys.length === 0)
|
|
19
|
+
return undefined;
|
|
20
|
+
const fields = querys.map((q) => `${q.name}?: ${queryOverrideEntryType(q)}`).join("; ");
|
|
21
|
+
return `{ ${fields} }`;
|
|
22
|
+
}
|
|
@@ -9,9 +9,10 @@ type ValidatorResult = string | null;
|
|
|
9
9
|
type ValidatorFn<T> = (v: T) => ValidatorResult;
|
|
10
10
|
type BundleResult<T> = T extends { bundle: () => infer R } ? R : T;
|
|
11
11
|
|
|
12
|
-
export type ApiCallParams<TResponse, TPaths = void
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
export type ApiCallParams<TResponse, TPaths = void, TQuery = void> = {
|
|
13
|
+
behavior?: Behavior<TResponse, ApiErrorResponse>;
|
|
14
|
+
} & (TPaths extends void ? object : { paths?: TPaths }) &
|
|
15
|
+
(TQuery extends void ? object : { queryOverride?: TQuery });
|
|
15
16
|
|
|
16
17
|
type PartialBuildedInput<T> = {
|
|
17
18
|
[K in Exclude<keyof T, "bundle">]?: BuildedInput<T[K]>;
|