@olenbetong/appframe-vite 6.14.0 → 6.15.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/README.md +40 -0
- package/lib/build.js +6 -0
- package/lib/cli-resources-edit.js +3 -0
- package/lib/procedureReturnType.test.d.ts +1 -0
- package/lib/procedureReturnType.test.js +48 -0
- package/lib/resourceGenerate.js +8 -1
- package/lib/resourcesConfig.d.ts +6 -2
- package/lib/resourcesConfig.js +5 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -279,11 +279,51 @@ procedures:
|
|
|
279
279
|
| `fields` | `string \| string[]` | Fields to include (all if omitted) |
|
|
280
280
|
| `transaction` | `boolean` | Procedures only. `false` stops the framework wrapping the call in a transaction |
|
|
281
281
|
| `timeout` | `number` | Procedures only. Milliseconds before the call is abandoned (default `30000`) |
|
|
282
|
+
| `returnType` | `object[] \| string` | Procedures only. The result type; see below |
|
|
282
283
|
|
|
283
284
|
A top-level `server` key can override the hostname (defaults to `appframe.proxy.hostname` from `package.json`).
|
|
284
285
|
|
|
285
286
|
**Master/child relationships:** set both `master` and `linkFields` on the child object. The generated data object will include `masterDataObject` and `linkFields` in the `generateApiDataObject(...)` options.
|
|
286
287
|
|
|
288
|
+
**Procedure result types:** the server only describes a procedure's parameters, so its result is
|
|
289
|
+
typed `{ Table?: unknown[] }` unless `returnType` says otherwise. Never type the result by editing
|
|
290
|
+
the generated file — every regeneration (a `resources.yaml` change seen by the dev server, a
|
|
291
|
+
`git pull --rebase` that touches it, `resources generate`) writes the file from scratch. With
|
|
292
|
+
`returnType` (and `types: true`) the generated file exports the type under the procedure's name,
|
|
293
|
+
next to the parameters type — `procIndexDNA` gets `IndexDNAParams` and `IndexDNAResult` — so
|
|
294
|
+
components import it from the generated file.
|
|
295
|
+
|
|
296
|
+
`returnType` is either a list of result sets, in the order the procedure returns them (`Table`,
|
|
297
|
+
`Table1`, …; `table` overrides the key), with each field's TypeScript type:
|
|
298
|
+
|
|
299
|
+
```yaml
|
|
300
|
+
procedures:
|
|
301
|
+
- id: procIndexDNA
|
|
302
|
+
resource: astp_Operations_IndexDNA
|
|
303
|
+
global: true
|
|
304
|
+
types: true
|
|
305
|
+
returnType:
|
|
306
|
+
- fields:
|
|
307
|
+
- name: IndexName
|
|
308
|
+
type: string
|
|
309
|
+
- name: CurrentFF
|
|
310
|
+
type: number
|
|
311
|
+
- fields:
|
|
312
|
+
- name: PageSort
|
|
313
|
+
type: number
|
|
314
|
+
output: src/features/IndexDetails/procIndexDNA.ts
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
or a TypeScript type written out as a string, for anything the list cannot express. It may refer
|
|
318
|
+
to types in the app's `custom.d.ts` through the `Custom.` prefix, which adds the import:
|
|
319
|
+
|
|
320
|
+
```yaml
|
|
321
|
+
returnType: "Custom.IndexDNAResult"
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
`procedureReturnTypes` in `types.json`, keyed by the procedure's database name, does the same
|
|
325
|
+
and is used when the entry has no `returnType`.
|
|
326
|
+
|
|
287
327
|
## `@olenbetong/appframe-vite/resources` export
|
|
288
328
|
|
|
289
329
|
Shared code generation utilities for Node.js consumers:
|
package/lib/build.js
CHANGED
|
@@ -38,6 +38,10 @@ export const PLATFORM_EXTERNALS = [
|
|
|
38
38
|
"highcharts-react-official",
|
|
39
39
|
"mqtt",
|
|
40
40
|
"temporal-polyfill",
|
|
41
|
+
// The code editor. Its stylesheet and language workers are served next to the entry, not
|
|
42
|
+
// linked by the template: only the pages that open an editor load them (synergi-react
|
|
43
|
+
// `CodeEditor` resolves them from the specifier).
|
|
44
|
+
"monaco-editor",
|
|
41
45
|
];
|
|
42
46
|
/**
|
|
43
47
|
* The IIFE global each platform specifier is published under (`buildConfig.entries` in the
|
|
@@ -68,6 +72,7 @@ export const PLATFORM_GLOBALS = {
|
|
|
68
72
|
"highcharts-react-official": "SynergiPlatform.highchartsReact",
|
|
69
73
|
mqtt: "SynergiPlatform.mqtt",
|
|
70
74
|
"temporal-polyfill": "SynergiPlatform.temporal",
|
|
75
|
+
"monaco-editor": "SynergiPlatform.monaco",
|
|
71
76
|
};
|
|
72
77
|
export function usesPlatform(appframe) {
|
|
73
78
|
return appframe?.build?.platform === true;
|
|
@@ -94,6 +99,7 @@ export const PLATFORM_UPSTREAM = [
|
|
|
94
99
|
"highcharts-react-official",
|
|
95
100
|
"mqtt",
|
|
96
101
|
"temporal-polyfill",
|
|
102
|
+
"monaco-editor",
|
|
97
103
|
];
|
|
98
104
|
/**
|
|
99
105
|
* Whether the app renders `ToolbarDrawerToggle` (synergi-react) anywhere under `src/`: a plain
|
|
@@ -144,7 +144,10 @@ export async function editResource(id) {
|
|
|
144
144
|
sortOrder = sortInput || undefined;
|
|
145
145
|
}
|
|
146
146
|
let relativeOutput = relative(process.cwd(), resolve(process.cwd(), output)).replace(/\\/g, "/");
|
|
147
|
+
// Start from the existing entry so settings the wizard does not ask about (returnType,
|
|
148
|
+
// master, linkFields, timeout, transaction, ...) are kept
|
|
147
149
|
let updatedEntry = {
|
|
150
|
+
...entry,
|
|
148
151
|
id: newId,
|
|
149
152
|
resource: entry.resource,
|
|
150
153
|
output: relativeOutput,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { getProcedureDefinition } from "./resourceGenerate.js";
|
|
3
|
+
import { entryToCLIOptions } from "./resourcesConfig.js";
|
|
4
|
+
const definition = {
|
|
5
|
+
Parameters: [{ ParamName: "pIndexID", TypeName: "int", has_default_value: false, is_nullable: false }],
|
|
6
|
+
};
|
|
7
|
+
function generate(returnType) {
|
|
8
|
+
let options = entryToCLIOptions({
|
|
9
|
+
id: "procIndexDNA",
|
|
10
|
+
resource: "astp_Operations_IndexDNA",
|
|
11
|
+
output: "src/procIndexDNA.ts",
|
|
12
|
+
global: true,
|
|
13
|
+
types: true,
|
|
14
|
+
returnType,
|
|
15
|
+
}, "example.com");
|
|
16
|
+
return getProcedureDefinition("astp_Operations_IndexDNA", definition, options);
|
|
17
|
+
}
|
|
18
|
+
describe("procedure return types", () => {
|
|
19
|
+
it("keeps the untyped default without a returnType", () => {
|
|
20
|
+
let output = generate(undefined);
|
|
21
|
+
expect(output).not.toContain("IndexDNAResult");
|
|
22
|
+
expect(output).toContain("ProcedureAPI<IndexDNAParams, { Table?: unknown[] }>");
|
|
23
|
+
});
|
|
24
|
+
it("names a structured returnType after the procedure", () => {
|
|
25
|
+
let output = generate([
|
|
26
|
+
{ fields: [{ name: "IndexName", type: "string" }] },
|
|
27
|
+
{ fields: [{ name: "PageSort", type: "number" }] },
|
|
28
|
+
]);
|
|
29
|
+
expect(output).toContain("export type IndexDNAResult = { Table: { IndexName: string }[]; Table1: { PageSort: number }[] };");
|
|
30
|
+
expect(output).toContain("ProcedureAPI<IndexDNAParams, IndexDNAResult>");
|
|
31
|
+
});
|
|
32
|
+
it("uses a string returnType as written", () => {
|
|
33
|
+
let output = generate("Custom.IndexDNAResult");
|
|
34
|
+
expect(output).toContain("export type IndexDNAResult = Custom.IndexDNAResult;");
|
|
35
|
+
expect(output).toContain('import type * as Custom from "');
|
|
36
|
+
});
|
|
37
|
+
it("does not emit a result type without types", () => {
|
|
38
|
+
let options = entryToCLIOptions({
|
|
39
|
+
id: "procIndexDNA",
|
|
40
|
+
resource: "astp_Operations_IndexDNA",
|
|
41
|
+
output: "src/procIndexDNA.ts",
|
|
42
|
+
global: true,
|
|
43
|
+
returnType: "{ Table: [] }",
|
|
44
|
+
}, "example.com");
|
|
45
|
+
let output = getProcedureDefinition("astp_Operations_IndexDNA", definition, options);
|
|
46
|
+
expect(output).not.toContain("IndexDNAResult");
|
|
47
|
+
});
|
|
48
|
+
});
|
package/lib/resourceGenerate.js
CHANGED
|
@@ -302,7 +302,14 @@ export function getProcedureDefinition(name, procDefinition, options) {
|
|
|
302
302
|
output.push(`export type ${paramTypeName} = null | undefined | Record<string, unknown>;\n`);
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
|
-
|
|
305
|
+
// A configured return type gets a name of its own, so components can import it and it
|
|
306
|
+
// survives every regeneration instead of being written into the output by hand.
|
|
307
|
+
let resultType = "{ Table?: unknown[] }";
|
|
308
|
+
if (options.types && options.typesJsonReturnType) {
|
|
309
|
+
resultType = `${paramTypeName.replace(/Params$/, "")}Result`;
|
|
310
|
+
output.push(`export type ${resultType} = ${options.typesJsonReturnType};\n`);
|
|
311
|
+
}
|
|
312
|
+
output.push(`export const ${procName} = new ${options.global ? "af." : ""}ProcedureAPI${options.types ? `<${paramTypeName}, ${resultType}>` : ""}({
|
|
306
313
|
procedureId: "${name}",
|
|
307
314
|
parameters: ${JSON.stringify(parameters, null, 2)},
|
|
308
315
|
timeout: ${options.timeout ?? 30000}${options.transaction === false ? ",\n transaction: false" : ""}
|
package/lib/resourcesConfig.d.ts
CHANGED
|
@@ -77,8 +77,12 @@ export type ResourceEntry = {
|
|
|
77
77
|
where?: string;
|
|
78
78
|
/** Fields to include: comma-separated or array of field names */
|
|
79
79
|
fields?: string | string[];
|
|
80
|
-
/**
|
|
81
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Procedures only. The result type, emitted as `export type <Name>Result` next to the
|
|
82
|
+
* parameters type. Either a list of result sets with their fields, or a TypeScript type
|
|
83
|
+
* written out as a string (which may reference `Custom.*` types from `custom.d.ts`).
|
|
84
|
+
*/
|
|
85
|
+
returnType?: ReturnTypeTable[] | string;
|
|
82
86
|
};
|
|
83
87
|
export type ResourcesConfig = {
|
|
84
88
|
/** Optional server hostname override (defaults to `appframe.proxy.hostname` from package.json) */
|
package/lib/resourcesConfig.js
CHANGED
|
@@ -113,7 +113,11 @@ export function entryToCLIOptions(entry, hostname) {
|
|
|
113
113
|
where: entry.where,
|
|
114
114
|
fields: arrayOrStringToString(entry.fields) ?? false,
|
|
115
115
|
output: resolve(process.cwd(), entry.output ?? `src/data/${entry.id}.ts`),
|
|
116
|
-
typesJsonReturnType:
|
|
116
|
+
typesJsonReturnType: typeof entry.returnType === "string"
|
|
117
|
+
? entry.returnType
|
|
118
|
+
: entry.returnType
|
|
119
|
+
? returnTypeToString(entry.returnType)
|
|
120
|
+
: undefined,
|
|
117
121
|
};
|
|
118
122
|
}
|
|
119
123
|
/** Convert a `CLIOptions` back to a `ResourceEntry` for saving to YAML. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@olenbetong/appframe-vite",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.15.0",
|
|
4
4
|
"description": "Tools to use and deploy Vite applications to Appframe",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"author": "Bjørnar Vister Hansen <bvh@olenbetong.no>",
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@olenbetong/appframe-auth": "0.1.0",
|
|
33
|
-
"@olenbetong/appframe-data": "1.8.2",
|
|
32
|
+
"@olenbetong/appframe-auth": "^0.1.0",
|
|
33
|
+
"@olenbetong/appframe-data": "^1.8.2",
|
|
34
34
|
"body-parser": "^2.3.0",
|
|
35
35
|
"chalk": "6.0.1",
|
|
36
36
|
"chokidar": "^5.0.0",
|