@nexushub/client 0.0.3 → 0.0.4
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/dist/tools/cli.js +108 -37
- package/dist/tools/cli.mjs +108 -37
- package/package.json +3 -3
package/dist/tools/cli.js
CHANGED
|
@@ -36,7 +36,9 @@ import_dotenv.default.config({ path: import_path.default.resolve(process.cwd(),
|
|
|
36
36
|
var program = new import_commander.Command();
|
|
37
37
|
program.name("nexus").description("NexusHub CLI - The Developer Toolbelt").version("1.0.0");
|
|
38
38
|
program.command("init").description("Initialize NexusHub configuration and seed data").action(initProject);
|
|
39
|
-
program.command("pull").description(
|
|
39
|
+
program.command("pull").description(
|
|
40
|
+
"Pull latest content from NexusHub to local cache (Offline Mode)"
|
|
41
|
+
).option("-k, --api-key <key>", "NexusHub API Key").option("-p, --project-id <id>", "NexusHub Project ID").option("-u, --api-url <url>", "NexusHub API URL").option("-o, --output <path>", "Output directory (default: .nexus)").option("-f, --force", "Force overwrite existing cache").action(syncContent);
|
|
40
42
|
program.command("seed").description("Seed content from local files to NexusHub").option("-i, --input <path>", "Input directory (default: .nexus/seed)").option("-p, --project-id <id>", "NexusHub Project ID").option("-k, --api-key <key>", "NexusHub API Key").option("-u, --api-url <url>", "NexusHub API URL").option("--dry-run", "Validate seed data without uploading").action(seedContent);
|
|
41
43
|
program.command("deploy").description("Trigger a production deployment").option("-p, --project-id <id>", "NexusHub Project ID").option("-k, --api-key <key>", "NexusHub API Key").option("-u, --api-url <url>", "NexusHub API URL").option("-e, --environment <env>", "Deployment environment", "production").action(triggerDeploy);
|
|
42
44
|
program.command("status").description("Check project status and content statistics").option("-p, --project-id <id>", "NexusHub Project ID").option("-k, --api-key <key>", "NexusHub API Key").option("-u, --api-url <url>", "NexusHub API URL").action(checkStatus);
|
|
@@ -48,7 +50,11 @@ async function getContext() {
|
|
|
48
50
|
const apiUrl = process.env.NEXUS_API_URL || "https://api.nexushub.com/v1";
|
|
49
51
|
if (!apiKey || !projectId) {
|
|
50
52
|
console.error(import_chalk.default.red("\n\u274C Missing Configuration"));
|
|
51
|
-
console.log(
|
|
53
|
+
console.log(
|
|
54
|
+
import_chalk.default.gray(
|
|
55
|
+
"Please set NEXUS_API_KEY and NEXUS_PROJECT_ID in your .env file."
|
|
56
|
+
)
|
|
57
|
+
);
|
|
52
58
|
process.exit(1);
|
|
53
59
|
}
|
|
54
60
|
return { apiKey, projectId, apiUrl };
|
|
@@ -59,7 +65,7 @@ async function fetchWithAuth(url, apiKey, timeout = 3e4) {
|
|
|
59
65
|
try {
|
|
60
66
|
const response = await (0, import_node_fetch.default)(url, {
|
|
61
67
|
headers: {
|
|
62
|
-
|
|
68
|
+
Authorization: `Bearer ${apiKey}`,
|
|
63
69
|
"Content-Type": "application/json",
|
|
64
70
|
"User-Agent": "NexusHub-CLI/1.0.0"
|
|
65
71
|
},
|
|
@@ -101,7 +107,12 @@ async function fetchCollections(baseUrl, apiKey, projectId) {
|
|
|
101
107
|
const itemsData = itemsRes;
|
|
102
108
|
result[collection.slug || collection.id] = itemsData.data || itemsData.items || [];
|
|
103
109
|
} catch (error) {
|
|
104
|
-
console.warn(
|
|
110
|
+
console.warn(
|
|
111
|
+
import_chalk.default.yellow(
|
|
112
|
+
`\u26A0\uFE0F Failed to fetch collection ${collection.id}:`,
|
|
113
|
+
error instanceof Error ? error.message : String(error)
|
|
114
|
+
)
|
|
115
|
+
);
|
|
105
116
|
}
|
|
106
117
|
})
|
|
107
118
|
);
|
|
@@ -163,9 +174,17 @@ async function initProject() {
|
|
|
163
174
|
copyright: "\xA9 2024 My Site"
|
|
164
175
|
}
|
|
165
176
|
};
|
|
166
|
-
await fs.writeJson(import_path.default.join(seedDir, "pages.json"), examplePages, {
|
|
167
|
-
|
|
168
|
-
|
|
177
|
+
await fs.writeJson(import_path.default.join(seedDir, "pages.json"), examplePages, {
|
|
178
|
+
spaces: 2
|
|
179
|
+
});
|
|
180
|
+
await fs.writeJson(
|
|
181
|
+
import_path.default.join(collectionsDir, "blog_posts.json"),
|
|
182
|
+
exampleCollection,
|
|
183
|
+
{ spaces: 2 }
|
|
184
|
+
);
|
|
185
|
+
await fs.writeJson(import_path.default.join(seedDir, "globals.json"), exampleGlobals, {
|
|
186
|
+
spaces: 2
|
|
187
|
+
});
|
|
169
188
|
const gitignorePath = import_path.default.join(nexusDir, ".gitignore");
|
|
170
189
|
await fs.writeFile(gitignorePath, "cache.json\n");
|
|
171
190
|
const readmePath = import_path.default.join(nexusDir, "README.md");
|
|
@@ -208,7 +227,9 @@ This directory contains seed data for your NexusHub project.
|
|
|
208
227
|
console.log(import_chalk.default.gray(" NEXUS_PROJECT_ID=your_project_id_here"));
|
|
209
228
|
console.log(import_chalk.default.gray(" 3. Run `npx nexus seed` to upload content"));
|
|
210
229
|
} catch (error) {
|
|
211
|
-
console.error(
|
|
230
|
+
console.error(
|
|
231
|
+
import_chalk.default.red(`\u274C Failed to initialize project: ${error.message}`)
|
|
232
|
+
);
|
|
212
233
|
process.exit(1);
|
|
213
234
|
}
|
|
214
235
|
}
|
|
@@ -220,16 +241,26 @@ async function syncContent(options) {
|
|
|
220
241
|
const outputDir = options.output || ".nexus";
|
|
221
242
|
if (!apiKey || !projectId) {
|
|
222
243
|
console.error(import_chalk.default.red("\u274C Missing API Key or Project ID"));
|
|
223
|
-
console.log(
|
|
244
|
+
console.log(
|
|
245
|
+
import_chalk.default.gray(
|
|
246
|
+
"Set NEXUS_API_KEY and NEXUS_PROJECT_ID in .env or use --api-key and --project-id flags"
|
|
247
|
+
)
|
|
248
|
+
);
|
|
224
249
|
process.exit(1);
|
|
225
250
|
}
|
|
226
|
-
const spinner = (0, import_ora.default)(
|
|
251
|
+
const spinner = (0, import_ora.default)(
|
|
252
|
+
`Fetching content for project: ${import_chalk.default.green(projectId)}...`
|
|
253
|
+
).start();
|
|
227
254
|
try {
|
|
228
255
|
const cacheDir = import_path.default.join(process.cwd(), outputDir);
|
|
229
256
|
const cacheFile = import_path.default.join(cacheDir, "cache.json");
|
|
230
257
|
if (await fs.pathExists(cacheFile) && !options.force) {
|
|
231
258
|
spinner.stop();
|
|
232
|
-
console.log(
|
|
259
|
+
console.log(
|
|
260
|
+
import_chalk.default.yellow(
|
|
261
|
+
"\u26A0\uFE0F Cache file already exists. Use --force to overwrite."
|
|
262
|
+
)
|
|
263
|
+
);
|
|
233
264
|
process.exit(0);
|
|
234
265
|
}
|
|
235
266
|
const [pages, collections, globals] = await Promise.all([
|
|
@@ -256,7 +287,9 @@ async function syncContent(options) {
|
|
|
256
287
|
spinner.succeed(import_chalk.default.green("Content synced successfully!"));
|
|
257
288
|
console.log(import_chalk.default.gray(` \u{1F4C1} Saved to: ${cacheFile}`));
|
|
258
289
|
console.log(import_chalk.default.gray(` \u{1F4CA} Pages: ${cacheData.meta.count.pages}`));
|
|
259
|
-
console.log(
|
|
290
|
+
console.log(
|
|
291
|
+
import_chalk.default.gray(` \u{1F4DA} Collections: ${cacheData.meta.count.collections}`)
|
|
292
|
+
);
|
|
260
293
|
console.log(import_chalk.default.yellow(` \u26A1 Mode: Offline / Local Cache Enabled`));
|
|
261
294
|
} catch (error) {
|
|
262
295
|
spinner.fail(import_chalk.default.red("Failed to sync content"));
|
|
@@ -264,7 +297,9 @@ async function syncContent(options) {
|
|
|
264
297
|
if (error.message.includes("401") || error.message.includes("403")) {
|
|
265
298
|
console.log(import_chalk.default.yellow(" \u{1F511} Check your API key and permissions"));
|
|
266
299
|
} else if (error.message.includes("404")) {
|
|
267
|
-
console.log(
|
|
300
|
+
console.log(
|
|
301
|
+
import_chalk.default.yellow(" \u{1F50D} Project not found. Check your project ID")
|
|
302
|
+
);
|
|
268
303
|
}
|
|
269
304
|
process.exit(1);
|
|
270
305
|
}
|
|
@@ -285,7 +320,9 @@ async function seedContent(options) {
|
|
|
285
320
|
console.error(import_chalk.default.red(`\u274C Seed directory not found: ${seedPath}`));
|
|
286
321
|
console.log(import_chalk.default.gray("Create a seed directory with your content files:"));
|
|
287
322
|
console.log(import_chalk.default.gray(" mkdir -p .nexus/seed"));
|
|
288
|
-
console.log(
|
|
323
|
+
console.log(
|
|
324
|
+
import_chalk.default.gray(" # Add pages.json, collections/ folder, globals.json")
|
|
325
|
+
);
|
|
289
326
|
process.exit(1);
|
|
290
327
|
}
|
|
291
328
|
const spinner = (0, import_ora.default)("Validating seed data...").start();
|
|
@@ -307,7 +344,12 @@ async function seedContent(options) {
|
|
|
307
344
|
if (await fs.pathExists(collectionsDir)) {
|
|
308
345
|
if (!dryRun) {
|
|
309
346
|
spinner.text = "Seeding collections...";
|
|
310
|
-
const counts = await seedCollections(
|
|
347
|
+
const counts = await seedCollections(
|
|
348
|
+
collectionsDir,
|
|
349
|
+
apiUrl,
|
|
350
|
+
projectId,
|
|
351
|
+
apiKey
|
|
352
|
+
);
|
|
311
353
|
collectionCount = counts.collections;
|
|
312
354
|
itemCount = counts.items;
|
|
313
355
|
} else {
|
|
@@ -343,7 +385,7 @@ async function seedPages(pages, apiUrl, projectId, apiKey) {
|
|
|
343
385
|
const response = await (0, import_node_fetch.default)(`${apiUrl}/content/${projectId}/pages`, {
|
|
344
386
|
method: "POST",
|
|
345
387
|
headers: {
|
|
346
|
-
|
|
388
|
+
Authorization: `Bearer ${apiKey}`,
|
|
347
389
|
"Content-Type": "application/json"
|
|
348
390
|
},
|
|
349
391
|
body: JSON.stringify(page)
|
|
@@ -376,14 +418,16 @@ async function seedCollections(collectionsDir, apiUrl, projectId, apiKey) {
|
|
|
376
418
|
const filePath = import_path.default.join(collectionsDir, file);
|
|
377
419
|
const items = await fs.readJson(filePath);
|
|
378
420
|
if (!Array.isArray(items)) {
|
|
379
|
-
console.warn(
|
|
421
|
+
console.warn(
|
|
422
|
+
import_chalk.default.yellow(` \u26A0\uFE0F ${file} does not contain a valid array of items`)
|
|
423
|
+
);
|
|
380
424
|
continue;
|
|
381
425
|
}
|
|
382
426
|
try {
|
|
383
427
|
await (0, import_node_fetch.default)(`${apiUrl}/content/${projectId}/collections`, {
|
|
384
428
|
method: "POST",
|
|
385
429
|
headers: {
|
|
386
|
-
|
|
430
|
+
Authorization: `Bearer ${apiKey}`,
|
|
387
431
|
"Content-Type": "application/json"
|
|
388
432
|
},
|
|
389
433
|
body: JSON.stringify({
|
|
@@ -395,23 +439,38 @@ async function seedCollections(collectionsDir, apiUrl, projectId, apiKey) {
|
|
|
395
439
|
let itemCount = 0;
|
|
396
440
|
for (const item of items) {
|
|
397
441
|
try {
|
|
398
|
-
await (0, import_node_fetch.default)(
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
"
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
442
|
+
await (0, import_node_fetch.default)(
|
|
443
|
+
`${apiUrl}/content/${projectId}/collections/${collectionName}/items`,
|
|
444
|
+
{
|
|
445
|
+
method: "POST",
|
|
446
|
+
headers: {
|
|
447
|
+
Authorization: `Bearer ${apiKey}`,
|
|
448
|
+
"Content-Type": "application/json"
|
|
449
|
+
},
|
|
450
|
+
body: JSON.stringify(item)
|
|
451
|
+
}
|
|
452
|
+
);
|
|
406
453
|
itemCount++;
|
|
407
454
|
} catch (error) {
|
|
408
|
-
console.warn(
|
|
455
|
+
console.warn(
|
|
456
|
+
import_chalk.default.yellow(
|
|
457
|
+
` Failed to create item in ${collectionName}: ${error instanceof Error ? error.message : String(error)}`
|
|
458
|
+
)
|
|
459
|
+
);
|
|
409
460
|
}
|
|
410
461
|
}
|
|
411
|
-
console.log(
|
|
462
|
+
console.log(
|
|
463
|
+
import_chalk.default.gray(
|
|
464
|
+
` \u2705 Created collection ${collectionName} with ${itemCount} items`
|
|
465
|
+
)
|
|
466
|
+
);
|
|
412
467
|
totalItems += itemCount;
|
|
413
468
|
} catch (error) {
|
|
414
|
-
console.warn(
|
|
469
|
+
console.warn(
|
|
470
|
+
import_chalk.default.yellow(
|
|
471
|
+
` \u274C Failed to create collection ${collectionName}: ${error.message}`
|
|
472
|
+
)
|
|
473
|
+
);
|
|
415
474
|
}
|
|
416
475
|
}
|
|
417
476
|
return { collections: jsonFiles.length, items: totalItems };
|
|
@@ -421,14 +480,16 @@ async function seedGlobals(globals, apiUrl, projectId, apiKey) {
|
|
|
421
480
|
await (0, import_node_fetch.default)(`${apiUrl}/project/${projectId}/globals`, {
|
|
422
481
|
method: "PUT",
|
|
423
482
|
headers: {
|
|
424
|
-
|
|
483
|
+
Authorization: `Bearer ${apiKey}`,
|
|
425
484
|
"Content-Type": "application/json"
|
|
426
485
|
},
|
|
427
486
|
body: JSON.stringify(globals)
|
|
428
487
|
});
|
|
429
488
|
console.log(import_chalk.default.gray(" \u2705 Created globals"));
|
|
430
489
|
} catch (error) {
|
|
431
|
-
console.warn(
|
|
490
|
+
console.warn(
|
|
491
|
+
import_chalk.default.yellow(` \u274C Failed to create globals: ${error.message}`)
|
|
492
|
+
);
|
|
432
493
|
}
|
|
433
494
|
}
|
|
434
495
|
async function triggerDeploy(options) {
|
|
@@ -446,7 +507,7 @@ async function triggerDeploy(options) {
|
|
|
446
507
|
const response = await (0, import_node_fetch.default)(`${apiUrl}/projects/${projectId}/deploy`, {
|
|
447
508
|
method: "POST",
|
|
448
509
|
headers: {
|
|
449
|
-
|
|
510
|
+
Authorization: `Bearer ${apiKey}`,
|
|
450
511
|
"Content-Type": "application/json"
|
|
451
512
|
},
|
|
452
513
|
body: JSON.stringify({
|
|
@@ -460,7 +521,9 @@ async function triggerDeploy(options) {
|
|
|
460
521
|
spinner.succeed(import_chalk.default.green("Deployment triggered successfully!"));
|
|
461
522
|
console.log(import_chalk.default.gray(` \u{1F517} Deployment ID: ${data.deploymentId}`));
|
|
462
523
|
console.log(import_chalk.default.gray(` \u{1F30D} Environment: ${environment}`));
|
|
463
|
-
console.log(
|
|
524
|
+
console.log(
|
|
525
|
+
import_chalk.default.gray(` \u23F0 Started at: ${(/* @__PURE__ */ new Date()).toLocaleTimeString()}`)
|
|
526
|
+
);
|
|
464
527
|
} else {
|
|
465
528
|
throw new Error(`API Error ${response.status}: ${response.statusText}`);
|
|
466
529
|
}
|
|
@@ -483,7 +546,7 @@ async function checkStatus(options) {
|
|
|
483
546
|
try {
|
|
484
547
|
const response = await (0, import_node_fetch.default)(`${apiUrl}/projects/${projectId}/status`, {
|
|
485
548
|
headers: {
|
|
486
|
-
|
|
549
|
+
Authorization: `Bearer ${apiKey}`,
|
|
487
550
|
"Content-Type": "application/json"
|
|
488
551
|
}
|
|
489
552
|
});
|
|
@@ -495,7 +558,11 @@ async function checkStatus(options) {
|
|
|
495
558
|
console.log(import_chalk.default.gray(` \u{1F4C4} Pages: ${data.stats.pages}`));
|
|
496
559
|
console.log(import_chalk.default.gray(` \u{1F4DA} Collections: ${data.stats.collections}`));
|
|
497
560
|
console.log(import_chalk.default.gray(` \u{1F465} Users: ${data.stats.users}`));
|
|
498
|
-
console.log(
|
|
561
|
+
console.log(
|
|
562
|
+
import_chalk.default.gray(
|
|
563
|
+
` \u{1F552} Last Updated: ${new Date(data.project.updatedAt).toLocaleString()}`
|
|
564
|
+
)
|
|
565
|
+
);
|
|
499
566
|
} else {
|
|
500
567
|
throw new Error(`API Error ${response.status}: ${response.statusText}`);
|
|
501
568
|
}
|
|
@@ -510,7 +577,7 @@ async function generateTypes(options) {
|
|
|
510
577
|
const spinner = (0, import_ora.default)("Fetching Content Schema...").start();
|
|
511
578
|
try {
|
|
512
579
|
const response = await (0, import_node_fetch.default)(`${apiUrl}/content/${projectId}/schema`, {
|
|
513
|
-
headers: {
|
|
580
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
514
581
|
});
|
|
515
582
|
if (!response.ok) throw new Error(`API Error: ${response.statusText}`);
|
|
516
583
|
const schema = await response.json();
|
|
@@ -550,7 +617,11 @@ async function generateTypes(options) {
|
|
|
550
617
|
await fs.writeFile(outputPath, typeDefs);
|
|
551
618
|
spinner.succeed(import_chalk.default.green(`Types generated at ${options.output}`));
|
|
552
619
|
if (collections.length > 0) {
|
|
553
|
-
console.log(
|
|
620
|
+
console.log(
|
|
621
|
+
import_chalk.default.gray(
|
|
622
|
+
` \u2728 You can now use: nexus.getCollection<${formatInterfaceName(collections[0].slug)}>('${collections[0].slug}')`
|
|
623
|
+
)
|
|
624
|
+
);
|
|
554
625
|
}
|
|
555
626
|
} catch (error) {
|
|
556
627
|
spinner.fail(import_chalk.default.red("Failed to generate types"));
|
package/dist/tools/cli.mjs
CHANGED
|
@@ -13,7 +13,9 @@ dotenv.config({ path: path.resolve(process.cwd(), ".env") });
|
|
|
13
13
|
var program = new Command();
|
|
14
14
|
program.name("nexus").description("NexusHub CLI - The Developer Toolbelt").version("1.0.0");
|
|
15
15
|
program.command("init").description("Initialize NexusHub configuration and seed data").action(initProject);
|
|
16
|
-
program.command("pull").description(
|
|
16
|
+
program.command("pull").description(
|
|
17
|
+
"Pull latest content from NexusHub to local cache (Offline Mode)"
|
|
18
|
+
).option("-k, --api-key <key>", "NexusHub API Key").option("-p, --project-id <id>", "NexusHub Project ID").option("-u, --api-url <url>", "NexusHub API URL").option("-o, --output <path>", "Output directory (default: .nexus)").option("-f, --force", "Force overwrite existing cache").action(syncContent);
|
|
17
19
|
program.command("seed").description("Seed content from local files to NexusHub").option("-i, --input <path>", "Input directory (default: .nexus/seed)").option("-p, --project-id <id>", "NexusHub Project ID").option("-k, --api-key <key>", "NexusHub API Key").option("-u, --api-url <url>", "NexusHub API URL").option("--dry-run", "Validate seed data without uploading").action(seedContent);
|
|
18
20
|
program.command("deploy").description("Trigger a production deployment").option("-p, --project-id <id>", "NexusHub Project ID").option("-k, --api-key <key>", "NexusHub API Key").option("-u, --api-url <url>", "NexusHub API URL").option("-e, --environment <env>", "Deployment environment", "production").action(triggerDeploy);
|
|
19
21
|
program.command("status").description("Check project status and content statistics").option("-p, --project-id <id>", "NexusHub Project ID").option("-k, --api-key <key>", "NexusHub API Key").option("-u, --api-url <url>", "NexusHub API URL").action(checkStatus);
|
|
@@ -25,7 +27,11 @@ async function getContext() {
|
|
|
25
27
|
const apiUrl = process.env.NEXUS_API_URL || "https://api.nexushub.com/v1";
|
|
26
28
|
if (!apiKey || !projectId) {
|
|
27
29
|
console.error(chalk.red("\n\u274C Missing Configuration"));
|
|
28
|
-
console.log(
|
|
30
|
+
console.log(
|
|
31
|
+
chalk.gray(
|
|
32
|
+
"Please set NEXUS_API_KEY and NEXUS_PROJECT_ID in your .env file."
|
|
33
|
+
)
|
|
34
|
+
);
|
|
29
35
|
process.exit(1);
|
|
30
36
|
}
|
|
31
37
|
return { apiKey, projectId, apiUrl };
|
|
@@ -36,7 +42,7 @@ async function fetchWithAuth(url, apiKey, timeout = 3e4) {
|
|
|
36
42
|
try {
|
|
37
43
|
const response = await fetch(url, {
|
|
38
44
|
headers: {
|
|
39
|
-
|
|
45
|
+
Authorization: `Bearer ${apiKey}`,
|
|
40
46
|
"Content-Type": "application/json",
|
|
41
47
|
"User-Agent": "NexusHub-CLI/1.0.0"
|
|
42
48
|
},
|
|
@@ -78,7 +84,12 @@ async function fetchCollections(baseUrl, apiKey, projectId) {
|
|
|
78
84
|
const itemsData = itemsRes;
|
|
79
85
|
result[collection.slug || collection.id] = itemsData.data || itemsData.items || [];
|
|
80
86
|
} catch (error) {
|
|
81
|
-
console.warn(
|
|
87
|
+
console.warn(
|
|
88
|
+
chalk.yellow(
|
|
89
|
+
`\u26A0\uFE0F Failed to fetch collection ${collection.id}:`,
|
|
90
|
+
error instanceof Error ? error.message : String(error)
|
|
91
|
+
)
|
|
92
|
+
);
|
|
82
93
|
}
|
|
83
94
|
})
|
|
84
95
|
);
|
|
@@ -140,9 +151,17 @@ async function initProject() {
|
|
|
140
151
|
copyright: "\xA9 2024 My Site"
|
|
141
152
|
}
|
|
142
153
|
};
|
|
143
|
-
await fs.writeJson(path.join(seedDir, "pages.json"), examplePages, {
|
|
144
|
-
|
|
145
|
-
|
|
154
|
+
await fs.writeJson(path.join(seedDir, "pages.json"), examplePages, {
|
|
155
|
+
spaces: 2
|
|
156
|
+
});
|
|
157
|
+
await fs.writeJson(
|
|
158
|
+
path.join(collectionsDir, "blog_posts.json"),
|
|
159
|
+
exampleCollection,
|
|
160
|
+
{ spaces: 2 }
|
|
161
|
+
);
|
|
162
|
+
await fs.writeJson(path.join(seedDir, "globals.json"), exampleGlobals, {
|
|
163
|
+
spaces: 2
|
|
164
|
+
});
|
|
146
165
|
const gitignorePath = path.join(nexusDir, ".gitignore");
|
|
147
166
|
await fs.writeFile(gitignorePath, "cache.json\n");
|
|
148
167
|
const readmePath = path.join(nexusDir, "README.md");
|
|
@@ -185,7 +204,9 @@ This directory contains seed data for your NexusHub project.
|
|
|
185
204
|
console.log(chalk.gray(" NEXUS_PROJECT_ID=your_project_id_here"));
|
|
186
205
|
console.log(chalk.gray(" 3. Run `npx nexus seed` to upload content"));
|
|
187
206
|
} catch (error) {
|
|
188
|
-
console.error(
|
|
207
|
+
console.error(
|
|
208
|
+
chalk.red(`\u274C Failed to initialize project: ${error.message}`)
|
|
209
|
+
);
|
|
189
210
|
process.exit(1);
|
|
190
211
|
}
|
|
191
212
|
}
|
|
@@ -197,16 +218,26 @@ async function syncContent(options) {
|
|
|
197
218
|
const outputDir = options.output || ".nexus";
|
|
198
219
|
if (!apiKey || !projectId) {
|
|
199
220
|
console.error(chalk.red("\u274C Missing API Key or Project ID"));
|
|
200
|
-
console.log(
|
|
221
|
+
console.log(
|
|
222
|
+
chalk.gray(
|
|
223
|
+
"Set NEXUS_API_KEY and NEXUS_PROJECT_ID in .env or use --api-key and --project-id flags"
|
|
224
|
+
)
|
|
225
|
+
);
|
|
201
226
|
process.exit(1);
|
|
202
227
|
}
|
|
203
|
-
const spinner = ora(
|
|
228
|
+
const spinner = ora(
|
|
229
|
+
`Fetching content for project: ${chalk.green(projectId)}...`
|
|
230
|
+
).start();
|
|
204
231
|
try {
|
|
205
232
|
const cacheDir = path.join(process.cwd(), outputDir);
|
|
206
233
|
const cacheFile = path.join(cacheDir, "cache.json");
|
|
207
234
|
if (await fs.pathExists(cacheFile) && !options.force) {
|
|
208
235
|
spinner.stop();
|
|
209
|
-
console.log(
|
|
236
|
+
console.log(
|
|
237
|
+
chalk.yellow(
|
|
238
|
+
"\u26A0\uFE0F Cache file already exists. Use --force to overwrite."
|
|
239
|
+
)
|
|
240
|
+
);
|
|
210
241
|
process.exit(0);
|
|
211
242
|
}
|
|
212
243
|
const [pages, collections, globals] = await Promise.all([
|
|
@@ -233,7 +264,9 @@ async function syncContent(options) {
|
|
|
233
264
|
spinner.succeed(chalk.green("Content synced successfully!"));
|
|
234
265
|
console.log(chalk.gray(` \u{1F4C1} Saved to: ${cacheFile}`));
|
|
235
266
|
console.log(chalk.gray(` \u{1F4CA} Pages: ${cacheData.meta.count.pages}`));
|
|
236
|
-
console.log(
|
|
267
|
+
console.log(
|
|
268
|
+
chalk.gray(` \u{1F4DA} Collections: ${cacheData.meta.count.collections}`)
|
|
269
|
+
);
|
|
237
270
|
console.log(chalk.yellow(` \u26A1 Mode: Offline / Local Cache Enabled`));
|
|
238
271
|
} catch (error) {
|
|
239
272
|
spinner.fail(chalk.red("Failed to sync content"));
|
|
@@ -241,7 +274,9 @@ async function syncContent(options) {
|
|
|
241
274
|
if (error.message.includes("401") || error.message.includes("403")) {
|
|
242
275
|
console.log(chalk.yellow(" \u{1F511} Check your API key and permissions"));
|
|
243
276
|
} else if (error.message.includes("404")) {
|
|
244
|
-
console.log(
|
|
277
|
+
console.log(
|
|
278
|
+
chalk.yellow(" \u{1F50D} Project not found. Check your project ID")
|
|
279
|
+
);
|
|
245
280
|
}
|
|
246
281
|
process.exit(1);
|
|
247
282
|
}
|
|
@@ -262,7 +297,9 @@ async function seedContent(options) {
|
|
|
262
297
|
console.error(chalk.red(`\u274C Seed directory not found: ${seedPath}`));
|
|
263
298
|
console.log(chalk.gray("Create a seed directory with your content files:"));
|
|
264
299
|
console.log(chalk.gray(" mkdir -p .nexus/seed"));
|
|
265
|
-
console.log(
|
|
300
|
+
console.log(
|
|
301
|
+
chalk.gray(" # Add pages.json, collections/ folder, globals.json")
|
|
302
|
+
);
|
|
266
303
|
process.exit(1);
|
|
267
304
|
}
|
|
268
305
|
const spinner = ora("Validating seed data...").start();
|
|
@@ -284,7 +321,12 @@ async function seedContent(options) {
|
|
|
284
321
|
if (await fs.pathExists(collectionsDir)) {
|
|
285
322
|
if (!dryRun) {
|
|
286
323
|
spinner.text = "Seeding collections...";
|
|
287
|
-
const counts = await seedCollections(
|
|
324
|
+
const counts = await seedCollections(
|
|
325
|
+
collectionsDir,
|
|
326
|
+
apiUrl,
|
|
327
|
+
projectId,
|
|
328
|
+
apiKey
|
|
329
|
+
);
|
|
288
330
|
collectionCount = counts.collections;
|
|
289
331
|
itemCount = counts.items;
|
|
290
332
|
} else {
|
|
@@ -320,7 +362,7 @@ async function seedPages(pages, apiUrl, projectId, apiKey) {
|
|
|
320
362
|
const response = await fetch(`${apiUrl}/content/${projectId}/pages`, {
|
|
321
363
|
method: "POST",
|
|
322
364
|
headers: {
|
|
323
|
-
|
|
365
|
+
Authorization: `Bearer ${apiKey}`,
|
|
324
366
|
"Content-Type": "application/json"
|
|
325
367
|
},
|
|
326
368
|
body: JSON.stringify(page)
|
|
@@ -353,14 +395,16 @@ async function seedCollections(collectionsDir, apiUrl, projectId, apiKey) {
|
|
|
353
395
|
const filePath = path.join(collectionsDir, file);
|
|
354
396
|
const items = await fs.readJson(filePath);
|
|
355
397
|
if (!Array.isArray(items)) {
|
|
356
|
-
console.warn(
|
|
398
|
+
console.warn(
|
|
399
|
+
chalk.yellow(` \u26A0\uFE0F ${file} does not contain a valid array of items`)
|
|
400
|
+
);
|
|
357
401
|
continue;
|
|
358
402
|
}
|
|
359
403
|
try {
|
|
360
404
|
await fetch(`${apiUrl}/content/${projectId}/collections`, {
|
|
361
405
|
method: "POST",
|
|
362
406
|
headers: {
|
|
363
|
-
|
|
407
|
+
Authorization: `Bearer ${apiKey}`,
|
|
364
408
|
"Content-Type": "application/json"
|
|
365
409
|
},
|
|
366
410
|
body: JSON.stringify({
|
|
@@ -372,23 +416,38 @@ async function seedCollections(collectionsDir, apiUrl, projectId, apiKey) {
|
|
|
372
416
|
let itemCount = 0;
|
|
373
417
|
for (const item of items) {
|
|
374
418
|
try {
|
|
375
|
-
await fetch(
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
"
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
419
|
+
await fetch(
|
|
420
|
+
`${apiUrl}/content/${projectId}/collections/${collectionName}/items`,
|
|
421
|
+
{
|
|
422
|
+
method: "POST",
|
|
423
|
+
headers: {
|
|
424
|
+
Authorization: `Bearer ${apiKey}`,
|
|
425
|
+
"Content-Type": "application/json"
|
|
426
|
+
},
|
|
427
|
+
body: JSON.stringify(item)
|
|
428
|
+
}
|
|
429
|
+
);
|
|
383
430
|
itemCount++;
|
|
384
431
|
} catch (error) {
|
|
385
|
-
console.warn(
|
|
432
|
+
console.warn(
|
|
433
|
+
chalk.yellow(
|
|
434
|
+
` Failed to create item in ${collectionName}: ${error instanceof Error ? error.message : String(error)}`
|
|
435
|
+
)
|
|
436
|
+
);
|
|
386
437
|
}
|
|
387
438
|
}
|
|
388
|
-
console.log(
|
|
439
|
+
console.log(
|
|
440
|
+
chalk.gray(
|
|
441
|
+
` \u2705 Created collection ${collectionName} with ${itemCount} items`
|
|
442
|
+
)
|
|
443
|
+
);
|
|
389
444
|
totalItems += itemCount;
|
|
390
445
|
} catch (error) {
|
|
391
|
-
console.warn(
|
|
446
|
+
console.warn(
|
|
447
|
+
chalk.yellow(
|
|
448
|
+
` \u274C Failed to create collection ${collectionName}: ${error.message}`
|
|
449
|
+
)
|
|
450
|
+
);
|
|
392
451
|
}
|
|
393
452
|
}
|
|
394
453
|
return { collections: jsonFiles.length, items: totalItems };
|
|
@@ -398,14 +457,16 @@ async function seedGlobals(globals, apiUrl, projectId, apiKey) {
|
|
|
398
457
|
await fetch(`${apiUrl}/project/${projectId}/globals`, {
|
|
399
458
|
method: "PUT",
|
|
400
459
|
headers: {
|
|
401
|
-
|
|
460
|
+
Authorization: `Bearer ${apiKey}`,
|
|
402
461
|
"Content-Type": "application/json"
|
|
403
462
|
},
|
|
404
463
|
body: JSON.stringify(globals)
|
|
405
464
|
});
|
|
406
465
|
console.log(chalk.gray(" \u2705 Created globals"));
|
|
407
466
|
} catch (error) {
|
|
408
|
-
console.warn(
|
|
467
|
+
console.warn(
|
|
468
|
+
chalk.yellow(` \u274C Failed to create globals: ${error.message}`)
|
|
469
|
+
);
|
|
409
470
|
}
|
|
410
471
|
}
|
|
411
472
|
async function triggerDeploy(options) {
|
|
@@ -423,7 +484,7 @@ async function triggerDeploy(options) {
|
|
|
423
484
|
const response = await fetch(`${apiUrl}/projects/${projectId}/deploy`, {
|
|
424
485
|
method: "POST",
|
|
425
486
|
headers: {
|
|
426
|
-
|
|
487
|
+
Authorization: `Bearer ${apiKey}`,
|
|
427
488
|
"Content-Type": "application/json"
|
|
428
489
|
},
|
|
429
490
|
body: JSON.stringify({
|
|
@@ -437,7 +498,9 @@ async function triggerDeploy(options) {
|
|
|
437
498
|
spinner.succeed(chalk.green("Deployment triggered successfully!"));
|
|
438
499
|
console.log(chalk.gray(` \u{1F517} Deployment ID: ${data.deploymentId}`));
|
|
439
500
|
console.log(chalk.gray(` \u{1F30D} Environment: ${environment}`));
|
|
440
|
-
console.log(
|
|
501
|
+
console.log(
|
|
502
|
+
chalk.gray(` \u23F0 Started at: ${(/* @__PURE__ */ new Date()).toLocaleTimeString()}`)
|
|
503
|
+
);
|
|
441
504
|
} else {
|
|
442
505
|
throw new Error(`API Error ${response.status}: ${response.statusText}`);
|
|
443
506
|
}
|
|
@@ -460,7 +523,7 @@ async function checkStatus(options) {
|
|
|
460
523
|
try {
|
|
461
524
|
const response = await fetch(`${apiUrl}/projects/${projectId}/status`, {
|
|
462
525
|
headers: {
|
|
463
|
-
|
|
526
|
+
Authorization: `Bearer ${apiKey}`,
|
|
464
527
|
"Content-Type": "application/json"
|
|
465
528
|
}
|
|
466
529
|
});
|
|
@@ -472,7 +535,11 @@ async function checkStatus(options) {
|
|
|
472
535
|
console.log(chalk.gray(` \u{1F4C4} Pages: ${data.stats.pages}`));
|
|
473
536
|
console.log(chalk.gray(` \u{1F4DA} Collections: ${data.stats.collections}`));
|
|
474
537
|
console.log(chalk.gray(` \u{1F465} Users: ${data.stats.users}`));
|
|
475
|
-
console.log(
|
|
538
|
+
console.log(
|
|
539
|
+
chalk.gray(
|
|
540
|
+
` \u{1F552} Last Updated: ${new Date(data.project.updatedAt).toLocaleString()}`
|
|
541
|
+
)
|
|
542
|
+
);
|
|
476
543
|
} else {
|
|
477
544
|
throw new Error(`API Error ${response.status}: ${response.statusText}`);
|
|
478
545
|
}
|
|
@@ -487,7 +554,7 @@ async function generateTypes(options) {
|
|
|
487
554
|
const spinner = ora("Fetching Content Schema...").start();
|
|
488
555
|
try {
|
|
489
556
|
const response = await fetch(`${apiUrl}/content/${projectId}/schema`, {
|
|
490
|
-
headers: {
|
|
557
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
491
558
|
});
|
|
492
559
|
if (!response.ok) throw new Error(`API Error: ${response.statusText}`);
|
|
493
560
|
const schema = await response.json();
|
|
@@ -527,7 +594,11 @@ async function generateTypes(options) {
|
|
|
527
594
|
await fs.writeFile(outputPath, typeDefs);
|
|
528
595
|
spinner.succeed(chalk.green(`Types generated at ${options.output}`));
|
|
529
596
|
if (collections.length > 0) {
|
|
530
|
-
console.log(
|
|
597
|
+
console.log(
|
|
598
|
+
chalk.gray(
|
|
599
|
+
` \u2728 You can now use: nexus.getCollection<${formatInterfaceName(collections[0].slug)}>('${collections[0].slug}')`
|
|
600
|
+
)
|
|
601
|
+
);
|
|
531
602
|
}
|
|
532
603
|
} catch (error) {
|
|
533
604
|
spinner.fail(chalk.red("Failed to generate types"));
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexushub/client",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.4",
|
|
5
5
|
"description": "The God-Tier NexusHub SDK",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
7
7
|
"types": "dist/src/index.d.ts",
|
|
8
8
|
"bin": {
|
|
9
|
-
"nexus": "
|
|
9
|
+
"nexus": "dist/tools/cli.js"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
13
|
"README.md"
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
|
-
"build": "tsup src/index.ts tools/cli.ts --format esm,cjs --dts --clean",
|
|
16
|
+
"build": "tsup src/index.ts tools/cli.ts --format esm,cjs --dts --clean && chmod +x ./dist/tools/cli.js",
|
|
17
17
|
"dev": "tsup src/index.ts tools/cli.ts --format esm,cjs --dts --watch",
|
|
18
18
|
"test": "jest",
|
|
19
19
|
"test:coverage": "jest --coverage",
|