@jsonpages/cli 3.0.27 → 3.0.29
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/assets/src_tenant_alpha.sh +235 -73
- package/package.json +29 -29
|
@@ -29,7 +29,8 @@ echo "Creating src/App.tsx..."
|
|
|
29
29
|
cat << 'END_OF_FILE_CONTENT' > "src/App.tsx"
|
|
30
30
|
/**
|
|
31
31
|
* Thin Entry Point (Tenant).
|
|
32
|
-
*
|
|
32
|
+
* Data from getHydratedData (file-backed or draft); assets from public/assets/images.
|
|
33
|
+
* Persistence: Export JSON (download) + optional Salva su repo (POST /api/persist → git push).
|
|
33
34
|
*/
|
|
34
35
|
import { useState, useEffect } from 'react';
|
|
35
36
|
import { JsonPagesEngine } from '@jsonpages/core';
|
|
@@ -40,14 +41,13 @@ import { addSectionConfig } from '@/lib/addSectionConfig';
|
|
|
40
41
|
import { getHydratedData } from '@/lib/draftStorage';
|
|
41
42
|
import type { JsonPagesConfig } from '@jsonpages/core';
|
|
42
43
|
import type { PageConfig, SiteConfig, ThemeConfig, MenuConfig } from '@/types';
|
|
44
|
+
import type { ProjectState } from '@jsonpages/core';
|
|
43
45
|
|
|
44
|
-
// Tenant data (file-backed fallback)
|
|
45
46
|
import siteData from '@/data/config/site.json';
|
|
46
47
|
import themeData from '@/data/config/theme.json';
|
|
47
48
|
import menuData from '@/data/config/menu.json';
|
|
48
49
|
import homeData from '@/data/pages/home.json';
|
|
49
50
|
|
|
50
|
-
// Tenant CSS
|
|
51
51
|
import tenantCss from './index.css?inline';
|
|
52
52
|
|
|
53
53
|
const themeConfig = themeData as unknown as ThemeConfig;
|
|
@@ -59,6 +59,8 @@ const filePages: Record<string, PageConfig> = {
|
|
|
59
59
|
};
|
|
60
60
|
const fileSiteConfig = siteData as unknown as SiteConfig;
|
|
61
61
|
|
|
62
|
+
const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024; // 5MB, must match server
|
|
63
|
+
|
|
62
64
|
function getInitialData() {
|
|
63
65
|
return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
|
|
64
66
|
}
|
|
@@ -67,17 +69,6 @@ function App() {
|
|
|
67
69
|
const [{ pages, siteConfig }] = useState(getInitialData);
|
|
68
70
|
const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
|
|
69
71
|
|
|
70
|
-
// #region agent log
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
fetch('http://127.0.0.1:7588/ingest/86d71502-47e1-433c-9b6d-5a1390d00813',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'34bba5'},body:JSON.stringify({sessionId:'34bba5',location:'App.tsx',message:'App mounted',data:{},timestamp:Date.now(),hypothesisId:'H1,H3'})}).catch(()=>{});
|
|
73
|
-
const onSubmit = (e: Event) => {
|
|
74
|
-
fetch('http://127.0.0.1:7588/ingest/86d71502-47e1-433c-9b6d-5a1390d00813',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'34bba5'},body:JSON.stringify({sessionId:'34bba5',location:'App.tsx:submit',message:'form submit captured',data:{targetTag:(e.target as HTMLElement)?.tagName},timestamp:Date.now(),hypothesisId:'H4'})}).catch(()=>{});
|
|
75
|
-
};
|
|
76
|
-
document.addEventListener('submit', onSubmit, true);
|
|
77
|
-
return () => document.removeEventListener('submit', onSubmit, true);
|
|
78
|
-
}, []);
|
|
79
|
-
// #endregion
|
|
80
|
-
|
|
81
72
|
useEffect(() => {
|
|
82
73
|
fetch('/api/list-assets')
|
|
83
74
|
.then((r) => (r.ok ? r.json() : []))
|
|
@@ -96,50 +87,59 @@ function App() {
|
|
|
96
87
|
themeCss: { tenant: tenantCss },
|
|
97
88
|
addSection: addSectionConfig,
|
|
98
89
|
persistence: {
|
|
99
|
-
async
|
|
100
|
-
const res = await fetch('/api/
|
|
90
|
+
async saveToRepo(state: ProjectState, slug: string) {
|
|
91
|
+
const res = await fetch('/api/persist', {
|
|
101
92
|
method: 'POST',
|
|
102
93
|
headers: { 'Content-Type': 'application/json' },
|
|
103
|
-
body: JSON.stringify({
|
|
94
|
+
body: JSON.stringify({ projectState: state, slug }),
|
|
104
95
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
96
|
+
const body = (await res.json().catch(() => ({}))) as { error?: string; message?: string };
|
|
97
|
+
if (!res.ok) {
|
|
98
|
+
const msg = body.error ?? `Save failed: ${res.status}`;
|
|
99
|
+
alert(msg);
|
|
100
|
+
throw new Error(msg);
|
|
101
|
+
}
|
|
108
102
|
},
|
|
109
103
|
},
|
|
110
104
|
assets: {
|
|
111
105
|
assetsBaseUrl: '/assets',
|
|
112
106
|
assetsManifest,
|
|
113
107
|
async onAssetUpload(file: File): Promise<string> {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
108
|
+
if (!file.type.startsWith('image/')) {
|
|
109
|
+
throw new Error('Invalid file type. Allowed: JPEG, PNG, WebP, GIF, SVG, AVIF.');
|
|
110
|
+
}
|
|
111
|
+
if (file.size > MAX_UPLOAD_SIZE_BYTES) {
|
|
112
|
+
throw new Error(`File too large. Maximum size is ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
|
|
113
|
+
}
|
|
114
|
+
const base64 = await new Promise<string>((resolve, reject) => {
|
|
115
|
+
const reader = new FileReader();
|
|
116
|
+
reader.onload = () => {
|
|
117
|
+
const dataUrl = reader.result as string;
|
|
118
|
+
const b64 = dataUrl.includes(',') ? dataUrl.split(',')[1] : '';
|
|
119
|
+
resolve(b64 || '');
|
|
120
|
+
};
|
|
121
|
+
reader.onerror = () => reject(reader.error);
|
|
122
|
+
reader.readAsDataURL(file);
|
|
123
|
+
});
|
|
124
|
+
const res = await fetch('/api/upload-asset', {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: { 'Content-Type': 'application/json' },
|
|
127
|
+
body: JSON.stringify({
|
|
128
|
+
filename: file.name,
|
|
129
|
+
mimeType: file.type || undefined,
|
|
130
|
+
data: base64,
|
|
131
|
+
}),
|
|
132
|
+
});
|
|
133
|
+
const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
|
|
134
|
+
if (!res.ok) {
|
|
135
|
+
throw new Error(body.error || `Upload failed: ${res.status}`);
|
|
136
|
+
}
|
|
137
|
+
if (typeof body.url !== 'string') {
|
|
138
|
+
throw new Error('Invalid server response: missing url');
|
|
139
|
+
}
|
|
140
|
+
return body.url;
|
|
141
|
+
},
|
|
141
142
|
},
|
|
142
|
-
},
|
|
143
143
|
};
|
|
144
144
|
|
|
145
145
|
return <JsonPagesEngine config={config} />;
|
|
@@ -1070,11 +1070,6 @@ export const ImageBreak: React.FC<{ data: ImageBreakData; settings?: ImageBreakS
|
|
|
1070
1070
|
</div>
|
|
1071
1071
|
)}
|
|
1072
1072
|
</div>
|
|
1073
|
-
{data.label && (
|
|
1074
|
-
<div className="jp-section-label sr-only" data-jp-field="label">
|
|
1075
|
-
{data.label}
|
|
1076
|
-
</div>
|
|
1077
|
-
)}
|
|
1078
1073
|
</>
|
|
1079
1074
|
) : (
|
|
1080
1075
|
<div className="flex flex-col items-center justify-center py-24 text-[var(--local-text-muted)]">
|
|
@@ -1097,19 +1092,16 @@ END_OF_FILE_CONTENT
|
|
|
1097
1092
|
echo "Creating src/components/image-break/schema.ts..."
|
|
1098
1093
|
cat << 'END_OF_FILE_CONTENT' > "src/components/image-break/schema.ts"
|
|
1099
1094
|
import { z } from 'zod';
|
|
1100
|
-
import { BaseSectionData } from '@/lib/base-schemas';
|
|
1101
|
-
|
|
1102
|
-
const ImageSelectionSchema = z.object({
|
|
1103
|
-
url: z.string(),
|
|
1104
|
-
alt: z.string().optional(),
|
|
1105
|
-
}).describe('ui:image-picker');
|
|
1095
|
+
import { BaseSectionData, ImageSelectionSchema } from '@/lib/base-schemas';
|
|
1106
1096
|
|
|
1107
1097
|
export const ImageBreakSchema = BaseSectionData.extend({
|
|
1108
|
-
label: z.string().optional().describe('ui:text'),
|
|
1109
1098
|
image: ImageSelectionSchema.default({ url: '', alt: '' }),
|
|
1110
|
-
caption: z.string().optional().describe('ui:
|
|
1099
|
+
caption: z.string().optional().describe('ui:text'),
|
|
1111
1100
|
});
|
|
1112
1101
|
|
|
1102
|
+
export const ImageBreakSettingsSchema = z.object({
|
|
1103
|
+
height: z.enum(['sm', 'md', 'lg', 'full']).default('md').describe('ui:select'),
|
|
1104
|
+
});
|
|
1113
1105
|
END_OF_FILE_CONTENT
|
|
1114
1106
|
echo "Creating src/components/image-break/types.ts..."
|
|
1115
1107
|
cat << 'END_OF_FILE_CONTENT' > "src/components/image-break/types.ts"
|
|
@@ -2503,18 +2495,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/pages/home.json"
|
|
|
2503
2495
|
},
|
|
2504
2496
|
"settings": {}
|
|
2505
2497
|
},
|
|
2506
|
-
|
|
2507
|
-
"id": "image-break-1",
|
|
2508
|
-
"type": "image-break",
|
|
2509
|
-
"data": {
|
|
2510
|
-
"image": {
|
|
2511
|
-
"url": "https://images.unsplash.com/photo-1464226184884-fa280b87c399?w=800",
|
|
2512
|
-
"alt": "Verdure fresche dall'orto"
|
|
2513
|
-
},
|
|
2514
|
-
"caption": "Il nostro orto a pochi passi dalla cucina — raccolto ogni mattina all'alba"
|
|
2515
|
-
},
|
|
2516
|
-
"settings": {}
|
|
2517
|
-
},
|
|
2498
|
+
|
|
2518
2499
|
{
|
|
2519
2500
|
"id": "problem-statement",
|
|
2520
2501
|
"type": "problem-statement",
|
|
@@ -3268,6 +3249,17 @@ echo "Creating src/lib/base-schemas.ts..."
|
|
|
3268
3249
|
cat << 'END_OF_FILE_CONTENT' > "src/lib/base-schemas.ts"
|
|
3269
3250
|
import { z } from 'zod';
|
|
3270
3251
|
|
|
3252
|
+
/**
|
|
3253
|
+
* Image picker field: object { url, alt? } with ui:image-picker for Form Factory.
|
|
3254
|
+
* Use in section data and resolve with resolveAssetUrl(url, tenantId) in View.
|
|
3255
|
+
*/
|
|
3256
|
+
export const ImageSelectionSchema = z
|
|
3257
|
+
.object({
|
|
3258
|
+
url: z.string(),
|
|
3259
|
+
alt: z.string().optional(),
|
|
3260
|
+
})
|
|
3261
|
+
.describe('ui:image-picker');
|
|
3262
|
+
|
|
3271
3263
|
/**
|
|
3272
3264
|
* Base schemas shared by section capsules (CIP governance).
|
|
3273
3265
|
* Capsules extend these for consistent anchorId, array items, and settings.
|
|
@@ -3436,6 +3428,176 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
|
3436
3428
|
|
|
3437
3429
|
END_OF_FILE_CONTENT
|
|
3438
3430
|
# SKIP: src/registry-types.ts è un file binario e non può essere convertito in testo.
|
|
3431
|
+
mkdir -p "src/server"
|
|
3432
|
+
echo "Creating src/server/persistToRepo.ts..."
|
|
3433
|
+
cat << 'END_OF_FILE_CONTENT' > "src/server/persistToRepo.ts"
|
|
3434
|
+
/**
|
|
3435
|
+
* Enterprise-grade persist: write JSON to disk and git add/commit/push.
|
|
3436
|
+
* Used by POST /api/persist. Credentials via env (GITHUB_TOKEN); no secrets in code.
|
|
3437
|
+
*/
|
|
3438
|
+
import fs from 'fs';
|
|
3439
|
+
import path from 'path';
|
|
3440
|
+
import { simpleGit, type SimpleGit } from 'simple-git';
|
|
3441
|
+
|
|
3442
|
+
/** Minimal ProjectState shape for validation (matches Core kernel). */
|
|
3443
|
+
export interface PersistBody {
|
|
3444
|
+
projectState: {
|
|
3445
|
+
page: unknown;
|
|
3446
|
+
site: unknown;
|
|
3447
|
+
menu: unknown;
|
|
3448
|
+
theme: unknown;
|
|
3449
|
+
};
|
|
3450
|
+
slug: string;
|
|
3451
|
+
}
|
|
3452
|
+
|
|
3453
|
+
const SLUG_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
|
|
3454
|
+
|
|
3455
|
+
function isNonEmptyObject(v: unknown): v is Record<string, unknown> {
|
|
3456
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
3457
|
+
}
|
|
3458
|
+
|
|
3459
|
+
export function validatePersistBody(body: unknown): { ok: true; data: PersistBody } | { ok: false; status: number; error: string } {
|
|
3460
|
+
if (!isNonEmptyObject(body) || !isNonEmptyObject(body.projectState)) {
|
|
3461
|
+
return { ok: false, status: 400, error: 'Missing or invalid projectState' };
|
|
3462
|
+
}
|
|
3463
|
+
const { projectState, slug } = body as PersistBody;
|
|
3464
|
+
if (typeof slug !== 'string' || !SLUG_REGEX.test(slug)) {
|
|
3465
|
+
return { ok: false, status: 400, error: 'Invalid slug (use lowercase alphanumeric and hyphens)' };
|
|
3466
|
+
}
|
|
3467
|
+
if (
|
|
3468
|
+
projectState.page == null ||
|
|
3469
|
+
projectState.site == null ||
|
|
3470
|
+
projectState.menu == null ||
|
|
3471
|
+
projectState.theme == null
|
|
3472
|
+
) {
|
|
3473
|
+
return { ok: false, status: 400, error: 'projectState must include page, site, menu, theme' };
|
|
3474
|
+
}
|
|
3475
|
+
return { ok: true, data: { projectState, slug } };
|
|
3476
|
+
}
|
|
3477
|
+
|
|
3478
|
+
export interface PersistOptions {
|
|
3479
|
+
/** Absolute path to data dir (e.g. .../src/data). */
|
|
3480
|
+
dataDir: string;
|
|
3481
|
+
/** Git working directory (repo root). Defaults to dataDir's app root. */
|
|
3482
|
+
gitDir: string;
|
|
3483
|
+
/** Optional: inject into origin for push (e.g. GITHUB_TOKEN). */
|
|
3484
|
+
token?: string;
|
|
3485
|
+
}
|
|
3486
|
+
|
|
3487
|
+
function writeFiles(data: PersistBody, dataDir: string): string[] {
|
|
3488
|
+
const configDir = path.join(dataDir, 'config');
|
|
3489
|
+
const pagesDir = path.join(dataDir, 'pages');
|
|
3490
|
+
const written: string[] = [];
|
|
3491
|
+
|
|
3492
|
+
[configDir, pagesDir].forEach((dir) => {
|
|
3493
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
3494
|
+
});
|
|
3495
|
+
|
|
3496
|
+
const { projectState, slug } = data;
|
|
3497
|
+
const sitePath = path.join(configDir, 'site.json');
|
|
3498
|
+
const themePath = path.join(configDir, 'theme.json');
|
|
3499
|
+
const menuPath = path.join(configDir, 'menu.json');
|
|
3500
|
+
const pagePath = path.join(pagesDir, `${slug}.json`);
|
|
3501
|
+
|
|
3502
|
+
fs.writeFileSync(sitePath, JSON.stringify(projectState.site, null, 2), 'utf8');
|
|
3503
|
+
written.push(sitePath);
|
|
3504
|
+
fs.writeFileSync(themePath, JSON.stringify(projectState.theme, null, 2), 'utf8');
|
|
3505
|
+
written.push(themePath);
|
|
3506
|
+
fs.writeFileSync(menuPath, JSON.stringify(projectState.menu, null, 2), 'utf8');
|
|
3507
|
+
written.push(menuPath);
|
|
3508
|
+
fs.writeFileSync(pagePath, JSON.stringify(projectState.page, null, 2), 'utf8');
|
|
3509
|
+
written.push(pagePath);
|
|
3510
|
+
|
|
3511
|
+
return written;
|
|
3512
|
+
}
|
|
3513
|
+
|
|
3514
|
+
/** Inject token into HTTPS origin for push (no token stored in config). */
|
|
3515
|
+
function originWithToken(origin: string, token: string): string {
|
|
3516
|
+
if (origin.startsWith('https://')) {
|
|
3517
|
+
return origin.replace(/^https:\/\/([^@]*@)?/, `https://${token}@`);
|
|
3518
|
+
}
|
|
3519
|
+
return origin;
|
|
3520
|
+
}
|
|
3521
|
+
|
|
3522
|
+
async function gitAddCommitPush(
|
|
3523
|
+
gitDir: string,
|
|
3524
|
+
relativePaths: string[],
|
|
3525
|
+
commitMessage: string,
|
|
3526
|
+
token?: string
|
|
3527
|
+
): Promise<{ success: true } | { success: false; error: string }> {
|
|
3528
|
+
const git: SimpleGit = simpleGit(gitDir);
|
|
3529
|
+
let originalOrigin: string | null = null;
|
|
3530
|
+
|
|
3531
|
+
try {
|
|
3532
|
+
const status = await git.status();
|
|
3533
|
+
if (status.isBare()) {
|
|
3534
|
+
return { success: false, error: 'Repository is bare; cannot commit' };
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3537
|
+
if (token) {
|
|
3538
|
+
const remotes = await git.getRemotes(true);
|
|
3539
|
+
const origin = remotes.origin?.refs?.fetch ?? remotes.origin?.refs?.push;
|
|
3540
|
+
if (origin) {
|
|
3541
|
+
originalOrigin = origin;
|
|
3542
|
+
const authUrl = originWithToken(origin, token);
|
|
3543
|
+
await git.raw(['remote', 'set-url', 'origin', authUrl]);
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
await git.add(relativePaths);
|
|
3548
|
+
const commitResult = await git.commit(commitMessage);
|
|
3549
|
+
if (commitResult.commit === null && relativePaths.length > 0) {
|
|
3550
|
+
return { success: false, error: 'Nothing to commit (files unchanged or not in repo)' };
|
|
3551
|
+
}
|
|
3552
|
+
await git.push();
|
|
3553
|
+
return { success: true };
|
|
3554
|
+
} catch (e) {
|
|
3555
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
3556
|
+
return { success: false, error: msg };
|
|
3557
|
+
} finally {
|
|
3558
|
+
if (originalOrigin) {
|
|
3559
|
+
try {
|
|
3560
|
+
await git.raw(['remote', 'set-url', 'origin', originalOrigin]);
|
|
3561
|
+
} catch {
|
|
3562
|
+
// best-effort restore; do not mask original error
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3568
|
+
/**
|
|
3569
|
+
* Write project state to disk and push to remote.
|
|
3570
|
+
* File paths are resolved from dataDir; git paths are relative to gitDir.
|
|
3571
|
+
*/
|
|
3572
|
+
export async function runPersist(
|
|
3573
|
+
data: PersistBody,
|
|
3574
|
+
options: PersistOptions
|
|
3575
|
+
): Promise<{ success: true; message: string } | { success: false; status: number; error: string }> {
|
|
3576
|
+
const { dataDir, gitDir, token } = options;
|
|
3577
|
+
|
|
3578
|
+
if (!fs.existsSync(dataDir)) {
|
|
3579
|
+
return { success: false, status: 500, error: 'Data directory does not exist' };
|
|
3580
|
+
}
|
|
3581
|
+
|
|
3582
|
+
const written = writeFiles(data, dataDir);
|
|
3583
|
+
const relativePaths = written.map((p) => path.relative(gitDir, p)).filter((p) => !p.startsWith('..'));
|
|
3584
|
+
if (relativePaths.length === 0) {
|
|
3585
|
+
return { success: false, status: 500, error: 'Computed paths are outside git directory' };
|
|
3586
|
+
}
|
|
3587
|
+
|
|
3588
|
+
const commitMessage = `jsonpages: persist ${data.slug} @ ${new Date().toISOString().slice(0, 19)}Z`;
|
|
3589
|
+
const result = await gitAddCommitPush(gitDir, relativePaths, commitMessage, token);
|
|
3590
|
+
|
|
3591
|
+
if (result.success) {
|
|
3592
|
+
return { success: true, message: 'Saved and pushed to remote' };
|
|
3593
|
+
}
|
|
3594
|
+
if (result.error.toLowerCase().includes('failed to push') || result.error.toLowerCase().includes('authentication')) {
|
|
3595
|
+
return { success: false, status: 502, error: result.error };
|
|
3596
|
+
}
|
|
3597
|
+
return { success: false, status: 500, error: result.error };
|
|
3598
|
+
}
|
|
3599
|
+
|
|
3600
|
+
END_OF_FILE_CONTENT
|
|
3439
3601
|
echo "Creating src/types.ts..."
|
|
3440
3602
|
cat << 'END_OF_FILE_CONTENT' > "src/types.ts"
|
|
3441
3603
|
import type { MenuItem } from '@jsonpages/core';
|
package/package.json
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@jsonpages/cli",
|
|
3
|
-
"version": "3.0.
|
|
4
|
-
"description": "JsonPages CLI - Sovereign Projection Engine",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"jsonpages": "./src/index.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"src",
|
|
11
|
-
"assets/src_tenant_alpha.sh"
|
|
12
|
-
],
|
|
13
|
-
"scripts": {
|
|
14
|
-
"build": "tsc -p ."
|
|
15
|
-
},
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@jsonpages/stack": "^1.0.0",
|
|
18
|
-
"chalk": "^5.3.0",
|
|
19
|
-
"commander": "^12.1.0",
|
|
20
|
-
"execa": "^9.0.2",
|
|
21
|
-
"fs-extra": "^11.2.0",
|
|
22
|
-
"ora": "^8.0.1"
|
|
23
|
-
},
|
|
24
|
-
"devDependencies": {
|
|
25
|
-
"@types/fs-extra": "^11.0.4",
|
|
26
|
-
"@types/node": "^22.13.1",
|
|
27
|
-
"typescript": "^5.7.3"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@jsonpages/cli",
|
|
3
|
+
"version": "3.0.29",
|
|
4
|
+
"description": "JsonPages CLI - Sovereign Projection Engine",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"jsonpages": "./src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"src",
|
|
11
|
+
"assets/src_tenant_alpha.sh"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc -p ."
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@jsonpages/stack": "^1.0.0",
|
|
18
|
+
"chalk": "^5.3.0",
|
|
19
|
+
"commander": "^12.1.0",
|
|
20
|
+
"execa": "^9.0.2",
|
|
21
|
+
"fs-extra": "^11.2.0",
|
|
22
|
+
"ora": "^8.0.1"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/fs-extra": "^11.0.4",
|
|
26
|
+
"@types/node": "^22.13.1",
|
|
27
|
+
"typescript": "^5.7.3"
|
|
28
|
+
}
|
|
29
|
+
}
|