primo-cli 0.1.8 → 0.1.10
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/commands/deploy.js +3 -3
- package/dist/commands/dev.js +230 -73
- package/dist/commands/push.js +8 -0
- package/package.json +1 -1
package/dist/commands/deploy.js
CHANGED
|
@@ -221,11 +221,11 @@ async function check_provider_auth(provider) {
|
|
|
221
221
|
return false;
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
|
-
// Pinned to the upstream-published image.
|
|
224
|
+
// Pinned to the upstream-published image. primocms's main.yml workflow
|
|
225
225
|
// publishes branch tags for whitelisted prefixes (main, feature/**, rc/**),
|
|
226
226
|
// with slashes slugified to dashes (feature/local-dev-cli → :feature-local-dev-cli).
|
|
227
|
-
// Bump to a release tag (:v3.0.0) when
|
|
228
|
-
const PRIMO_SERVER_IMAGE = 'ghcr.io/
|
|
227
|
+
// Bump to a release tag (:v3.0.0) when primocms cuts a stable release.
|
|
228
|
+
const PRIMO_SERVER_IMAGE = 'ghcr.io/primocms/primo:main';
|
|
229
229
|
async function generate_dockerfile(inventory) {
|
|
230
230
|
// One-line Dockerfile: pull the published palacms image and run it
|
|
231
231
|
// unchanged. Workspace data (server.yaml, sites/, library/) is uploaded
|
package/dist/commands/dev.js
CHANGED
|
@@ -36,8 +36,12 @@ let site_sync_baselines = new Map();
|
|
|
36
36
|
// the same conflict block every pull cycle when palacms's serialization
|
|
37
37
|
// keeps producing the same divergence (e.g. data-key mangling, key reorder).
|
|
38
38
|
const last_logged_conflicts = new Map();
|
|
39
|
-
// Track files written by sync to prevent watcher from re-pushing them
|
|
40
|
-
//
|
|
39
|
+
// Track files written by sync to prevent watcher from re-pushing them.
|
|
40
|
+
// Keyed by absolute path; value is the SHA-256 of the content we wrote.
|
|
41
|
+
// We compare against the file's *current* hash in the watcher, so a user
|
|
42
|
+
// edit that happens within the polling cycle is detected by content
|
|
43
|
+
// divergence rather than mtime±tolerance (which used to drop edits made
|
|
44
|
+
// within 3 seconds of a sync-write).
|
|
41
45
|
const synced_files = new Map();
|
|
42
46
|
const synced_deleted_paths = new Map();
|
|
43
47
|
const warned_empty_schema_writebacks = new Set();
|
|
@@ -49,11 +53,25 @@ const MCP_CONFIG_FILE = '.mcp.json';
|
|
|
49
53
|
const SITE_SYNC_DIRS = ['blocks', 'page-types', 'pages', 'site'];
|
|
50
54
|
const LOCAL_PUSH_DEBOUNCE_MS = 150;
|
|
51
55
|
const LOCAL_ZIP_COMPRESSION_LEVEL = 0;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
//
|
|
56
|
-
|
|
56
|
+
function hash_content(content) {
|
|
57
|
+
return createHash('sha256').update(content).digest('hex');
|
|
58
|
+
}
|
|
59
|
+
// Returns true iff the file at `full_path` still has the exact content
|
|
60
|
+
// we last synced to it. Used by watcher event handlers to ignore their
|
|
61
|
+
// own writes without the old mtime-tolerance race that swallowed user
|
|
62
|
+
// edits made within seconds of a sync-write.
|
|
63
|
+
async function event_matches_synced_write(full_path) {
|
|
64
|
+
const expected_hash = synced_files.get(full_path);
|
|
65
|
+
if (!expected_hash)
|
|
66
|
+
return false;
|
|
67
|
+
try {
|
|
68
|
+
const current = await fs.readFile(full_path);
|
|
69
|
+
return hash_content(current) === expected_hash;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
57
75
|
// When the user writes a file locally, suppress CMS->local pulls for this
|
|
58
76
|
// long to prevent a pull that was in-flight before the watcher fired from
|
|
59
77
|
// stomping the just-written content on arrival.
|
|
@@ -636,8 +654,11 @@ export async function dev_server(options) {
|
|
|
636
654
|
if (sites.length > 0 || !is_server_mode) {
|
|
637
655
|
console.log('');
|
|
638
656
|
}
|
|
639
|
-
// Start watching for file changes
|
|
640
|
-
|
|
657
|
+
// Start watching for file changes. `uploads` is included so dropping
|
|
658
|
+
// an image into uploads/ triggers a push — the server-side reconcile
|
|
659
|
+
// creates a site_uploads record for it and the writeback renames
|
|
660
|
+
// the local file to the canonical (suffixed) name.
|
|
661
|
+
const dirs_to_watch = ['blocks', 'page-types', 'pages', 'site', 'uploads'];
|
|
641
662
|
const known_sites = new Set(sites.map(s => s.dir));
|
|
642
663
|
if (is_server_mode) {
|
|
643
664
|
const library_path = path.join(base_dir, LIBRARY_DIR);
|
|
@@ -676,16 +697,18 @@ export async function dev_server(options) {
|
|
|
676
697
|
const on_event = (full_path) => {
|
|
677
698
|
if (should_skip_synced_delete(full_path))
|
|
678
699
|
return;
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
//
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
700
|
+
if (synced_files.has(full_path)) {
|
|
701
|
+
// We last wrote this file from a CMS pull. If the
|
|
702
|
+
// content on disk still matches our write, the
|
|
703
|
+
// chokidar event is just our own write echoing
|
|
704
|
+
// back — drop it. If it differs, the user edited
|
|
705
|
+
// the file (possibly very shortly after our pull
|
|
706
|
+
// landed) and we must push.
|
|
707
|
+
event_matches_synced_write(full_path)
|
|
708
|
+
.then(matches => {
|
|
688
709
|
synced_files.delete(full_path);
|
|
710
|
+
if (matches)
|
|
711
|
+
return;
|
|
689
712
|
push_or_ignore_library_change(full_path);
|
|
690
713
|
})
|
|
691
714
|
.catch(() => {
|
|
@@ -818,7 +841,16 @@ export async function dev_server(options) {
|
|
|
818
841
|
}
|
|
819
842
|
console.log(chalk.dim(` ${site.config.name}: normalize ${normalize_ms}ms, zip ${import_timings.zip_ms}ms, ${import_timings.mode} ${import_timings.request_ms}ms${reload_ms ? `, reload ${reload_ms}ms` : ''}`));
|
|
820
843
|
console.log(chalk.green(` ✓ ${site.config.name} pushed`));
|
|
821
|
-
|
|
844
|
+
if (import_timings.warning_count > 0) {
|
|
845
|
+
await write_sync_status(site.dir, {
|
|
846
|
+
ok: true,
|
|
847
|
+
warnings: import_timings.warning_count,
|
|
848
|
+
warned_at: new Date().toISOString()
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
else {
|
|
852
|
+
await write_sync_status(site.dir, { ok: true });
|
|
853
|
+
}
|
|
822
854
|
}
|
|
823
855
|
catch (err) {
|
|
824
856
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -880,15 +912,20 @@ export async function dev_server(options) {
|
|
|
880
912
|
const on_event = (full_path) => {
|
|
881
913
|
if (should_skip_synced_delete(full_path))
|
|
882
914
|
return;
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
915
|
+
if (synced_files.has(full_path)) {
|
|
916
|
+
// We last wrote this file from a CMS pull. Compare
|
|
917
|
+
// the file's *current* content against the hash we
|
|
918
|
+
// stored: if identical, this watcher event is the
|
|
919
|
+
// echo of our own write and must be ignored; if
|
|
920
|
+
// different, the user edited the file (possibly
|
|
921
|
+
// within ms of our pull) and the edit must push,
|
|
922
|
+
// which is exactly the case the old mtime-tolerance
|
|
923
|
+
// check used to swallow for site/head.svelte.
|
|
924
|
+
event_matches_synced_write(full_path)
|
|
925
|
+
.then(matches => {
|
|
891
926
|
synced_files.delete(full_path);
|
|
927
|
+
if (matches)
|
|
928
|
+
return;
|
|
892
929
|
push_or_ignore_file_change(full_path);
|
|
893
930
|
})
|
|
894
931
|
.catch(() => {
|
|
@@ -1273,15 +1310,8 @@ function track_duplicate(duplicates, category, id, occurrence) {
|
|
|
1273
1310
|
existing.push(occurrence);
|
|
1274
1311
|
by_id.set(id, existing);
|
|
1275
1312
|
}
|
|
1276
|
-
|
|
1277
|
-
synced_files.set(file_path,
|
|
1278
|
-
try {
|
|
1279
|
-
const stat = await fs.stat(file_path);
|
|
1280
|
-
synced_files.set(file_path, stat.mtimeMs);
|
|
1281
|
-
}
|
|
1282
|
-
catch {
|
|
1283
|
-
// Ignore files that disappeared
|
|
1284
|
-
}
|
|
1313
|
+
function mark_written_file(file_path, content) {
|
|
1314
|
+
synced_files.set(file_path, hash_content(content));
|
|
1285
1315
|
}
|
|
1286
1316
|
function mark_deleted_path(file_path) {
|
|
1287
1317
|
synced_deleted_paths.set(file_path, Date.now());
|
|
@@ -1831,8 +1861,10 @@ async function import_site_files(site_dir, api_url, config, port, server_config,
|
|
|
1831
1861
|
const zip_started = Date.now();
|
|
1832
1862
|
const zip_buffer = await create_site_zip(site_dir, preparation.excluded_paths);
|
|
1833
1863
|
const zip_ms = Date.now() - zip_started;
|
|
1834
|
-
//
|
|
1835
|
-
//
|
|
1864
|
+
// Dev sites route via *.localhost. The host is computed from name+port
|
|
1865
|
+
// here and passed only to bootstrap (which seeds new sites with this
|
|
1866
|
+
// routing host); site.yaml does not carry host at all, and the regular
|
|
1867
|
+
// import path never sets host — so dashboard-managed routing is safe.
|
|
1836
1868
|
const host = local_dev_host(config.name || path.basename(site_dir), port);
|
|
1837
1869
|
if (!use_bootstrap) {
|
|
1838
1870
|
const import_form = new FormData();
|
|
@@ -2103,18 +2135,7 @@ async function create_site_zip(dir, excluded_paths = new Set()) {
|
|
|
2103
2135
|
}
|
|
2104
2136
|
const site_json = path.join(dir, SITE_CONFIG_FILE);
|
|
2105
2137
|
if (!is_excluded_path(SITE_CONFIG_FILE, excluded_paths)) {
|
|
2106
|
-
|
|
2107
|
-
// but older dirs pulled by previous CLI versions still do,
|
|
2108
|
-
// and the local CMS's /import endpoint persists whatever
|
|
2109
|
-
// host the zipped yaml declares, overwriting bootstrap's
|
|
2110
|
-
// *.localhost value and breaking preview routing.
|
|
2111
|
-
const sanitized = await read_site_yaml_without_host(site_json);
|
|
2112
|
-
if (sanitized !== null) {
|
|
2113
|
-
archive.append(sanitized, { name: SITE_CONFIG_FILE });
|
|
2114
|
-
}
|
|
2115
|
-
else {
|
|
2116
|
-
archive.file(site_json, { name: SITE_CONFIG_FILE });
|
|
2117
|
-
}
|
|
2138
|
+
archive.file(site_json, { name: SITE_CONFIG_FILE });
|
|
2118
2139
|
}
|
|
2119
2140
|
await archive.finalize();
|
|
2120
2141
|
}
|
|
@@ -2124,22 +2145,6 @@ async function create_site_zip(dir, excluded_paths = new Set()) {
|
|
|
2124
2145
|
})();
|
|
2125
2146
|
});
|
|
2126
2147
|
}
|
|
2127
|
-
async function read_site_yaml_without_host(site_yaml_path) {
|
|
2128
|
-
try {
|
|
2129
|
-
const raw = await fs.readFile(site_yaml_path, 'utf-8');
|
|
2130
|
-
const parsed = load_yaml(raw);
|
|
2131
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
2132
|
-
return null;
|
|
2133
|
-
const data = parsed;
|
|
2134
|
-
if (!('host' in data))
|
|
2135
|
-
return null;
|
|
2136
|
-
const { host: _, ...rest } = data;
|
|
2137
|
-
return dump_yaml(rest, { lineWidth: -1, noRefs: true });
|
|
2138
|
-
}
|
|
2139
|
-
catch {
|
|
2140
|
-
return null;
|
|
2141
|
-
}
|
|
2142
|
-
}
|
|
2143
2148
|
async function sync_from_cms(site_dir, api_url, config, server_config, workspace_dir, sync_policy = { mode: 'both' }) {
|
|
2144
2149
|
const response = await fetch_with_timeout(`${api_url}/api/palacms/export/${config.site_id}`, {}, 15000);
|
|
2145
2150
|
if (!response.ok)
|
|
@@ -2321,13 +2326,12 @@ async function sync_directory(src, dest, relative_path = '', options = {}) {
|
|
|
2321
2326
|
console.log(chalk.dim(` trash failed for ${file_relative}`));
|
|
2322
2327
|
}
|
|
2323
2328
|
}
|
|
2324
|
-
// Track this file BEFORE writing
|
|
2325
|
-
//
|
|
2326
|
-
synced_files
|
|
2329
|
+
// Track this file's content hash BEFORE writing so the
|
|
2330
|
+
// chokidar event our own write produces can be matched
|
|
2331
|
+
// against `synced_files` and dropped, while a genuine user
|
|
2332
|
+
// edit (different content) still falls through and pushes.
|
|
2333
|
+
synced_files.set(dest_path, hash_content(src_content));
|
|
2327
2334
|
await fs.writeFile(dest_path, src_content);
|
|
2328
|
-
// Update with actual mtime after write
|
|
2329
|
-
const stat = await fs.stat(dest_path);
|
|
2330
|
-
synced_files.set(dest_path, stat.mtimeMs);
|
|
2331
2335
|
// Surface shrinkage on the change line itself so a user
|
|
2332
2336
|
// scanning the dev log notices when a YAML list silently
|
|
2333
2337
|
// loses entries (the failure mode reported during the
|
|
@@ -2392,6 +2396,14 @@ async function has_library_content(library_dir) {
|
|
|
2392
2396
|
}
|
|
2393
2397
|
async function write_created_ids(site_dir, created_ids, server_config, workspace_dir) {
|
|
2394
2398
|
const format_options = resolve_format_options(server_config);
|
|
2399
|
+
// Upload writeback is special: rename local files and rewrite any yaml
|
|
2400
|
+
// that still references symbolic paths. Surfaced under a non-standard
|
|
2401
|
+
// `_uploads` key in created_ids["uploads/.manifest.json"] so the rest of
|
|
2402
|
+
// this loop's `_id`/`sections` logic doesn't get confused by it.
|
|
2403
|
+
const uploads_payload = created_ids['uploads/.manifest.json'];
|
|
2404
|
+
if (uploads_payload && typeof uploads_payload._uploads === 'object' && uploads_payload._uploads !== null) {
|
|
2405
|
+
await write_upload_writeback(site_dir, uploads_payload._uploads, server_config, workspace_dir);
|
|
2406
|
+
}
|
|
2395
2407
|
for (const [relative_path, id_data] of Object.entries(created_ids)) {
|
|
2396
2408
|
if (!id_data._id && !Array.isArray(id_data.sections))
|
|
2397
2409
|
continue;
|
|
@@ -2427,7 +2439,7 @@ async function write_created_ids(site_dir, created_ids, server_config, workspace
|
|
|
2427
2439
|
const raw = dump_yaml(data, { lineWidth: -1 });
|
|
2428
2440
|
const formatted = await format_file_contents(file_path, raw, workspace_dir, format_options);
|
|
2429
2441
|
await fs.writeFile(file_path, formatted, 'utf-8');
|
|
2430
|
-
|
|
2442
|
+
mark_written_file(file_path, formatted);
|
|
2431
2443
|
}
|
|
2432
2444
|
}
|
|
2433
2445
|
catch {
|
|
@@ -2435,3 +2447,148 @@ async function write_created_ids(site_dir, created_ids, server_config, workspace
|
|
|
2435
2447
|
}
|
|
2436
2448
|
}
|
|
2437
2449
|
}
|
|
2450
|
+
// write_upload_writeback reconciles the local uploads/ folder and yaml refs
|
|
2451
|
+
// with what the server actually stored on the latest push.
|
|
2452
|
+
//
|
|
2453
|
+
// For each entry `symbolic -> {id, canonical}`:
|
|
2454
|
+
// - If canonical != symbolic, rename uploads/<symbolic> to uploads/<canonical>
|
|
2455
|
+
// on disk so subsequent pulls/pushes round-trip without churn.
|
|
2456
|
+
// - Rewrite any yaml file that still says `upload: "uploads/<symbolic>"` to
|
|
2457
|
+
// the record id, matching what the server stored. This converges the local
|
|
2458
|
+
// copy with the server's canonical content shape without needing a pull.
|
|
2459
|
+
//
|
|
2460
|
+
// The watcher is told about each rename and rewrite via mark_written_file /
|
|
2461
|
+
// mark_deleted_path so it doesn't echo our own writes back as user edits.
|
|
2462
|
+
async function write_upload_writeback(site_dir, uploads_map, server_config, workspace_dir) {
|
|
2463
|
+
// Build a symbolic -> record-id map for the yaml rewrite pass. Skip
|
|
2464
|
+
// malformed entries instead of failing the whole writeback so a partial
|
|
2465
|
+
// server response can still rename what it can.
|
|
2466
|
+
const symbolic_to_id = new Map();
|
|
2467
|
+
const renames = [];
|
|
2468
|
+
for (const [symbolic, raw_entry] of Object.entries(uploads_map)) {
|
|
2469
|
+
if (!raw_entry || typeof raw_entry !== 'object')
|
|
2470
|
+
continue;
|
|
2471
|
+
const entry = raw_entry;
|
|
2472
|
+
const id = typeof entry.id === 'string' ? entry.id : '';
|
|
2473
|
+
const canonical = typeof entry.canonical === 'string' ? entry.canonical : '';
|
|
2474
|
+
if (!id)
|
|
2475
|
+
continue;
|
|
2476
|
+
symbolic_to_id.set(symbolic, id);
|
|
2477
|
+
if (canonical && canonical !== symbolic) {
|
|
2478
|
+
renames.push({ symbolic, canonical });
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
// Rename the on-disk files. Best-effort: if the symbolic file is missing
|
|
2482
|
+
// (already renamed by a prior push, or removed by the user) we silently
|
|
2483
|
+
// move on — the yaml rewrite still keeps content consistent.
|
|
2484
|
+
const uploads_dir = path.join(site_dir, 'uploads');
|
|
2485
|
+
for (const { symbolic, canonical } of renames) {
|
|
2486
|
+
const from = path.join(uploads_dir, symbolic);
|
|
2487
|
+
const to = path.join(uploads_dir, canonical);
|
|
2488
|
+
try {
|
|
2489
|
+
await fs.rename(from, to);
|
|
2490
|
+
mark_deleted_path(from);
|
|
2491
|
+
mark_written_file(to, await fs.readFile(to));
|
|
2492
|
+
}
|
|
2493
|
+
catch {
|
|
2494
|
+
// missing source or permission issue — skip
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
// Rewrite symbolic upload references across all yaml under the site dir.
|
|
2498
|
+
// Walking the tree is bounded by site size and only re-marshals files
|
|
2499
|
+
// that mention `uploads/` literally, so the cost is small even on large
|
|
2500
|
+
// sites. Restricting to known sync subdirs avoids touching artifacts in
|
|
2501
|
+
// dot-directories or build output.
|
|
2502
|
+
if (symbolic_to_id.size === 0)
|
|
2503
|
+
return;
|
|
2504
|
+
const format_options = resolve_format_options(server_config);
|
|
2505
|
+
for (const subdir of SITE_SYNC_DIRS) {
|
|
2506
|
+
const root = path.join(site_dir, subdir);
|
|
2507
|
+
const files = await walk_yaml_files(root).catch(() => []);
|
|
2508
|
+
for (const file_path of files) {
|
|
2509
|
+
try {
|
|
2510
|
+
const content = await fs.readFile(file_path, 'utf-8');
|
|
2511
|
+
if (!content.includes('uploads/'))
|
|
2512
|
+
continue;
|
|
2513
|
+
const parsed = load_yaml(content);
|
|
2514
|
+
if (!parsed || typeof parsed !== 'object')
|
|
2515
|
+
continue;
|
|
2516
|
+
const { value: rewritten, changed } = rewrite_symbolic_upload_refs(parsed, symbolic_to_id);
|
|
2517
|
+
if (!changed)
|
|
2518
|
+
continue;
|
|
2519
|
+
const raw = dump_yaml(rewritten, { lineWidth: -1 });
|
|
2520
|
+
const formatted = await format_file_contents(file_path, raw, workspace_dir, format_options);
|
|
2521
|
+
await fs.writeFile(file_path, formatted, 'utf-8');
|
|
2522
|
+
mark_written_file(file_path, formatted);
|
|
2523
|
+
}
|
|
2524
|
+
catch {
|
|
2525
|
+
// skip unreadable / unparseable file
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
async function walk_yaml_files(root) {
|
|
2531
|
+
const out = [];
|
|
2532
|
+
const stack = [root];
|
|
2533
|
+
while (stack.length > 0) {
|
|
2534
|
+
const dir = stack.pop();
|
|
2535
|
+
let entries;
|
|
2536
|
+
try {
|
|
2537
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
2538
|
+
}
|
|
2539
|
+
catch {
|
|
2540
|
+
continue;
|
|
2541
|
+
}
|
|
2542
|
+
for (const entry of entries) {
|
|
2543
|
+
if (entry.name.startsWith('.'))
|
|
2544
|
+
continue;
|
|
2545
|
+
const full = path.join(dir, entry.name);
|
|
2546
|
+
if (entry.isDirectory()) {
|
|
2547
|
+
stack.push(full);
|
|
2548
|
+
}
|
|
2549
|
+
else if (entry.isFile() && entry.name.endsWith('.yaml')) {
|
|
2550
|
+
out.push(full);
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
return out;
|
|
2555
|
+
}
|
|
2556
|
+
// rewrite_symbolic_upload_refs is the CLI mirror of the server's rewriteUploadRefs.
|
|
2557
|
+
// Walking on the CLI side handles the case where the server's in-zip rewrite
|
|
2558
|
+
// updated the imported content but the source files on disk still reference
|
|
2559
|
+
// the symbolic path. Without this pass, the next push would resend the
|
|
2560
|
+
// symbolic ref and force the server to re-resolve every time.
|
|
2561
|
+
function rewrite_symbolic_upload_refs(value, map) {
|
|
2562
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
2563
|
+
const obj = value;
|
|
2564
|
+
let changed = false;
|
|
2565
|
+
const result = {};
|
|
2566
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
2567
|
+
if (k === 'upload' && typeof v === 'string' && v.startsWith('uploads/')) {
|
|
2568
|
+
const filename = v.substring('uploads/'.length);
|
|
2569
|
+
const id = map.get(filename);
|
|
2570
|
+
if (id) {
|
|
2571
|
+
result[k] = id;
|
|
2572
|
+
changed = true;
|
|
2573
|
+
continue;
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
const child = rewrite_symbolic_upload_refs(v, map);
|
|
2577
|
+
result[k] = child.value;
|
|
2578
|
+
if (child.changed)
|
|
2579
|
+
changed = true;
|
|
2580
|
+
}
|
|
2581
|
+
return { value: result, changed };
|
|
2582
|
+
}
|
|
2583
|
+
if (Array.isArray(value)) {
|
|
2584
|
+
let changed = false;
|
|
2585
|
+
const result = value.map(item => {
|
|
2586
|
+
const child = rewrite_symbolic_upload_refs(item, map);
|
|
2587
|
+
if (child.changed)
|
|
2588
|
+
changed = true;
|
|
2589
|
+
return child.value;
|
|
2590
|
+
});
|
|
2591
|
+
return { value: result, changed };
|
|
2592
|
+
}
|
|
2593
|
+
return { value, changed: false };
|
|
2594
|
+
}
|
package/dist/commands/push.js
CHANGED
|
@@ -302,6 +302,14 @@ async function try_bootstrap_site(server, token, zip_buffer, config, site_id) {
|
|
|
302
302
|
form.append('name', config.name);
|
|
303
303
|
if (config?.group)
|
|
304
304
|
form.append('group', config.group);
|
|
305
|
+
// Register the site against the deploy URL's host so the first visit to
|
|
306
|
+
// that domain finds a matching site instead of dropping into CreateSite.
|
|
307
|
+
try {
|
|
308
|
+
form.append('host', new URL(server).host);
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
// Malformed server URL — let the server fall back to its own default.
|
|
312
|
+
}
|
|
305
313
|
form.append('file', new Blob([zip_buffer]), 'site.zip');
|
|
306
314
|
const headers = {};
|
|
307
315
|
if (token)
|