@entrinsik/vite-plugin-informer 1.0.0 → 1.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import dotenv from 'dotenv';
4
- import { readFile } from 'node:fs/promises';
4
+ import { readFile, writeFile } from 'node:fs/promises';
5
5
  import { resolve } from 'node:path';
6
6
  import { deploy } from '../src/deploy.js';
7
7
 
@@ -24,33 +24,58 @@ if (!baseUrl || (!apiKey && (!user || !pass))) {
24
24
  }
25
25
 
26
26
  // Read project metadata from package.json
27
- let slug;
27
+ const pkgPath = resolve('package.json');
28
+ let pkg;
28
29
  let displayName;
29
30
  let description;
31
+ let savedId;
32
+
30
33
  try {
31
- const pkg = JSON.parse(await readFile(resolve('package.json'), 'utf8'));
32
- slug = pkg.name;
33
- // Strip scope prefix if present (e.g. @org/my-report -> my-report)
34
- if (slug && slug.startsWith('@') && slug.includes('/')) {
35
- slug = slug.split('/')[1];
36
- }
37
- // Read display name and description from informer config section
34
+ pkg = JSON.parse(await readFile(pkgPath, 'utf8'));
35
+
36
+ // Read display name, description, and saved ID from informer config section
38
37
  displayName = pkg.informer?.name;
39
38
  description = pkg.informer?.description;
39
+ savedId = pkg.informer?.id;
40
40
  } catch {
41
41
  console.error('Could not read package.json in current directory.');
42
42
  process.exit(1);
43
43
  }
44
44
 
45
- if (!slug) {
46
- console.error('package.json must have a "name" field.');
45
+ if (!displayName) {
46
+ // Fall back to package name for display
47
+ displayName = pkg.name;
48
+ if (displayName && displayName.startsWith('@') && displayName.includes('/')) {
49
+ displayName = displayName.split('/')[1];
50
+ }
51
+ }
52
+
53
+ if (!displayName) {
54
+ console.error('package.json must have an "informer.name" or "name" field.');
47
55
  process.exit(1);
48
56
  }
49
57
 
50
58
  const distDir = resolve('dist');
51
59
 
52
60
  try {
53
- await deploy({ baseUrl, apiKey, user, pass, slug, distDir, name: displayName, description });
61
+ const result = await deploy({
62
+ baseUrl,
63
+ apiKey,
64
+ user,
65
+ pass,
66
+ distDir,
67
+ name: displayName,
68
+ description,
69
+ id: savedId
70
+ });
71
+
72
+ // Save the report ID back to package.json for future deploys
73
+ if (result.id && result.id !== savedId) {
74
+ if (!pkg.informer) pkg.informer = {};
75
+ pkg.informer.id = result.id;
76
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
77
+ console.log(`Saved report ID "${result.id}" to package.json`);
78
+ }
54
79
  } catch (err) {
55
80
  console.error('Deploy failed:', err.message);
56
81
  process.exit(1);
package/bin/init.js CHANGED
@@ -4,6 +4,7 @@ import { readFile, writeFile, mkdir, cp, access, readdir } from 'node:fs/promise
4
4
  import { resolve, dirname, join, basename } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { createInterface } from 'node:readline';
7
+ import { randomUUID } from 'node:crypto';
7
8
 
8
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
10
  const cwd = process.cwd();
@@ -176,27 +177,17 @@ function prompt(question, defaultValue) {
176
177
  }
177
178
 
178
179
  /**
179
- * Convert slug to friendly name.
180
+ * Convert package name to friendly display name.
180
181
  * "magic-quickbooks-report" -> "Magic Quickbooks Report"
182
+ * "@org/my-report" -> "My Report"
181
183
  */
182
- function slugToName(slug) {
183
- return slug
184
+ function packageNameToDisplayName(name) {
185
+ return name
184
186
  .replace(/^@[^/]+\//, '') // Remove scope
185
187
  .replace(/[-_]/g, ' ')
186
188
  .replace(/\b\w/g, c => c.toUpperCase());
187
189
  }
188
190
 
189
- /**
190
- * Convert name to slug.
191
- * "Magic Quickbooks Report" -> "magic-quickbooks-report"
192
- */
193
- function nameToSlug(name) {
194
- return name
195
- .toLowerCase()
196
- .replace(/[^a-z0-9]+/g, '-')
197
- .replace(/^-|-$/g, '');
198
- }
199
-
200
191
  async function init() {
201
192
  console.log('Initializing Informer Magic Report project...\n');
202
193
 
@@ -219,24 +210,21 @@ async function init() {
219
210
  }
220
211
 
221
212
  // 3. Get report info from user
222
- const defaultSlug = (pkg.name || basename(cwd)).replace(/^@[^/]+\//, '');
223
- const existingName = pkg.informer?.name;
224
- const defaultName = existingName || slugToName(defaultSlug);
213
+ const defaultName = pkg.informer?.name || packageNameToDisplayName(pkg.name || basename(cwd));
225
214
 
226
215
  console.log('Configure your Magic Report:\n');
227
216
 
228
217
  const reportName = await prompt('Report name', defaultName);
229
- const reportSlug = await prompt('Report slug', nameToSlug(reportName) || defaultSlug);
230
218
 
231
219
  console.log('');
232
220
 
233
221
  // 4. Update package.json
234
222
  if (!pkg.informer) pkg.informer = {};
235
223
  pkg.informer.name = reportName;
236
-
237
- // Update package name to match slug if it was the default
238
- if (pkg.name === defaultSlug || !pkg.name) {
239
- pkg.name = reportSlug;
224
+ // Generate a stable UUID for this report
225
+ if (!pkg.informer.id) {
226
+ pkg.informer.id = randomUUID();
227
+ console.log(`Generated report ID: ${pkg.informer.id}`);
240
228
  }
241
229
 
242
230
  // 5. Add plugin to dependencies if not present
@@ -254,7 +242,7 @@ async function init() {
254
242
  }
255
243
 
256
244
  await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
257
- console.log(`Configured report: "${reportName}" (slug: ${reportSlug})`);
245
+ console.log(`Updated package.json with informer.name="${reportName}"`);
258
246
 
259
247
  // 7. Update vite.config
260
248
  await updateViteConfig();
@@ -269,9 +257,6 @@ INFORMER_API_KEY=your-api-key
269
257
  # Option 2: Basic auth
270
258
  # INFORMER_USER=admin
271
259
  # INFORMER_PASS=yourpassword
272
-
273
- # Optional: defaults to current user
274
- # INFORMER_OWNER=admin
275
260
  `;
276
261
 
277
262
  const envExamplePath = resolve(cwd, '.env.example');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@entrinsik/vite-plugin-informer",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Vite plugin and publish tool for local Magic Report development",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -10,7 +10,8 @@
10
10
  },
11
11
  "bin": {
12
12
  "informer-deploy": "./bin/deploy.js",
13
- "informer-init": "./bin/init.js"
13
+ "informer-init": "./bin/init.js",
14
+ "create-magic-report": "./bin/init.js"
14
15
  },
15
16
  "files": [
16
17
  "src",
package/src/deploy.js CHANGED
@@ -16,38 +16,51 @@ const ROOT_CONFIG_FILES = ['data-access.yaml'];
16
16
  /**
17
17
  * Deploy a built Vite project to Informer as a Magic Report.
18
18
  *
19
- * @param {{ baseUrl: string, apiKey?: string, user?: string, pass?: string, slug: string, distDir: string, name?: string, description?: string }} opts
19
+ * @param {{ baseUrl: string, apiKey?: string, user?: string, pass?: string, distDir: string, name: string, description?: string, id?: string }} opts
20
+ * @returns {Promise<{ id: string, url: string }>} The report's natural ID and URL
20
21
  */
21
- export async function deploy({ baseUrl, apiKey, user, pass, slug, distDir, name, description }) {
22
+ export async function deploy({ baseUrl, apiKey, user, pass, distDir, name, description, id }) {
22
23
  const api = createClient({ baseUrl, apiKey, user, pass });
23
24
 
24
- // 1. Get current user for natural ID
25
- const me = await api.get('me');
26
- const naturalId = `${me.username}:${slug}`;
25
+ let report = null;
26
+ let naturalId = null;
27
27
 
28
- // 2. Lookup report by natural ID
29
- let report = await api.get(`reports/${naturalId}`);
28
+ // 1. If we have a saved ID, try to look it up
29
+ if (id) {
30
+ console.log(`Looking up report "${id}"...`);
31
+ report = await api.get(`reports/${id}`);
32
+ if (report) {
33
+ naturalId = report.naturalId || id;
34
+ console.log(`Found existing report: ${naturalId}`);
35
+ } else {
36
+ console.log(`Report "${id}" not found, will create new...`);
37
+ }
38
+ }
30
39
 
31
- // 3. Create if not found
40
+ // 2. Create if not found (or no ID was provided)
32
41
  if (!report) {
33
- console.log(`Report "${naturalId}" not found, creating...`);
34
- await api.post('reports', {
42
+ console.log(`Creating new report "${name}"...`);
43
+ const payload = {
35
44
  type: 'magicReport',
36
- name: name || slug,
45
+ name: name,
37
46
  description: description || ''
38
- });
39
- // Re-fetch to get the full report
40
- report = await api.get(`reports/${naturalId}`);
41
- if (!report) {
42
- throw new Error(`Failed to create report "${naturalId}"`);
47
+ };
48
+ // If we have a saved ID (UUID), use it for the new report
49
+ if (id) {
50
+ payload.id = id;
43
51
  }
44
- console.log(`Created report: ${report.id}`);
52
+ report = await api.post('reports', payload);
53
+ if (!report || !report.id) {
54
+ throw new Error(`Failed to create report "${name}"`);
55
+ }
56
+ naturalId = report.naturalId || `${report.ownerId}:${report.slug}`;
57
+ console.log(`Created report: ${naturalId}`);
45
58
  }
46
59
 
47
- // 4. Update name/description if provided
60
+ // 3. Update name/description if provided (for existing reports)
48
61
  const updates = {};
49
- if (name) updates.name = name;
50
- if (description !== undefined) updates.description = description;
62
+ if (name && report.name !== name) updates.name = name;
63
+ if (description !== undefined && report.description !== description) updates.description = description;
51
64
  if (Object.keys(updates).length > 0) {
52
65
  await api.put(`reports/${naturalId}`, updates);
53
66
  }
@@ -125,10 +138,13 @@ export async function deploy({ baseUrl, apiKey, user, pass, slug, distDir, name,
125
138
  }
126
139
  }
127
140
 
128
- // 9. Print report URL
141
+ // 9. Print report URL and return UUID for saving
129
142
  const totalFiles = entries.length + configCount;
130
143
  const reportUrl = `${baseUrl.replace(/\/+$/, '')}/reports/r/${naturalId}`;
131
144
  console.log(`\nPublished ${totalFiles} files to: ${reportUrl}`);
145
+
146
+ // Return the stable UUID (not natural ID) for package.json storage
147
+ return { id: report.id, url: reportUrl };
132
148
  }
133
149
 
134
150
  /**