@xano/cli 1.2.0 → 1.3.1-beta.0
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 +381 -4
- package/dist/base-command.d.ts +54 -36
- package/dist/base-command.js +246 -169
- package/dist/commands/auth/index.d.ts +1 -1
- package/dist/commands/auth/index.js +7 -7
- package/dist/commands/branch/create/index.d.ts +1 -1
- package/dist/commands/branch/create/index.js +1 -1
- package/dist/commands/branch/set_live/index.d.ts +22 -0
- package/dist/commands/branch/set_live/index.js +60 -1
- package/dist/commands/ephemeral/create/index.d.ts +18 -0
- package/dist/commands/ephemeral/create/index.js +103 -0
- package/dist/commands/ephemeral/delete/index.d.ts +18 -0
- package/dist/commands/ephemeral/delete/index.js +99 -0
- package/dist/commands/ephemeral/edit/index.d.ts +18 -0
- package/dist/commands/ephemeral/edit/index.js +109 -0
- package/dist/commands/ephemeral/get/index.d.ts +21 -0
- package/dist/commands/ephemeral/get/index.js +125 -0
- package/dist/commands/ephemeral/impersonate/index.d.ts +20 -0
- package/dist/commands/ephemeral/impersonate/index.js +120 -0
- package/dist/commands/ephemeral/list/index.d.ts +19 -0
- package/dist/commands/ephemeral/list/index.js +123 -0
- package/dist/commands/ephemeral/pull/index.d.ts +19 -0
- package/dist/commands/ephemeral/pull/index.js +163 -0
- package/dist/commands/ephemeral/push/index.d.ts +35 -0
- package/dist/commands/ephemeral/push/index.js +228 -0
- package/dist/commands/ephemeral/static_host/build/delete/index.d.ts +20 -0
- package/dist/commands/ephemeral/static_host/build/delete/index.js +121 -0
- package/dist/commands/ephemeral/static_host/build/get/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/build/get/index.js +137 -0
- package/dist/commands/ephemeral/static_host/build/list/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/build/list/index.js +137 -0
- package/dist/commands/ephemeral/static_host/build/pull/index.d.ts +53 -0
- package/dist/commands/ephemeral/static_host/build/pull/index.js +308 -0
- package/dist/commands/ephemeral/static_host/build/push/index.d.ts +24 -0
- package/dist/commands/ephemeral/static_host/build/push/index.js +250 -0
- package/dist/commands/ephemeral/static_host/create/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/create/index.js +92 -0
- package/dist/commands/ephemeral/static_host/deploy/index.d.ts +19 -0
- package/dist/commands/ephemeral/static_host/deploy/index.js +112 -0
- package/dist/commands/ephemeral/static_host/edit/index.d.ts +24 -0
- package/dist/commands/ephemeral/static_host/edit/index.js +158 -0
- package/dist/commands/ephemeral/static_host/get/index.d.ts +19 -0
- package/dist/commands/ephemeral/static_host/get/index.js +101 -0
- package/dist/commands/ephemeral/static_host/list/index.d.ts +17 -0
- package/dist/commands/ephemeral/static_host/list/index.js +132 -0
- package/dist/commands/flatten/index.d.ts +34 -0
- package/dist/commands/flatten/index.js +140 -0
- package/dist/commands/function/create/index.d.ts +15 -0
- package/dist/commands/function/create/index.js +41 -1
- package/dist/commands/function/edit/index.d.ts +15 -0
- package/dist/commands/function/edit/index.js +66 -32
- package/dist/commands/function/get/index.js +2 -3
- package/dist/commands/function/run/index.d.ts +1 -0
- package/dist/commands/function/run/index.js +19 -6
- package/dist/commands/policy/catalogue/index.d.ts +15 -0
- package/dist/commands/policy/catalogue/index.js +29 -0
- package/dist/commands/policy/create/index.d.ts +18 -0
- package/dist/commands/policy/create/index.js +39 -0
- package/dist/commands/policy/delete/index.d.ts +19 -0
- package/dist/commands/policy/delete/index.js +61 -0
- package/dist/commands/policy/evaluate/index.d.ts +32 -0
- package/dist/commands/policy/evaluate/index.js +71 -0
- package/dist/commands/policy/list/index.d.ts +14 -0
- package/dist/commands/policy/list/index.js +21 -0
- package/dist/commands/policy/parse/index.d.ts +19 -0
- package/dist/commands/policy/parse/index.js +18 -0
- package/dist/commands/policy/publish/index.d.ts +20 -0
- package/dist/commands/policy/publish/index.js +38 -0
- package/dist/commands/policy/runs/index.d.ts +54 -0
- package/dist/commands/policy/runs/index.js +273 -0
- package/dist/commands/policy/status/index.d.ts +21 -0
- package/dist/commands/policy/status/index.js +54 -0
- package/dist/commands/profile/create/index.d.ts +1 -1
- package/dist/commands/profile/create/index.js +7 -7
- package/dist/commands/profile/me/index.js +1 -1
- package/dist/commands/profile/use/index.js +0 -4
- package/dist/commands/profile/wizard/index.d.ts +1 -1
- package/dist/commands/profile/wizard/index.js +12 -10
- package/dist/commands/profile/workspace/set/index.js +1 -1
- package/dist/commands/release/create/index.js +7 -0
- package/dist/commands/release/deploy/index.d.ts +28 -0
- package/dist/commands/release/deploy/index.js +90 -6
- package/dist/commands/release/edit/index.js +2 -2
- package/dist/commands/release/export/index.js +1 -2
- package/dist/commands/release/import/index.js +6 -2
- package/dist/commands/release/list/index.d.ts +7 -0
- package/dist/commands/release/list/index.js +54 -10
- package/dist/commands/release/pull/index.d.ts +0 -6
- package/dist/commands/release/pull/index.js +13 -99
- package/dist/commands/release/push/index.js +21 -3
- package/dist/commands/sandbox/env/get_all/index.js +1 -1
- package/dist/commands/sandbox/env/set_all/index.js +1 -1
- package/dist/commands/sandbox/get/index.js +1 -1
- package/dist/commands/sandbox/license/get/index.js +1 -1
- package/dist/commands/sandbox/license/set/index.js +1 -1
- package/dist/commands/sandbox/pull/index.d.ts +0 -1
- package/dist/commands/sandbox/pull/index.js +13 -81
- package/dist/commands/sandbox/push/index.d.ts +1 -1
- package/dist/commands/sandbox/push/index.js +11 -6
- package/dist/commands/skills/pull/index.d.ts +30 -0
- package/dist/commands/skills/pull/index.js +118 -0
- package/dist/commands/static_host/build/create/index.d.ts +1 -1
- package/dist/commands/static_host/build/create/index.js +2 -2
- package/dist/commands/static_host/build/pull/index.js +1 -1
- package/dist/commands/static_host/build/push/index.js +1 -1
- package/dist/commands/static_host/edit/index.js +1 -1
- package/dist/commands/tenant/backup/export/index.js +1 -2
- package/dist/commands/tenant/backup/import/index.js +1 -1
- package/dist/commands/tenant/cluster/create/index.js +1 -1
- package/dist/commands/tenant/cluster/license/get/index.js +1 -1
- package/dist/commands/tenant/cluster/license/set/index.js +1 -1
- package/dist/commands/tenant/create/index.d.ts +4 -1
- package/dist/commands/tenant/create/index.js +26 -5
- package/dist/commands/tenant/deploy_platform/index.js +1 -1
- package/dist/commands/tenant/deploy_release/index.d.ts +40 -0
- package/dist/commands/tenant/deploy_release/index.js +186 -4
- package/dist/commands/tenant/edit/index.d.ts +2 -0
- package/dist/commands/tenant/edit/index.js +46 -11
- package/dist/commands/tenant/env/get_all/index.js +1 -1
- package/dist/commands/tenant/env/set_all/index.js +1 -1
- package/dist/commands/tenant/impersonate/index.d.ts +1 -1
- package/dist/commands/tenant/impersonate/index.js +14 -14
- package/dist/commands/tenant/license/get/index.js +1 -1
- package/dist/commands/tenant/license/set/index.js +1 -1
- package/dist/commands/tenant/pull/index.d.ts +0 -6
- package/dist/commands/tenant/pull/index.js +14 -100
- package/dist/commands/tenant_deploy_request/bypass/index.d.ts +31 -0
- package/dist/commands/tenant_deploy_request/bypass/index.js +109 -0
- package/dist/commands/tenant_deploy_request/create/index.d.ts +21 -0
- package/dist/commands/tenant_deploy_request/create/index.js +108 -0
- package/dist/commands/tenant_deploy_request/edit/index.d.ts +22 -0
- package/dist/commands/tenant_deploy_request/edit/index.js +103 -0
- package/dist/commands/tenant_deploy_request/get/index.d.ts +25 -0
- package/dist/commands/tenant_deploy_request/get/index.js +116 -0
- package/dist/commands/tenant_deploy_request/list/index.d.ts +20 -0
- package/dist/commands/tenant_deploy_request/list/index.js +122 -0
- package/dist/commands/tenant_deploy_request/revision/index.d.ts +21 -0
- package/dist/commands/tenant_deploy_request/revision/index.js +83 -0
- package/dist/commands/tenant_deploy_request/set_status/index.d.ts +38 -0
- package/dist/commands/tenant_deploy_request/set_status/index.js +159 -0
- package/dist/commands/workspace/git/pull/index.d.ts +4 -4
- package/dist/commands/workspace/git/pull/index.js +158 -97
- package/dist/commands/workspace/pull/index.d.ts +9 -5
- package/dist/commands/workspace/pull/index.js +83 -106
- package/dist/commands/workspace/push/index.d.ts +19 -0
- package/dist/commands/workspace/push/index.js +124 -5
- package/dist/help.js +4 -2
- package/dist/policy-command.d.ts +90 -0
- package/dist/policy-command.js +94 -0
- package/dist/update-check.d.ts +1 -1
- package/dist/update-check.js +1 -1
- package/dist/utils/document-parser.d.ts +126 -0
- package/dist/utils/document-parser.js +332 -4
- package/dist/utils/flatten.d.ts +36 -0
- package/dist/utils/flatten.js +68 -0
- package/dist/utils/knowledge-sync.d.ts +1 -0
- package/dist/utils/knowledge-sync.js +11 -6
- package/dist/utils/microservice-wait.d.ts +53 -0
- package/dist/utils/microservice-wait.js +84 -0
- package/dist/utils/multidoc-push.d.ts +143 -2
- package/dist/utils/multidoc-push.js +479 -40
- package/dist/utils/paging.d.ts +2 -1
- package/dist/utils/paging.js +0 -1
- package/dist/utils/policy/catalogue.d.ts +7 -0
- package/dist/utils/policy/catalogue.js +72 -0
- package/dist/utils/policy/errors.d.ts +12 -0
- package/dist/utils/policy/errors.js +61 -0
- package/dist/utils/policy/feedback.d.ts +68 -0
- package/dist/utils/policy/feedback.js +165 -0
- package/dist/utils/policy/findings.d.ts +23 -0
- package/dist/utils/policy/findings.js +65 -0
- package/dist/utils/policy/gate.d.ts +161 -0
- package/dist/utils/policy/gate.js +201 -0
- package/dist/utils/policy/goal.d.ts +46 -0
- package/dist/utils/policy/goal.js +95 -0
- package/dist/utils/policy/permission.d.ts +23 -0
- package/dist/utils/policy/permission.js +84 -0
- package/dist/utils/policy/push-gate.d.ts +5 -0
- package/dist/utils/policy/push-gate.js +29 -0
- package/dist/utils/policy/release.d.ts +85 -0
- package/dist/utils/policy/release.js +115 -0
- package/dist/utils/policy/request.d.ts +55 -0
- package/dist/utils/policy/request.js +115 -0
- package/dist/utils/policy/runs.d.ts +30 -0
- package/dist/utils/policy/runs.js +101 -0
- package/dist/utils/policy/status.d.ts +47 -0
- package/dist/utils/policy/status.js +117 -0
- package/dist/utils/policy/types.d.ts +185 -0
- package/dist/utils/policy/types.js +1 -0
- package/dist/utils/reference-checker.js +14 -3
- package/oclif.manifest.json +8538 -3445
- package/package.json +16 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class EphemeralStaticHostGet extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
'static-host': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
/** Print a one-line summary for a dev/prod env if it has been deployed. */
|
|
18
|
+
private logEnv;
|
|
19
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../../base-command.js';
|
|
3
|
+
export default class EphemeralStaticHostGet extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
tenant_name: Args.string({
|
|
6
|
+
description: 'Ephemeral tenant name',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
9
|
+
};
|
|
10
|
+
static description = 'Get a single static host\'s details (name, git config, dev/prod environments) for an ephemeral tenant';
|
|
11
|
+
static examples = [
|
|
12
|
+
`$ xano ephemeral static_host get e4f2-9ab1-xyz1 --static-host newsite
|
|
13
|
+
Static Host: newsite
|
|
14
|
+
ID: 5
|
|
15
|
+
Dev: https://newsite-dev-....dev.xano.io (v2)
|
|
16
|
+
`,
|
|
17
|
+
`$ xano ephemeral static_host get e4f2-9ab1-xyz1 -H newsite -w 40 -o json`,
|
|
18
|
+
];
|
|
19
|
+
static flags = {
|
|
20
|
+
...BaseCommand.baseFlags,
|
|
21
|
+
output: Flags.string({
|
|
22
|
+
char: 'o',
|
|
23
|
+
default: 'summary',
|
|
24
|
+
description: 'Output format',
|
|
25
|
+
options: ['summary', 'json'],
|
|
26
|
+
required: false,
|
|
27
|
+
}),
|
|
28
|
+
'static-host': Flags.string({
|
|
29
|
+
char: 'H',
|
|
30
|
+
description: 'Static host name',
|
|
31
|
+
required: true,
|
|
32
|
+
}),
|
|
33
|
+
workspace: Flags.string({
|
|
34
|
+
char: 'w',
|
|
35
|
+
description: 'Workspace ID (optional if set in profile)',
|
|
36
|
+
required: false,
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
async run() {
|
|
40
|
+
const { args, flags } = await this.parse(EphemeralStaticHostGet);
|
|
41
|
+
const { profile, profileName } = this.resolveProfile(flags);
|
|
42
|
+
const tenantName = args.tenant_name;
|
|
43
|
+
let workspaceId;
|
|
44
|
+
if (flags.workspace) {
|
|
45
|
+
workspaceId = flags.workspace;
|
|
46
|
+
}
|
|
47
|
+
else if (profile.workspace) {
|
|
48
|
+
workspaceId = profile.workspace;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.error(`Workspace ID is required. Either:\n` +
|
|
52
|
+
` 1. Provide it as a flag: xano ephemeral static_host get <tenant_name> --static-host <static_host> -w <workspace_id>\n` +
|
|
53
|
+
` 2. Set it in your profile using: xano profile edit ${profileName} -w <workspace_id>`);
|
|
54
|
+
}
|
|
55
|
+
const staticHost = flags['static-host'];
|
|
56
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}/static_host/${staticHost}`;
|
|
57
|
+
try {
|
|
58
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
59
|
+
headers: {
|
|
60
|
+
accept: 'application/json',
|
|
61
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
62
|
+
},
|
|
63
|
+
method: 'GET',
|
|
64
|
+
}, flags.verbose, profile.access_token);
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
const message = await this.parseApiError(response, `Failed to get static host '${staticHost}'`);
|
|
67
|
+
this.error(message);
|
|
68
|
+
}
|
|
69
|
+
const host = (await response.json());
|
|
70
|
+
if (flags.output === 'json') {
|
|
71
|
+
this.log(JSON.stringify(host, null, 2));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.log(`Static Host: ${host.name}`);
|
|
75
|
+
this.log(`ID: ${host.id}`);
|
|
76
|
+
if (host.description)
|
|
77
|
+
this.log(`Description: ${host.description}`);
|
|
78
|
+
if (host.git?.repo)
|
|
79
|
+
this.log(`Git: ${host.git.repo}`);
|
|
80
|
+
this.logEnv('Dev', host.dev);
|
|
81
|
+
this.logEnv('Prod', host.prod);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
if (error instanceof Error) {
|
|
86
|
+
this.error(`Failed to get static host: ${error.message}`);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
this.error(`Failed to get static host: ${String(error)}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Print a one-line summary for a dev/prod env if it has been deployed. */
|
|
94
|
+
logEnv(label, env) {
|
|
95
|
+
if (!env?.default_url)
|
|
96
|
+
return;
|
|
97
|
+
const modeInfo = env.mode ? ` (${env.mode})` : '';
|
|
98
|
+
const custom = env.custom_url ? ` [custom: ${env.custom_url}]` : '';
|
|
99
|
+
this.log(`${label}: ${env.default_url}${modeInfo}${custom}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class EphemeralStaticHostList extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../../base-command.js';
|
|
3
|
+
import { buildPagingJson, buildPagingParams, formatPagingFooter, normalizeListResponse, pagingFlags } from '../../../../utils/paging.js';
|
|
4
|
+
export default class EphemeralStaticHostList extends BaseCommand {
|
|
5
|
+
static args = {
|
|
6
|
+
tenant_name: Args.string({
|
|
7
|
+
description: 'Ephemeral tenant name',
|
|
8
|
+
required: true,
|
|
9
|
+
}),
|
|
10
|
+
};
|
|
11
|
+
static description = 'List all static hosts for an ephemeral tenant from the Xano Metadata API';
|
|
12
|
+
static examples = [
|
|
13
|
+
`$ xano ephemeral static_host list e4f2-9ab1-xyz1 -w 40
|
|
14
|
+
Available static hosts:
|
|
15
|
+
- my-static-host (ID: 1)
|
|
16
|
+
- another-host (ID: 2)
|
|
17
|
+
`,
|
|
18
|
+
`$ xano ephemeral static_host list e4f2-9ab1-xyz1 --profile production
|
|
19
|
+
Available static hosts:
|
|
20
|
+
- my-static-host (ID: 1)
|
|
21
|
+
- another-host (ID: 2)
|
|
22
|
+
`,
|
|
23
|
+
`$ xano ephemeral static_host list e4f2-9ab1-xyz1 -w 40 --output json
|
|
24
|
+
{
|
|
25
|
+
"count": 1,
|
|
26
|
+
"page": 1,
|
|
27
|
+
"total": 3,
|
|
28
|
+
"items": [
|
|
29
|
+
{
|
|
30
|
+
"id": 1,
|
|
31
|
+
"name": "my-static-host"
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
`,
|
|
36
|
+
`$ xano ephemeral static_host list e4f2-9ab1-xyz1 -p staging -o json --page 2
|
|
37
|
+
[
|
|
38
|
+
{
|
|
39
|
+
"id": 3,
|
|
40
|
+
"name": "static-host-3"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
`,
|
|
44
|
+
];
|
|
45
|
+
static flags = {
|
|
46
|
+
...BaseCommand.baseFlags,
|
|
47
|
+
output: Flags.string({
|
|
48
|
+
char: 'o',
|
|
49
|
+
default: 'summary',
|
|
50
|
+
description: 'Output format',
|
|
51
|
+
options: ['summary', 'json'],
|
|
52
|
+
required: false,
|
|
53
|
+
}),
|
|
54
|
+
...pagingFlags('page-only-envelope', { fixedPerPage: 100 }),
|
|
55
|
+
workspace: Flags.string({
|
|
56
|
+
char: 'w',
|
|
57
|
+
description: 'Workspace ID (optional if set in profile)',
|
|
58
|
+
required: false,
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
61
|
+
async run() {
|
|
62
|
+
const { args, flags } = await this.parse(EphemeralStaticHostList);
|
|
63
|
+
const { profile, profileName } = this.resolveProfile(flags);
|
|
64
|
+
const tenantName = args.tenant_name;
|
|
65
|
+
// Determine workspace_id from flag or profile
|
|
66
|
+
let workspaceId;
|
|
67
|
+
if (flags.workspace) {
|
|
68
|
+
workspaceId = flags.workspace;
|
|
69
|
+
}
|
|
70
|
+
else if (profile.workspace) {
|
|
71
|
+
workspaceId = profile.workspace;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.error(`Workspace ID is required. Either:\n` +
|
|
75
|
+
` 1. Provide it as a flag: xano ephemeral static_host list <tenant_name> -w <workspace_id>\n` +
|
|
76
|
+
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
77
|
+
}
|
|
78
|
+
// Build query parameters
|
|
79
|
+
const queryParams = new URLSearchParams(buildPagingParams(flags, 'page-only-envelope'));
|
|
80
|
+
// Construct the API URL
|
|
81
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}/static_host?${queryParams.toString()}`;
|
|
82
|
+
// Fetch static hosts from the API
|
|
83
|
+
try {
|
|
84
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
85
|
+
headers: {
|
|
86
|
+
'accept': 'application/json',
|
|
87
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
88
|
+
},
|
|
89
|
+
method: 'GET',
|
|
90
|
+
}, flags.verbose, profile.access_token);
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
const errorText = await response.text();
|
|
93
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
94
|
+
}
|
|
95
|
+
const list = normalizeListResponse(await response.json(), ['static_hosts']);
|
|
96
|
+
const staticHosts = list.items;
|
|
97
|
+
// Output results
|
|
98
|
+
if (flags.output === 'json') {
|
|
99
|
+
this.log(JSON.stringify(buildPagingJson(list, { page: flags.page, tier: 'page-only-envelope' }), null, 2));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// summary format
|
|
103
|
+
if (staticHosts.length === 0) {
|
|
104
|
+
this.log('No static hosts found');
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
this.log('Available static hosts:');
|
|
108
|
+
for (const host of staticHosts) {
|
|
109
|
+
if (host.id === undefined) {
|
|
110
|
+
this.log(` - ${host.name}`);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
const domainInfo = host.domain ? ` - ${host.domain}` : '';
|
|
114
|
+
this.log(` - ${host.name} (ID: ${host.id})${domainInfo}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const footer = formatPagingFooter(list, { noun: 'static host', page: flags.page, tier: 'page-only-envelope' });
|
|
119
|
+
if (footer)
|
|
120
|
+
this.log(footer);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (error instanceof Error) {
|
|
125
|
+
this.error(`Failed to fetch static hosts: ${error.message}`);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
this.error(`Failed to fetch static hosts: ${String(error)}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* Split a multi-document `.xs` bundle into the standard one-document-per-file
|
|
4
|
+
* layout that the push pipeline requires.
|
|
5
|
+
*
|
|
6
|
+
* A hand-authored bundle keeps every object (workspace, tables, queries,
|
|
7
|
+
* microservices, …) in a single file joined by `---`. That is convenient to
|
|
8
|
+
* edit but unsupported by `push`: the partial-diff filter and GUID writeback
|
|
9
|
+
* both parse only the first document in a file, so such a bundle silently
|
|
10
|
+
* pushes nothing (partial mode) or corrupts GUIDs (full mode). `flatten` turns
|
|
11
|
+
* it back into the exact tree `pull` would have produced, using the shared
|
|
12
|
+
* placement logic so the two never drift.
|
|
13
|
+
*
|
|
14
|
+
* Not a BaseCommand: this is a purely local, offline transform — no profile,
|
|
15
|
+
* token, or network — so it extends oclif's Command directly.
|
|
16
|
+
*/
|
|
17
|
+
export default class Flatten extends Command {
|
|
18
|
+
static args: {
|
|
19
|
+
input: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
20
|
+
};
|
|
21
|
+
static description: string;
|
|
22
|
+
static examples: string[];
|
|
23
|
+
static flags: {
|
|
24
|
+
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
25
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
26
|
+
'keep-source': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
27
|
+
output: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
28
|
+
};
|
|
29
|
+
run(): Promise<void>;
|
|
30
|
+
/** Recursively collect `.xs` files under a dir that actually hold >1 document. */
|
|
31
|
+
private collectMultiDocFiles;
|
|
32
|
+
/** Flatten a single bundle file. Returns the number of documents written. */
|
|
33
|
+
private flattenOne;
|
|
34
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Args, Command, Flags, ux } from '@oclif/core';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import { join, relative, resolve } from 'node:path';
|
|
4
|
+
import { flattenBundleFile } from '../../utils/flatten.js';
|
|
5
|
+
/**
|
|
6
|
+
* Split a multi-document `.xs` bundle into the standard one-document-per-file
|
|
7
|
+
* layout that the push pipeline requires.
|
|
8
|
+
*
|
|
9
|
+
* A hand-authored bundle keeps every object (workspace, tables, queries,
|
|
10
|
+
* microservices, …) in a single file joined by `---`. That is convenient to
|
|
11
|
+
* edit but unsupported by `push`: the partial-diff filter and GUID writeback
|
|
12
|
+
* both parse only the first document in a file, so such a bundle silently
|
|
13
|
+
* pushes nothing (partial mode) or corrupts GUIDs (full mode). `flatten` turns
|
|
14
|
+
* it back into the exact tree `pull` would have produced, using the shared
|
|
15
|
+
* placement logic so the two never drift.
|
|
16
|
+
*
|
|
17
|
+
* Not a BaseCommand: this is a purely local, offline transform — no profile,
|
|
18
|
+
* token, or network — so it extends oclif's Command directly.
|
|
19
|
+
*/
|
|
20
|
+
export default class Flatten extends Command {
|
|
21
|
+
static args = {
|
|
22
|
+
input: Args.string({
|
|
23
|
+
description: 'A multi-document .xs file, or a directory containing them, to split',
|
|
24
|
+
required: true,
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
static description = 'Split a multi-document .xs bundle (documents joined by `---`) into the standard ' +
|
|
28
|
+
'one-document-per-file layout that push requires. Deletes the original bundle by ' +
|
|
29
|
+
'default (use --keep-source to keep it). Purely local — no network.';
|
|
30
|
+
static examples = [
|
|
31
|
+
`$ xano flatten secret/pdf-micro/multidoc.xs
|
|
32
|
+
Split the bundle in place into per-document files (removes multidoc.xs)`,
|
|
33
|
+
`$ xano flatten ./bundle.xs -o ./workspace
|
|
34
|
+
Write the split files under ./workspace instead of alongside the bundle`,
|
|
35
|
+
`$ xano flatten secret/pdf-micro/multidoc.xs --dry-run
|
|
36
|
+
Preview the file layout without writing anything`,
|
|
37
|
+
`$ xano flatten ./dir-of-bundles --keep-source
|
|
38
|
+
Flatten every multi-doc .xs under a directory, keeping the originals`,
|
|
39
|
+
];
|
|
40
|
+
static flags = {
|
|
41
|
+
'dry-run': Flags.boolean({
|
|
42
|
+
default: false,
|
|
43
|
+
description: 'Show the resulting file layout without writing anything',
|
|
44
|
+
}),
|
|
45
|
+
force: Flags.boolean({
|
|
46
|
+
char: 'f',
|
|
47
|
+
default: false,
|
|
48
|
+
description: 'Overwrite existing destination files instead of erroring',
|
|
49
|
+
}),
|
|
50
|
+
'keep-source': Flags.boolean({
|
|
51
|
+
default: false,
|
|
52
|
+
description: 'Keep the original multi-document file(s) (default: delete after a successful split)',
|
|
53
|
+
}),
|
|
54
|
+
output: Flags.string({
|
|
55
|
+
char: 'o',
|
|
56
|
+
description: 'Output directory (default: the directory each bundle file lives in)',
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
async run() {
|
|
60
|
+
const { args, flags } = await this.parse(Flatten);
|
|
61
|
+
const inputPath = resolve(args.input);
|
|
62
|
+
if (!fs.existsSync(inputPath)) {
|
|
63
|
+
this.error(`Not found: ${inputPath}`);
|
|
64
|
+
}
|
|
65
|
+
// Resolve the set of bundle files to flatten.
|
|
66
|
+
const bundleFiles = fs.statSync(inputPath).isDirectory()
|
|
67
|
+
? this.collectMultiDocFiles(inputPath)
|
|
68
|
+
: [inputPath];
|
|
69
|
+
if (bundleFiles.length === 0) {
|
|
70
|
+
this.error(fs.statSync(inputPath).isDirectory()
|
|
71
|
+
? `No multi-document .xs files found under ${inputPath}`
|
|
72
|
+
: `${inputPath} is not a .xs file`);
|
|
73
|
+
}
|
|
74
|
+
let totalWritten = 0;
|
|
75
|
+
for (const bundleFile of bundleFiles) {
|
|
76
|
+
totalWritten += this.flattenOne(bundleFile, flags);
|
|
77
|
+
}
|
|
78
|
+
if (flags['dry-run']) {
|
|
79
|
+
this.log('');
|
|
80
|
+
this.log(ux.colorize('dim', `Dry run — ${totalWritten} file(s) would be written. Nothing changed.`));
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
this.log('');
|
|
84
|
+
this.log(`Flattened ${bundleFiles.length} bundle(s) into ${totalWritten} file(s).`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Recursively collect `.xs` files under a dir that actually hold >1 document. */
|
|
88
|
+
collectMultiDocFiles(dir) {
|
|
89
|
+
const out = [];
|
|
90
|
+
const walk = (d) => {
|
|
91
|
+
for (const entry of fs.readdirSync(d, { withFileTypes: true })) {
|
|
92
|
+
const full = join(d, entry.name);
|
|
93
|
+
if (entry.isDirectory()) {
|
|
94
|
+
walk(full);
|
|
95
|
+
}
|
|
96
|
+
else if (entry.isFile() && entry.name.endsWith('.xs')) {
|
|
97
|
+
const content = fs.readFileSync(full, 'utf8');
|
|
98
|
+
if (/^---$/m.test(content))
|
|
99
|
+
out.push(full);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
walk(dir);
|
|
104
|
+
return out.sort();
|
|
105
|
+
}
|
|
106
|
+
/** Flatten a single bundle file. Returns the number of documents written. */
|
|
107
|
+
flattenOne(bundleFile, flags) {
|
|
108
|
+
const rel = relative(process.cwd(), bundleFile) || bundleFile;
|
|
109
|
+
let result;
|
|
110
|
+
try {
|
|
111
|
+
// Buffer the plan so we can print a header before it only when there's work.
|
|
112
|
+
const planLines = [];
|
|
113
|
+
result = flattenBundleFile(bundleFile, {
|
|
114
|
+
dryRun: flags['dry-run'],
|
|
115
|
+
force: flags.force,
|
|
116
|
+
keepSource: flags['keep-source'],
|
|
117
|
+
log: (msg) => planLines.push(msg),
|
|
118
|
+
outputDir: flags.output,
|
|
119
|
+
});
|
|
120
|
+
if (result.skipped === 'empty') {
|
|
121
|
+
this.warn(`No parseable documents in ${rel}; skipping.`);
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
if (result.skipped === 'single') {
|
|
125
|
+
this.warn(`${rel} holds a single document; nothing to split. Skipping.`);
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
this.log('');
|
|
129
|
+
this.log(ux.colorize('bold', `${rel} → ${result.written.length} documents`));
|
|
130
|
+
for (const l of planLines) {
|
|
131
|
+
// Colorize the WRITE marker; dim the incidental notes.
|
|
132
|
+
this.log(l.startsWith(' WRITE') ? l.replace('WRITE', ux.colorize('green', 'WRITE')) : ux.colorize('dim', l));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
this.error(error.message);
|
|
137
|
+
}
|
|
138
|
+
return result.written.length;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -7,13 +7,28 @@ export default class FunctionCreate extends BaseCommand {
|
|
|
7
7
|
edit: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
8
|
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
'policy-override': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
stdin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
12
|
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
14
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
15
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
16
|
};
|
|
17
|
+
/** Set once the publish policy gate refuses, the one failure that exits 2. */
|
|
18
|
+
private refusedByPolicyGate;
|
|
19
|
+
/** Exit 2 is the publish gate's refusal; every other failure, flag errors included, exits 1. */
|
|
20
|
+
protected catch(error: Error & {
|
|
21
|
+
oclif?: {
|
|
22
|
+
exit?: number;
|
|
23
|
+
};
|
|
24
|
+
}): Promise<void>;
|
|
16
25
|
run(): Promise<void>;
|
|
17
26
|
private editFile;
|
|
18
27
|
private readStdin;
|
|
28
|
+
/**
|
|
29
|
+
* The live-branch publish gate refused the new function: the verdict on stdout (under `-o json`,
|
|
30
|
+
* `{created: false, message, policy_gate}`), then the platform's message and how to proceed,
|
|
31
|
+
* exiting 2.
|
|
32
|
+
*/
|
|
33
|
+
private refusePublish;
|
|
19
34
|
}
|
|
@@ -2,8 +2,9 @@ import { Flags } from '@oclif/core';
|
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import * as fs from 'node:fs';
|
|
4
4
|
import * as os from 'node:os';
|
|
5
|
-
import
|
|
5
|
+
import path from 'node:path';
|
|
6
6
|
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
import { BLANK_POLICY_OVERRIDE, blankPolicyOverride, gateRefusal, publishRefusal, quoted } from '../../../utils/policy/gate.js';
|
|
7
8
|
export default class FunctionCreate extends BaseCommand {
|
|
8
9
|
static args = {};
|
|
9
10
|
static description = 'Create a new function in a workspace';
|
|
@@ -36,6 +37,7 @@ Name: my_function
|
|
|
36
37
|
...
|
|
37
38
|
}
|
|
38
39
|
`,
|
|
40
|
+
`$ xano function:create -f function.xs --policy-override "Exception approved; finding tracked in JIRA-12"`,
|
|
39
41
|
];
|
|
40
42
|
static flags = {
|
|
41
43
|
...BaseCommand.baseFlags,
|
|
@@ -59,6 +61,7 @@ Name: my_function
|
|
|
59
61
|
options: ['summary', 'json'],
|
|
60
62
|
required: false,
|
|
61
63
|
}),
|
|
64
|
+
'policy-override': Flags.string({ description: 'Create the function past a blocking live-branch publish policy gate with an audited reason (requires workspace:policy update)' }),
|
|
62
65
|
stdin: Flags.boolean({
|
|
63
66
|
char: 's',
|
|
64
67
|
default: false,
|
|
@@ -72,8 +75,18 @@ Name: my_function
|
|
|
72
75
|
required: false,
|
|
73
76
|
}),
|
|
74
77
|
};
|
|
78
|
+
/** Set once the publish policy gate refuses, the one failure that exits 2. */
|
|
79
|
+
refusedByPolicyGate = false;
|
|
80
|
+
/** Exit 2 is the publish gate's refusal; every other failure, flag errors included, exits 1. */
|
|
81
|
+
async catch(error) {
|
|
82
|
+
if (this.refusedByPolicyGate)
|
|
83
|
+
return super.catch(error);
|
|
84
|
+
return this.catchAsOperational(error);
|
|
85
|
+
}
|
|
75
86
|
async run() {
|
|
76
87
|
const { flags } = await this.parse(FunctionCreate);
|
|
88
|
+
if (blankPolicyOverride(flags['policy-override']))
|
|
89
|
+
this.error(BLANK_POLICY_OVERRIDE);
|
|
77
90
|
const { profile, profileName } = this.resolveProfile(flags);
|
|
78
91
|
// Determine workspace_id from flag or profile
|
|
79
92
|
let workspaceId;
|
|
@@ -133,6 +146,10 @@ Name: my_function
|
|
|
133
146
|
const queryParams = new URLSearchParams({
|
|
134
147
|
include_xanoscript: 'false',
|
|
135
148
|
});
|
|
149
|
+
// A reason from someone with workspace:policy update saves past a blocking publish gate.
|
|
150
|
+
const overrideReason = flags['policy-override']?.trim();
|
|
151
|
+
if (overrideReason)
|
|
152
|
+
queryParams.set('override_reason', overrideReason);
|
|
136
153
|
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function?${queryParams.toString()}`;
|
|
137
154
|
// Create function via API
|
|
138
155
|
try {
|
|
@@ -146,6 +163,9 @@ Name: my_function
|
|
|
146
163
|
method: 'POST',
|
|
147
164
|
}, flags.verbose, profile.access_token);
|
|
148
165
|
if (!response.ok) {
|
|
166
|
+
const refused = await gateRefusal(response);
|
|
167
|
+
if (refused)
|
|
168
|
+
this.refusePublish(refused, flags);
|
|
149
169
|
const errorText = await response.text();
|
|
150
170
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
151
171
|
}
|
|
@@ -166,6 +186,8 @@ Name: my_function
|
|
|
166
186
|
}
|
|
167
187
|
}
|
|
168
188
|
catch (error) {
|
|
189
|
+
if (this.refusedByPolicyGate)
|
|
190
|
+
throw error;
|
|
169
191
|
if (error instanceof Error) {
|
|
170
192
|
this.error(`Failed to create function: ${error.message}`);
|
|
171
193
|
}
|
|
@@ -239,4 +261,22 @@ Name: my_function
|
|
|
239
261
|
process.stdin.resume();
|
|
240
262
|
});
|
|
241
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* The live-branch publish gate refused the new function: the verdict on stdout (under `-o json`,
|
|
266
|
+
* `{created: false, message, policy_gate}`), then the platform's message and how to proceed,
|
|
267
|
+
* exiting 2.
|
|
268
|
+
*/
|
|
269
|
+
refusePublish(refused, flags) {
|
|
270
|
+
const workspace = flags.workspace ? ` -w ${quoted(flags.workspace)}` : '';
|
|
271
|
+
const source = flags.file ? ` -f ${quoted(flags.file)}` : ' --stdin';
|
|
272
|
+
const { lines, message } = publishRefusal(refused, {
|
|
273
|
+
json: flags.output === 'json',
|
|
274
|
+
outcome: 'created',
|
|
275
|
+
rerun: `xano function create${workspace}${source} --policy-override "<why>"`,
|
|
276
|
+
});
|
|
277
|
+
for (const line of lines)
|
|
278
|
+
this.log(line);
|
|
279
|
+
this.refusedByPolicyGate = true;
|
|
280
|
+
this.error(message, { exit: 2 });
|
|
281
|
+
}
|
|
242
282
|
}
|
|
@@ -9,6 +9,7 @@ export default class FunctionEdit extends BaseCommand {
|
|
|
9
9
|
edit: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
10
|
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
11
|
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
'policy-override': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
publish: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
14
|
stdin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
15
|
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -16,10 +17,24 @@ export default class FunctionEdit extends BaseCommand {
|
|
|
16
17
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
18
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
19
|
};
|
|
20
|
+
/** Set once the publish policy gate refuses, the one failure that exits 2. */
|
|
21
|
+
private refusedByPolicyGate;
|
|
22
|
+
/** Exit 2 is the publish gate's refusal; every other failure, flag errors included, exits 1. */
|
|
23
|
+
protected catch(error: Error & {
|
|
24
|
+
oclif?: {
|
|
25
|
+
exit?: number;
|
|
26
|
+
};
|
|
27
|
+
}): Promise<void>;
|
|
19
28
|
run(): Promise<void>;
|
|
20
29
|
private editFile;
|
|
21
30
|
private editFunctionContent;
|
|
22
31
|
private fetchFunctionCode;
|
|
23
32
|
private promptForFunctionId;
|
|
24
33
|
private readStdin;
|
|
34
|
+
/**
|
|
35
|
+
* The live-branch publish gate refused the update: the verdict on stdout (under `-o json`,
|
|
36
|
+
* `{updated: false, message, policy_gate}`), then the platform's message and how to proceed,
|
|
37
|
+
* exiting 2.
|
|
38
|
+
*/
|
|
39
|
+
private refusePublish;
|
|
25
40
|
}
|