@olenbetong/appframe-vite 6.14.0 → 6.16.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/appEntry.d.ts +15 -0
- package/lib/appEntry.js +19 -0
- package/lib/appframeDataGlobal.js +2 -0
- package/lib/build.js +8 -9
- package/lib/cli-resources-edit.js +3 -0
- package/lib/cli-resources-migrate.js +4 -0
- package/lib/devServer.js +2 -10
- package/lib/index.js +14 -0
- package/lib/procedureReturnType.test.d.ts +1 -0
- package/lib/procedureReturnType.test.js +48 -0
- package/lib/resourceGenerate.js +17 -3
- package/lib/resourcesConfig.d.ts +6 -2
- package/lib/resourcesConfig.js +5 -1
- package/package.json +8 -8
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:
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the app's entry module. Apps have no `index.html`: the dev server fetches the
|
|
3
|
+
* article's HTML from Synergi and injects a `<script type="module">` pointing at this file,
|
|
4
|
+
* and the production build uses it as the single rolldown input.
|
|
5
|
+
*
|
|
6
|
+
* `relative` is a POSIX path from the root, usable as a glob pattern (`optimizeDeps.entries`
|
|
7
|
+
* takes globs, and an absolute Windows path would be read as one with escapes in it).
|
|
8
|
+
* The last candidate is returned even when it does not exist, so a missing entry fails
|
|
9
|
+
* with a resolve error that names the file instead of a vaguer one later on.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getAppEntry(root?: string): {
|
|
12
|
+
absolute: string;
|
|
13
|
+
relative: string;
|
|
14
|
+
url: string;
|
|
15
|
+
};
|
package/lib/appEntry.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
/** The entry file candidates, in the order they are tried, relative to the app root. */
|
|
4
|
+
const ENTRY_CANDIDATES = ["src/index.tsx", "src/index.ts", "src/index.js"];
|
|
5
|
+
/**
|
|
6
|
+
* Resolves the app's entry module. Apps have no `index.html`: the dev server fetches the
|
|
7
|
+
* article's HTML from Synergi and injects a `<script type="module">` pointing at this file,
|
|
8
|
+
* and the production build uses it as the single rolldown input.
|
|
9
|
+
*
|
|
10
|
+
* `relative` is a POSIX path from the root, usable as a glob pattern (`optimizeDeps.entries`
|
|
11
|
+
* takes globs, and an absolute Windows path would be read as one with escapes in it).
|
|
12
|
+
* The last candidate is returned even when it does not exist, so a missing entry fails
|
|
13
|
+
* with a resolve error that names the file instead of a vaguer one later on.
|
|
14
|
+
*/
|
|
15
|
+
export function getAppEntry(root = process.cwd()) {
|
|
16
|
+
let found = ENTRY_CANDIDATES.find((candidate) => existsSync(resolve(root, candidate)));
|
|
17
|
+
let relative = found ?? ENTRY_CANDIDATES[ENTRY_CANDIDATES.length - 1];
|
|
18
|
+
return { absolute: resolve(root, relative), relative, url: `/${relative}` };
|
|
19
|
+
}
|
package/lib/build.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
1
|
import { readdir, readFile, writeFile } from "node:fs/promises";
|
|
3
2
|
import { dirname, resolve } from "node:path";
|
|
4
3
|
import { visualizer } from "rollup-plugin-visualizer";
|
|
5
4
|
import { esmExternalRequirePlugin } from "vite";
|
|
5
|
+
import { getAppEntry } from "./appEntry.js";
|
|
6
6
|
import { importJson } from "./importJson.js";
|
|
7
7
|
/**
|
|
8
8
|
* The bare specifiers the 2026 site templates resolve through their import map to the
|
|
@@ -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
|
|
@@ -196,16 +202,9 @@ export async function addAppframeBuildConfig(config) {
|
|
|
196
202
|
sourcemap: true,
|
|
197
203
|
};
|
|
198
204
|
config.plugins = config.plugins ?? [];
|
|
199
|
-
let entry = resolve(process.cwd(), "./src/index.tsx");
|
|
200
|
-
if (!existsSync(resolve(entry))) {
|
|
201
|
-
entry = resolve(process.cwd(), "./src/index.ts");
|
|
202
|
-
}
|
|
203
|
-
if (!existsSync(resolve(entry))) {
|
|
204
|
-
entry = resolve(process.cwd(), "./src/index.js");
|
|
205
|
-
}
|
|
206
205
|
config.appType = "custom";
|
|
207
206
|
config.build.rolldownOptions = {
|
|
208
|
-
input: { main:
|
|
207
|
+
input: { main: getAppEntry().absolute },
|
|
209
208
|
plugins: [
|
|
210
209
|
// Cast visualizer plugin to any to avoid Rollup type version clashes
|
|
211
210
|
visualizer({ filename: "./dist/stats.html", gzipSize: true }),
|
|
@@ -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,
|
|
@@ -182,6 +182,10 @@ function dataObjectToEntry(datasource, fields, outputDir, warnings) {
|
|
|
182
182
|
if (sortFields.length > 0) {
|
|
183
183
|
entry.sortOrder = sortFields.map((f) => {
|
|
184
184
|
let direction = (f.AscDesc ?? "asc").toLowerCase();
|
|
185
|
+
// Keep the null placement of AscNullsLast / DescNullsFirst
|
|
186
|
+
if (direction === "ascnullslast" || direction === "descnullsfirst") {
|
|
187
|
+
return { field: f.FieldName, direction };
|
|
188
|
+
}
|
|
185
189
|
return direction.startsWith("desc") ? { field: f.FieldName, direction: "desc" } : { field: f.FieldName };
|
|
186
190
|
});
|
|
187
191
|
}
|
package/lib/devServer.js
CHANGED
|
@@ -5,6 +5,7 @@ import { AuthenticationError, authenticate, loginHint, resolveCredentials } from
|
|
|
5
5
|
import { Client } from "@olenbetong/appframe-data";
|
|
6
6
|
import dotenv from "dotenv";
|
|
7
7
|
import { JSDOM } from "jsdom";
|
|
8
|
+
import { getAppEntry } from "./appEntry.js";
|
|
8
9
|
import { importJson } from "./importJson.js";
|
|
9
10
|
import { getStringCache } from "./localization.js";
|
|
10
11
|
import { diagnoseServerResponse } from "./utils.js";
|
|
@@ -68,16 +69,7 @@ export function getProxyRoutes() {
|
|
|
68
69
|
* @returns
|
|
69
70
|
*/
|
|
70
71
|
async function transformArticleHtml(html) {
|
|
71
|
-
let entry;
|
|
72
|
-
if (existsSync(resolve(process.cwd(), "./src/index.tsx"))) {
|
|
73
|
-
entry = "/src/index.tsx";
|
|
74
|
-
}
|
|
75
|
-
else if (existsSync(resolve(process.cwd(), "./src/index.ts"))) {
|
|
76
|
-
entry = "/src/index.ts";
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
entry = "/src/index.js";
|
|
80
|
-
}
|
|
72
|
+
let entry = getAppEntry().url;
|
|
81
73
|
let article = appframe.article?.id;
|
|
82
74
|
let { hostname } = await getAppHostname();
|
|
83
75
|
let dom = new JSDOM(html);
|
package/lib/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import bodyParser from "body-parser";
|
|
4
4
|
import { watch } from "chokidar";
|
|
5
|
+
import { getAppEntry } from "./appEntry.js";
|
|
5
6
|
import { appframeDataGlobal } from "./appframeDataGlobal.js";
|
|
6
7
|
import { addAppframeBuildConfig, PLATFORM_EXTERNALS, PLATFORM_GLOBALS, PLATFORM_UPSTREAM, platformManifest, usesPlatform, } from "./build.js";
|
|
7
8
|
import { generateFromConfig } from "./cli-resources-generate.js";
|
|
@@ -184,6 +185,19 @@ export default function appframe(options = {}) {
|
|
|
184
185
|
...config.server,
|
|
185
186
|
proxy,
|
|
186
187
|
};
|
|
188
|
+
// Apps have no index.html, so the dependency scanner Vite runs at startup has
|
|
189
|
+
// nothing to crawl (it globs "**/*.html") and skips pre-bundling altogether. Every
|
|
190
|
+
// dependency is then discovered when a module first imports it: the ones behind
|
|
191
|
+
// the initial page load are batched, but a lazy route that pulls in a new one
|
|
192
|
+
// re-runs the optimizer and forces a full reload, which lands back on the page
|
|
193
|
+
// you navigated from. Crawling from the entry module finds the lazy imports up
|
|
194
|
+
// front, so the deps are ready before the browser asks for them.
|
|
195
|
+
if (!config.optimizeDeps?.entries) {
|
|
196
|
+
config.optimizeDeps = {
|
|
197
|
+
...config.optimizeDeps,
|
|
198
|
+
entries: [getAppEntry().relative],
|
|
199
|
+
};
|
|
200
|
+
}
|
|
187
201
|
}
|
|
188
202
|
return config;
|
|
189
203
|
},
|
|
@@ -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
|
@@ -169,6 +169,13 @@ export function parseYamlConfig(fileContent) {
|
|
|
169
169
|
// ---------------------------------------------------------------------------
|
|
170
170
|
// Code generation helpers
|
|
171
171
|
// ---------------------------------------------------------------------------
|
|
172
|
+
/**
|
|
173
|
+
* A field name as an object key in generated code, quoted when it is not a valid identifier,
|
|
174
|
+
* such as a view column named `EU-Neste`.
|
|
175
|
+
*/
|
|
176
|
+
function propertyKey(name) {
|
|
177
|
+
return /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name);
|
|
178
|
+
}
|
|
172
179
|
/**
|
|
173
180
|
* Convert Appframe (SQL) data types to typescript/field definition types
|
|
174
181
|
*/
|
|
@@ -302,7 +309,14 @@ export function getProcedureDefinition(name, procDefinition, options) {
|
|
|
302
309
|
output.push(`export type ${paramTypeName} = null | undefined | Record<string, unknown>;\n`);
|
|
303
310
|
}
|
|
304
311
|
}
|
|
305
|
-
|
|
312
|
+
// A configured return type gets a name of its own, so components can import it and it
|
|
313
|
+
// survives every regeneration instead of being written into the output by hand.
|
|
314
|
+
let resultType = "{ Table?: unknown[] }";
|
|
315
|
+
if (options.types && options.typesJsonReturnType) {
|
|
316
|
+
resultType = `${paramTypeName.replace(/Params$/, "")}Result`;
|
|
317
|
+
output.push(`export type ${resultType} = ${options.typesJsonReturnType};\n`);
|
|
318
|
+
}
|
|
319
|
+
output.push(`export const ${procName} = new ${options.global ? "af." : ""}ProcedureAPI${options.types ? `<${paramTypeName}, ${resultType}>` : ""}({
|
|
306
320
|
procedureId: "${name}",
|
|
307
321
|
parameters: ${JSON.stringify(parameters, null, 2)},
|
|
308
322
|
timeout: ${options.timeout ?? 30000}${options.transaction === false ? ",\n transaction: false" : ""}
|
|
@@ -387,7 +401,7 @@ export function getDataObjectDefinition(name, viewDefinition, options) {
|
|
|
387
401
|
type += " | null";
|
|
388
402
|
}
|
|
389
403
|
}
|
|
390
|
-
fieldTypes += ` ${field.name}: ${type};\n`;
|
|
404
|
+
fieldTypes += ` ${propertyKey(field.name)}: ${type};\n`;
|
|
391
405
|
}
|
|
392
406
|
types = `export type ${typeName} = {
|
|
393
407
|
${fieldTypes}}`;
|
|
@@ -458,7 +472,7 @@ ${fieldTypes}}`;
|
|
|
458
472
|
order = "DescNullsFirst";
|
|
459
473
|
break;
|
|
460
474
|
}
|
|
461
|
-
return `{ ${field}: ${sortPrefix}${order} }`;
|
|
475
|
+
return `{ ${propertyKey(field)}: ${sortPrefix}${order} }`;
|
|
462
476
|
})
|
|
463
477
|
.join(", ");
|
|
464
478
|
parametersOption.push(`sortOrder: [${sortOrder}]`);
|
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.16.0",
|
|
4
4
|
"description": "Tools to use and deploy Vite applications to Appframe",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -29,25 +29,25 @@
|
|
|
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.
|
|
32
|
+
"@olenbetong/appframe-auth": "^0.1.0",
|
|
33
|
+
"@olenbetong/appframe-data": "^1.8.3",
|
|
34
34
|
"body-parser": "^2.3.0",
|
|
35
35
|
"chalk": "6.0.1",
|
|
36
36
|
"chokidar": "^5.0.0",
|
|
37
37
|
"commander": "15.0.0",
|
|
38
|
-
"dotenv": "^18.0.
|
|
38
|
+
"dotenv": "^18.0.8",
|
|
39
39
|
"fuzzy": "^0.1.3",
|
|
40
|
-
"inquirer": "^14.2.
|
|
40
|
+
"inquirer": "^14.2.4",
|
|
41
41
|
"inquirer-autocomplete-standalone": "^0.8.1",
|
|
42
|
-
"jsdom": "30.1.
|
|
42
|
+
"jsdom": "30.1.3",
|
|
43
43
|
"rollup-plugin-visualizer": "^7.1.1",
|
|
44
44
|
"yaml": "^2.9.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/jsdom": "^30.0.0",
|
|
48
|
-
"@types/node": "26.6.
|
|
48
|
+
"@types/node": "26.6.5",
|
|
49
49
|
"typescript": "7.0.2",
|
|
50
|
-
"vite": "8.3.
|
|
50
|
+
"vite": "8.3.5"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"vite": ">=8.1.5"
|