@olenbetong/appframe-vite 6.1.2 → 6.3.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.
@@ -0,0 +1,180 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
5
+ /**
6
+ * Build an inline TypeScript type string from a structured returnType definition.
7
+ *
8
+ * The server always returns a DataSet serialized as `{ Table: T[], Table1: U[], ... }`.
9
+ * The first table is always `"Table"`, subsequent ones are `"Table1"`, `"Table2"`, etc.
10
+ * An explicit `table` name on the entry overrides the auto-generated key (for procedures
11
+ * that assign custom DataTable.TableName values).
12
+ */
13
+ export function returnTypeToString(returnType) {
14
+ if (returnType.length === 0) {
15
+ return "unknown";
16
+ }
17
+ function tableRowType(table) {
18
+ if (table.fields.length === 0) {
19
+ return "Record<string, unknown>";
20
+ }
21
+ let props = table.fields.map((f) => `${f.name}: ${f.type}`).join("; ");
22
+ return `{ ${props} }`;
23
+ }
24
+ function tableKey(table, index) {
25
+ if (table.table) {
26
+ return table.table;
27
+ }
28
+ return index === 0 ? "Table" : `Table${index}`;
29
+ }
30
+ let props = returnType.map((t, i) => `${tableKey(t, i)}: ${tableRowType(t)}[]`).join("; ");
31
+ return `{ ${props} }`;
32
+ }
33
+ // ---------------------------------------------------------------------------
34
+ // Config file path
35
+ // ---------------------------------------------------------------------------
36
+ export const RESOURCES_CONFIG_FILE = "resources.yaml";
37
+ export function getResourcesConfigPath(filePath) {
38
+ return resolve(process.cwd(), filePath ?? RESOURCES_CONFIG_FILE);
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // Read / write
42
+ // ---------------------------------------------------------------------------
43
+ export async function readResourcesConfig(filePath) {
44
+ let configPath = getResourcesConfigPath(filePath);
45
+ if (!existsSync(configPath)) {
46
+ return {};
47
+ }
48
+ let raw = await readFile(configPath, "utf-8");
49
+ return parseYaml(raw) ?? {};
50
+ }
51
+ export async function writeResourcesConfig(config, filePath) {
52
+ let configPath = getResourcesConfigPath(filePath);
53
+ let content = stringifyYaml(config, { lineWidth: 120 });
54
+ await writeFile(configPath, content, "utf-8");
55
+ }
56
+ // ---------------------------------------------------------------------------
57
+ // Convert ResourceEntry ↔ CLIOptions
58
+ // ---------------------------------------------------------------------------
59
+ function arrayOrStringToString(value) {
60
+ if (value === undefined)
61
+ return undefined;
62
+ if (Array.isArray(value))
63
+ return value.join(",");
64
+ return value;
65
+ }
66
+ /** Convert sortOrder (new object format, old string/array) to comma-separated CLIOptions string. */
67
+ function sortOrderToString(sortOrder) {
68
+ if (!sortOrder)
69
+ return undefined;
70
+ if (typeof sortOrder === "string")
71
+ return sortOrder;
72
+ if (Array.isArray(sortOrder) && sortOrder.length === 0)
73
+ return undefined;
74
+ if (typeof sortOrder[0] === "string")
75
+ return sortOrder.join(",");
76
+ return sortOrder
77
+ .map((s) => (s.direction ? `${s.field}:${s.direction}` : s.field))
78
+ .join(",");
79
+ }
80
+ /** Convert aggregates (new object format, old string/array) to comma-separated CLIOptions string. */
81
+ function aggregatesToString(aggregates) {
82
+ if (!aggregates)
83
+ return undefined;
84
+ if (typeof aggregates === "string")
85
+ return aggregates;
86
+ if (Array.isArray(aggregates) && aggregates.length === 0)
87
+ return undefined;
88
+ if (typeof aggregates[0] === "string")
89
+ return aggregates.join(",");
90
+ return aggregates.map((a) => `${a.field}:${a.aggregate}`).join(",");
91
+ }
92
+ /** Convert a `ResourceEntry` into a `CLIOptions`-compatible object for code generation. */
93
+ export function entryToCLIOptions(entry, hostname) {
94
+ return {
95
+ server: hostname,
96
+ id: entry.id,
97
+ global: entry.global ?? false,
98
+ dynamic: entry.dynamic ?? false,
99
+ types: entry.types,
100
+ maxRecords: entry.maxRecords !== undefined ? String(entry.maxRecords) : "50",
101
+ sortOrder: sortOrderToString(entry.sortOrder),
102
+ permissions: entry.permissions,
103
+ master: entry.master,
104
+ linkFields: arrayOrStringToString(entry.linkFields),
105
+ expose: entry.expose,
106
+ unique: entry.unique,
107
+ overrides: arrayOrStringToString(entry.overrides),
108
+ distinct: entry.distinct,
109
+ aggregates: aggregatesToString(entry.aggregates),
110
+ groupBy: entry.groupBy?.join(","),
111
+ where: entry.where,
112
+ fields: arrayOrStringToString(entry.fields) ?? false,
113
+ output: resolve(process.cwd(), entry.output ?? `src/data/${entry.id}.ts`),
114
+ typesJsonReturnType: entry.returnType ? returnTypeToString(entry.returnType) : undefined,
115
+ };
116
+ }
117
+ /** Convert a `CLIOptions` back to a `ResourceEntry` for saving to YAML. */
118
+ export function cliOptionsToEntry(resource, options) {
119
+ function maybeArray(value) {
120
+ if (!value)
121
+ return undefined;
122
+ let arr = value.split(",").filter(Boolean);
123
+ return arr.length > 1 ? arr : undefined;
124
+ }
125
+ let entry = {
126
+ id: options.id,
127
+ resource,
128
+ output: options.output ?? `src/data/${options.id}.ts`,
129
+ };
130
+ if (options.global)
131
+ entry.global = true;
132
+ if (options.types)
133
+ entry.types = true;
134
+ if (options.permissions)
135
+ entry.permissions = options.permissions;
136
+ if (options.maxRecords && options.maxRecords !== "50") {
137
+ entry.maxRecords = Number(options.maxRecords);
138
+ }
139
+ if (options.sortOrder) {
140
+ entry.sortOrder = options.sortOrder
141
+ .split(",")
142
+ .filter(Boolean)
143
+ .map((s) => {
144
+ const [field, direction] = s.split(":");
145
+ return direction ? { field, direction } : { field };
146
+ });
147
+ }
148
+ if (options.master)
149
+ entry.master = options.master;
150
+ if (options.linkFields)
151
+ entry.linkFields = maybeArray(options.linkFields) ?? options.linkFields;
152
+ if (options.expose !== undefined)
153
+ entry.expose = options.expose;
154
+ if (options.dynamic)
155
+ entry.dynamic = true;
156
+ if (options.unique)
157
+ entry.unique = options.unique;
158
+ if (options.overrides)
159
+ entry.overrides = maybeArray(options.overrides) ?? options.overrides;
160
+ if (options.distinct)
161
+ entry.distinct = true;
162
+ if (options.aggregates) {
163
+ entry.aggregates = options.aggregates
164
+ .split(",")
165
+ .filter(Boolean)
166
+ .map((a) => {
167
+ const [field, aggregate] = a.split(":");
168
+ return { field, aggregate: aggregate ?? "" };
169
+ });
170
+ }
171
+ if (options.groupBy)
172
+ entry.groupBy = options.groupBy.split(",").filter(Boolean);
173
+ if (options.where)
174
+ entry.where = options.where;
175
+ if (options.fields && typeof options.fields === "string") {
176
+ let fields = options.fields.split(",").filter(Boolean);
177
+ entry.fields = fields.length > 1 ? fields : options.fields;
178
+ }
179
+ return entry;
180
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olenbetong/appframe-vite",
3
- "version": "6.1.2",
3
+ "version": "6.3.0",
4
4
  "description": "Tools to use and deploy Vite applications to Appframe",
5
5
  "main": "./lib/index.js",
6
6
  "type": "module",
@@ -13,6 +13,10 @@
13
13
  "./proxy": {
14
14
  "import": "./lib/proxy.js",
15
15
  "types": "./lib/proxy.d.ts"
16
+ },
17
+ "./resources": {
18
+ "import": "./lib/resourceGenerate.js",
19
+ "types": "./lib/resourceGenerate.d.ts"
16
20
  }
17
21
  },
18
22
  "files": [
@@ -25,19 +29,25 @@
25
29
  "author": "Bjørnar Vister Hansen <bvh@olenbetong.no>",
26
30
  "license": "MIT",
27
31
  "dependencies": {
32
+ "@olenbetong/appframe-data": "1.5.0",
28
33
  "body-parser": "^2.2.2",
29
34
  "chalk": "^5.4.1",
30
35
  "chokidar": "^5.0.0",
36
+ "commander": "^14.0.1",
31
37
  "dotenv": "^17.2.3",
32
- "jsdom": "29.0.2",
38
+ "fuzzy": "^0.1.3",
39
+ "inquirer": "^12.9.6",
40
+ "inquirer-autocomplete-standalone": "^0.8.1",
41
+ "jsdom": "29.1.1",
33
42
  "rollup-plugin-visualizer": "^6.0.5",
34
- "@olenbetong/appframe-data": "1.5.0"
43
+ "yaml": "^2.8.4",
44
+ "@olenbetong/appframe-devtools": "0.1.0"
35
45
  },
36
46
  "devDependencies": {
37
47
  "@types/jsdom": "^27.0.0",
38
48
  "@types/node": "25.6.0",
39
49
  "typescript": "6.0.3",
40
- "vite": "8.0.8"
50
+ "vite": "8.0.10"
41
51
  },
42
52
  "peerDependencies": {
43
53
  "vite": ">=5.0.0"