@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 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
+ });
@@ -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
- output.push(`export const ${procName} = new ${options.global ? "af." : ""}ProcedureAPI${options.types ? `<${paramTypeName}, ${options.typesJsonReturnType ?? "{ Table?: unknown[] }"}>` : ""}({
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" : ""}
@@ -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
- /** Structured return type definition for procedures (generates inline TS type) */
81
- returnType?: ReturnTypeTable[];
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) */
@@ -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: entry.returnType ? returnTypeToString(entry.returnType) : undefined,
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.14.0",
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",