@xano/cli 1.0.2-beta.6 → 1.0.2-beta.8

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
@@ -505,6 +505,8 @@ xano sandbox push --review # Push and open sandbox
505
505
 
506
506
  # Review (open in browser)
507
507
  xano sandbox review
508
+ xano sandbox review --url-only # Print the URL without opening the browser
509
+ xano sandbox review --insecure # Skip TLS verification (self-signed certs)
508
510
 
509
511
  # Impersonate (open in browser)
510
512
  xano sandbox impersonate
@@ -3,6 +3,7 @@ export default class SandboxReview extends BaseCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
+ insecure: import("@oclif/core/interfaces").BooleanFlag<boolean>;
6
7
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
8
  'url-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
9
  config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -10,9 +10,16 @@ Review session started!
10
10
  `,
11
11
  `$ xano sandbox review -u`,
12
12
  `$ xano sandbox review -o json`,
13
+ `$ xano sandbox review --insecure`,
13
14
  ];
14
15
  static flags = {
15
16
  ...BaseCommand.baseFlags,
17
+ insecure: Flags.boolean({
18
+ char: 'k',
19
+ default: false,
20
+ description: 'Skip TLS certificate verification (for self-signed certificates)',
21
+ required: false,
22
+ }),
16
23
  output: Flags.string({
17
24
  char: 'o',
18
25
  default: 'summary',
@@ -29,6 +36,10 @@ Review session started!
29
36
  };
30
37
  async run() {
31
38
  const { flags } = await this.parse(SandboxReview);
39
+ if (flags.insecure) {
40
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
41
+ this.warn('TLS certificate verification is disabled (insecure mode)');
42
+ }
32
43
  const { profile } = this.resolveProfile(flags);
33
44
  const apiUrl = `${profile.instance_origin}/api:meta/sandbox/impersonate`;
34
45
  try {
@@ -54,6 +54,31 @@ export declare function countSummaryChanges(summary: Record<string, {
54
54
  truncated: number;
55
55
  updated: number;
56
56
  }>, shouldDelete: boolean): number;
57
+ /**
58
+ * Filter document entries down to the ones the dry-run preview reports as changed,
59
+ * used to send only the changed documents during a partial (non-`--sync`) push.
60
+ *
61
+ * The preview keys each operation as `${type}:${name}` (with the verb appended for
62
+ * API endpoints). Local documents are matched against that set.
63
+ *
64
+ * Triggers need special handling: they are authored with specific subtypes
65
+ * (workspace_trigger, error_trigger, table_trigger, agent_trigger,
66
+ * mcp_server_trigger, realtime_trigger) but the server buckets every one under the
67
+ * generic `trigger` type in the preview. We therefore match a local trigger against
68
+ * both its specific type and the generic `trigger` type — otherwise partial pushes
69
+ * silently drop triggers, requiring `--sync --force` to include them (DEV-7084).
70
+ */
71
+ export declare function filterChangedEntries(entries: Array<{
72
+ content: string;
73
+ filePath: string;
74
+ }>, operations: Array<{
75
+ action: string;
76
+ name: string;
77
+ type: string;
78
+ }>, includeRecords: boolean): Array<{
79
+ content: string;
80
+ filePath: string;
81
+ }>;
57
82
  /**
58
83
  * Recursively collect all .xs files from a directory, sorted for deterministic ordering.
59
84
  */
@@ -14,6 +14,44 @@ export const WORKSPACE_MISMATCH_THRESHOLD = 10;
14
14
  export function countSummaryChanges(summary, shouldDelete) {
15
15
  return Object.values(summary).reduce((sum, c) => sum + c.created + c.updated + (shouldDelete ? c.deleted : 0) + c.truncated, 0);
16
16
  }
17
+ /**
18
+ * Filter document entries down to the ones the dry-run preview reports as changed,
19
+ * used to send only the changed documents during a partial (non-`--sync`) push.
20
+ *
21
+ * The preview keys each operation as `${type}:${name}` (with the verb appended for
22
+ * API endpoints). Local documents are matched against that set.
23
+ *
24
+ * Triggers need special handling: they are authored with specific subtypes
25
+ * (workspace_trigger, error_trigger, table_trigger, agent_trigger,
26
+ * mcp_server_trigger, realtime_trigger) but the server buckets every one under the
27
+ * generic `trigger` type in the preview. We therefore match a local trigger against
28
+ * both its specific type and the generic `trigger` type — otherwise partial pushes
29
+ * silently drop triggers, requiring `--sync --force` to include them (DEV-7084).
30
+ */
31
+ export function filterChangedEntries(entries, operations, includeRecords) {
32
+ const changedKeys = new Set(operations
33
+ .filter((op) => op.action !== 'unchanged' && op.action !== 'delete' && op.action !== 'cascade_delete')
34
+ .map((op) => `${op.type}:${op.name}`));
35
+ return entries.filter((entry) => {
36
+ const parsed = parseDocument(entry.content);
37
+ if (!parsed)
38
+ return true;
39
+ // Workspace settings always use a fixed key in dry-run regardless of the actual name
40
+ if (parsed.type === 'workspace' && changedKeys.has('workspace:workspace'))
41
+ return true;
42
+ const opName = parsed.verb ? `${parsed.name} ${parsed.verb}` : parsed.name;
43
+ if (changedKeys.has(`${parsed.type}:${opName}`))
44
+ return true;
45
+ // The dry-run preview reports all trigger subtypes under the generic `trigger`
46
+ // type, so match triggers against that bucket too (DEV-7084).
47
+ if (parsed.type.endsWith('_trigger') && changedKeys.has(`trigger:${opName}`))
48
+ return true;
49
+ // Keep table documents that contain records when --records is active
50
+ if (includeRecords && parsed.type === 'table' && /\bitems\s*=\s*\[/m.test(entry.content))
51
+ return true;
52
+ return false;
53
+ });
54
+ }
17
55
  // ── File Collection ─────────────────────────────────────────────────────────
18
56
  /**
19
57
  * Recursively collect all .xs files from a directory, sorted for deterministic ordering.
@@ -520,24 +558,7 @@ export async function executePush(ctx, target, flags) {
520
558
  }
521
559
  // ── Partial push: filter to changed documents only ────────────────────
522
560
  if (isPartial && dryRunPreview) {
523
- const changedKeys = new Set(dryRunPreview.operations
524
- .filter((op) => op.action !== 'unchanged' && op.action !== 'delete' && op.action !== 'cascade_delete')
525
- .map((op) => `${op.type}:${op.name}`));
526
- const filteredEntries = documentEntries.filter((entry) => {
527
- const parsed = parseDocument(entry.content);
528
- if (!parsed)
529
- return true;
530
- // Workspace settings always use a fixed key in dry-run regardless of the actual name
531
- if (parsed.type === 'workspace' && changedKeys.has('workspace:workspace'))
532
- return true;
533
- const opName = parsed.verb ? `${parsed.name} ${parsed.verb}` : parsed.name;
534
- if (changedKeys.has(`${parsed.type}:${opName}`))
535
- return true;
536
- // Keep table documents that contain records when --records is active
537
- if (flags.records && parsed.type === 'table' && /\bitems\s*=\s*\[/m.test(entry.content))
538
- return true;
539
- return false;
540
- });
561
+ const filteredEntries = filterChangedEntries(documentEntries, dryRunPreview.operations, flags.records);
541
562
  if (filteredEntries.length === 0) {
542
563
  log('No changes to push.');
543
564
  return;