@entrinsik/vite-plugin-informer 1.0.5 → 2.0.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/bin/deploy.js +2 -2
- package/package.json +16 -7
- package/src/client.js +6 -5
- package/src/deploy.js +69 -43
package/bin/deploy.js
CHANGED
|
@@ -72,12 +72,12 @@ try {
|
|
|
72
72
|
id: savedId
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
// Save the
|
|
75
|
+
// Save the app ID back to package.json for future deploys
|
|
76
76
|
if (result.id && result.id !== savedId) {
|
|
77
77
|
if (!pkg.informer) pkg.informer = {};
|
|
78
78
|
pkg.informer.id = result.id;
|
|
79
79
|
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
80
|
-
console.log(`Saved
|
|
80
|
+
console.log(`Saved app ID "${result.id}" to package.json`);
|
|
81
81
|
}
|
|
82
82
|
} catch (err) {
|
|
83
83
|
console.error('Deploy failed:', err.message);
|
package/package.json
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entrinsik/vite-plugin-informer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Vite plugin and publish tool for local Magic Report development",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/entrinsik-org/i5.git",
|
|
8
|
+
"directory": "packages/vite-plugin-informer"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"registry": "https://docker.entrinsik.com/repository/entNPM/"
|
|
12
|
+
},
|
|
13
|
+
"author": "Entrinsik Inc.",
|
|
14
|
+
"license": "UNLICENSED",
|
|
5
15
|
"type": "module",
|
|
6
16
|
"types": "index.d.ts",
|
|
7
17
|
"main": "./src/index.js",
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18.0.0"
|
|
20
|
+
},
|
|
8
21
|
"exports": {
|
|
9
22
|
".": {
|
|
10
23
|
"types": "./index.d.ts",
|
|
@@ -26,10 +39,6 @@
|
|
|
26
39
|
"vite": ">=5.0.0"
|
|
27
40
|
},
|
|
28
41
|
"dependencies": {
|
|
29
|
-
"dotenv": "
|
|
30
|
-
}
|
|
31
|
-
"engines": {
|
|
32
|
-
"node": ">=18.0.0"
|
|
33
|
-
},
|
|
34
|
-
"license": "UNLICENSED"
|
|
42
|
+
"dotenv": "16.6.1"
|
|
43
|
+
}
|
|
35
44
|
}
|
package/src/client.js
CHANGED
|
@@ -60,10 +60,11 @@ export function createClient({ baseUrl, apiKey, user, pass }) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
* Upload a file using chunked Flow.js protocol, then assemble into
|
|
64
|
-
* @param {{ reportId
|
|
63
|
+
* Upload a file using chunked Flow.js protocol, then assemble into an entity's library.
|
|
64
|
+
* @param {{ entityPath?: string, reportId?: string, path: string, buffer: Buffer, filename: string }} opts
|
|
65
65
|
*/
|
|
66
|
-
async function uploadChunked({ reportId, path, buffer, filename }) {
|
|
66
|
+
async function uploadChunked({ entityPath, reportId, path, buffer, filename }) {
|
|
67
|
+
const basePath = entityPath || `reports/${reportId}`;
|
|
67
68
|
const totalChunks = Math.ceil(buffer.length / CHUNK_SIZE);
|
|
68
69
|
const uploadId = `publish-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
69
70
|
|
|
@@ -97,8 +98,8 @@ export function createClient({ baseUrl, apiKey, user, pass }) {
|
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
|
|
100
|
-
// Assemble chunks into a file in the
|
|
101
|
-
await request('POST',
|
|
101
|
+
// Assemble chunks into a file in the entity's library at the specified path
|
|
102
|
+
await request('POST', `${basePath}/_upload`, {
|
|
102
103
|
uploadId,
|
|
103
104
|
path
|
|
104
105
|
});
|
package/src/deploy.js
CHANGED
|
@@ -14,88 +14,112 @@ const CHUNK_THRESHOLD = 512 * 1024; // 512KB
|
|
|
14
14
|
const ROOT_CONFIG_FILES = ['data-access.yaml'];
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Deploy a built Vite project to Informer as
|
|
17
|
+
* Deploy a built Vite project to Informer as an App (or legacy Magic Report).
|
|
18
|
+
*
|
|
19
|
+
* Tries the new /api/apps endpoint first. If the server doesn't support it,
|
|
20
|
+
* falls back to the legacy /api/reports endpoint transparently.
|
|
18
21
|
*
|
|
19
22
|
* @param {{ baseUrl: string, apiKey?: string, user?: string, pass?: string, distDir: string, name: string, description?: string, icon?: string, id?: string }} opts
|
|
20
|
-
* @returns {Promise<{ id: string, url: string }>} The
|
|
23
|
+
* @returns {Promise<{ id: string, url: string }>} The entity's stable UUID and URL
|
|
21
24
|
*/
|
|
22
25
|
export async function deploy({ baseUrl, apiKey, user, pass, distDir, name, description, icon, id }) {
|
|
23
26
|
const api = createClient({ baseUrl, apiKey, user, pass });
|
|
24
27
|
|
|
25
|
-
let
|
|
28
|
+
let entity = null;
|
|
26
29
|
let naturalId = null;
|
|
30
|
+
let apiPrefix = 'apps';
|
|
27
31
|
|
|
28
|
-
// 1.
|
|
32
|
+
// 1. Try to find existing entity — apps API first, then legacy reports
|
|
29
33
|
if (id) {
|
|
30
|
-
console.log(`Looking up
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
console.log(`Looking up "${id}"...`);
|
|
35
|
+
entity = await api.get(`apps/${id}`);
|
|
36
|
+
if (entity) {
|
|
37
|
+
apiPrefix = 'apps';
|
|
38
|
+
naturalId = entity.naturalId || id;
|
|
39
|
+
console.log(`Found existing app: ${naturalId}`);
|
|
35
40
|
} else {
|
|
36
|
-
|
|
41
|
+
entity = await api.get(`reports/${id}`);
|
|
42
|
+
if (entity) {
|
|
43
|
+
apiPrefix = 'reports';
|
|
44
|
+
naturalId = entity.naturalId || id;
|
|
45
|
+
console.log(`Found existing report: ${naturalId} (legacy API)`);
|
|
46
|
+
} else {
|
|
47
|
+
console.log(`"${id}" not found, will create new...`);
|
|
48
|
+
}
|
|
37
49
|
}
|
|
38
50
|
}
|
|
39
51
|
|
|
40
|
-
// 2. Create if not found
|
|
41
|
-
if (!
|
|
42
|
-
console.log(`Creating new report "${name}"...`);
|
|
52
|
+
// 2. Create if not found — try apps API, fall back to reports
|
|
53
|
+
if (!entity) {
|
|
43
54
|
const payload = {
|
|
44
|
-
type: 'magicReport',
|
|
45
55
|
name: name,
|
|
46
56
|
description: description || ''
|
|
47
57
|
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
if (id) payload.id = id;
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
console.log(`Creating new app "${name}"...`);
|
|
62
|
+
entity = await api.post('apps', { ...payload, type: 'report' });
|
|
63
|
+
apiPrefix = 'apps';
|
|
64
|
+
} catch (e) {
|
|
65
|
+
// If apps endpoint doesn't exist (404), fall back to legacy reports
|
|
66
|
+
if (e.status === 404) {
|
|
67
|
+
console.log('Apps API not available, using legacy reports API...');
|
|
68
|
+
entity = await api.post('reports', { ...payload, type: 'magicReport' });
|
|
69
|
+
apiPrefix = 'reports';
|
|
70
|
+
} else {
|
|
71
|
+
throw e;
|
|
72
|
+
}
|
|
51
73
|
}
|
|
52
|
-
|
|
53
|
-
if (!
|
|
54
|
-
throw new Error(`Failed to create
|
|
74
|
+
|
|
75
|
+
if (!entity || !entity.id) {
|
|
76
|
+
throw new Error(`Failed to create "${name}"`);
|
|
55
77
|
}
|
|
56
|
-
naturalId =
|
|
57
|
-
console.log(`Created
|
|
78
|
+
naturalId = entity.naturalId || `${entity.ownerId}:${entity.slug}`;
|
|
79
|
+
console.log(`Created: ${naturalId}`);
|
|
58
80
|
}
|
|
59
81
|
|
|
60
|
-
|
|
82
|
+
const entityPath = `${apiPrefix}/${naturalId}`;
|
|
83
|
+
|
|
84
|
+
// 3. Update name/description/icon if provided (for existing entities)
|
|
61
85
|
const updates = {};
|
|
62
|
-
if (name &&
|
|
63
|
-
if (description !== undefined &&
|
|
86
|
+
if (name && entity.name !== name) updates.name = name;
|
|
87
|
+
if (description !== undefined && entity.description !== description) updates.description = description;
|
|
64
88
|
if (icon) {
|
|
65
|
-
const currentDefn =
|
|
89
|
+
const currentDefn = entity.defn || {};
|
|
66
90
|
if (currentDefn.icon !== icon) {
|
|
67
91
|
updates.defn = { ...currentDefn, icon };
|
|
68
92
|
}
|
|
69
93
|
}
|
|
70
94
|
if (Object.keys(updates).length > 0) {
|
|
71
|
-
await api.put(
|
|
95
|
+
await api.put(entityPath, updates);
|
|
72
96
|
}
|
|
73
97
|
|
|
74
|
-
//
|
|
98
|
+
// 4. Snapshot
|
|
75
99
|
console.log('Creating snapshot...');
|
|
76
|
-
await api.post(
|
|
100
|
+
await api.post(`${entityPath}/snapshots`, {
|
|
77
101
|
trigger: 'refresh',
|
|
78
102
|
limit: 10
|
|
79
103
|
});
|
|
80
104
|
|
|
81
|
-
//
|
|
105
|
+
// 5. Clear existing files
|
|
82
106
|
console.log('Clearing existing files...');
|
|
83
|
-
const files = await api.get(
|
|
107
|
+
const files = await api.get(`${entityPath}/files?start=0&end=10000`);
|
|
84
108
|
if (files && Array.isArray(files)) {
|
|
85
109
|
// Delete non-directories first
|
|
86
110
|
const nonDirs = files.filter(f => !f.directory);
|
|
87
|
-
await Promise.all(nonDirs.map(f => api.del(
|
|
111
|
+
await Promise.all(nonDirs.map(f => api.del(`${entityPath}/files/${f.id}`)));
|
|
88
112
|
|
|
89
113
|
// Then delete directories in reverse order (deepest first by path length)
|
|
90
114
|
const dirs = files
|
|
91
115
|
.filter(f => f.directory)
|
|
92
116
|
.sort((a, b) => (b.path || '').length - (a.path || '').length);
|
|
93
117
|
for (const d of dirs) {
|
|
94
|
-
await api.del(
|
|
118
|
+
await api.del(`${entityPath}/files/${d.id}`);
|
|
95
119
|
}
|
|
96
120
|
}
|
|
97
121
|
|
|
98
|
-
//
|
|
122
|
+
// 6. Upload dist/ contents
|
|
99
123
|
console.log(`Uploading files from ${distDir}...`);
|
|
100
124
|
const entries = await walkDir(distDir);
|
|
101
125
|
|
|
@@ -106,7 +130,7 @@ export async function deploy({ baseUrl, apiKey, user, pass, distDir, name, descr
|
|
|
106
130
|
if (content.length > CHUNK_THRESHOLD) {
|
|
107
131
|
// Large file: chunked upload via Flow.js protocol
|
|
108
132
|
await api.uploadChunked({
|
|
109
|
-
|
|
133
|
+
entityPath,
|
|
110
134
|
path: relPath,
|
|
111
135
|
buffer: content,
|
|
112
136
|
filename: basename(filePath)
|
|
@@ -120,12 +144,12 @@ export async function deploy({ baseUrl, apiKey, user, pass, distDir, name, descr
|
|
|
120
144
|
? { content: content.toString('utf8'), encoding: 'utf8' }
|
|
121
145
|
: { content: content.toString('base64'), encoding: 'base64' };
|
|
122
146
|
|
|
123
|
-
await api.put(
|
|
147
|
+
await api.put(`${entityPath}/contents/${relPath}`, payload);
|
|
124
148
|
console.log(` ${relPath}`);
|
|
125
149
|
}
|
|
126
150
|
}
|
|
127
151
|
|
|
128
|
-
//
|
|
152
|
+
// 7. Upload config files from project root (e.g., data-access.yaml)
|
|
129
153
|
const projectRoot = dirname(distDir);
|
|
130
154
|
let configCount = 0;
|
|
131
155
|
for (const configFile of ROOT_CONFIG_FILES) {
|
|
@@ -133,7 +157,7 @@ export async function deploy({ baseUrl, apiKey, user, pass, distDir, name, descr
|
|
|
133
157
|
try {
|
|
134
158
|
await access(configPath);
|
|
135
159
|
const content = await readFile(configPath, 'utf8');
|
|
136
|
-
await api.put(
|
|
160
|
+
await api.put(`${entityPath}/contents/${configFile}`, {
|
|
137
161
|
content,
|
|
138
162
|
encoding: 'utf8'
|
|
139
163
|
});
|
|
@@ -144,13 +168,15 @@ export async function deploy({ baseUrl, apiKey, user, pass, distDir, name, descr
|
|
|
144
168
|
}
|
|
145
169
|
}
|
|
146
170
|
|
|
147
|
-
//
|
|
171
|
+
// 8. Print URL and return UUID for saving
|
|
148
172
|
const totalFiles = entries.length + configCount;
|
|
149
|
-
const
|
|
150
|
-
|
|
173
|
+
const base = baseUrl.replace(/\/+$/, '');
|
|
174
|
+
const entityUrl = apiPrefix === 'apps'
|
|
175
|
+
? `${base}/api/apps/${naturalId}/view`
|
|
176
|
+
: `${base}/reports/r/${naturalId}`;
|
|
177
|
+
console.log(`\nPublished ${totalFiles} files to: ${entityUrl}`);
|
|
151
178
|
|
|
152
|
-
|
|
153
|
-
return { id: report.id, url: reportUrl };
|
|
179
|
+
return { id: entity.id, url: entityUrl };
|
|
154
180
|
}
|
|
155
181
|
|
|
156
182
|
/**
|