@funnelsgrove/cli 0.1.6 → 0.1.7

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/cli.js CHANGED
@@ -7,7 +7,7 @@ import { fileURLToPath } from 'node:url';
7
7
  import { Command } from 'commander';
8
8
  import { callTrpcProcedure } from './apiClient.js';
9
9
  import { clearActiveContext, getDefaultAuthConfigPath, loadActiveContext, loadAuthToken, saveActiveContext, saveAuthToken, } from './authStore.js';
10
- import { buildSyncManifest, chunkChangedSourceFiles, collectChangedSourceFiles, collectSourceFiles, ensureGitignore, readSyncManifest, writeLocalEnvFile, writeSourceFiles, writeSyncManifest, } from './localSync.js';
10
+ import { buildSyncManifest, chunkChangedSourceFiles, collectChangedSourceFiles, collectSourceFiles, ensureGitignore, formatSyncUploadSummary, readSyncManifest, writeLocalEnvFile, writeSourceFiles, writeSyncManifest, } from './localSync.js';
11
11
  import { pullEnvFile } from './envSync.js';
12
12
  import { formatGitHubConnectRows, formatGitHubJobRows, formatGitHubStatusRows, } from './githubOutput.js';
13
13
  import { syncGitHubDraftIfConnected } from './githubSyncFlow.js';
@@ -513,6 +513,7 @@ addExamples(syncCommand
513
513
  let syncedFileCount = 0;
514
514
  let deletedFileCount = 0;
515
515
  if (changes) {
516
+ console.log(formatSyncUploadSummary(changes).join('\n'));
516
517
  const batches = chunkChangedSourceFiles(changes);
517
518
  for (const batch of batches) {
518
519
  result = await callApi({
@@ -532,6 +533,8 @@ addExamples(syncCommand
532
533
  }
533
534
  }
534
535
  else {
536
+ const files = await collectSourceFiles(target.sourceDir);
537
+ console.log(formatSyncUploadSummary({ deletedPaths: [], files }).join('\n'));
535
538
  result = await callApi({
536
539
  path: 'funnels.importSource',
537
540
  type: 'mutation',
@@ -540,7 +543,7 @@ addExamples(syncCommand
540
543
  workspaceId: target.workspaceId,
541
544
  funnelId: target.funnelId,
542
545
  message: options.message,
543
- files: await collectSourceFiles(target.sourceDir),
546
+ files,
544
547
  },
545
548
  });
546
549
  syncedFileCount = result.syncedFiles.length;
@@ -36,4 +36,5 @@ export declare function readSyncManifest(rootDir: string): Promise<SyncManifest
36
36
  export declare function collectSourceFiles(rootDir: string): Promise<SourceFile[]>;
37
37
  export declare function collectChangedSourceFiles(rootDir: string, previousManifest: SyncManifest): Promise<ChangedSourceFiles>;
38
38
  export declare function chunkChangedSourceFiles(changes: Pick<ChangedSourceFiles, 'deletedPaths' | 'files'>, maxContentChars?: number): SourceFilePatchBatch[];
39
+ export declare function formatSyncUploadSummary(changes: Pick<ChangedSourceFiles, 'deletedPaths' | 'files'>, largestFileLimit?: number): string[];
39
40
  export declare function writeSourceFiles(rootDir: string, files: SourceFile[]): Promise<void>;
package/dist/localSync.js CHANGED
@@ -3,16 +3,15 @@ import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  export const SYNC_MANIFEST_FILE = '.funnelsgrove-sync.json';
5
5
  export const DEFAULT_PATCH_BATCH_CONTENT_CHARS = 12_000_000;
6
- const EXCLUDED_PATH_PARTS = new Set(['node_modules', '.next', 'out']);
6
+ const EXCLUDED_LOCAL_DIRECTORY_NAMES = ['.git', '.playwright-cli', 'node_modules', '.next', 'out', 'output'];
7
+ const EXCLUDED_PATH_PARTS = new Set(EXCLUDED_LOCAL_DIRECTORY_NAMES);
7
8
  const LOCAL_ONLY_GITIGNORE_LINES = [
8
9
  '.env',
9
10
  '.env.local',
10
11
  '.env.*',
11
12
  '!.env.example',
12
13
  SYNC_MANIFEST_FILE,
13
- 'node_modules',
14
- '.next',
15
- 'out',
14
+ ...EXCLUDED_LOCAL_DIRECTORY_NAMES.filter((directoryName) => directoryName !== '.git'),
16
15
  ];
17
16
  export function normalizeSyncPath(filePath) {
18
17
  const normalized = path.posix.normalize(filePath.replaceAll('\\', '/'));
@@ -157,6 +156,41 @@ export function chunkChangedSourceFiles(changes, maxContentChars = DEFAULT_PATCH
157
156
  pushCurrentBatch();
158
157
  return batches;
159
158
  }
159
+ export function formatSyncUploadSummary(changes, largestFileLimit = 5) {
160
+ const fileSizes = changes.files
161
+ .map((file) => ({
162
+ path: file.path,
163
+ bytes: Buffer.byteLength(file.content, 'utf8'),
164
+ }))
165
+ .sort((left, right) => right.bytes - left.bytes || left.path.localeCompare(right.path));
166
+ const totalBytes = fileSizes.reduce((total, file) => total + file.bytes, 0);
167
+ const lines = [
168
+ `Preparing sync upload: ${formatCount(fileSizes.length, 'file')}, ${formatCount(changes.deletedPaths.length, 'deleted path')}, ${formatByteSize(totalBytes)} content.`,
169
+ ];
170
+ if (fileSizes.length > 0 && largestFileLimit > 0) {
171
+ lines.push('Largest included files:');
172
+ for (const file of fileSizes.slice(0, largestFileLimit)) {
173
+ lines.push(` ${formatByteSize(file.bytes).padStart(6)} ${file.path}`);
174
+ }
175
+ }
176
+ return lines;
177
+ }
178
+ function formatCount(count, singular) {
179
+ return `${count} ${count === 1 ? singular : `${singular}s`}`;
180
+ }
181
+ function formatByteSize(byteCount) {
182
+ if (byteCount < 1024) {
183
+ return `${byteCount} B`;
184
+ }
185
+ const units = ['KB', 'MB', 'GB'];
186
+ let value = byteCount / 1024;
187
+ let unitIndex = 0;
188
+ while (value >= 1024 && unitIndex < units.length - 1) {
189
+ value /= 1024;
190
+ unitIndex += 1;
191
+ }
192
+ return `${value.toFixed(1)} ${units[unitIndex]}`;
193
+ }
160
194
  async function readSourceFiles(rootDir, manifestFiles) {
161
195
  const files = await Promise.all(manifestFiles.map(async (file) => {
162
196
  const absolutePath = path.join(rootDir, assertSafeSyncPath(file.path));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "FunnelsGrove command-line tools for editing, syncing, and publishing funnels",
5
5
  "type": "module",
6
6
  "bin": {