@xano/cli 0.0.79 → 0.0.80-beta.1

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/README.md CHANGED
@@ -123,6 +123,7 @@ xano workspace push ./my-workspace --delete # Delete objects not in
123
123
  xano workspace push ./my-workspace --records # Include table records
124
124
  xano workspace push ./my-workspace --env # Include environment variables
125
125
  xano workspace push ./my-workspace --truncate # Truncate tables before import
126
+ xano workspace push ./my-workspace --no-transaction # Disable database transaction wrapping
126
127
  xano workspace push ./my-workspace --no-sync-guids # Skip writing GUIDs back to local files
127
128
  xano workspace push ./my-workspace --force # Skip preview and confirmation (for CI/CD)
128
129
 
@@ -331,9 +332,10 @@ xano tenant pull ./my-tenant -t <tenant_name> --draft
331
332
 
332
333
  # Push local files to tenant
333
334
  xano tenant push ./my-tenant -t <tenant_name>
334
- xano tenant push ./my-tenant -t <tenant_name> --records # Include table records
335
- xano tenant push ./my-tenant -t <tenant_name> --env # Include environment variables
335
+ xano tenant push ./my-tenant -t <tenant_name> --records # Include table records
336
+ xano tenant push ./my-tenant -t <tenant_name> --env # Include environment variables
336
337
  xano tenant push ./my-tenant -t <tenant_name> --truncate
338
+ xano tenant push ./my-tenant -t <tenant_name> --no-transaction # Disable transaction wrapping
337
339
  ```
338
340
 
339
341
  #### Deployments
@@ -9,6 +9,7 @@ export default class Push extends BaseCommand {
9
9
  env: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
10
  records: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
11
  tenant: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ transaction: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
13
  truncate: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
14
  workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
15
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -50,6 +50,12 @@ Truncate all table records before importing
50
50
  description: 'Tenant name to push to',
51
51
  required: true,
52
52
  }),
53
+ transaction: Flags.boolean({
54
+ allowNo: true,
55
+ default: true,
56
+ description: 'Wrap import in a database transaction (use --no-transaction for debugging purposes)',
57
+ required: false,
58
+ }),
53
59
  truncate: Flags.boolean({
54
60
  default: false,
55
61
  description: 'Truncate all table records before importing',
@@ -157,6 +163,7 @@ Truncate all table records before importing
157
163
  const queryParams = new URLSearchParams({
158
164
  env: flags.env.toString(),
159
165
  records: flags.records.toString(),
166
+ transaction: flags.transaction.toString(),
160
167
  truncate: flags.truncate.toString(),
161
168
  });
162
169
  const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}/multidoc?${queryParams.toString()}`;
@@ -1,6 +1,7 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import { execSync } from 'node:child_process';
3
3
  import BaseCommand from '../../base-command.js';
4
+ import { clearUpdateCache } from '../../update-check.js';
4
5
  export default class Update extends BaseCommand {
5
6
  static description = 'Update the Xano CLI to the latest version';
6
7
  static examples = [`$ xano update`, `$ xano update --check`, `$ xano update --beta`];
@@ -31,6 +32,7 @@ export default class Update extends BaseCommand {
31
32
  }
32
33
  this.log(`Updating @xano/cli ${currentVersion} → ${latest}...`);
33
34
  execSync(`npm install -g @xano/cli@${tag} --no-fund`, { stdio: 'inherit' });
35
+ clearUpdateCache();
34
36
  this.log(`Updated to ${latest}`);
35
37
  }
36
38
  catch (error) {
@@ -13,6 +13,7 @@ export default class Push extends BaseCommand {
13
13
  partial: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
14
  records: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
15
  'sync-guids': import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ transaction: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
17
  truncate: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
18
  workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
19
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -86,6 +86,12 @@ Truncate all table records before importing
86
86
  description: 'Write server-assigned GUIDs back to local files (use --no-sync-guids to skip)',
87
87
  required: false,
88
88
  }),
89
+ transaction: Flags.boolean({
90
+ allowNo: true,
91
+ default: true,
92
+ description: 'Wrap import in a database transaction (use --no-transaction for debugging purposes)',
93
+ required: false,
94
+ }),
89
95
  truncate: Flags.boolean({
90
96
  default: false,
91
97
  description: 'Truncate all table records before importing',
@@ -179,6 +185,7 @@ Truncate all table records before importing
179
185
  env: flags.env.toString(),
180
186
  partial: flags.partial.toString(),
181
187
  records: flags.records.toString(),
188
+ transaction: flags.transaction.toString(),
182
189
  truncate: flags.truncate.toString(),
183
190
  });
184
191
  // POST the multidoc to the API
@@ -245,7 +252,27 @@ Truncate all table records before importing
245
252
  this.renderPreview(preview, shouldDelete, workspaceId, flags.verbose);
246
253
  // Check if there are any actual changes (exclude deletes when --delete is off)
247
254
  const hasChanges = Object.values(preview.summary).some((c) => c.created > 0 || c.updated > 0 || (shouldDelete && c.deleted > 0) || c.truncated > 0);
248
- if (!hasChanges) {
255
+ // Detect if local files contain records that would be imported
256
+ const tablesWithRecords = flags.records
257
+ ? documentEntries
258
+ .filter((d) => /^table\s+/m.test(d.content) && /\bitems\s*=\s*\[/m.test(d.content))
259
+ .map((d) => {
260
+ const nameMatch = d.content.match(/^table\s+(\S+)/m);
261
+ const itemCount = (d.content.match(/^\s*\{$/gm) || []).length;
262
+ return { name: nameMatch ? nameMatch[1] : 'unknown', records: itemCount };
263
+ })
264
+ : [];
265
+ const hasLocalRecords = tablesWithRecords.length > 0;
266
+ if (hasLocalRecords) {
267
+ this.log('');
268
+ this.log(ux.colorize('bold', '--- Records ---'));
269
+ this.log('');
270
+ for (const t of tablesWithRecords) {
271
+ this.log(` ${ux.colorize('yellow', 'UPSERT'.padEnd(16))} ${'table'.padEnd(18)} ${t.name} (${t.records} records)`);
272
+ }
273
+ this.log('');
274
+ }
275
+ if (!hasChanges && !hasLocalRecords) {
249
276
  this.log('');
250
277
  this.log('No changes to push.');
251
278
  return;
@@ -1,3 +1,4 @@
1
+ export declare function clearUpdateCache(): void;
1
2
  /**
2
3
  * Check if an update is available. Returns an update message string if there
3
4
  * is an update, or null if the CLI is up to date / on a beta / check not due.
@@ -7,6 +7,18 @@ const CHECK_INTERVAL_MS = 8 * 60 * 60 * 1000; // 8 hours
7
7
  function isBeta(version) {
8
8
  return version.includes('beta') || version.includes('alpha') || version.includes('rc');
9
9
  }
10
+ /** Returns true if `latest` is a higher semver than `current`. */
11
+ function isNewer(latest, current) {
12
+ const a = latest.split('.').map(Number);
13
+ const b = current.split('.').map(Number);
14
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
15
+ if ((a[i] ?? 0) > (b[i] ?? 0))
16
+ return true;
17
+ if ((a[i] ?? 0) < (b[i] ?? 0))
18
+ return false;
19
+ }
20
+ return false;
21
+ }
10
22
  function readCache() {
11
23
  try {
12
24
  if (!fs.existsSync(UPDATE_CHECK_FILE))
@@ -21,6 +33,16 @@ function readCache() {
21
33
  return null;
22
34
  }
23
35
  }
36
+ export function clearUpdateCache() {
37
+ try {
38
+ if (fs.existsSync(UPDATE_CHECK_FILE)) {
39
+ fs.unlinkSync(UPDATE_CHECK_FILE);
40
+ }
41
+ }
42
+ catch {
43
+ // Silently fail
44
+ }
45
+ }
24
46
  function writeCache(latestVersion) {
25
47
  try {
26
48
  const dir = path.dirname(UPDATE_CHECK_FILE);
@@ -62,7 +84,7 @@ export function checkForUpdate(currentVersion, forceCheck = false) {
62
84
  writeCache(latestVersion);
63
85
  }
64
86
  }
65
- if (!latestVersion || latestVersion === currentVersion)
87
+ if (!latestVersion || !isNewer(latestVersion, currentVersion))
66
88
  return null;
67
89
  const yellow = '\u001B[33m';
68
90
  const cyan = '\u001B[36m';