@xanots/sdk 0.0.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/LICENSE +21 -0
- package/README.md +384 -0
- package/dist/agent-file-refresh-7XCACU34.js +95 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +184 -0
- package/dist/capture-HUV5BNTC.js +32 -0
- package/dist/chunk-26CNBZE2.js +963 -0
- package/dist/chunk-33ND6LKO.js +78 -0
- package/dist/chunk-3DUAGYGP.js +1550 -0
- package/dist/chunk-3IGNIP6R.js +89 -0
- package/dist/chunk-47WDWMBJ.js +14 -0
- package/dist/chunk-4BXJGVZ3.js +318 -0
- package/dist/chunk-4HT3BNZ7.js +675 -0
- package/dist/chunk-4YMD2OOZ.js +23 -0
- package/dist/chunk-5WGEURVI.js +156 -0
- package/dist/chunk-5YTBTF25.js +29 -0
- package/dist/chunk-6DHBYBTO.js +166 -0
- package/dist/chunk-76QBEIGO.js +66 -0
- package/dist/chunk-7DKX2SPN.js +100 -0
- package/dist/chunk-7REDODS2.js +468 -0
- package/dist/chunk-C56BC2FY.js +254 -0
- package/dist/chunk-DGSF2Q5H.js +60 -0
- package/dist/chunk-EMCAWMHC.js +3982 -0
- package/dist/chunk-EZG76F7R.js +159 -0
- package/dist/chunk-FJ6ZS5KB.js +3607 -0
- package/dist/chunk-GNPVYOPB.js +17 -0
- package/dist/chunk-HJPTWBLH.js +38 -0
- package/dist/chunk-IZW6MK3U.js +248 -0
- package/dist/chunk-LH6Z6KFV.js +247 -0
- package/dist/chunk-MZUI3JNX.js +93 -0
- package/dist/chunk-NDZFBZHC.js +167 -0
- package/dist/chunk-NOO35BQ2.js +37 -0
- package/dist/chunk-NUQCEOKA.js +13 -0
- package/dist/chunk-QTNO2WD6.js +56 -0
- package/dist/chunk-RVB7ITKN.js +49 -0
- package/dist/chunk-TRU5OHQ5.js +6904 -0
- package/dist/chunk-VKFQJEMW.js +814 -0
- package/dist/chunk-VTIL47DT.js +125 -0
- package/dist/chunk-WHOJWOSV.js +112 -0
- package/dist/chunk-WKP4D5MO.js +1692 -0
- package/dist/chunk-X4DVXBFY.js +38 -0
- package/dist/chunk-XQ22GLYS.js +118 -0
- package/dist/chunk-YDZYMZBE.js +670 -0
- package/dist/chunk-YGPIY3WT.js +1146 -0
- package/dist/chunk-YYRDLFSJ.js +602 -0
- package/dist/chunk-ZUTSMMAG.js +46 -0
- package/dist/cli.d.ts +331 -0
- package/dist/cli.js +33 -0
- package/dist/codegen-command-OOKK6AZK.js +40 -0
- package/dist/codegen.d.ts +114 -0
- package/dist/codegen.js +71 -0
- package/dist/completion-TIPPZN7H.js +298 -0
- package/dist/config-NL33PN4D.js +84 -0
- package/dist/deploy-command-4EQ7XX5G.js +451 -0
- package/dist/env-target-IFJ5QDHC.js +16 -0
- package/dist/ephemeral-command-3X3I4ULC.js +242 -0
- package/dist/index.d.ts +4477 -0
- package/dist/index.js +300 -0
- package/dist/init-command-R6H7ZXW5.js +27 -0
- package/dist/internal.d.ts +74 -0
- package/dist/internal.js +2065 -0
- package/dist/io-AMIKRLPC.js +11 -0
- package/dist/live-diff-Y3AKETHE.js +196 -0
- package/dist/lock-3CVKALKT.js +37 -0
- package/dist/lock-commands-XTR2YKLY.js +232 -0
- package/dist/login-command-ACJF6KWQ.js +167 -0
- package/dist/logout-command-MX3MJS5U.js +68 -0
- package/dist/loop-OM3P2Z6O.js +93 -0
- package/dist/marketplace-command-RATXTKXN.js +379 -0
- package/dist/meta-client-OW5WKWW7.js +170 -0
- package/dist/node.d.ts +59 -0
- package/dist/node.js +442 -0
- package/dist/profile-command-SWJ3SPKR.js +77 -0
- package/dist/release-command-VV5UVZKL.js +427 -0
- package/dist/response-CVAE2kMj.d.ts +1915 -0
- package/dist/routes-manifest-SP3ZXLMR.js +244 -0
- package/dist/runtime-V4C3AC3A.js +13 -0
- package/dist/sandbox-details-command-HJE5SPVG.js +18 -0
- package/dist/sandbox-export-command-QCJY4GMV.js +24 -0
- package/dist/static-host-D6KS7X45.js +138 -0
- package/dist/store-BQ97nKIF.d.ts +7760 -0
- package/dist/test-command-TSPBQHBM.js +383 -0
- package/dist/validate-command-4ERHJSCO.js +122 -0
- package/dist/verify-rollout-FAVQ6EJY.js +40 -0
- package/dist/workspace-K72NP7SX.js +49 -0
- package/dist/workspace-command-OF23DPNI.js +156 -0
- package/dist/workspace-export-AJMGN3CQ.js +10 -0
- package/llms/fields.md +104 -0
- package/llms/filters.md +183 -0
- package/llms/kinds-agent-mcp.md +11 -0
- package/llms/kinds-core.md +68 -0
- package/llms/kinds-realtime.md +76 -0
- package/llms/lambda.md +111 -0
- package/llms/legacy.md +43 -0
- package/llms/object-kinds.md +31 -0
- package/llms/statements-calls.md +23 -0
- package/llms/statements-catalog.md +309 -0
- package/llms/statements-data.md +58 -0
- package/llms/statements-runtime.md +48 -0
- package/llms/tests.md +58 -0
- package/llms/triggers.md +22 -0
- package/llms/values.md +29 -0
- package/llms-full.txt +1672 -0
- package/llms.txt +483 -0
- package/manifest.json +9592 -0
- package/package.json +110 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
describeTransportFailure,
|
|
3
|
+
httpFailure,
|
|
4
|
+
retryingFetch
|
|
5
|
+
} from "./chunk-3IGNIP6R.js";
|
|
6
|
+
|
|
7
|
+
// src/deploy/import.ts
|
|
8
|
+
var IMPORT_TIMEOUT_MS = 12e4;
|
|
9
|
+
var PROBE_TIMEOUT_MS = 15e3;
|
|
10
|
+
function query(opts) {
|
|
11
|
+
const params = new URLSearchParams();
|
|
12
|
+
if (opts.mode !== void 0) params.set("mode", opts.mode);
|
|
13
|
+
if (opts.prune !== void 0) params.set("delete", String(opts.prune));
|
|
14
|
+
if (opts.records !== void 0) params.set("records", String(opts.records));
|
|
15
|
+
if (opts.truncate !== void 0) params.set("truncate", String(opts.truncate));
|
|
16
|
+
const qs = params.toString();
|
|
17
|
+
return qs === "" ? "" : `?${qs}`;
|
|
18
|
+
}
|
|
19
|
+
function endpoint(opts, suffix = "", qs = "") {
|
|
20
|
+
return `${opts.baseUrl.replace(/\/$/, "")}/api:meta/workspace/${opts.workspaceId ?? 1}/import${suffix}${qs}`;
|
|
21
|
+
}
|
|
22
|
+
function explainDuplicateIdentity(body2) {
|
|
23
|
+
if (!/duplicate key value violates unique constraint/i.test(body2)) return void 0;
|
|
24
|
+
return "The import tried to CREATE objects that already exist under different identities, and the engine refused the duplicate.\nThis is what a stale `xano.lock` looks like: it names guids the workspace no longer has, so nothing matched and everything was planned as new. `--replace` causes it by minting fresh identities.\nRun `xanots lock adopt` against this workspace to re-pin the lock to what is actually there, then release again.";
|
|
25
|
+
}
|
|
26
|
+
var ImportHttpError = class extends Error {
|
|
27
|
+
constructor(status, action, statusText, body2) {
|
|
28
|
+
const explained = explainDuplicateIdentity(body2);
|
|
29
|
+
super(
|
|
30
|
+
explained === void 0 ? httpFailure(action, { status, statusText }, body2) : `${httpFailure(action, { status, statusText }, body2)}
|
|
31
|
+
|
|
32
|
+
${explained}`
|
|
33
|
+
);
|
|
34
|
+
this.status = status;
|
|
35
|
+
this.name = "ImportHttpError";
|
|
36
|
+
}
|
|
37
|
+
status;
|
|
38
|
+
};
|
|
39
|
+
function mergeUnsupported(baseUrl, why) {
|
|
40
|
+
return new Error(
|
|
41
|
+
`Refusing to merge: ${why}
|
|
42
|
+
instance: ${baseUrl}
|
|
43
|
+
An instance that predates merge support silently IGNORES \`mode=merge\`, runs a full replace \u2014 clearing every object, every table row, and all history in the target workspace \u2014 and still answers 200. That is not recoverable, so this is not attempted on an unverified instance.
|
|
44
|
+
Update the instance, or use \`xanots deploy\` to ship to an ephemeral, or \`xanots release --replace\` if wiping the workspace is genuinely what you want.`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
async function assertMergeSupported(auth, opts) {
|
|
48
|
+
let res;
|
|
49
|
+
try {
|
|
50
|
+
res = await fetch(endpoint(opts, "/dry-run"), {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers: { accept: "application/json", Authorization: `Bearer ${auth.access_token}` },
|
|
53
|
+
signal: AbortSignal.timeout(PROBE_TIMEOUT_MS)
|
|
54
|
+
});
|
|
55
|
+
} catch (err) {
|
|
56
|
+
throw mergeUnsupported(opts.baseUrl, `could not reach it to check for merge support (${String(err)}).`);
|
|
57
|
+
}
|
|
58
|
+
if (res.status === 404) {
|
|
59
|
+
throw mergeUnsupported(opts.baseUrl, "this instance has no `/import/dry-run` route, so it cannot merge.");
|
|
60
|
+
}
|
|
61
|
+
if (res.ok || res.status === 400 || res.status === 422) return;
|
|
62
|
+
const text = await res.text().catch(() => "");
|
|
63
|
+
throw mergeUnsupported(
|
|
64
|
+
opts.baseUrl,
|
|
65
|
+
`the merge-support check answered ${res.status} ${res.statusText}, which is neither support nor its absence:
|
|
66
|
+
${text}`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function body(archive) {
|
|
70
|
+
const form = new FormData();
|
|
71
|
+
form.append("file", new Blob([archive], { type: "application/gzip" }), "workspace.gz");
|
|
72
|
+
return form;
|
|
73
|
+
}
|
|
74
|
+
async function post(auth, url, archive, action, writes) {
|
|
75
|
+
let res;
|
|
76
|
+
try {
|
|
77
|
+
const init = {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: { accept: "application/json", Authorization: `Bearer ${auth.access_token}` },
|
|
80
|
+
body: body(archive),
|
|
81
|
+
signal: AbortSignal.timeout(IMPORT_TIMEOUT_MS)
|
|
82
|
+
};
|
|
83
|
+
res = writes ? await fetch(url, init) : await retryingFetch(url, init);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
const aftermath = writes ? `The request did not complete, so the import ran either not at all or entirely \u2014 it is applied in a single transaction. Re-run with \`--dry-run\` to see the workspace's current state before retrying.` : `A dry run writes nothing, so the workspace is untouched. Retry when the instance is reachable.`;
|
|
86
|
+
throw new Error(`${action} could not reach ${url}: ${describeTransportFailure(err)}
|
|
87
|
+
${aftermath}`, {
|
|
88
|
+
cause: err
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const text = await res.text();
|
|
92
|
+
if (!res.ok) {
|
|
93
|
+
throw new ImportHttpError(res.status, action, res.statusText, text);
|
|
94
|
+
}
|
|
95
|
+
return text;
|
|
96
|
+
}
|
|
97
|
+
async function importWorkspaceArchive(auth, opts) {
|
|
98
|
+
if (opts.mode === "merge") await assertMergeSupported(auth, opts);
|
|
99
|
+
const url = endpoint(opts, "", query(opts));
|
|
100
|
+
const text = await post(auth, url, opts.archive, "import", true);
|
|
101
|
+
let workspaceId;
|
|
102
|
+
try {
|
|
103
|
+
const parsed = JSON.parse(text);
|
|
104
|
+
if (typeof parsed.id === "number") workspaceId = parsed.id;
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
return { workspaceId, raw: text };
|
|
108
|
+
}
|
|
109
|
+
async function planWorkspaceImport(auth, opts) {
|
|
110
|
+
const url = endpoint(opts, "/dry-run", query(opts));
|
|
111
|
+
let text;
|
|
112
|
+
try {
|
|
113
|
+
text = await post(auth, url, opts.archive, "import dry run", false);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
if (err instanceof ImportHttpError && err.status === 404) {
|
|
116
|
+
throw opts.mode === "merge" ? mergeUnsupported(opts.baseUrl, "this instance has no `/import/dry-run` route, so it cannot merge.") : new Error(
|
|
117
|
+
`This instance cannot preview an import: it has no \`/import/dry-run\` route (${opts.baseUrl}).
|
|
118
|
+
The import itself is unaffected \u2014 only the preview is unavailable. Update the instance to see a plan before importing.`,
|
|
119
|
+
{ cause: err }
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
124
|
+
let parsed;
|
|
125
|
+
try {
|
|
126
|
+
parsed = JSON.parse(text);
|
|
127
|
+
} catch {
|
|
128
|
+
throw new Error(`import dry run: could not parse the /import/dry-run response as JSON:
|
|
129
|
+
${text}`);
|
|
130
|
+
}
|
|
131
|
+
const rawOps = Array.isArray(parsed.operations) ? parsed.operations : [];
|
|
132
|
+
const operations = rawOps.map((entry) => {
|
|
133
|
+
const op = entry ?? {};
|
|
134
|
+
return {
|
|
135
|
+
type: typeof op.type === "string" ? op.type : "",
|
|
136
|
+
name: typeof op.name === "string" ? op.name : "",
|
|
137
|
+
action: typeof op.action === "string" ? op.action : "",
|
|
138
|
+
...typeof op.details === "string" ? { details: op.details } : {},
|
|
139
|
+
...typeof op.reason === "string" ? { reason: op.reason } : {}
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
workspaceName: typeof parsed.workspace_name === "string" ? parsed.workspace_name : void 0,
|
|
144
|
+
summary: parsed.summary !== null && typeof parsed.summary === "object" && !Array.isArray(parsed.summary) ? parsed.summary : {},
|
|
145
|
+
hasRecords: parsed.has_records === true,
|
|
146
|
+
hasEnv: parsed.has_env === true,
|
|
147
|
+
operations,
|
|
148
|
+
raw: text
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export {
|
|
153
|
+
importWorkspaceArchive,
|
|
154
|
+
planWorkspaceImport
|
|
155
|
+
};
|
|
156
|
+
//# sourceMappingURL=chunk-5WGEURVI.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UsageError
|
|
3
|
+
} from "./chunk-IZW6MK3U.js";
|
|
4
|
+
import {
|
|
5
|
+
style
|
|
6
|
+
} from "./chunk-EZG76F7R.js";
|
|
7
|
+
|
|
8
|
+
// src/emit/prompt.ts
|
|
9
|
+
import { createInterface } from "readline";
|
|
10
|
+
async function confirm(message, opts) {
|
|
11
|
+
if (process.stdin.isTTY !== true) {
|
|
12
|
+
throw new UsageError(
|
|
13
|
+
`Cannot ask for confirmation: stdin is not a terminal. Re-run with ${opts.flag} to confirm without being asked.`,
|
|
14
|
+
{ helpFor: opts.helpFor }
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
18
|
+
try {
|
|
19
|
+
const answer = await new Promise((res) => rl.question(`${style.yellow("?")} ${message} (y/N) `, res));
|
|
20
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
21
|
+
} finally {
|
|
22
|
+
rl.close();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
confirm
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=chunk-5YTBTF25.js.map
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import {
|
|
2
|
+
fetchOrExplain,
|
|
3
|
+
httpFailure
|
|
4
|
+
} from "./chunk-3IGNIP6R.js";
|
|
5
|
+
|
|
6
|
+
// src/deploy/ephemeral.ts
|
|
7
|
+
var TIMEOUT_MS = 3e4;
|
|
8
|
+
var READY_TIMEOUT_MS = 12e4;
|
|
9
|
+
var READY_INTERVAL_MS = 2e3;
|
|
10
|
+
function asString(v) {
|
|
11
|
+
return typeof v === "string" && v !== "" ? v : void 0;
|
|
12
|
+
}
|
|
13
|
+
function asNumber(v) {
|
|
14
|
+
return typeof v === "number" ? v : void 0;
|
|
15
|
+
}
|
|
16
|
+
function tenantBaseUrl(tenant, instance) {
|
|
17
|
+
const host = asString(tenant.xano_domain);
|
|
18
|
+
if (host !== void 0) {
|
|
19
|
+
const scheme = host.startsWith("localhost:") ? "http" : "https";
|
|
20
|
+
return `${scheme}://${host}`;
|
|
21
|
+
}
|
|
22
|
+
const name = asString(tenant.name);
|
|
23
|
+
if (name !== void 0) return new URL(`/tenant/${name}`, instance).href.replace(/\/$/, "");
|
|
24
|
+
return instance.replace(/\/$/, "");
|
|
25
|
+
}
|
|
26
|
+
function expiresAtMs(expiresAt) {
|
|
27
|
+
if (expiresAt === void 0 || expiresAt === null) return NaN;
|
|
28
|
+
return typeof expiresAt === "number" ? expiresAt * 1e3 : Date.parse(String(expiresAt).replace(" ", "T"));
|
|
29
|
+
}
|
|
30
|
+
function isExpired(expiresAt) {
|
|
31
|
+
const ms = expiresAtMs(expiresAt);
|
|
32
|
+
return Number.isFinite(ms) && ms <= Date.now();
|
|
33
|
+
}
|
|
34
|
+
function project(tenant, instance) {
|
|
35
|
+
const name = asString(tenant.name);
|
|
36
|
+
return {
|
|
37
|
+
id: asNumber(tenant.id),
|
|
38
|
+
name: name ?? "",
|
|
39
|
+
display: asString(tenant.display),
|
|
40
|
+
url: name !== void 0 || asString(tenant.xano_domain) ? tenantBaseUrl(tenant, instance) : void 0,
|
|
41
|
+
state: asString(tenant.state),
|
|
42
|
+
expiresAt: tenant.ephemeral_expires_at ?? void 0,
|
|
43
|
+
workspaceId: asNumber(tenant.workspace_id)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function metaFetch(auth, path, action, init) {
|
|
47
|
+
const url = new URL(path, auth.instance);
|
|
48
|
+
return fetchOrExplain(
|
|
49
|
+
url.href,
|
|
50
|
+
{
|
|
51
|
+
...init,
|
|
52
|
+
headers: {
|
|
53
|
+
accept: "application/json",
|
|
54
|
+
Authorization: `Bearer ${auth.access_token}`,
|
|
55
|
+
...init?.headers ?? {}
|
|
56
|
+
},
|
|
57
|
+
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
58
|
+
},
|
|
59
|
+
action,
|
|
60
|
+
TIMEOUT_MS
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
async function readJson(res, action, pathname) {
|
|
64
|
+
const text = await res.text();
|
|
65
|
+
if (!res.ok) throw new Error(httpFailure(action, res, text));
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(text);
|
|
68
|
+
} catch {
|
|
69
|
+
throw new Error(`${action}: could not parse the ${pathname} response as JSON:
|
|
70
|
+
${text}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function isEphemeralDisabled(err) {
|
|
74
|
+
return err instanceof Error && /ephemeral tenants are not enabled/i.test(err.message);
|
|
75
|
+
}
|
|
76
|
+
async function createEphemeral(auth, opts) {
|
|
77
|
+
const path = `/api:meta/workspace/${opts.parentWorkspaceId}/ephemeral`;
|
|
78
|
+
const body = { display: opts.display, tag: [] };
|
|
79
|
+
if (opts.description !== void 0) body.description = opts.description;
|
|
80
|
+
if (opts.expiresHours !== void 0) body.expires_hours = opts.expiresHours;
|
|
81
|
+
const res = await metaFetch(auth, path, "create ephemeral", {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: { "Content-Type": "application/json" },
|
|
84
|
+
body: JSON.stringify(body)
|
|
85
|
+
});
|
|
86
|
+
const tenant = await readJson(res, "create ephemeral", path);
|
|
87
|
+
return project(tenant, auth.instance);
|
|
88
|
+
}
|
|
89
|
+
async function getEphemeral(auth, opts) {
|
|
90
|
+
const path = `/api:meta/workspace/${opts.parentWorkspaceId}/tenant/${opts.name}`;
|
|
91
|
+
const res = await metaFetch(auth, path, "get ephemeral", { method: "GET" });
|
|
92
|
+
if (res.status === 404) {
|
|
93
|
+
await res.text().catch(() => "");
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const tenant = await readJson(res, "get ephemeral", path);
|
|
97
|
+
return project(tenant, auth.instance);
|
|
98
|
+
}
|
|
99
|
+
async function listEphemeral(auth, opts) {
|
|
100
|
+
const path = `/api:meta/workspace/${opts.parentWorkspaceId}/ephemeral`;
|
|
101
|
+
const data = await readJson(await metaFetch(auth, path, "list ephemerals", { method: "GET" }), "list ephemerals", path);
|
|
102
|
+
return toRows(data).map((t) => project(t, auth.instance));
|
|
103
|
+
}
|
|
104
|
+
async function listAllEphemeral(auth) {
|
|
105
|
+
const path = `/api:meta/ephemeral`;
|
|
106
|
+
const data = await readJson(await metaFetch(auth, path, "list ephemerals", { method: "GET" }), "list ephemerals", path);
|
|
107
|
+
return toRows(data).map((t) => project(t, auth.instance));
|
|
108
|
+
}
|
|
109
|
+
function toRows(data) {
|
|
110
|
+
if (Array.isArray(data)) return data;
|
|
111
|
+
if (data && typeof data === "object" && Array.isArray(data.items)) {
|
|
112
|
+
return data.items;
|
|
113
|
+
}
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
async function deleteEphemeral(auth, opts) {
|
|
117
|
+
const path = `/api:meta/workspace/${opts.parentWorkspaceId}/tenant/${opts.name}`;
|
|
118
|
+
const res = await metaFetch(auth, path, "delete ephemeral", { method: "DELETE" });
|
|
119
|
+
if (res.status === 404) {
|
|
120
|
+
await res.text().catch(() => "");
|
|
121
|
+
return { alreadyGone: true };
|
|
122
|
+
}
|
|
123
|
+
const text = await res.text();
|
|
124
|
+
if (!res.ok) throw new Error(httpFailure("delete ephemeral", res, text));
|
|
125
|
+
return { alreadyGone: false };
|
|
126
|
+
}
|
|
127
|
+
async function impersonateEphemeral(auth, opts) {
|
|
128
|
+
const query = opts.guest ? "?guest_read_only=true" : "";
|
|
129
|
+
const path = `/api:meta/workspace/${opts.parentWorkspaceId}/tenant/${encodeURIComponent(opts.name)}/impersonate${query}`;
|
|
130
|
+
const data = await readJson(await metaFetch(auth, path, "impersonate ephemeral", { method: "GET" }), "impersonate ephemeral", path);
|
|
131
|
+
if (typeof data._ti !== "string" || data._ti === "") {
|
|
132
|
+
throw new Error(`impersonate ephemeral: no one-time token (\`_ti\`) returned for "${opts.name}".`);
|
|
133
|
+
}
|
|
134
|
+
return { _ti: data._ti };
|
|
135
|
+
}
|
|
136
|
+
async function waitUntilReady(auth, opts, cfg = {}) {
|
|
137
|
+
const timeoutMs = cfg.timeoutMs ?? READY_TIMEOUT_MS;
|
|
138
|
+
const intervalMs = cfg.intervalMs ?? READY_INTERVAL_MS;
|
|
139
|
+
const sleep = cfg.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
140
|
+
const deadline = Date.now() + timeoutMs;
|
|
141
|
+
let last = null;
|
|
142
|
+
for (; ; ) {
|
|
143
|
+
last = await getEphemeral(auth, opts);
|
|
144
|
+
if (last && last.state === "ok") return last;
|
|
145
|
+
if (Date.now() >= deadline) {
|
|
146
|
+
throw new Error(
|
|
147
|
+
`Ephemeral "${opts.name}" did not become ready within ${Math.round(timeoutMs / 1e3)}s (last state: ${last?.state ?? "gone"}). Try \`xanots deploy\` again.`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
await sleep(intervalMs);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export {
|
|
155
|
+
tenantBaseUrl,
|
|
156
|
+
isExpired,
|
|
157
|
+
isEphemeralDisabled,
|
|
158
|
+
createEphemeral,
|
|
159
|
+
getEphemeral,
|
|
160
|
+
listEphemeral,
|
|
161
|
+
listAllEphemeral,
|
|
162
|
+
deleteEphemeral,
|
|
163
|
+
impersonateEphemeral,
|
|
164
|
+
waitUntilReady
|
|
165
|
+
};
|
|
166
|
+
//# sourceMappingURL=chunk-6DHBYBTO.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/validate/kinds.ts
|
|
2
|
+
var ROUND_TRIP_KINDS = [
|
|
3
|
+
{ key: "dbo", fixtureDir: "tables" },
|
|
4
|
+
{ key: "function", fixtureDir: "statements", runnable: true },
|
|
5
|
+
{ key: "query", fixtureDir: "query" },
|
|
6
|
+
{ key: "trigger", fixtureDir: "triggers" },
|
|
7
|
+
{ key: "task", fixtureDir: "task" },
|
|
8
|
+
{ key: "toolset", fixtureDir: "toolset" },
|
|
9
|
+
{ key: "tool", fixtureDir: "toolset" },
|
|
10
|
+
{ key: "middleware", fixtureDir: "middleware" },
|
|
11
|
+
{ key: "addon", fixtureDir: "addon" },
|
|
12
|
+
{ key: "workflow_test", fixtureDir: "workflow-test" }
|
|
13
|
+
];
|
|
14
|
+
var FIXTURE_DIR_BY_KIND = new Map(ROUND_TRIP_KINDS.map((k) => [k.key, k.fixtureDir]));
|
|
15
|
+
var RUNNABLE_KINDS = new Set(ROUND_TRIP_KINDS.filter((k) => k.runnable).map((k) => k.key));
|
|
16
|
+
function fixtureDirForKind(key) {
|
|
17
|
+
return FIXTURE_DIR_BY_KIND.get(key);
|
|
18
|
+
}
|
|
19
|
+
function kindIsRunnable(key) {
|
|
20
|
+
return RUNNABLE_KINDS.has(key);
|
|
21
|
+
}
|
|
22
|
+
function strField(obj, key) {
|
|
23
|
+
const v = obj[key];
|
|
24
|
+
return typeof v === "string" && v !== "" ? v : void 0;
|
|
25
|
+
}
|
|
26
|
+
function indexByIdentity(objects) {
|
|
27
|
+
const byGuid = /* @__PURE__ */ new Map();
|
|
28
|
+
const byName = /* @__PURE__ */ new Map();
|
|
29
|
+
const duplicateGuids = /* @__PURE__ */ new Set();
|
|
30
|
+
const duplicateNames = /* @__PURE__ */ new Set();
|
|
31
|
+
for (const obj of objects) {
|
|
32
|
+
const guid = strField(obj, "guid");
|
|
33
|
+
if (guid !== void 0) {
|
|
34
|
+
if (byGuid.has(guid)) duplicateGuids.add(guid);
|
|
35
|
+
else byGuid.set(guid, obj);
|
|
36
|
+
}
|
|
37
|
+
const name = strField(obj, "name");
|
|
38
|
+
if (name !== void 0) {
|
|
39
|
+
if (byName.has(name)) duplicateNames.add(name);
|
|
40
|
+
else byName.set(name, obj);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { byGuid, byName, duplicateGuids, duplicateNames };
|
|
44
|
+
}
|
|
45
|
+
function resolveMatch(compiled, index) {
|
|
46
|
+
const guid = strField(compiled, "guid");
|
|
47
|
+
if (guid !== void 0 && index.byGuid.has(guid)) {
|
|
48
|
+
if (index.duplicateGuids.has(guid)) return { outcome: "ambiguous" };
|
|
49
|
+
return { outcome: "found", fetched: index.byGuid.get(guid) };
|
|
50
|
+
}
|
|
51
|
+
const name = strField(compiled, "name");
|
|
52
|
+
if (name !== void 0 && index.byName.has(name)) {
|
|
53
|
+
if (index.duplicateNames.has(name)) return { outcome: "ambiguous" };
|
|
54
|
+
return { outcome: "found", fetched: index.byName.get(name) };
|
|
55
|
+
}
|
|
56
|
+
return { outcome: "missing" };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export {
|
|
60
|
+
ROUND_TRIP_KINDS,
|
|
61
|
+
fixtureDirForKind,
|
|
62
|
+
kindIsRunnable,
|
|
63
|
+
indexByIdentity,
|
|
64
|
+
resolveMatch
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=chunk-76QBEIGO.js.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {
|
|
2
|
+
printMicroserviceSection,
|
|
3
|
+
readMicroservices
|
|
4
|
+
} from "./chunk-NDZFBZHC.js";
|
|
5
|
+
import {
|
|
6
|
+
isMachineOutput,
|
|
7
|
+
writeJson
|
|
8
|
+
} from "./chunk-NUQCEOKA.js";
|
|
9
|
+
import {
|
|
10
|
+
fetchOrExplain,
|
|
11
|
+
httpFailure
|
|
12
|
+
} from "./chunk-3IGNIP6R.js";
|
|
13
|
+
import {
|
|
14
|
+
getAccessToken
|
|
15
|
+
} from "./chunk-C56BC2FY.js";
|
|
16
|
+
import {
|
|
17
|
+
formatFields,
|
|
18
|
+
stdoutStyle
|
|
19
|
+
} from "./chunk-EZG76F7R.js";
|
|
20
|
+
|
|
21
|
+
// src/emit/sandbox-details-command.ts
|
|
22
|
+
var SANDBOX_TIMEOUT_MS = 3e4;
|
|
23
|
+
function asString(v) {
|
|
24
|
+
return typeof v === "string" && v !== "" ? v : void 0;
|
|
25
|
+
}
|
|
26
|
+
function sandboxBaseUrl(tenant, instance) {
|
|
27
|
+
const host = asString(tenant.xano_domain);
|
|
28
|
+
if (host !== void 0) {
|
|
29
|
+
const scheme = host.startsWith("localhost:") ? "http" : "https";
|
|
30
|
+
return `${scheme}://${host}`;
|
|
31
|
+
}
|
|
32
|
+
const name = asString(tenant.name);
|
|
33
|
+
if (name !== void 0) {
|
|
34
|
+
return new URL(`/tenant/${name}`, instance).href.replace(/\/$/, "");
|
|
35
|
+
}
|
|
36
|
+
return instance.replace(/\/$/, "");
|
|
37
|
+
}
|
|
38
|
+
async function fetchSandboxDetails(args) {
|
|
39
|
+
const { access_token, instance } = await getAccessToken(args);
|
|
40
|
+
const url = new URL("/api:meta/sandbox/me", instance);
|
|
41
|
+
const res = await fetchOrExplain(
|
|
42
|
+
url.href,
|
|
43
|
+
{
|
|
44
|
+
headers: { accept: "application/json", Authorization: `Bearer ${access_token}` },
|
|
45
|
+
signal: AbortSignal.timeout(SANDBOX_TIMEOUT_MS)
|
|
46
|
+
},
|
|
47
|
+
"sandbox details",
|
|
48
|
+
SANDBOX_TIMEOUT_MS
|
|
49
|
+
);
|
|
50
|
+
const text = await res.text();
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
throw new Error(httpFailure("sandbox details", res, text));
|
|
53
|
+
}
|
|
54
|
+
let tenant = {};
|
|
55
|
+
try {
|
|
56
|
+
tenant = JSON.parse(text);
|
|
57
|
+
} catch {
|
|
58
|
+
throw new Error(`sandbox details: could not parse the ${url.pathname} response as JSON:
|
|
59
|
+
${text}`);
|
|
60
|
+
}
|
|
61
|
+
const expiresAt = tenant.sandbox_expires_at;
|
|
62
|
+
return {
|
|
63
|
+
baseUrl: sandboxBaseUrl(tenant, instance),
|
|
64
|
+
sandbox: {
|
|
65
|
+
id: typeof tenant.id === "number" ? tenant.id : void 0,
|
|
66
|
+
name: asString(tenant.name),
|
|
67
|
+
display: asString(tenant.display),
|
|
68
|
+
state: asString(tenant.state),
|
|
69
|
+
xanoDomain: asString(tenant.xano_domain),
|
|
70
|
+
expiresAt: typeof expiresAt === "string" || typeof expiresAt === "number" ? expiresAt : void 0
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function prettyDetails(d) {
|
|
75
|
+
const s = stdoutStyle();
|
|
76
|
+
const { sandbox } = d;
|
|
77
|
+
const label = [sandbox.display ?? sandbox.name, sandbox.state && s.dim(`(${sandbox.state})`)].filter(Boolean).join(" ");
|
|
78
|
+
const rows = [["Base URL", s.bold(s.cyan(d.baseUrl))]];
|
|
79
|
+
if (label !== "") rows.push(["Sandbox", label]);
|
|
80
|
+
if (sandbox.expiresAt !== void 0) rows.push(["Expires", s.dim(String(sandbox.expiresAt))]);
|
|
81
|
+
return "\n" + formatFields(rows);
|
|
82
|
+
}
|
|
83
|
+
async function runSandboxDetailsCommand(args) {
|
|
84
|
+
const details = await fetchSandboxDetails(args);
|
|
85
|
+
const auth = await getAccessToken(args);
|
|
86
|
+
const microservices = await readMicroservices(auth, details.baseUrl);
|
|
87
|
+
if (isMachineOutput(args)) {
|
|
88
|
+
writeJson({ ...details, microservices });
|
|
89
|
+
} else {
|
|
90
|
+
process.stdout.write(prettyDetails(details));
|
|
91
|
+
printMicroserviceSection(microservices);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export {
|
|
96
|
+
sandboxBaseUrl,
|
|
97
|
+
fetchSandboxDetails,
|
|
98
|
+
runSandboxDetailsCommand
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=chunk-7DKX2SPN.js.map
|