veryfront 0.1.976 → 0.1.977

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.
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import type { InferSchema } from "../../../src/extensions/schema/index.js";
10
10
  import { type ApiClient } from "../../shared/config.js";
11
+ import { type PullSource } from "../pull/index.js";
11
12
  /**
12
13
  * Schema factory for push command arguments
13
14
  */
@@ -72,8 +73,21 @@ export interface BranchResponse {
72
73
  name: string;
73
74
  projectId: string;
74
75
  }
76
+ interface BranchListItem {
77
+ id: string;
78
+ name: string;
79
+ }
80
+ interface RemoteFile {
81
+ path: string;
82
+ }
75
83
  export declare function generateBranchName(): string;
76
84
  export declare function createBranch(client: ApiClient, projectSlug: string, branchName: string): Promise<BranchResponse>;
85
+ export declare function ensureBranch(client: ApiClient, projectSlug: string, branchName: string): Promise<BranchListItem>;
86
+ export declare function resolvePushRemoteFiles(client: ApiClient, projectSlug: string, branchName: string, mainFiles: RemoteFile[]): Promise<{
87
+ branchId: string | null;
88
+ remoteFiles: RemoteFile[];
89
+ source: PullSource;
90
+ }>;
77
91
  export declare function uploadFiles(client: ApiClient, projectSlug: string, branchId: string | null, ops: UploadOp[], dryRun: boolean): Promise<{
78
92
  uploaded: number;
79
93
  failed: number;
@@ -83,4 +97,5 @@ export declare function deleteFiles(client: ApiClient, projectSlug: string, bran
83
97
  failed: number;
84
98
  }>;
85
99
  export declare function pushCommand(options?: PushOptions): Promise<void>;
100
+ export {};
86
101
  //# sourceMappingURL=command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/push/command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAK3E,OAAO,EACL,KAAK,SAAS,EAKf,MAAM,wBAAwB,CAAC;AAShC;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;GAS7B,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;;GAAgC,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC;AAEzE;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;;GAOxB,CAAC;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,oDAAoD;IACpD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAkCD,wBAAgB,kBAAkB,IAAI,MAAM,CAG3C;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,CAAC,CAEzB;AAQD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,GAAG,EAAE,QAAQ,EAAE,EACf,MAAM,EAAE,OAAO,GACd,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAqB/C;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,KAAK,EAAE,MAAM,EAAE,EACf,MAAM,EAAE,OAAO,GACd,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAqB9C;AA+BD,wBAAgB,WAAW,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyKpE"}
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/push/command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAK3E,OAAO,EACL,KAAK,SAAS,EAKf,MAAM,wBAAwB,CAAC;AAMhC,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGjE;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;GAS7B,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;;GAAgC,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC;AAEzE;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;;GAOxB,CAAC;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,oDAAoD;IACpD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AASD,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAkCD,wBAAgB,kBAAkB,IAAI,MAAM,CAG3C;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,CAAC,CAEzB;AAoCD,wBAAsB,YAAY,CAChC,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,CAAC,CAWzB;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,UAAU,EAAE,GACtB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,UAAU,EAAE,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC,CAUrF;AAQD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,GAAG,EAAE,QAAQ,EAAE,EACf,MAAM,EAAE,OAAO,GACd,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAqB/C;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,KAAK,EAAE,MAAM,EAAE,EACf,MAAM,EAAE,OAAO,GACd,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAqB9C;AA+BD,wBAAgB,WAAW,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8KpE"}
@@ -13,7 +13,7 @@ import { cwd } from "../../../src/platform/index.js";
13
13
  import { createFileSystem } from "../../../src/platform/index.js";
14
14
  import { createApiClient, resolveConfigWithAuth, writeProjectSlug, } from "../../shared/config.js";
15
15
  import { reserveProjectSlug } from "../../shared/reserve-slug.js";
16
- import { confirmPrompt, logError, logInfo, logSuccess, logWarning } from "../../utils/index.js";
16
+ import { confirmPrompt, logInfo, logSuccess, logWarning } from "../../utils/index.js";
17
17
  import { createNoopSpinner, createSpinner } from "../../ui/index.js";
18
18
  import { withSpan } from "../../../src/observability/tracing/otlp-setup.js";
19
19
  import { createIgnoreChecker, loadIgnorePatterns } from "../../sync/ignore.js";
@@ -72,6 +72,52 @@ export function generateBranchName() {
72
72
  export function createBranch(client, projectSlug, branchName) {
73
73
  return client.post(`/projects/${projectSlug}/branches`, { name: branchName });
74
74
  }
75
+ function getErrorStatus(error) {
76
+ if (typeof error !== "object" || error === null)
77
+ return undefined;
78
+ const status = error.status;
79
+ return typeof status === "number" ? status : undefined;
80
+ }
81
+ async function getBranchByName(client, projectSlug, branchName) {
82
+ let cursor;
83
+ do {
84
+ const params = {
85
+ search: branchName,
86
+ limit: "100",
87
+ ...(cursor ? { cursor } : {}),
88
+ };
89
+ const response = await client.get(`/projects/${projectSlug}/branches`, params);
90
+ const branch = response.data.find((candidate) => candidate.name === branchName);
91
+ if (branch)
92
+ return branch;
93
+ cursor = response.page_info?.next;
94
+ } while (cursor);
95
+ return null;
96
+ }
97
+ export async function ensureBranch(client, projectSlug, branchName) {
98
+ try {
99
+ return await createBranch(client, projectSlug, branchName);
100
+ }
101
+ catch (error) {
102
+ if (getErrorStatus(error) !== 409)
103
+ throw error;
104
+ const existingBranch = await getBranchByName(client, projectSlug, branchName);
105
+ if (existingBranch)
106
+ return existingBranch;
107
+ throw error;
108
+ }
109
+ }
110
+ export async function resolvePushRemoteFiles(client, projectSlug, branchName, mainFiles) {
111
+ const mainSource = { type: "main" };
112
+ if (branchName === "main")
113
+ return { branchId: null, remoteFiles: mainFiles, source: mainSource };
114
+ const existingBranch = await getBranchByName(client, projectSlug, branchName);
115
+ if (!existingBranch)
116
+ return { branchId: null, remoteFiles: mainFiles, source: mainSource };
117
+ const branchSource = { type: "branch", name: branchName };
118
+ const remoteFiles = await listAllFiles(client, projectSlug, branchSource);
119
+ return { branchId: existingBranch.id, remoteFiles, source: branchSource };
120
+ }
75
121
  function buildFileUrl(projectSlug, path, branchId) {
76
122
  const encodedPath = encodeURIComponent(path);
77
123
  const base = `/projects/${projectSlug}/files/${encodedPath}`;
@@ -157,15 +203,16 @@ export function pushCommand(options = {}) {
157
203
  const localPaths = new Set(ops.map((op) => op.path));
158
204
  spinner.update("Fetching remote files...");
159
205
  const client = createApiClient(config);
206
+ const branchName = branch || generateBranchName();
207
+ const isMainBranch = branchName === "main";
160
208
  // First-push: If project doesn't exist on server yet, create it
161
- let remoteFiles = [];
209
+ let mainFiles = [];
162
210
  try {
163
- remoteFiles = await listAllFiles(client, config.projectSlug, { type: "main" });
211
+ mainFiles = await listAllFiles(client, config.projectSlug, { type: "main" });
164
212
  }
165
213
  catch (error) {
166
- const message = error instanceof Error ? error.message : String(error);
167
214
  // Project doesn't exist yet - create it on first push
168
- if (message.includes("404") || message.includes("not found")) {
215
+ if (getErrorStatus(error) === 404) {
169
216
  spinner.update("Creating project...");
170
217
  const reserveResult = await reserveProjectSlug(config.projectSlug, config.apiToken);
171
218
  if (reserveResult.slug !== config.projectSlug) {
@@ -175,18 +222,19 @@ export function pushCommand(options = {}) {
175
222
  config = { ...config, projectSlug: reserveResult.slug };
176
223
  // Now try to get files again (should be empty for new project)
177
224
  try {
178
- remoteFiles = await listAllFiles(client, config.projectSlug, { type: "main" });
225
+ mainFiles = await listAllFiles(client, config.projectSlug, { type: "main" });
179
226
  }
180
227
  catch {
181
228
  // Project just created, no files yet
182
- remoteFiles = [];
229
+ mainFiles = [];
183
230
  }
184
231
  }
185
232
  else {
186
233
  throw error;
187
234
  }
188
235
  }
189
- const toDelete = remoteFiles.map((f) => f.path).filter((p) => !localPaths.has(p));
236
+ const target = await resolvePushRemoteFiles(client, config.projectSlug, branchName, mainFiles);
237
+ const toDelete = target.remoteFiles.map((f) => f.path).filter((p) => !localPaths.has(p));
190
238
  if (ops.length === 0 && toDelete.length === 0) {
191
239
  spinner.stop();
192
240
  if (!quiet)
@@ -194,8 +242,6 @@ export function pushCommand(options = {}) {
194
242
  return;
195
243
  }
196
244
  spinner.stop();
197
- const branchName = branch || generateBranchName();
198
- const isMainBranch = branchName === "main";
199
245
  if (!quiet) {
200
246
  const parts = buildSummaryParts(ops, toDelete);
201
247
  cliLogger.info(`\nFound ${formatParts(parts)} for ${isMainBranch ? "main" : `branch "${branchName}"`}.`);
@@ -204,7 +250,9 @@ export function pushCommand(options = {}) {
204
250
  const parts = buildConfirmParts(ops, toDelete);
205
251
  const confirmMessage = isMainBranch
206
252
  ? `Push to main (${parts.join(", ")} files)?`
207
- : `Create branch "${branchName}" and ${parts.join(", ")} files?`;
253
+ : target.branchId
254
+ ? `Push to branch "${branchName}" and ${parts.join(", ")} files?`
255
+ : `Create branch "${branchName}" and ${parts.join(", ")} files?`;
208
256
  const confirmed = await confirmPrompt(confirmMessage, true);
209
257
  if (!confirmed) {
210
258
  cliLogger.info("Push cancelled.");
@@ -212,10 +260,11 @@ export function pushCommand(options = {}) {
212
260
  }
213
261
  }
214
262
  if (dryRun) {
215
- if (ops.length > 0)
216
- await uploadFiles(client, config.projectSlug, null, ops, true);
263
+ if (ops.length > 0) {
264
+ await uploadFiles(client, config.projectSlug, target.branchId, ops, true);
265
+ }
217
266
  if (toDelete.length > 0) {
218
- await deleteFiles(client, config.projectSlug, null, toDelete, true);
267
+ await deleteFiles(client, config.projectSlug, target.branchId, toDelete, true);
219
268
  }
220
269
  if (!quiet) {
221
270
  const parts = buildConfirmParts(ops, toDelete);
@@ -223,24 +272,22 @@ export function pushCommand(options = {}) {
223
272
  }
224
273
  return;
225
274
  }
226
- let branchId = null;
227
- const uploadMsg = isMainBranch ? "Pushing to main..." : `Creating branch "${branchName}"...`;
275
+ let branchId = target.branchId;
276
+ const uploadMsg = isMainBranch
277
+ ? "Pushing to main..."
278
+ : branchId
279
+ ? `Pushing to branch "${branchName}"...`
280
+ : `Creating branch "${branchName}"...`;
228
281
  spinner = quiet ? createNoopSpinner() : createSpinner(uploadMsg);
229
- if (!isMainBranch) {
282
+ if (!isMainBranch && !branchId) {
230
283
  try {
231
- const createdBranch = await createBranch(client, config.projectSlug, branchName);
232
- branchId = createdBranch.id;
284
+ const preparedBranch = await ensureBranch(client, config.projectSlug, branchName);
285
+ branchId = preparedBranch.id;
233
286
  }
234
287
  catch (error) {
235
288
  spinner.stop();
236
289
  const message = error instanceof Error ? error.message : String(error);
237
- if (message.includes("already exists")) {
238
- logError(`Branch "${branchName}" already exists. Use --branch to specify a different name.`);
239
- }
240
- else {
241
- logError(`Failed to create branch: ${message}`);
242
- }
243
- return;
290
+ throw new Error(`Failed to prepare branch "${branchName}": ${message}`);
244
291
  }
245
292
  }
246
293
  let uploadResult = { uploaded: 0, failed: 0 };
package/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.976",
3
+ "version": "0.1.977",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "minimumDependencyAge": {
@@ -1,3 +1,3 @@
1
1
  /** Shared version value. */
2
- export declare const VERSION = "0.1.976";
2
+ export declare const VERSION = "0.1.977";
3
3
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,4 +1,4 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
3
  /** Shared version value. */
4
- export const VERSION = "0.1.976";
4
+ export const VERSION = "0.1.977";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.976",
3
+ "version": "0.1.977",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",