adminforth 1.18.0 → 1.20.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.
Files changed (34) hide show
  1. package/commands/bundle.js +15 -21
  2. package/commands/callTsProxy.js +121 -0
  3. package/commands/cli.js +52 -2
  4. package/commands/createApp/templates/.dockerignore.hbs +4 -0
  5. package/commands/createApp/templates/.env.prod.hbs +6 -0
  6. package/commands/createApp/templates/Dockerfile.hbs +7 -0
  7. package/commands/createApp/templates/index.ts.hbs +2 -2
  8. package/commands/createApp/templates/package.json.hbs +8 -5
  9. package/commands/createApp/templates/readme.md.hbs +14 -2
  10. package/commands/createApp/utils.js +38 -13
  11. package/commands/createCustomComponent/configLoader.js +57 -0
  12. package/commands/createCustomComponent/configUpdater.js +207 -0
  13. package/commands/createCustomComponent/fileGenerator.js +72 -0
  14. package/commands/createCustomComponent/main.js +138 -0
  15. package/commands/createCustomComponent/templates/customFields/create.vue.hbs +41 -0
  16. package/commands/createCustomComponent/templates/customFields/edit.vue.hbs +41 -0
  17. package/commands/createCustomComponent/templates/customFields/list.vue.hbs +18 -0
  18. package/commands/createCustomComponent/templates/customFields/show.vue.hbs +17 -0
  19. package/commands/proxy.ts +60 -0
  20. package/dist/commands/proxy.d.ts +2 -0
  21. package/dist/commands/proxy.d.ts.map +1 -0
  22. package/dist/commands/proxy.js +71 -0
  23. package/dist/commands/proxy.js.map +1 -0
  24. package/dist/index.js +1 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/modules/codeInjector.d.ts +2 -0
  27. package/dist/modules/codeInjector.d.ts.map +1 -1
  28. package/dist/modules/codeInjector.js +64 -13
  29. package/dist/modules/codeInjector.js.map +1 -1
  30. package/dist/spa/src/afcl/Dropzone.vue +14 -3
  31. package/dist/spa/src/renderers/CompactField.vue +3 -1
  32. package/dist/spa/src/renderers/CompactUUID.vue +3 -1
  33. package/dist/spa/src/views/EditView.vue +3 -1
  34. package/package.json +4 -1
@@ -0,0 +1,207 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ import recast from 'recast'; // Import recast
5
+ import * as typescriptParser from 'recast/parsers/typescript.js'; // Import the parser using ESM and include the .js extension
6
+
7
+ const b = recast.types.builders; // Like t.* in babel/types
8
+ const n = recast.types.namedTypes; // Like t.is* in babel/types
9
+
10
+
11
+ async function findResourceFilePath(resourceId) {
12
+ const projectRoot = process.cwd();
13
+ const resourcesDir = path.resolve(projectRoot, 'resources');
14
+ console.log(chalk.dim(`Scanning for resource files in: ${resourcesDir}`));
15
+
16
+ let tsFiles = [];
17
+ try {
18
+ const entries = await fs.readdir(resourcesDir, { withFileTypes: true });
19
+ tsFiles = entries
20
+ .filter(dirent => dirent.isFile() && dirent.name.endsWith('.ts') && !dirent.name.endsWith('.d.ts'))
21
+ .map(dirent => dirent.name);
22
+ } catch (error) {
23
+ if (error.code === 'ENOENT') {
24
+ throw new Error(`Resources directory not found at ${resourcesDir}. Please ensure it exists.`);
25
+ }
26
+ throw new Error(`Failed to read resources directory ${resourcesDir}: ${error.message}`);
27
+ }
28
+
29
+ console.log(chalk.dim(`Found .ts files to scan: ${tsFiles.join(', ') || 'None'}`));
30
+
31
+ for (const file of tsFiles) {
32
+ const filePath = path.resolve(resourcesDir, file);
33
+ console.log(chalk.dim(`Attempting to process file: ${file}`));
34
+ try {
35
+ const content = await fs.readFile(filePath, 'utf-8');
36
+ const ast = recast.parse(content, {
37
+ parser: typescriptParser
38
+ });
39
+
40
+ let foundResourceId = null;
41
+
42
+ recast.visit(ast, {
43
+ visitExportDefaultDeclaration(path) {
44
+ if (foundResourceId !== null) return false; // Stop visiting deeper if already found
45
+
46
+ const declaration = path.node.declaration;
47
+ let objectExpressionNode = null;
48
+
49
+ if (n.TSAsExpression.check(declaration) && n.ObjectExpression.check(declaration.expression)) {
50
+ objectExpressionNode = declaration.expression;
51
+ }
52
+ else if (n.ObjectExpression.check(declaration)) {
53
+ objectExpressionNode = declaration;
54
+ }
55
+
56
+ if (objectExpressionNode) {
57
+ const resourceIdProp = objectExpressionNode.properties.find(prop =>
58
+ n.ObjectProperty.check(prop) &&
59
+ n.Identifier.check(prop.key) &&
60
+ prop.key.name === 'resourceId' &&
61
+ n.StringLiteral.check(prop.value)
62
+ );
63
+ if (resourceIdProp) {
64
+ foundResourceId = resourceIdProp.value.value; // Get the string value
65
+ console.log(chalk.dim(` Extracted resourceId '${foundResourceId}' from ${file}`));
66
+ this.abort(); // Stop traversal for this file once found
67
+ }
68
+ }
69
+ return false;
70
+ }
71
+ });
72
+
73
+ console.log(chalk.dim(` Finished processing ${file}. Found resourceId: ${foundResourceId || 'null'}`));
74
+
75
+ if (foundResourceId === resourceId) {
76
+ console.log(chalk.dim(` Match found! Returning path: ${filePath}`));
77
+ return filePath;
78
+ }
79
+ } catch (parseError) {
80
+ if (parseError.message.includes('require is not defined')) {
81
+ console.error(chalk.red(`❌ Internal Error: Failed to load Recast parser in ESM context for ${file}.`));
82
+ } else {
83
+ console.warn(chalk.yellow(`⚠️ Warning: Could not process file ${file}. Skipping. Error: ${parseError.message}`));
84
+ }
85
+ }
86
+ }
87
+
88
+ throw new Error(`Could not find a resource file in '${resourcesDir}' with resourceId: '${resourceId}'`);
89
+ }
90
+
91
+
92
+ export async function updateResourceConfig(resourceId, columnName, fieldType, componentPathForConfig) {
93
+ const filePath = await findResourceFilePath(resourceId);
94
+ console.log(chalk.dim(`Attempting to update resource config: ${filePath}`));
95
+
96
+ let content;
97
+ try {
98
+ content = await fs.readFile(filePath, 'utf-8');
99
+ } catch (error) {
100
+ console.error(chalk.red(`❌ Error reading resource file: ${filePath}`));
101
+ console.error(error);
102
+ throw new Error(`Could not read resource file ${filePath}.`);
103
+ }
104
+
105
+ try {
106
+ const ast = recast.parse(content, {
107
+ parser: typescriptParser
108
+ });
109
+
110
+ let updateApplied = false;
111
+
112
+ recast.visit(ast, {
113
+ visitExportDefaultDeclaration(path) {
114
+ const declaration = path.node.declaration;
115
+ let objectExpressionNode = null;
116
+
117
+ if (n.TSAsExpression.check(declaration) && n.ObjectExpression.check(declaration.expression)) {
118
+ objectExpressionNode = declaration.expression;
119
+ } else if (n.ObjectExpression.check(declaration)) {
120
+ objectExpressionNode = declaration;
121
+ }
122
+
123
+ if (!objectExpressionNode) {
124
+ console.warn(chalk.yellow(`Warning: Default export in ${filePath} is not a recognized ObjectExpression or TSAsExpression containing one. Skipping update.`));
125
+ return false;
126
+ }
127
+
128
+ const properties = objectExpressionNode.properties;
129
+ const columnsProperty = properties.find(prop =>
130
+ n.ObjectProperty.check(prop) && n.Identifier.check(prop.key) && prop.key.name === 'columns'
131
+ );
132
+
133
+ if (!columnsProperty || !n.ArrayExpression.check(columnsProperty.value)) {
134
+ console.warn(chalk.yellow(`Warning: Could not find 'columns' array in the default export of ${filePath}. Skipping update.`));
135
+ return false;
136
+ }
137
+
138
+ const columnsArray = columnsProperty.value.elements;
139
+ const targetColumn = columnsArray.find(col => {
140
+ if (n.ObjectExpression.check(col)) {
141
+ const nameProp = col.properties.find(p =>
142
+ n.ObjectProperty.check(p) && n.Identifier.check(p.key) && p.key.name === 'name' &&
143
+ n.StringLiteral.check(p.value) && p.value.value === columnName
144
+ );
145
+ return !!nameProp;
146
+ }
147
+ return false;
148
+ });
149
+
150
+ if (!targetColumn || !n.ObjectExpression.check(targetColumn)) {
151
+ return false;
152
+ }
153
+
154
+ let componentsProperty = targetColumn.properties.find(p =>
155
+ n.ObjectProperty.check(p) && n.Identifier.check(p.key) && p.key.name === 'components'
156
+ );
157
+
158
+ if (!componentsProperty) {
159
+ const newComponentsObject = b.objectExpression([]);
160
+ componentsProperty = b.objectProperty(b.identifier('components'), newComponentsObject);
161
+
162
+ const nameIndex = targetColumn.properties.findIndex(p => n.ObjectProperty.check(p) && n.Identifier.check(p.key) && p.key.name === 'name');
163
+ targetColumn.properties.splice(nameIndex !== -1 ? nameIndex + 1 : targetColumn.properties.length, 0, componentsProperty);
164
+ console.log(chalk.dim(`Added 'components' object to column '${columnName}'.`));
165
+
166
+ } else if (!n.ObjectExpression.check(componentsProperty.value)) {
167
+ console.warn(chalk.yellow(`Warning: 'components' property in column '${columnName}' is not an object. Skipping update.`));
168
+ return false;
169
+ }
170
+
171
+ const componentsObject = componentsProperty.value;
172
+ let fieldTypeProperty = componentsObject.properties.find(p =>
173
+ n.ObjectProperty.check(p) && n.Identifier.check(p.key) && p.key.name === fieldType
174
+ );
175
+
176
+ const newComponentValue = b.stringLiteral(componentPathForConfig);
177
+
178
+ if (fieldTypeProperty) {
179
+ fieldTypeProperty.value = newComponentValue;
180
+ console.log(chalk.dim(`Updated '${fieldType}' component path in column '${columnName}'.`));
181
+ } else {
182
+ fieldTypeProperty = b.objectProperty(b.identifier(fieldType), newComponentValue);
183
+ componentsObject.properties.push(fieldTypeProperty);
184
+ console.log(chalk.dim(`Added '${fieldType}' component path to column '${columnName}'.`));
185
+ }
186
+
187
+ updateApplied = true;
188
+ this.abort();
189
+ return false;
190
+ }
191
+ });
192
+
193
+ if (!updateApplied) {
194
+ throw new Error(`Could not find column '${columnName}' or apply update within the default export's 'columns' array in ${filePath}.`);
195
+ }
196
+
197
+ const outputCode = recast.print(ast).code;
198
+
199
+ await fs.writeFile(filePath, outputCode, 'utf-8');
200
+ console.log(chalk.dim(`Successfully updated resource configuration file (preserving formatting): ${filePath}`));
201
+
202
+ } catch (error) {
203
+ console.error(chalk.red(`❌ Error processing resource file: ${filePath}`));
204
+ console.error(error);
205
+ throw new Error(`Failed to update resource file ${path.basename(filePath)}: ${error.message}`);
206
+ }
207
+ }
@@ -0,0 +1,72 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ import Handlebars from 'handlebars';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ async function renderHBSTemplate(templatePath, data) {
8
+ try {
9
+ const templateContent = await fs.readFile(templatePath, 'utf-8');
10
+ const compiled = Handlebars.compile(templateContent);
11
+ return compiled(data);
12
+ } catch (error) {
13
+ console.error(chalk.red(`❌ Error reading or compiling template: ${templatePath}`));
14
+ throw error;
15
+ }
16
+ }
17
+
18
+ async function generateVueContent(fieldType, { resource, column }) {
19
+ const componentName = `${resource.label}${column.label}${fieldType.charAt(0).toUpperCase() + fieldType.slice(1)}`;
20
+ const columnName = column.name;
21
+ const resourceId = resource.resourceId;
22
+
23
+ const __filename = fileURLToPath(import.meta.url);
24
+ const __dirname = path.dirname(__filename);
25
+ const templatePath = path.join(__dirname, 'templates', 'customFields', `${fieldType}.vue.hbs`);
26
+
27
+ console.log(chalk.dim(`Using template: ${templatePath}`));
28
+
29
+ const context = {
30
+ componentName,
31
+ columnName,
32
+ resourceId,
33
+ resource,
34
+ column
35
+ };
36
+
37
+ try {
38
+ const fileContent = await renderHBSTemplate(templatePath, context);
39
+ return fileContent;
40
+ } catch (error) {
41
+ console.error(chalk.red(`❌ Failed to generate content for ${componentName}.vue`));
42
+ throw error;
43
+ }
44
+ }
45
+
46
+ export async function generateComponentFile(componentFileName, fieldType, context, config) {
47
+
48
+ const customDirRelative = 'custom';
49
+
50
+ const projectRoot = process.cwd();
51
+ const customDirPath = path.resolve(projectRoot, customDirRelative);
52
+ const absoluteComponentPath = path.resolve(customDirPath, componentFileName);
53
+
54
+ try {
55
+ await fs.mkdir(customDirPath, { recursive: true });
56
+ console.log(chalk.dim(`Ensured custom directory exists: ${customDirPath}`));
57
+
58
+ const fileContent = await generateVueContent(fieldType, context);
59
+
60
+ await fs.writeFile(absoluteComponentPath, fileContent, 'utf-8');
61
+ console.log(chalk.green(`✅ Generated component file: ${absoluteComponentPath}`));
62
+
63
+ return absoluteComponentPath;
64
+
65
+ } catch (error) {
66
+ console.error(chalk.red(`❌ Error creating component file at ${absoluteComponentPath}:`));
67
+ if (!error.message.includes('template')) {
68
+ console.error(error);
69
+ }
70
+ throw error;
71
+ }
72
+ }
@@ -0,0 +1,138 @@
1
+ import { select, confirm, Separator } from '@inquirer/prompts';
2
+ import chalk from 'chalk';
3
+ import path from 'path'; // Import path
4
+ import { loadAdminForthConfig } from './configLoader.js'; // Helper to load config
5
+ import { generateComponentFile } from './fileGenerator.js'; // Helper to create the .vue file
6
+ import { updateResourceConfig } from './configUpdater.js'; // Helper to modify resource .ts file
7
+ // import { openFileInIde } from './ideHelper.js'; // Helper to open file
8
+
9
+ export default async function createComponent(args) {
10
+ console.log('This command will help you to generate boilerplate for component.\n');
11
+
12
+ const config = await loadAdminForthConfig();
13
+ const resources = config.resources;
14
+
15
+ const componentType = await select({
16
+ message: 'What component type would you like to add?',
17
+ choices: [
18
+ { name: `🔤 Custom fields ${chalk.grey('fields')}`, value: 'fields' },
19
+ { name: `➖ CRUD page injections ${chalk.grey('crudPage')}`, value: 'crudPage' },
20
+ { name: `🔐 Login page injections ${chalk.grey('login')}`, value: 'login' },
21
+ { name: `🌐 Global Injections ${chalk.grey('global')}`, value: 'global' },
22
+ ],
23
+ });
24
+
25
+ if (componentType === 'fields') {
26
+ await handleFieldComponentCreation(config, resources);
27
+ } else if (componentType === 'crudPage') {
28
+ await handleCrudPageInjectionCreation(config, resources);
29
+ } else if (componentType === 'login') {
30
+ await handleLoginPageInjectionCreation(config);
31
+ } else if (componentType === 'global') {
32
+ await handleGlobalInjectionCreation(config);
33
+ }
34
+ }
35
+
36
+ async function handleFieldComponentCreation(config, resources) {
37
+ console.log(chalk.grey('Selected ❯ 🔤 Custom fields'));
38
+
39
+ const fieldType = await select({
40
+ message: 'What view component would you like to add?',
41
+ choices: [
42
+ { name: '🔸 list', value: 'list' },
43
+ { name: '📃 show', value: 'show' },
44
+ { name: '✏️ edit', value: 'edit' },
45
+ { name: '➕ create', value: 'create' },
46
+ new Separator(),
47
+ { name: '🔙 BACK', value: '__BACK__' },
48
+ ]
49
+ });
50
+ if (fieldType === '__BACK__') return createComponent([]); // Go back to main menu
51
+
52
+ console.log(chalk.grey(`Selected ❯ 🔤 Custom fields ❯ ${fieldType}`));
53
+
54
+ const resourceId = await select({
55
+ message: 'Select resource for which you want to change component:',
56
+ choices: [
57
+ ...resources.map(r => ({ name: `${r.label} ${chalk.grey(`${r.resourceId}`)}`, value: r.resourceId })),
58
+ new Separator(),
59
+ { name: '🔙 BACK', value: '__BACK__' },
60
+ ]
61
+ });
62
+ if (resourceId === '__BACK__') return handleFieldComponentCreation(config, resources); // Pass config back
63
+
64
+ const selectedResource = resources.find(r => r.resourceId === resourceId);
65
+ console.log(chalk.grey(`Selected ❯ 🔤 Custom fields ❯ ${fieldType} ❯ ${selectedResource.label}`));
66
+
67
+ const columnName = await select({
68
+ message: 'Select column for which you want to create component:',
69
+ choices: [
70
+ ...selectedResource.columns.map(c => ({ name: `${c.label} ${chalk.grey(`${c.name}`)}`, value: c.name })),
71
+ new Separator(),
72
+ { name: '🔙 BACK', value: '__BACK__' },
73
+ ]
74
+ });
75
+ if (columnName === '__BACK__') return handleFieldComponentCreation(config, resources); // Pass config back
76
+
77
+ const selectedColumn = selectedResource.columns.find(c => c.name === columnName);
78
+ console.log(chalk.grey(`Selected ❯ 🔤 Custom fields ❯ ${fieldType} ❯ ${selectedResource.label} ❯ ${selectedColumn.label}`));
79
+ console.log(chalk.dim(`One-line alternative: |adminforth component fields.${fieldType}.${resourceId}.${columnName}|`));
80
+
81
+
82
+ const existingComponentPath = null;
83
+
84
+ if (existingComponentPath) {
85
+ const action = await select({
86
+ message: 'You already have a component for this field, open it in editor?',
87
+ choices: [
88
+ { name: '✏️ Open in IDE', value: 'open' },
89
+ { name: '🔙 BACK', value: '__BACK__' },
90
+ { name: '🚪 Exit', value: '__EXIT__' },
91
+ ]
92
+ });
93
+ if (action === 'open') {
94
+ // await openFileInIde(existingComponentPath); // Needs absolute path
95
+ console.log(`Opening ${existingComponentPath}... (Implementation needed)`);
96
+ } else if (action === '__BACK__') {
97
+ return handleFieldComponentCreation(config, resources); // Pass config back
98
+ } else {
99
+ process.exit(0);
100
+ }
101
+ } else {
102
+ const safeResourceLabel = selectedResource.label.replace(/[^a-zA-Z0-9]/g, '');
103
+ const safeColumnLabel = selectedColumn.label.replace(/[^a-zA-Z0-9]/g, '');
104
+ const componentFileName = `${safeResourceLabel}${safeColumnLabel}${fieldType.charAt(0).toUpperCase() + fieldType.slice(1)}.vue`; // e.g., UserEmailShow.vue
105
+ const componentPathForConfig = `@@/${componentFileName}`; // Path relative to custom dir for config
106
+
107
+ let absoluteComponentPath;
108
+ try {
109
+ absoluteComponentPath = await generateComponentFile(
110
+ componentFileName,
111
+ fieldType,
112
+ { resource: selectedResource, column: selectedColumn },
113
+ config
114
+ );
115
+ console.log(chalk.dim(`Component generation successful: ${absoluteComponentPath}`));
116
+
117
+ await updateResourceConfig(selectedResource.resourceId, columnName, fieldType, componentPathForConfig);
118
+ console.log(chalk.green(`\n✅ Successfully created component ${componentPathForConfig} and updated configuration.`));
119
+
120
+ const openNow = await confirm({
121
+ message: 'Open the new component file in your IDE?',
122
+ default: true
123
+ });
124
+ if (openNow) { // await openFileInIde(absoluteComponentPath); // Use the absolute path here
125
+ console.log(`Opening ${absoluteComponentPath}... (Implementation needed)`);
126
+ }
127
+
128
+ } catch (error) {
129
+ console.error(chalk.red('\n❌ Component creation failed. Please check the errors above.'));
130
+ process.exit(1);
131
+ }
132
+ }
133
+ }
134
+
135
+ // --- TODO: Implement similar handlers for other component types (pass config) ---
136
+ async function handleCrudPageInjectionCreation(config, resources) { console.log('CRUD Page Injection creation not implemented yet.'); }
137
+ async function handleLoginPageInjectionCreation(config) { console.log('Login Page Injection creation not implemented yet.'); }
138
+ async function handleGlobalInjectionCreation(config) { console.log('Global Injection creation not implemented yet.'); }
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <Select
3
+ class="w-full"
4
+ :options="column.enum"
5
+ :model-value="record[column.name]"
6
+ @update:model-value="emit('update:value', $event)"
7
+ >
8
+ <template #item="{option}">
9
+ <span class="text-xl inline-flex">\{{ getCountryFlag(option.value) }}</span> \{{ option.label }}
10
+ </template>
11
+
12
+ <template #selected-item="{option}">
13
+ <span class="text-xl inline-flex">\{{ getCountryFlag(option.value) }}</span> \{{ option.label }}
14
+ </template>
15
+ </Select>
16
+ </template>
17
+
18
+ <script setup lang="ts">
19
+ import Select from "@/afcl/Select.vue";
20
+ import type {
21
+ AdminForthResourceColumnCommon,
22
+ AdminForthResourceCommon,
23
+ AdminUser,
24
+ } from "@/types/Common";
25
+
26
+ const props = defineProps<{
27
+ column: AdminForthResourceColumnCommon;
28
+ record: any;
29
+ meta: any;
30
+ resource: AdminForthResourceCommon;
31
+ adminUser: AdminUser;
32
+ }>();
33
+
34
+ const emit = defineEmits(["update:value"]);
35
+
36
+ function getCountryFlag(countryCode: string) {
37
+ return countryCode?.toUpperCase()
38
+ .replace(/./g, (char) => String.fromCodePoint(char.charCodeAt(0) + 127397));
39
+ }
40
+
41
+ </script>
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <Select
3
+ class="w-full"
4
+ :options="column.enum"
5
+ :model-value="record[column.name]"
6
+ @update:model-value="emit('update:value', $event)"
7
+ >
8
+ <template #item="{option}">
9
+ <span class="text-xl inline-flex">\{{ getCountryFlag(option.value) }}</span> \{{ option.label }}
10
+ </template>
11
+
12
+ <template #selected-item="{option}">
13
+ <span class="text-xl inline-flex">\{{ getCountryFlag(option.value) }}</span> \{{ option.label }}
14
+ </template>
15
+ </Select>
16
+ </template>
17
+
18
+ <script setup lang="ts">
19
+ import Select from "@/afcl/Select.vue";
20
+ import type {
21
+ AdminForthResourceColumnCommon,
22
+ AdminForthResourceCommon,
23
+ AdminUser,
24
+ } from "@/types/Common";
25
+
26
+ const props = defineProps<{
27
+ column: AdminForthResourceColumnCommon;
28
+ record: any;
29
+ meta: any;
30
+ resource: AdminForthResourceCommon;
31
+ adminUser: AdminUser;
32
+ }>();
33
+
34
+ const emit = defineEmits(["update:value"]);
35
+
36
+ function getCountryFlag(countryCode: string) {
37
+ return countryCode?.toUpperCase()
38
+ .replace(/./g, (char) => String.fromCodePoint(char.charCodeAt(0) + 127397));
39
+ }
40
+
41
+ </script>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <div v-if='record[column.name]'
3
+ class="text-red-500"
4
+ >\{{ record[column.name] }}</div>
5
+ </template>
6
+
7
+ <script setup>
8
+
9
+ defineProps({
10
+ record: Object,
11
+ resource: Object,
12
+ adminUser: Object,
13
+ meta: Object,
14
+ column: Object
15
+ });
16
+
17
+ </script>
18
+
@@ -0,0 +1,17 @@
1
+ <template>
2
+ <div v-if='record[column.name]'
3
+ class="text-red-500"
4
+ >\{{ record[column.name] }}</div>
5
+ </template>
6
+
7
+ <script setup>
8
+
9
+ defineProps({
10
+ record: Object,
11
+ resource: Object,
12
+ adminUser: Object,
13
+ meta: Object,
14
+ column: Object
15
+ });
16
+
17
+ </script>
@@ -0,0 +1,60 @@
1
+ // tsproxy.ts
2
+ import { writeFile, unlink } from 'fs/promises';
3
+ import { randomUUID } from 'crypto';
4
+ import { pathToFileURL } from 'url';
5
+ import path from 'path';
6
+
7
+ (async () => {
8
+ const chunks: Buffer[] = [];
9
+
10
+ for await (const chunk of process.stdin) {
11
+ chunks.push(chunk);
12
+ }
13
+
14
+ const code = Buffer.concat(chunks).toString();
15
+
16
+ const tmpFileName = `.tmp-tsproxy-${randomUUID()}.ts`;
17
+
18
+ const tmpFile = path.join(process.cwd(), tmpFileName);
19
+
20
+ const origLog = console.log;
21
+ let capturedLogs: any[] = [];
22
+ console.log = (...args: any[]) => {
23
+ capturedLogs.push(args);
24
+ }
25
+
26
+ process.env.HEAVY_DEBUG && console.log(`🪲 TMP proxy file: ${tmpFile}`);
27
+ process.env.HEAVY_DEBUG && console.log(`🪲 Current working directory: ${process.cwd()}`);
28
+
29
+ try {
30
+ // Save code to a temp file
31
+ await writeFile(tmpFile, code);
32
+
33
+ // Dynamically import the file
34
+ const module = await import(pathToFileURL(tmpFile).href);
35
+
36
+ if (typeof module.exec !== 'function') {
37
+ throw new Error("Module does not export an 'exec' function");
38
+ }
39
+
40
+ const result = await Promise.resolve(module.exec());
41
+
42
+ // Restore original console.log
43
+ console.log = origLog;
44
+ console.log(JSON.stringify({
45
+ result,
46
+ capturedLogs,
47
+ error: null
48
+ }));
49
+ } catch (error: any) {
50
+ // Restore original console.log
51
+ console.log = origLog;
52
+ console.log(JSON.stringify({
53
+ error: error.message,
54
+ stack: error.stack,
55
+ capturedLogs
56
+ }));
57
+ } finally {
58
+ await unlink(tmpFile).catch(() => {});
59
+ }
60
+ })();
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../commands/proxy.ts"],"names":[],"mappings":""}
@@ -0,0 +1,71 @@
1
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
2
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
3
+ var m = o[Symbol.asyncIterator], i;
4
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
5
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
6
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
7
+ };
8
+ // tsproxy.ts
9
+ import { writeFile, unlink } from 'fs/promises';
10
+ import { randomUUID } from 'crypto';
11
+ import { pathToFileURL } from 'url';
12
+ import path from 'path';
13
+ (async () => {
14
+ var _a, e_1, _b, _c;
15
+ const chunks = [];
16
+ try {
17
+ for (var _d = true, _e = __asyncValues(process.stdin), _f; _f = await _e.next(), _a = _f.done, !_a; _d = true) {
18
+ _c = _f.value;
19
+ _d = false;
20
+ const chunk = _c;
21
+ chunks.push(chunk);
22
+ }
23
+ }
24
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
25
+ finally {
26
+ try {
27
+ if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
28
+ }
29
+ finally { if (e_1) throw e_1.error; }
30
+ }
31
+ const code = Buffer.concat(chunks).toString();
32
+ const tmpFileName = `.tmp-tsproxy-${randomUUID()}.ts`;
33
+ const tmpFile = path.join(process.cwd(), tmpFileName);
34
+ const origLog = console.log;
35
+ let capturedLogs = [];
36
+ console.log = (...args) => {
37
+ capturedLogs.push(args);
38
+ };
39
+ process.env.HEAVY_DEBUG && console.log(`🪲 TMP proxy file: ${tmpFile}`);
40
+ process.env.HEAVY_DEBUG && console.log(`🪲 Current working directory: ${process.cwd()}`);
41
+ try {
42
+ // Save code to a temp file
43
+ await writeFile(tmpFile, code);
44
+ // Dynamically import the file
45
+ const module = await import(pathToFileURL(tmpFile).href);
46
+ if (typeof module.exec !== 'function') {
47
+ throw new Error("Module does not export an 'exec' function");
48
+ }
49
+ const result = await Promise.resolve(module.exec());
50
+ // Restore original console.log
51
+ console.log = origLog;
52
+ console.log(JSON.stringify({
53
+ result,
54
+ capturedLogs,
55
+ error: null
56
+ }));
57
+ }
58
+ catch (error) {
59
+ // Restore original console.log
60
+ console.log = origLog;
61
+ console.log(JSON.stringify({
62
+ error: error.message,
63
+ stack: error.stack,
64
+ capturedLogs
65
+ }));
66
+ }
67
+ finally {
68
+ await unlink(tmpFile).catch(() => { });
69
+ }
70
+ })();
71
+ //# sourceMappingURL=proxy.js.map