@abloatai/ablo 0.34.0 → 0.34.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/AGENTS.md +2 -0
- package/CHANGELOG.md +10 -0
- package/dist/cli.cjs +488 -426
- package/docs/index.md +2 -0
- package/docs/operating-on-your-database.md +109 -0
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -787,8 +787,8 @@ ${import_picocolors2.default.gray(d2)} ${t}
|
|
|
787
787
|
if (i) process.stdout.write(`${I2} ${l2}...`);
|
|
788
788
|
else if (t === "timer") process.stdout.write(`${I2} ${l2} ${O2(g2)}`);
|
|
789
789
|
else {
|
|
790
|
-
const
|
|
791
|
-
process.stdout.write(`${I2} ${l2}${
|
|
790
|
+
const z9 = ".".repeat(Math.floor(w2)).slice(0, 3);
|
|
791
|
+
process.stdout.write(`${I2} ${l2}${z9}`);
|
|
792
792
|
}
|
|
793
793
|
h2 = h2 + 1 < n.length ? h2 + 1 : 0, w2 = w2 < n.length ? w2 + 0.125 : 0;
|
|
794
794
|
}, r2);
|
|
@@ -5605,7 +5605,8 @@ async function registerDirectDataSource(opts) {
|
|
|
5605
5605
|
console.log(
|
|
5606
5606
|
`
|
|
5607
5607
|
${import_picocolors6.default.green("\u2713")} Registered${body2.host ? ` ${import_picocolors6.default.dim(body2.host)}` : ""}${body2.id ? ` ${import_picocolors6.default.dim(`(${body2.id})`)}` : ""} as a direct DataSource (${statusNote}).
|
|
5608
|
-
|
|
5608
|
+
Your database is connected. Reads follow its replication stream; writes go through Ablo
|
|
5609
|
+
and land in your own tables. Check the connection anytime with ${import_picocolors6.default.cyan("ablo connect check")}.
|
|
5609
5610
|
`
|
|
5610
5611
|
);
|
|
5611
5612
|
return true;
|
|
@@ -5840,17 +5841,134 @@ var init_disconnect = __esm({
|
|
|
5840
5841
|
}
|
|
5841
5842
|
});
|
|
5842
5843
|
|
|
5844
|
+
// src/cli/connectOwnership.ts
|
|
5845
|
+
function ownershipBlockers(rows) {
|
|
5846
|
+
return rows.filter((row) => !row.can_manage && !row.is_superuser).map((row) => ({
|
|
5847
|
+
relation: row.relation,
|
|
5848
|
+
owner: row.owner,
|
|
5849
|
+
canGrantInherit: row.can_grant_inherit
|
|
5850
|
+
}));
|
|
5851
|
+
}
|
|
5852
|
+
function ownershipRemediation(blockers, admin) {
|
|
5853
|
+
const grantableByOwner = /* @__PURE__ */ new Map();
|
|
5854
|
+
for (const blocker of blockers) {
|
|
5855
|
+
grantableByOwner.set(
|
|
5856
|
+
blocker.owner,
|
|
5857
|
+
(grantableByOwner.get(blocker.owner) ?? false) || blocker.canGrantInherit
|
|
5858
|
+
);
|
|
5859
|
+
}
|
|
5860
|
+
const resolvedOwners = /* @__PURE__ */ new Set();
|
|
5861
|
+
const inheritGrants = [];
|
|
5862
|
+
for (const [owner, grantable] of grantableByOwner) {
|
|
5863
|
+
if (grantable) {
|
|
5864
|
+
inheritGrants.push(`GRANT ${quoteIdent(owner)} TO ${quoteIdent(admin)} WITH INHERIT TRUE;`);
|
|
5865
|
+
resolvedOwners.add(owner);
|
|
5866
|
+
}
|
|
5867
|
+
}
|
|
5868
|
+
const unresolved = blockers.filter((blocker) => !resolvedOwners.has(blocker.owner));
|
|
5869
|
+
return { inheritGrants, unresolved };
|
|
5870
|
+
}
|
|
5871
|
+
async function publishedTableBlockers(sql, tables) {
|
|
5872
|
+
const scoped = tables.length > 0;
|
|
5873
|
+
const raw = await sql.unsafe(
|
|
5874
|
+
`SELECT format('%I.%I', n.nspname, c.relname) AS relation, ${OWNERSHIP_COLUMNS}
|
|
5875
|
+
${OWNERSHIP_FROM}
|
|
5876
|
+
WHERE c.relkind = 'r'
|
|
5877
|
+
AND n.nspname = 'public'
|
|
5878
|
+
AND c.relname <> 'ablo_idempotency'
|
|
5879
|
+
${scoped ? "AND c.relname = ANY($1)" : ""}`,
|
|
5880
|
+
scoped ? [tables] : []
|
|
5881
|
+
);
|
|
5882
|
+
return ownershipBlockers(import_zod7.z.array(ownedRelationRowSchema).parse(raw));
|
|
5883
|
+
}
|
|
5884
|
+
async function ledgerBlocker(sql) {
|
|
5885
|
+
const raw = await sql.unsafe(
|
|
5886
|
+
`SELECT format('%I.%I', n.nspname, c.relname) AS relation, ${OWNERSHIP_COLUMNS}
|
|
5887
|
+
${OWNERSHIP_FROM}
|
|
5888
|
+
WHERE c.relkind = 'r'
|
|
5889
|
+
AND n.nspname = 'public'
|
|
5890
|
+
AND c.relname = 'ablo_idempotency'`
|
|
5891
|
+
);
|
|
5892
|
+
const rows = import_zod7.z.array(ownedRelationRowSchema).parse(raw);
|
|
5893
|
+
const row = rows[0];
|
|
5894
|
+
if (!row) return null;
|
|
5895
|
+
return ownershipBlockers([row])[0] ?? null;
|
|
5896
|
+
}
|
|
5897
|
+
function formatUnresolvedOwnership(unresolved, admin, target) {
|
|
5898
|
+
const list = unresolved.map((b4) => `${b4.relation} (owned by ${b4.owner})`).join("\n ");
|
|
5899
|
+
const owners = [...new Set(unresolved.map((b4) => b4.owner))];
|
|
5900
|
+
const ownerNames = owners.map((o2) => import_picocolors8.default.bold(o2)).join(", ");
|
|
5901
|
+
const grants = owners.map((o2) => `GRANT ${quoteIdent(o2)} TO ${quoteIdent(admin)} WITH INHERIT TRUE;`).join(" ");
|
|
5902
|
+
const one = unresolved.length === 1;
|
|
5903
|
+
const hasLedger = unresolved.some((b4) => b4.relation.endsWith(".ablo_idempotency"));
|
|
5904
|
+
return import_picocolors8.default.red(
|
|
5905
|
+
`
|
|
5906
|
+
${import_picocolors8.default.bold(String(unresolved.length))} relation${one ? "" : "s"} on ${target} ${one ? "is" : "are"} owned by a role ${import_picocolors8.default.bold(admin)} can't manage or take over:`
|
|
5907
|
+
) + `
|
|
5908
|
+
${list}
|
|
5909
|
+
|
|
5910
|
+
Apply grants your admin inheritance of an owning role for you when it may, but ${admin}
|
|
5911
|
+
isn't a member with admin option of ${ownerNames}, so it can't here. Re-run ${import_picocolors8.default.bold("--url")} as a
|
|
5912
|
+
role that owns ${one ? "it" : "them"}, or have a role with admin option on ${ownerNames} (or a
|
|
5913
|
+
superuser) run:
|
|
5914
|
+
${import_picocolors8.default.cyan(grants)}
|
|
5915
|
+
` + (hasLedger ? `
|
|
5916
|
+
For ${import_picocolors8.default.bold("ablo_idempotency")} you can instead drop it \u2014 it holds only idempotency replay
|
|
5917
|
+
records, safe to drop when no commit is in flight \u2014 and Ablo recreates it under this admin:
|
|
5918
|
+
${import_picocolors8.default.cyan("DROP TABLE ablo_idempotency;")}
|
|
5919
|
+
` : "");
|
|
5920
|
+
}
|
|
5921
|
+
var import_zod7, import_picocolors8, ownedRelationRowSchema, OWNERSHIP_COLUMNS, OWNERSHIP_FROM;
|
|
5922
|
+
var init_connectOwnership = __esm({
|
|
5923
|
+
"src/cli/connectOwnership.ts"() {
|
|
5924
|
+
"use strict";
|
|
5925
|
+
init_cjs_shims();
|
|
5926
|
+
import_zod7 = require("zod");
|
|
5927
|
+
import_picocolors8 = __toESM(require_picocolors(), 1);
|
|
5928
|
+
init_connectSetup();
|
|
5929
|
+
ownedRelationRowSchema = import_zod7.z.object({
|
|
5930
|
+
/** Schema-qualified relation, e.g. `public.documents`. */
|
|
5931
|
+
relation: import_zod7.z.string(),
|
|
5932
|
+
owner: import_zod7.z.string(),
|
|
5933
|
+
/**
|
|
5934
|
+
* The admin can act as the owner for grants — it owns the relation OR is an
|
|
5935
|
+
* INHERITing member of the owning role (`pg_has_role(current_user, owner,
|
|
5936
|
+
* 'USAGE')`). A plain NOINHERIT membership is false: the plan doesn't
|
|
5937
|
+
* `SET ROLE`, so the grant would fail.
|
|
5938
|
+
*/
|
|
5939
|
+
can_manage: import_zod7.z.boolean(),
|
|
5940
|
+
is_superuser: import_zod7.z.boolean(),
|
|
5941
|
+
/**
|
|
5942
|
+
* The admin is a member of the owning role WITH admin option, so it can run
|
|
5943
|
+
* `GRANT <owner> TO <admin> WITH INHERIT TRUE` itself to gain the inheritance
|
|
5944
|
+
* the grants need — the runnable fix reassigning ownership can't be.
|
|
5945
|
+
*/
|
|
5946
|
+
can_grant_inherit: import_zod7.z.boolean()
|
|
5947
|
+
});
|
|
5948
|
+
OWNERSHIP_COLUMNS = `
|
|
5949
|
+
pg_get_userbyid(c.relowner) AS owner,
|
|
5950
|
+
pg_has_role(current_user, c.relowner, 'USAGE') AS can_manage,
|
|
5951
|
+
r.rolsuper AS is_superuser,
|
|
5952
|
+
EXISTS (
|
|
5953
|
+
SELECT 1 FROM pg_auth_members m
|
|
5954
|
+
WHERE m.member = r.oid AND m.roleid = c.relowner AND m.admin_option
|
|
5955
|
+
) AS can_grant_inherit`;
|
|
5956
|
+
OWNERSHIP_FROM = `
|
|
5957
|
+
FROM pg_class c
|
|
5958
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
5959
|
+
JOIN pg_roles r ON r.rolname = current_user`;
|
|
5960
|
+
}
|
|
5961
|
+
});
|
|
5962
|
+
|
|
5843
5963
|
// src/cli/connectApply.ts
|
|
5844
5964
|
var connectApply_exports = {};
|
|
5845
5965
|
__export(connectApply_exports, {
|
|
5846
5966
|
connectApplyPlan: () => connectApplyPlan,
|
|
5847
5967
|
detectProvider: () => detectProvider,
|
|
5848
|
-
ledgerBlockedBy: () => ledgerBlockedBy,
|
|
5849
5968
|
logicalReplicationGuidance: () => logicalReplicationGuidance,
|
|
5850
5969
|
passwordClause: () => passwordClause,
|
|
5851
5970
|
postRegistrationOutcome: () => postRegistrationOutcome,
|
|
5852
|
-
runConnectApply: () => runConnectApply
|
|
5853
|
-
tableOwnershipBlockers: () => tableOwnershipBlockers
|
|
5971
|
+
runConnectApply: () => runConnectApply
|
|
5854
5972
|
});
|
|
5855
5973
|
function postRegistrationOutcome(input) {
|
|
5856
5974
|
if (input.registered) return { exitCode: 0, notice: null };
|
|
@@ -5902,7 +6020,16 @@ EXCEPTION WHEN duplicate_object THEN NULL;
|
|
|
5902
6020
|
END $$;`
|
|
5903
6021
|
];
|
|
5904
6022
|
const publicationDetail = reconcile2?.sql.length === 0 ? "already publishing exactly these tables \u2014 nothing to change" : tables.length > 0 ? `a read stream of the ${tables.length} table${tables.length === 1 ? "" : "s"} you chose` : "a read stream of your tables";
|
|
6023
|
+
const ownStep = input.inheritGrants && input.inheritGrants.length > 0 ? [
|
|
6024
|
+
{
|
|
6025
|
+
key: "own",
|
|
6026
|
+
title: "Let this admin manage tables owned by another role",
|
|
6027
|
+
detail: "your admin inherits the owning role so the steps below apply \u2014 reversible, no ownership change",
|
|
6028
|
+
sql: input.inheritGrants
|
|
6029
|
+
}
|
|
6030
|
+
] : [];
|
|
5905
6031
|
return [
|
|
6032
|
+
...ownStep,
|
|
5906
6033
|
...walStep,
|
|
5907
6034
|
{
|
|
5908
6035
|
key: "publication",
|
|
@@ -5944,15 +6071,15 @@ function printPlan2(steps, showSql) {
|
|
|
5944
6071
|
console.log(` This sets up your database for Ablo:
|
|
5945
6072
|
`);
|
|
5946
6073
|
for (const step of steps) {
|
|
5947
|
-
console.log(` ${
|
|
6074
|
+
console.log(` ${import_picocolors9.default.green("\u2022")} ${step.title}`);
|
|
5948
6075
|
if (showSql) {
|
|
5949
6076
|
for (const statement of step.sql) {
|
|
5950
|
-
for (const line of statement.split("\n")) console.log(` ${
|
|
6077
|
+
for (const line of statement.split("\n")) console.log(` ${import_picocolors9.default.dim(line)}`);
|
|
5951
6078
|
}
|
|
5952
6079
|
}
|
|
5953
6080
|
}
|
|
5954
6081
|
console.log(
|
|
5955
|
-
|
|
6082
|
+
import_picocolors9.default.dim(
|
|
5956
6083
|
`
|
|
5957
6084
|
Your admin password stays on this machine.${showSql ? "" : " (--show-sql for the exact statements)"}
|
|
5958
6085
|
`
|
|
@@ -5965,45 +6092,6 @@ async function adminCanCreateRoles(sql) {
|
|
|
5965
6092
|
);
|
|
5966
6093
|
return rows[0] ?? null;
|
|
5967
6094
|
}
|
|
5968
|
-
function ledgerBlockedBy(row) {
|
|
5969
|
-
if (!row || row.is_owner || row.is_superuser) return null;
|
|
5970
|
-
return row.owner;
|
|
5971
|
-
}
|
|
5972
|
-
async function unmanageableLedgerOwner(sql) {
|
|
5973
|
-
const rows = await sql.unsafe(
|
|
5974
|
-
`SELECT pg_get_userbyid(c.relowner) AS owner,
|
|
5975
|
-
c.relowner = r.oid AS is_owner,
|
|
5976
|
-
r.rolsuper AS is_superuser
|
|
5977
|
-
FROM pg_class c
|
|
5978
|
-
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
5979
|
-
JOIN pg_roles r ON r.rolname = current_user
|
|
5980
|
-
WHERE c.relkind = 'r'
|
|
5981
|
-
AND c.relname = 'ablo_idempotency'
|
|
5982
|
-
AND n.nspname = 'public'`
|
|
5983
|
-
);
|
|
5984
|
-
return ledgerBlockedBy(rows[0]);
|
|
5985
|
-
}
|
|
5986
|
-
function tableOwnershipBlockers(rows) {
|
|
5987
|
-
return rows.filter((row) => !row.can_manage && !row.is_superuser).map((row) => ({ relation: row.relation, owner: row.owner }));
|
|
5988
|
-
}
|
|
5989
|
-
async function unmanageablePublishedTableOwners(sql, tables) {
|
|
5990
|
-
const scoped = tables.length > 0;
|
|
5991
|
-
const rows = await sql.unsafe(
|
|
5992
|
-
`SELECT format('%I.%I', n.nspname, c.relname) AS relation,
|
|
5993
|
-
pg_get_userbyid(c.relowner) AS owner,
|
|
5994
|
-
pg_has_role(current_user, c.relowner, 'USAGE') AS can_manage,
|
|
5995
|
-
r.rolsuper AS is_superuser
|
|
5996
|
-
FROM pg_class c
|
|
5997
|
-
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
5998
|
-
JOIN pg_roles r ON r.rolname = current_user
|
|
5999
|
-
WHERE c.relkind = 'r'
|
|
6000
|
-
AND n.nspname = 'public'
|
|
6001
|
-
AND c.relname <> 'ablo_idempotency'
|
|
6002
|
-
${scoped ? "AND c.relname = ANY($1)" : ""}`,
|
|
6003
|
-
scoped ? [tables] : []
|
|
6004
|
-
);
|
|
6005
|
-
return tableOwnershipBlockers(rows);
|
|
6006
|
-
}
|
|
6007
6095
|
function detectProvider(hostOrTarget) {
|
|
6008
6096
|
const host = hostOrTarget.toLowerCase();
|
|
6009
6097
|
if (host.includes("neon.tech")) return "neon";
|
|
@@ -6062,8 +6150,8 @@ async function runConnectApply(args) {
|
|
|
6062
6150
|
const adminUrl = args.url ?? readProjectAdminDatabaseUrl();
|
|
6063
6151
|
if (!adminUrl) {
|
|
6064
6152
|
console.error(
|
|
6065
|
-
|
|
6066
|
-
` Pass ${
|
|
6153
|
+
import_picocolors9.default.red(" No admin connection string.") + import_picocolors9.default.dim(
|
|
6154
|
+
` Pass ${import_picocolors9.default.bold("--url <admin-conn>")} (or set ${import_picocolors9.default.bold("DATABASE_URL")}) and re-run.`
|
|
6067
6155
|
)
|
|
6068
6156
|
);
|
|
6069
6157
|
process.exit(1);
|
|
@@ -6078,19 +6166,19 @@ async function runConnectApply(args) {
|
|
|
6078
6166
|
const apiKey = resolveApiKey();
|
|
6079
6167
|
if (!apiKey) {
|
|
6080
6168
|
console.error(
|
|
6081
|
-
|
|
6082
|
-
` Run ${
|
|
6169
|
+
import_picocolors9.default.red(" Not logged in.") + import_picocolors9.default.dim(
|
|
6170
|
+
` Run ${import_picocolors9.default.bold("ablo login")} (or set ${import_picocolors9.default.bold("ABLO_API_KEY")}) so Ablo knows which project to register this database for.`
|
|
6083
6171
|
)
|
|
6084
6172
|
);
|
|
6085
6173
|
process.exit(1);
|
|
6086
6174
|
}
|
|
6087
6175
|
console.log(
|
|
6088
6176
|
`
|
|
6089
|
-
${brand("ablo")} ${
|
|
6177
|
+
${brand("ablo")} ${import_picocolors9.default.dim(verb)} ${import_picocolors9.default.dim(rotating ? "re-key the scoped roles" : "set up your database for Ablo")}
|
|
6090
6178
|
`
|
|
6091
6179
|
);
|
|
6092
6180
|
console.log(
|
|
6093
|
-
` ${
|
|
6181
|
+
` ${import_picocolors9.default.dim("\u2192")} ${import_picocolors9.default.bold(target)}${adminSource === "DATABASE_URL" ? import_picocolors9.default.dim(" (admin via DATABASE_URL)") : ""}
|
|
6094
6182
|
`
|
|
6095
6183
|
);
|
|
6096
6184
|
const admin = src_default(adminUrl, {
|
|
@@ -6106,53 +6194,25 @@ async function runConnectApply(args) {
|
|
|
6106
6194
|
} catch (err) {
|
|
6107
6195
|
await admin.end({ timeout: 2 }).catch(() => void 0);
|
|
6108
6196
|
const pg = err ?? {};
|
|
6109
|
-
console.error(
|
|
6197
|
+
console.error(import_picocolors9.default.red(` Couldn't connect: ${pg.message ?? String(err)}`));
|
|
6110
6198
|
process.exit(1);
|
|
6111
6199
|
}
|
|
6112
6200
|
if (!capability || !(capability.rolsuper || capability.rolcreaterole)) {
|
|
6113
6201
|
await admin.end({ timeout: 2 });
|
|
6114
6202
|
console.error(
|
|
6115
|
-
|
|
6203
|
+
import_picocolors9.default.red(` ${capability?.rolname ?? "This role"} can't create roles.`) + import_picocolors9.default.dim(` Point ${import_picocolors9.default.bold("--url")} at your owner/admin connection and re-run.`)
|
|
6116
6204
|
);
|
|
6117
6205
|
process.exit(1);
|
|
6118
6206
|
}
|
|
6119
|
-
const
|
|
6120
|
-
|
|
6207
|
+
const ledger = await ledgerBlocker(admin).catch(() => null);
|
|
6208
|
+
const foreignTables = await publishedTableBlockers(admin, args.tables).catch(() => []);
|
|
6209
|
+
const { inheritGrants, unresolved } = ownershipRemediation(
|
|
6210
|
+
[...ledger ? [ledger] : [], ...foreignTables],
|
|
6211
|
+
capability.rolname
|
|
6212
|
+
);
|
|
6213
|
+
if (unresolved.length > 0) {
|
|
6121
6214
|
await admin.end({ timeout: 2 });
|
|
6122
|
-
console.error(
|
|
6123
|
-
import_picocolors8.default.red(
|
|
6124
|
-
`
|
|
6125
|
-
${import_picocolors8.default.bold("ablo_idempotency")} already exists on ${target}, owned by ${import_picocolors8.default.bold(blockingOwner)}, but you connected as ${import_picocolors8.default.bold(capability.rolname)}.`
|
|
6126
|
-
) + `
|
|
6127
|
-
Ablo's setup grants the writer role access to this ledger, and Postgres reserves that
|
|
6128
|
-
for the table's owner. Either re-run pointing ${import_picocolors8.default.bold("--url")} at ${import_picocolors8.default.bold(blockingOwner)}'s connection,
|
|
6129
|
-
or drop the existing ledger so Ablo recreates it under this admin \u2014 it holds only
|
|
6130
|
-
idempotency replay records, safe to drop when no commit is in flight:
|
|
6131
|
-
${import_picocolors8.default.cyan("DROP TABLE ablo_idempotency;")}
|
|
6132
|
-
`
|
|
6133
|
-
);
|
|
6134
|
-
process.exit(1);
|
|
6135
|
-
}
|
|
6136
|
-
const foreignTables = await unmanageablePublishedTableOwners(admin, args.tables).catch(() => []);
|
|
6137
|
-
if (foreignTables.length > 0) {
|
|
6138
|
-
await admin.end({ timeout: 2 });
|
|
6139
|
-
const list = foreignTables.map((t) => `${t.relation} (owned by ${t.owner})`).join("\n ");
|
|
6140
|
-
const alters = foreignTables.map((t) => `ALTER TABLE ${t.relation} OWNER TO ${quoteIdent(capability.rolname)};`).join(" ");
|
|
6141
|
-
const plural = foreignTables.length === 1;
|
|
6142
|
-
console.error(
|
|
6143
|
-
import_picocolors8.default.red(
|
|
6144
|
-
`
|
|
6145
|
-
${import_picocolors8.default.bold(String(foreignTables.length))} published table${plural ? "" : "s"} ${plural ? "is" : "are"} owned by another role, but you connected as ${import_picocolors8.default.bold(capability.rolname)}:`
|
|
6146
|
-
) + `
|
|
6147
|
-
${list}
|
|
6148
|
-
|
|
6149
|
-
Ablo grants the writer role access to your published tables, and Postgres reserves that
|
|
6150
|
-
for the table's owner. Reassign them to your admin \u2014 metadata only, your rows and RLS
|
|
6151
|
-
policies are untouched, and it works when your admin is a member of the owning role:
|
|
6152
|
-
${import_picocolors8.default.cyan(alters)}
|
|
6153
|
-
Or re-run pointing ${import_picocolors8.default.bold("--url")} at the owning role's connection.
|
|
6154
|
-
`
|
|
6155
|
-
);
|
|
6215
|
+
console.error(formatUnresolvedOwnership(unresolved, capability.rolname, target));
|
|
6156
6216
|
process.exit(1);
|
|
6157
6217
|
}
|
|
6158
6218
|
const provider = detectProvider(target);
|
|
@@ -6173,18 +6233,19 @@ async function runConnectApply(args) {
|
|
|
6173
6233
|
},
|
|
6174
6234
|
walAlreadyLogical: walReady,
|
|
6175
6235
|
provider,
|
|
6176
|
-
existingPublication
|
|
6236
|
+
existingPublication,
|
|
6237
|
+
inheritGrants
|
|
6177
6238
|
});
|
|
6178
6239
|
const steps = buildPlan("scram-verifier");
|
|
6179
6240
|
if (pubReconcile.removed.length > 0 || pubReconcile.recreated) {
|
|
6180
6241
|
console.log(
|
|
6181
|
-
` ${
|
|
6242
|
+
` ${import_picocolors9.default.yellow("!")} ${import_picocolors9.default.bold(ABLO_PUBLICATION)} already publishes a different set; reconciling to your ${import_picocolors9.default.bold("--tables")}:`
|
|
6182
6243
|
);
|
|
6183
|
-
for (const t of pubReconcile.added) console.log(` ${
|
|
6244
|
+
for (const t of pubReconcile.added) console.log(` ${import_picocolors9.default.green("+")} ${t}`);
|
|
6184
6245
|
for (const t of pubReconcile.removed)
|
|
6185
|
-
console.log(` ${
|
|
6246
|
+
console.log(` ${import_picocolors9.default.red("-")} ${t} ${import_picocolors9.default.dim("(stops replicating to Ablo)")}`);
|
|
6186
6247
|
if (pubReconcile.recreated && existingPublication.allTables)
|
|
6187
|
-
console.log(` ${
|
|
6248
|
+
console.log(` ${import_picocolors9.default.red("-")} ${import_picocolors9.default.dim("every other table (was FOR ALL TABLES)")}`);
|
|
6188
6249
|
console.log();
|
|
6189
6250
|
}
|
|
6190
6251
|
printPlan2(steps, args.showSql);
|
|
@@ -6192,7 +6253,7 @@ async function runConnectApply(args) {
|
|
|
6192
6253
|
if (!process.stdout.isTTY) {
|
|
6193
6254
|
await admin.end({ timeout: 2 });
|
|
6194
6255
|
console.error(
|
|
6195
|
-
|
|
6256
|
+
import_picocolors9.default.dim(` Re-run with ${import_picocolors9.default.bold("--yes")} to apply this in a non-interactive session.
|
|
6196
6257
|
`)
|
|
6197
6258
|
);
|
|
6198
6259
|
process.exit(1);
|
|
@@ -6204,7 +6265,7 @@ async function runConnectApply(args) {
|
|
|
6204
6265
|
if (pD(proceed) || !proceed) {
|
|
6205
6266
|
await admin.end({ timeout: 2 });
|
|
6206
6267
|
console.log(
|
|
6207
|
-
|
|
6268
|
+
import_picocolors9.default.dim(` Nothing applied. Run ${import_picocolors9.default.bold("ablo connect")} to see the manual recipe.
|
|
6208
6269
|
`)
|
|
6209
6270
|
);
|
|
6210
6271
|
process.exit(0);
|
|
@@ -6216,8 +6277,8 @@ async function runConnectApply(args) {
|
|
|
6216
6277
|
await admin.end({ timeout: 2 }).catch(() => void 0);
|
|
6217
6278
|
const pg = err ?? {};
|
|
6218
6279
|
console.error(
|
|
6219
|
-
|
|
6220
|
-
Setup stopped: ${pg.message ?? String(err)}`) +
|
|
6280
|
+
import_picocolors9.default.red(`
|
|
6281
|
+
Setup stopped: ${pg.message ?? String(err)}`) + import_picocolors9.default.dim(` Every step is safe to re-run.
|
|
6221
6282
|
`)
|
|
6222
6283
|
);
|
|
6223
6284
|
process.exit(1);
|
|
@@ -6228,14 +6289,14 @@ async function runConnectApply(args) {
|
|
|
6228
6289
|
const replicationUrl = rewriteDatabaseUrl(adminUrl, role, replicationPassword);
|
|
6229
6290
|
const writeUrl = rewriteDatabaseUrl(adminUrl, writeRole, writePassword);
|
|
6230
6291
|
console.log(`
|
|
6231
|
-
${
|
|
6292
|
+
${import_picocolors9.default.green("\u2713")} Roles ${rotating ? "re-keyed" : "created"}.
|
|
6232
6293
|
`);
|
|
6233
6294
|
if (!walReady) {
|
|
6234
6295
|
console.error(
|
|
6235
|
-
` ${
|
|
6296
|
+
` ${import_picocolors9.default.yellow("!")} One step left \u2014 logical replication isn't on yet.
|
|
6236
6297
|
${logicalReplicationGuidance(provider)}.
|
|
6237
6298
|
|
|
6238
|
-
Then re-run: ${
|
|
6299
|
+
Then re-run: ${import_picocolors9.default.cyan(`npx ablo ${verb}`)}
|
|
6239
6300
|
`
|
|
6240
6301
|
);
|
|
6241
6302
|
process.exit(1);
|
|
@@ -6252,15 +6313,15 @@ async function runConnectApply(args) {
|
|
|
6252
6313
|
items = await probeReadiness(verifier);
|
|
6253
6314
|
} catch {
|
|
6254
6315
|
items = null;
|
|
6255
|
-
console.log(
|
|
6316
|
+
console.log(import_picocolors9.default.dim(` Couldn't verify from here; Ablo will validate from its own network.
|
|
6256
6317
|
`));
|
|
6257
6318
|
}
|
|
6258
6319
|
await verifier.end({ timeout: 2 }).catch(() => void 0);
|
|
6259
6320
|
const failed = items?.filter((i) => !i.ok) ?? [];
|
|
6260
6321
|
if (failed.length > 0) {
|
|
6261
|
-
for (const item of failed) console.log(` ${
|
|
6322
|
+
for (const item of failed) console.log(` ${import_picocolors9.default.yellow("!")} ${item.label}`);
|
|
6262
6323
|
console.log(`
|
|
6263
|
-
${
|
|
6324
|
+
${import_picocolors9.default.dim("Resolve, then re-run")} ${import_picocolors9.default.cyan(`npx ablo ${verb}`)}
|
|
6264
6325
|
`);
|
|
6265
6326
|
process.exit(1);
|
|
6266
6327
|
}
|
|
@@ -6275,20 +6336,21 @@ async function runConnectApply(args) {
|
|
|
6275
6336
|
const outcome = postRegistrationOutcome({ rotating, registered });
|
|
6276
6337
|
if (outcome.notice) {
|
|
6277
6338
|
console.error(`
|
|
6278
|
-
${
|
|
6339
|
+
${import_picocolors9.default.red(outcome.notice.split("\n").join("\n "))}
|
|
6279
6340
|
`);
|
|
6280
6341
|
}
|
|
6281
6342
|
process.exit(outcome.exitCode);
|
|
6282
6343
|
}
|
|
6283
|
-
var
|
|
6344
|
+
var import_picocolors9;
|
|
6284
6345
|
var init_connectApply = __esm({
|
|
6285
6346
|
"src/cli/connectApply.ts"() {
|
|
6286
6347
|
"use strict";
|
|
6287
6348
|
init_cjs_shims();
|
|
6288
|
-
|
|
6349
|
+
import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
6289
6350
|
init_src();
|
|
6290
6351
|
init_dist2();
|
|
6291
6352
|
init_connectSetup();
|
|
6353
|
+
init_connectOwnership();
|
|
6292
6354
|
init_dbRole();
|
|
6293
6355
|
init_config();
|
|
6294
6356
|
init_push();
|
|
@@ -218313,11 +218375,11 @@ var require_commonjs2 = __commonJS({
|
|
|
218313
218375
|
if (pad) {
|
|
218314
218376
|
const need = width - c.length;
|
|
218315
218377
|
if (need > 0) {
|
|
218316
|
-
const
|
|
218378
|
+
const z9 = new Array(need + 1).join("0");
|
|
218317
218379
|
if (i < 0) {
|
|
218318
|
-
c = "-" +
|
|
218380
|
+
c = "-" + z9 + c.slice(1);
|
|
218319
218381
|
} else {
|
|
218320
|
-
c =
|
|
218382
|
+
c = z9 + c;
|
|
218321
218383
|
}
|
|
218322
218384
|
}
|
|
218323
218385
|
}
|
|
@@ -282246,7 +282308,7 @@ Node text: ${this.#forgottenText}`;
|
|
|
282246
282308
|
// src/cli/index.ts
|
|
282247
282309
|
init_cjs_shims();
|
|
282248
282310
|
init_dist2();
|
|
282249
|
-
var
|
|
282311
|
+
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
282250
282312
|
var import_fs12 = require("fs");
|
|
282251
282313
|
var import_path7 = require("path");
|
|
282252
282314
|
var import_child_process3 = require("child_process");
|
|
@@ -282437,7 +282499,7 @@ async function migrate(argv) {
|
|
|
282437
282499
|
// src/cli/connect.ts
|
|
282438
282500
|
init_cjs_shims();
|
|
282439
282501
|
init_errors();
|
|
282440
|
-
var
|
|
282502
|
+
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
282441
282503
|
init_src();
|
|
282442
282504
|
init_dbRole();
|
|
282443
282505
|
init_config();
|
|
@@ -282669,7 +282731,7 @@ function printConnectRecipe(args) {
|
|
|
282669
282731
|
});
|
|
282670
282732
|
console.log(
|
|
282671
282733
|
`
|
|
282672
|
-
${brand("ablo")} ${
|
|
282734
|
+
${brand("ablo")} ${import_picocolors10.default.dim("connect")} ${import_picocolors10.default.dim("direct writes + WAL-settled sync")}
|
|
282673
282735
|
`
|
|
282674
282736
|
);
|
|
282675
282737
|
console.log(
|
|
@@ -282677,29 +282739,29 @@ function printConnectRecipe(args) {
|
|
|
282677
282739
|
observes what committed, orders it with external writes, and confirms it in the sync log.
|
|
282678
282740
|
Your database stays authoritative; Ablo never owns or migrates your application tables.
|
|
282679
282741
|
|
|
282680
|
-
${
|
|
282681
|
-
connection and leaves your app holding only ${
|
|
282682
|
-
run this once against your Postgres ${
|
|
282742
|
+
${import_picocolors10.default.bold("ablo connect apply")} runs every step below for you from a one-time admin
|
|
282743
|
+
connection and leaves your app holding only ${import_picocolors10.default.bold("ABLO_API_KEY")}. To do it by hand,
|
|
282744
|
+
run this once against your Postgres ${import_picocolors10.default.dim("(as a superuser / DB owner)")}:
|
|
282683
282745
|
`
|
|
282684
282746
|
);
|
|
282685
282747
|
console.log(
|
|
282686
|
-
` ${
|
|
282748
|
+
` ${import_picocolors10.default.bold("1.")} Enable logical decoding ${import_picocolors10.default.dim("(then RESTART Postgres \u2014 wal_level is not reloadable)")}`
|
|
282687
282749
|
);
|
|
282688
|
-
console.log(` ${
|
|
282750
|
+
console.log(` ${import_picocolors10.default.cyan(sql[0])}`);
|
|
282689
282751
|
console.log(
|
|
282690
|
-
|
|
282691
|
-
` Managed hosts don't take ALTER SYSTEM: on Amazon RDS / Aurora set ${
|
|
282752
|
+
import_picocolors10.default.dim(
|
|
282753
|
+
` Managed hosts don't take ALTER SYSTEM: on Amazon RDS / Aurora set ${import_picocolors10.default.bold("rds.logical_replication = 1")}
|
|
282692
282754
|
in the parameter group and reboot; on Neon or Supabase enable logical replication in the
|
|
282693
|
-
project settings. ${
|
|
282755
|
+
project settings. ${import_picocolors10.default.bold("ablo connect apply")} detects the host and does the right thing.`
|
|
282694
282756
|
)
|
|
282695
282757
|
);
|
|
282696
282758
|
console.log(`
|
|
282697
|
-
${
|
|
282698
|
-
console.log(` ${
|
|
282759
|
+
${import_picocolors10.default.bold("2.")} Publish the tables Ablo should read`);
|
|
282760
|
+
console.log(` ${import_picocolors10.default.cyan(sql[1])}`);
|
|
282699
282761
|
if (args.tables.length === 0) {
|
|
282700
282762
|
console.log(
|
|
282701
|
-
|
|
282702
|
-
` (Scope it with ${
|
|
282763
|
+
import_picocolors10.default.dim(
|
|
282764
|
+
` (Scope it with ${import_picocolors10.default.bold("ablo connect --tables a,b,c")} to publish a subset.)`
|
|
282703
282765
|
)
|
|
282704
282766
|
);
|
|
282705
282767
|
}
|
|
@@ -282708,52 +282770,52 @@ function printConnectRecipe(args) {
|
|
|
282708
282770
|
const writerStatements = sql.slice(writerStart);
|
|
282709
282771
|
console.log(
|
|
282710
282772
|
`
|
|
282711
|
-
${
|
|
282773
|
+
${import_picocolors10.default.bold("3.")} Create the scoped replication role ${import_picocolors10.default.dim("(pick your own replication password)")}`
|
|
282712
282774
|
);
|
|
282713
282775
|
for (const statement of replicationStatements) {
|
|
282714
|
-
for (const line of statement.split("\n")) console.log(` ${
|
|
282776
|
+
for (const line of statement.split("\n")) console.log(` ${import_picocolors10.default.cyan(line)}`);
|
|
282715
282777
|
}
|
|
282716
282778
|
console.log(
|
|
282717
|
-
|
|
282779
|
+
import_picocolors10.default.dim(
|
|
282718
282780
|
` On Amazon RDS, the REPLICATION attribute is granted, not set directly:
|
|
282719
|
-
${
|
|
282781
|
+
${import_picocolors10.default.bold(`GRANT rds_replication TO ${quoteIdent(args.role)};`)}`
|
|
282720
282782
|
)
|
|
282721
282783
|
);
|
|
282722
282784
|
console.log(
|
|
282723
282785
|
`
|
|
282724
|
-
${
|
|
282786
|
+
${import_picocolors10.default.bold("4.")} Create the separate DML role and the idempotency ledger ` + import_picocolors10.default.dim("(pick your own write password)")
|
|
282725
282787
|
);
|
|
282726
282788
|
for (const statement of writerStatements) {
|
|
282727
|
-
for (const line of statement.split("\n")) console.log(` ${
|
|
282789
|
+
for (const line of statement.split("\n")) console.log(` ${import_picocolors10.default.cyan(line)}`);
|
|
282728
282790
|
}
|
|
282729
282791
|
console.log(
|
|
282730
|
-
|
|
282792
|
+
import_picocolors10.default.dim(
|
|
282731
282793
|
` The writer gets row DML + ledger access only. It has no REPLICATION, schema CREATE,
|
|
282732
282794
|
role administration, database creation, ownership, or customer-table DDL. Direct uses
|
|
282733
|
-
${
|
|
282734
|
-
Each ledger row carries an ${
|
|
282795
|
+
${import_picocolors10.default.bold("ablo_idempotency")} but no outbox; WAL carries the committed row changes.
|
|
282796
|
+
Each ledger row carries an ${import_picocolors10.default.bold("expires_at")}; the writer can't DELETE (tamper-
|
|
282735
282797
|
resistance), so prune it from your own admin/cron when convenient:
|
|
282736
|
-
${
|
|
282798
|
+
${import_picocolors10.default.bold("DELETE FROM ablo_idempotency WHERE expires_at < now();")}`
|
|
282737
282799
|
)
|
|
282738
282800
|
);
|
|
282739
282801
|
console.log(
|
|
282740
282802
|
`
|
|
282741
|
-
${
|
|
282742
|
-
Ablo holds them from here, so your app keeps only ${
|
|
282743
|
-
${
|
|
282744
|
-
${
|
|
282745
|
-
${
|
|
282803
|
+
${import_picocolors10.default.bold("5.")} Register the two roles with Ablo. Set them just long enough to register \u2014
|
|
282804
|
+
Ablo holds them from here, so your app keeps only ${import_picocolors10.default.bold("ABLO_API_KEY")}:
|
|
282805
|
+
${import_picocolors10.default.bold("ABLO_REPLICATION_DATABASE_URL")} ${import_picocolors10.default.dim(`\u2192 ${args.role} (replication only)`)}
|
|
282806
|
+
${import_picocolors10.default.bold("ABLO_WRITE_DATABASE_URL")} ${import_picocolors10.default.dim(`\u2192 ${args.writeRole} (DML only)`)}
|
|
282807
|
+
${import_picocolors10.default.cyan("npx ablo connect register")}
|
|
282746
282808
|
`
|
|
282747
282809
|
);
|
|
282748
282810
|
console.log(
|
|
282749
|
-
` ${
|
|
282750
|
-
${
|
|
282811
|
+
` ${import_picocolors10.default.bold("6.")} Verify readiness ${import_picocolors10.default.dim("(checked from Ablo\u2019s side \u2014 needs only ABLO_API_KEY):")}
|
|
282812
|
+
${import_picocolors10.default.cyan("npx ablo connect check")}
|
|
282751
282813
|
`
|
|
282752
282814
|
);
|
|
282753
282815
|
console.log(
|
|
282754
|
-
|
|
282816
|
+
import_picocolors10.default.dim(
|
|
282755
282817
|
` Reachable databases use this direct path. If no inbound route can be established, use the
|
|
282756
|
-
signed ${
|
|
282818
|
+
signed ${import_picocolors10.default.bold("dataSource()")} endpoint fallback; its correlated events confirm writes
|
|
282757
282819
|
without an Ablo-side customer database socket.`
|
|
282758
282820
|
)
|
|
282759
282821
|
);
|
|
@@ -282768,11 +282830,11 @@ var SYNC_INFRA_RELATIONS = [
|
|
|
282768
282830
|
var SYNC_INFRA_TYPES = ["participant_kind", "backfill_provenance", "confirmation_state"];
|
|
282769
282831
|
function printCheckItem(item) {
|
|
282770
282832
|
if (item.ok) {
|
|
282771
|
-
console.log(` ${
|
|
282833
|
+
console.log(` ${import_picocolors10.default.green("\u2713")} ${item.label}`);
|
|
282772
282834
|
} else {
|
|
282773
|
-
console.log(` ${
|
|
282835
|
+
console.log(` ${import_picocolors10.default.red("\u2717")} ${item.label}`);
|
|
282774
282836
|
if (item.fix) {
|
|
282775
|
-
for (const line of item.fix.split("\n")) console.log(` ${
|
|
282837
|
+
for (const line of item.fix.split("\n")) console.log(` ${import_picocolors10.default.red("\u2022")} ${line}`);
|
|
282776
282838
|
}
|
|
282777
282839
|
}
|
|
282778
282840
|
}
|
|
@@ -282793,7 +282855,7 @@ async function probeDirectWriteReadiness(sql, opts = {}) {
|
|
|
282793
282855
|
ok: false,
|
|
282794
282856
|
label: `ABLO_WRITE_DATABASE_URL is not a scoped DML role`,
|
|
282795
282857
|
fix: `Use ${ABLO_WRITE_ROLE} from the setup recipe: NOSUPERUSER, NOBYPASSRLS, NOCREATEDB, NOCREATEROLE, NOREPLICATION.`
|
|
282796
|
-
} : { ok: true, label: `write role ${
|
|
282858
|
+
} : { ok: true, label: `write role ${import_picocolors10.default.bold(role?.rolname ?? ABLO_WRITE_ROLE)} is DML-only` }
|
|
282797
282859
|
);
|
|
282798
282860
|
const schemaRows = await sql.unsafe(
|
|
282799
282861
|
`SELECT
|
|
@@ -282820,10 +282882,10 @@ async function probeDirectWriteReadiness(sql, opts = {}) {
|
|
|
282820
282882
|
);
|
|
282821
282883
|
const ledger = ledgerRows[0];
|
|
282822
282884
|
items.push(
|
|
282823
|
-
ledger?.present && ledger.writes && !ledger.deletes ? { ok: true, label: `${
|
|
282885
|
+
ledger?.present && ledger.writes && !ledger.deletes ? { ok: true, label: `${import_picocolors10.default.bold("ablo_idempotency")} is durable and protected from DELETE` } : {
|
|
282824
282886
|
ok: false,
|
|
282825
|
-
label: `${
|
|
282826
|
-
fix: `Apply the DML-role and idempotency-ledger statements printed by ${
|
|
282887
|
+
label: `${import_picocolors10.default.bold("ablo_idempotency")} is missing or has unsafe grants`,
|
|
282888
|
+
fix: `Apply the DML-role and idempotency-ledger statements printed by ${import_picocolors10.default.bold("ablo connect")}.`
|
|
282827
282889
|
}
|
|
282828
282890
|
);
|
|
282829
282891
|
const tableRows = await sql.unsafe(
|
|
@@ -282912,8 +282974,8 @@ function requireScopedUrl(kind, verb) {
|
|
|
282912
282974
|
const resolved = readProjectReplicationUrlWithSource();
|
|
282913
282975
|
if (resolved) return resolved.url;
|
|
282914
282976
|
console.error(
|
|
282915
|
-
|
|
282916
|
-
` Set ${
|
|
282977
|
+
import_picocolors10.default.red(" No replication connection found (checked process env, .env.local, .env).") + import_picocolors10.default.dim(
|
|
282978
|
+
` Set ${import_picocolors10.default.bold("ABLO_REPLICATION_DATABASE_URL")} to the ${ABLO_REPLICATION_ROLE} connection printed by ${import_picocolors10.default.bold("ablo connect")}, then re-run ${import_picocolors10.default.bold(`ablo connect ${verb}`)}.`
|
|
282917
282979
|
)
|
|
282918
282980
|
);
|
|
282919
282981
|
process.exit(1);
|
|
@@ -282921,8 +282983,8 @@ function requireScopedUrl(kind, verb) {
|
|
|
282921
282983
|
const dbUrl = readProjectWriteDatabaseUrl();
|
|
282922
282984
|
if (dbUrl) return dbUrl;
|
|
282923
282985
|
console.error(
|
|
282924
|
-
|
|
282925
|
-
` Set it to the ${ABLO_WRITE_ROLE} connection printed by ${
|
|
282986
|
+
import_picocolors10.default.red(" No ABLO_WRITE_DATABASE_URL found (checked process env, .env.local, .env).") + import_picocolors10.default.dim(
|
|
282987
|
+
` Set it to the ${ABLO_WRITE_ROLE} connection printed by ${import_picocolors10.default.bold("ablo connect")}, then re-run ${import_picocolors10.default.bold(`ablo connect ${verb}`)}. The replication credential is never reused for DML.`
|
|
282926
282988
|
)
|
|
282927
282989
|
);
|
|
282928
282990
|
process.exit(1);
|
|
@@ -282938,7 +283000,7 @@ async function probeAndReport(dbUrl, kind = "replication") {
|
|
|
282938
283000
|
const dial = dialFailureReason(err);
|
|
282939
283001
|
if (dial) return { kind: "no-dial", reason: dial };
|
|
282940
283002
|
const pg = err ?? {};
|
|
282941
|
-
console.error(
|
|
283003
|
+
console.error(import_picocolors10.default.red(` Couldn't read the database: ${pg.message ?? String(err)}`));
|
|
282942
283004
|
process.exit(1);
|
|
282943
283005
|
}
|
|
282944
283006
|
await sql.end({ timeout: 2 });
|
|
@@ -282948,14 +283010,14 @@ async function probeAndReport(dbUrl, kind = "replication") {
|
|
|
282948
283010
|
async function runCheck() {
|
|
282949
283011
|
console.log(
|
|
282950
283012
|
`
|
|
282951
|
-
${brand("ablo")} ${
|
|
283013
|
+
${brand("ablo")} ${import_picocolors10.default.dim("connect check")} ${import_picocolors10.default.dim("direct-write + WAL readiness")}
|
|
282952
283014
|
`
|
|
282953
283015
|
);
|
|
282954
283016
|
const apiKey = resolveApiKey();
|
|
282955
283017
|
if (!apiKey) {
|
|
282956
283018
|
console.error(
|
|
282957
|
-
|
|
282958
|
-
` Run ${
|
|
283019
|
+
import_picocolors10.default.red(" No API key found.") + import_picocolors10.default.dim(
|
|
283020
|
+
` Run ${import_picocolors10.default.bold("ablo login")} (or set ${import_picocolors10.default.bold("ABLO_API_KEY")}), then re-run ${import_picocolors10.default.bold("ablo connect check")}.`
|
|
282959
283021
|
)
|
|
282960
283022
|
);
|
|
282961
283023
|
process.exit(1);
|
|
@@ -282965,19 +283027,19 @@ async function runCheck() {
|
|
|
282965
283027
|
if (!result.ok) {
|
|
282966
283028
|
if (result.code === "no_data_source_registered") {
|
|
282967
283029
|
console.error(
|
|
282968
|
-
` ${
|
|
282969
|
-
` +
|
|
282970
|
-
` Connect one with ${
|
|
283030
|
+
` ${import_picocolors10.default.yellow("\u2014")} No database is connected to this plane yet, so there's nothing to check.
|
|
283031
|
+
` + import_picocolors10.default.dim(
|
|
283032
|
+
` Connect one with ${import_picocolors10.default.bold("ablo connect apply")}, then re-run ${import_picocolors10.default.bold("ablo connect check")}.
|
|
282971
283033
|
`
|
|
282972
283034
|
)
|
|
282973
283035
|
);
|
|
282974
283036
|
process.exit(1);
|
|
282975
283037
|
}
|
|
282976
|
-
console.error(
|
|
283038
|
+
console.error(import_picocolors10.default.red(` The check failed: ${result.message}`));
|
|
282977
283039
|
if (result.code === "forbidden") {
|
|
282978
283040
|
console.error(
|
|
282979
|
-
|
|
282980
|
-
` Checking a connected database needs a ${
|
|
283041
|
+
import_picocolors10.default.dim(
|
|
283042
|
+
` Checking a connected database needs a ${import_picocolors10.default.bold("secret")} key (sk_\u2026). Run ${import_picocolors10.default.bold("ablo login")} for one.`
|
|
282981
283043
|
)
|
|
282982
283044
|
);
|
|
282983
283045
|
}
|
|
@@ -282986,12 +283048,12 @@ async function runCheck() {
|
|
|
282986
283048
|
}
|
|
282987
283049
|
if (!result.reachable) {
|
|
282988
283050
|
console.error(
|
|
282989
|
-
` ${
|
|
283051
|
+
` ${import_picocolors10.default.red("\u2717")} Ablo's infrastructure can't reach your database${result.reason ? ` ${import_picocolors10.default.dim(`(${result.reason})`)}` : ""}.`
|
|
282990
283052
|
);
|
|
282991
283053
|
console.error(
|
|
282992
|
-
|
|
283054
|
+
import_picocolors10.default.dim(
|
|
282993
283055
|
` Direct needs a route Ablo's servers can dial \u2014 public allowlist, PrivateLink, peering,
|
|
282994
|
-
or VPN. Only when no inbound route can exist, use the signed ${
|
|
283056
|
+
or VPN. Only when no inbound route can exist, use the signed ${import_picocolors10.default.bold("dataSource()")} endpoint fallback.
|
|
282995
283057
|
`
|
|
282996
283058
|
)
|
|
282997
283059
|
);
|
|
@@ -283004,14 +283066,14 @@ async function runCheck() {
|
|
|
283004
283066
|
console.log();
|
|
283005
283067
|
if (result.ready) {
|
|
283006
283068
|
console.log(
|
|
283007
|
-
` ${
|
|
283069
|
+
` ${import_picocolors10.default.green("\u2713")} Ready \u2014 checked from Ablo's infrastructure. Ablo can apply scoped DML and settle it from WAL.
|
|
283008
283070
|
`
|
|
283009
283071
|
);
|
|
283010
283072
|
process.exit(0);
|
|
283011
283073
|
}
|
|
283012
283074
|
const count = result.failures.length;
|
|
283013
283075
|
console.log(
|
|
283014
|
-
` ${
|
|
283076
|
+
` ${import_picocolors10.default.red(`${count} item${count === 1 ? "" : "s"} to fix`)} ${import_picocolors10.default.dim(`\u2014 apply the fixes above, then re-run ${import_picocolors10.default.bold("ablo connect check")}.`)}
|
|
283015
283077
|
`
|
|
283016
283078
|
);
|
|
283017
283079
|
process.exit(1);
|
|
@@ -283022,22 +283084,22 @@ async function runRegister(args) {
|
|
|
283022
283084
|
const apiKey = resolveApiKey();
|
|
283023
283085
|
if (!apiKey) {
|
|
283024
283086
|
console.error(
|
|
283025
|
-
|
|
283026
|
-
` Run ${
|
|
283087
|
+
import_picocolors10.default.red(" Not logged in.") + import_picocolors10.default.dim(
|
|
283088
|
+
` Run ${import_picocolors10.default.bold("ablo login")} (or set ${import_picocolors10.default.bold("ABLO_API_KEY")}) so Ablo knows which project to register this database for.`
|
|
283027
283089
|
)
|
|
283028
283090
|
);
|
|
283029
283091
|
process.exit(1);
|
|
283030
283092
|
}
|
|
283031
283093
|
console.log(
|
|
283032
283094
|
`
|
|
283033
|
-
${brand("ablo")} ${
|
|
283095
|
+
${brand("ablo")} ${import_picocolors10.default.dim("connect register")} ${import_picocolors10.default.dim("register a direct DataSource")}
|
|
283034
283096
|
`
|
|
283035
283097
|
);
|
|
283036
|
-
console.log(` ${
|
|
283098
|
+
console.log(` ${import_picocolors10.default.bold("Replication role")}
|
|
283037
283099
|
`);
|
|
283038
283100
|
const replication = await probeAndReport(dbUrl, "replication");
|
|
283039
283101
|
console.log(`
|
|
283040
|
-
${
|
|
283102
|
+
${import_picocolors10.default.bold("Direct-write role")}
|
|
283041
283103
|
`);
|
|
283042
283104
|
const write = await probeAndReport(writeDbUrl, "write");
|
|
283043
283105
|
const noDial = [
|
|
@@ -283047,7 +283109,7 @@ async function runRegister(args) {
|
|
|
283047
283109
|
const failures = (replication.kind === "probed" ? replication.failures : 0) + (write.kind === "probed" ? write.failures : 0);
|
|
283048
283110
|
if (noDial.length > 0) {
|
|
283049
283111
|
console.log(
|
|
283050
|
-
` This machine can't reach one or both scoped connections (${
|
|
283112
|
+
` This machine can't reach one or both scoped connections (${import_picocolors10.default.dim(noDial.join("; "))}) \u2014 continuing anyway.
|
|
283051
283113
|
Ablo validates both credentials from the infrastructure that will use them and refuses
|
|
283052
283114
|
registration unless replication and direct DML are both ready.
|
|
283053
283115
|
`
|
|
@@ -283056,7 +283118,7 @@ async function runRegister(args) {
|
|
|
283056
283118
|
if (failures > 0) {
|
|
283057
283119
|
console.log(
|
|
283058
283120
|
`
|
|
283059
|
-
${
|
|
283121
|
+
${import_picocolors10.default.red(`${failures} item${failures === 1 ? "" : "s"} to fix`)} ${import_picocolors10.default.dim("\u2014 direct registration requires both scoped roles. Fix the above, then re-run.")}
|
|
283060
283122
|
`
|
|
283061
283123
|
);
|
|
283062
283124
|
process.exit(1);
|
|
@@ -283080,31 +283142,31 @@ async function runScan() {
|
|
|
283080
283142
|
artifacts = await auditTenantSyncInfra(sql);
|
|
283081
283143
|
} catch (err) {
|
|
283082
283144
|
const pg = err ?? {};
|
|
283083
|
-
console.error(
|
|
283145
|
+
console.error(import_picocolors10.default.red(` Couldn't audit the database: ${pg.message ?? String(err)}`));
|
|
283084
283146
|
await sql.end({ timeout: 2 });
|
|
283085
283147
|
process.exit(1);
|
|
283086
283148
|
}
|
|
283087
283149
|
await sql.end({ timeout: 2 });
|
|
283088
283150
|
console.log(
|
|
283089
283151
|
`
|
|
283090
|
-
${brand("ablo")} ${
|
|
283152
|
+
${brand("ablo")} ${import_picocolors10.default.dim("connect scan")} ${import_picocolors10.default.dim("audit for leftover Ablo sync infrastructure")}
|
|
283091
283153
|
`
|
|
283092
283154
|
);
|
|
283093
283155
|
const present = artifacts.filter((a) => a.present);
|
|
283094
283156
|
if (present.length === 0) {
|
|
283095
|
-
console.log(` ${
|
|
283157
|
+
console.log(` ${import_picocolors10.default.green("\u2713")} No deprecated Ablo sync infrastructure found in public.
|
|
283096
283158
|
`);
|
|
283097
283159
|
process.exit(0);
|
|
283098
283160
|
}
|
|
283099
283161
|
for (const artifact of present) {
|
|
283100
283162
|
const label = artifact.kind === "type" ? "type" : "relation";
|
|
283101
|
-
console.log(` ${
|
|
283163
|
+
console.log(` ${import_picocolors10.default.yellow("!")} ${label} ${import_picocolors10.default.bold(`public.${artifact.name}`)} exists`);
|
|
283102
283164
|
}
|
|
283103
283165
|
console.log(
|
|
283104
283166
|
`
|
|
283105
|
-
${
|
|
283167
|
+
${import_picocolors10.default.yellow(`${present.length} artifact${present.length === 1 ? "" : "s"} found`)} ` + import_picocolors10.default.dim(
|
|
283106
283168
|
"\u2014 do not drop automatically. Confirm the org/environment is log-authoritative, then follow "
|
|
283107
|
-
) +
|
|
283169
|
+
) + import_picocolors10.default.bold("docs/runbooks/wal-stage5-customer-db-infra-cleanup.md") + import_picocolors10.default.dim(".\n")
|
|
283108
283170
|
);
|
|
283109
283171
|
process.exit(1);
|
|
283110
283172
|
}
|
|
@@ -283118,7 +283180,7 @@ async function connect(argv) {
|
|
|
283118
283180
|
try {
|
|
283119
283181
|
args = parseConnectArgs(argv);
|
|
283120
283182
|
} catch (err) {
|
|
283121
|
-
console.error(
|
|
283183
|
+
console.error(import_picocolors10.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
283122
283184
|
process.exit(1);
|
|
283123
283185
|
}
|
|
283124
283186
|
if (args.apply || args.rotate) {
|
|
@@ -283174,7 +283236,7 @@ init_cjs_shims();
|
|
|
283174
283236
|
init_errors();
|
|
283175
283237
|
var import_fs6 = require("fs");
|
|
283176
283238
|
var import_path4 = require("path");
|
|
283177
|
-
var
|
|
283239
|
+
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
283178
283240
|
var import_schema4 = require("@abloatai/ablo/schema");
|
|
283179
283241
|
init_push();
|
|
283180
283242
|
var DEFAULT_SCHEMA_PATH3 = "ablo/schema.ts";
|
|
@@ -283207,7 +283269,7 @@ async function generate(argv) {
|
|
|
283207
283269
|
try {
|
|
283208
283270
|
args = parseGenerateArgs(argv);
|
|
283209
283271
|
} catch (err) {
|
|
283210
|
-
console.error(
|
|
283272
|
+
console.error(import_picocolors11.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
283211
283273
|
process.exit(1);
|
|
283212
283274
|
}
|
|
283213
283275
|
let source;
|
|
@@ -283216,20 +283278,20 @@ async function generate(argv) {
|
|
|
283216
283278
|
const schemaJson = JSON.parse((0, import_schema4.serializeSchema)(schema));
|
|
283217
283279
|
source = (0, import_schema4.generateTypes)(schemaJson);
|
|
283218
283280
|
} catch (err) {
|
|
283219
|
-
console.error(
|
|
283281
|
+
console.error(import_picocolors11.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
283220
283282
|
process.exit(1);
|
|
283221
283283
|
}
|
|
283222
283284
|
const abs = (0, import_path4.resolve)(process.cwd(), args.out);
|
|
283223
283285
|
(0, import_fs6.mkdirSync)((0, import_path4.dirname)(abs), { recursive: true });
|
|
283224
283286
|
(0, import_fs6.writeFileSync)(abs, source);
|
|
283225
|
-
console.log(` ${
|
|
283287
|
+
console.log(` ${import_picocolors11.default.green("\u2713")} Generated types \u2192 ${import_picocolors11.default.bold(args.out)}`);
|
|
283226
283288
|
}
|
|
283227
283289
|
|
|
283228
283290
|
// src/cli/dev.ts
|
|
283229
283291
|
init_cjs_shims();
|
|
283230
283292
|
init_errors();
|
|
283231
283293
|
init_credentialPolicy();
|
|
283232
|
-
var
|
|
283294
|
+
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
283233
283295
|
init_dist2();
|
|
283234
283296
|
var import_fs7 = require("fs");
|
|
283235
283297
|
var import_path5 = require("path");
|
|
@@ -283271,25 +283333,25 @@ function classifyKey(apiKey, activeMode) {
|
|
|
283271
283333
|
if (!apiKey) {
|
|
283272
283334
|
return {
|
|
283273
283335
|
ok: false,
|
|
283274
|
-
reason: `No API key. Run ${
|
|
283336
|
+
reason: `No API key. Run ${import_picocolors12.default.bold("npx ablo login")} for the sandbox dev loop \u2014 or set ${import_picocolors12.default.bold("ABLO_API_KEY")} (${import_picocolors12.default.bold("sk_test_")} = sandbox; ${import_picocolors12.default.bold("sk_live_")} = deliberate production deploy). ` + import_picocolors12.default.dim(`Mode is currently '${activeMode}'.`)
|
|
283275
283337
|
};
|
|
283276
283338
|
}
|
|
283277
283339
|
if (apiKey.startsWith("sk_test_")) return { ok: true };
|
|
283278
283340
|
if (apiKey.startsWith("sk_live_")) {
|
|
283279
283341
|
return {
|
|
283280
283342
|
ok: false,
|
|
283281
|
-
reason: `Production schema deploys run one-shot: ${
|
|
283343
|
+
reason: `Production schema deploys run one-shot: ${import_picocolors12.default.bold("ABLO_API_KEY=sk_live_\u2026 npx ablo push")} (or ${import_picocolors12.default.bold("ablo mode production")}). ${import_picocolors12.default.bold("--watch")} is sandbox-only.`
|
|
283282
283344
|
};
|
|
283283
283345
|
}
|
|
283284
283346
|
if (classifyCredentialKind(apiKey) === "restricted") {
|
|
283285
283347
|
return {
|
|
283286
283348
|
ok: false,
|
|
283287
|
-
reason: `Restricted (${
|
|
283349
|
+
reason: `Restricted (${import_picocolors12.default.bold("rk_")}) keys can't push schema. Use a secret key: ${import_picocolors12.default.bold("sk_test_")} for the sandbox dev loop, or ${import_picocolors12.default.bold("sk_live_")} with ${import_picocolors12.default.bold("npx ablo push")} for a production deploy.`
|
|
283288
283350
|
};
|
|
283289
283351
|
}
|
|
283290
283352
|
return {
|
|
283291
283353
|
ok: false,
|
|
283292
|
-
reason: `${
|
|
283354
|
+
reason: `${import_picocolors12.default.bold("ABLO_API_KEY")} is not an Ablo key \u2014 expected ${import_picocolors12.default.bold("sk_test_\u2026")} (sandbox) or ${import_picocolors12.default.bold("sk_live_\u2026")} (production deploy via ${import_picocolors12.default.bold("npx ablo push")}).`
|
|
283293
283355
|
};
|
|
283294
283356
|
}
|
|
283295
283357
|
function wireEnvLocal(apiKey, cwd = process.cwd()) {
|
|
@@ -283299,7 +283361,7 @@ function wireEnvLocal(apiKey, cwd = process.cwd()) {
|
|
|
283299
283361
|
if (!(0, import_fs7.existsSync)(envPath)) {
|
|
283300
283362
|
(0, import_fs7.writeFileSync)(envPath, `${line}
|
|
283301
283363
|
`, { mode: 384 });
|
|
283302
|
-
action = `Created ${
|
|
283364
|
+
action = `Created ${import_picocolors12.default.bold(".env.local")} with ${import_picocolors12.default.bold("ABLO_API_KEY")}`;
|
|
283303
283365
|
} else {
|
|
283304
283366
|
const content = (0, import_fs7.readFileSync)(envPath, "utf8");
|
|
283305
283367
|
const match = /^ABLO_API_KEY=(.*)$/m.exec(content);
|
|
@@ -283307,12 +283369,12 @@ function wireEnvLocal(apiKey, cwd = process.cwd()) {
|
|
|
283307
283369
|
if (!match) {
|
|
283308
283370
|
(0, import_fs7.appendFileSync)(envPath, `${content.endsWith("\n") || content.length === 0 ? "" : "\n"}${line}
|
|
283309
283371
|
`);
|
|
283310
|
-
action = `Added ${
|
|
283372
|
+
action = `Added ${import_picocolors12.default.bold("ABLO_API_KEY")} to ${import_picocolors12.default.bold(".env.local")}`;
|
|
283311
283373
|
} else if (existing === apiKey) {
|
|
283312
|
-
action = `${
|
|
283374
|
+
action = `${import_picocolors12.default.bold(".env.local")} already has this key`;
|
|
283313
283375
|
} else {
|
|
283314
283376
|
(0, import_fs7.writeFileSync)(envPath, content.replace(/^ABLO_API_KEY=.*$/m, line));
|
|
283315
|
-
action = `Updated ${
|
|
283377
|
+
action = `Updated ${import_picocolors12.default.bold("ABLO_API_KEY")} in ${import_picocolors12.default.bold(".env.local")} ${import_picocolors12.default.dim(`(was ${existing.slice(0, 12)}\u2026)`)}`;
|
|
283316
283378
|
}
|
|
283317
283379
|
}
|
|
283318
283380
|
const gitignorePath = (0, import_path5.resolve)(cwd, ".gitignore");
|
|
@@ -283326,7 +283388,7 @@ function wireEnvLocal(apiKey, cwd = process.cwd()) {
|
|
|
283326
283388
|
`}.env.local
|
|
283327
283389
|
`
|
|
283328
283390
|
);
|
|
283329
|
-
gitignoreNote = ` Added ${
|
|
283391
|
+
gitignoreNote = ` Added ${import_picocolors12.default.bold(".env.local")} to ${import_picocolors12.default.bold(".gitignore")} so the key can't be committed.`;
|
|
283330
283392
|
}
|
|
283331
283393
|
return `${action}.${gitignoreNote}`;
|
|
283332
283394
|
}
|
|
@@ -283341,7 +283403,7 @@ async function runPush(schema, args) {
|
|
|
283341
283403
|
if (ok) {
|
|
283342
283404
|
return {
|
|
283343
283405
|
ok: true,
|
|
283344
|
-
message: body.unchanged ? `schema unchanged ${
|
|
283406
|
+
message: body.unchanged ? `schema unchanged ${import_picocolors12.default.dim(`(v${body.version})`)}` : `schema pushed (sandbox) ${import_picocolors12.default.dim(`(v${body.version}, hash ${body.hash})`)}`
|
|
283345
283407
|
};
|
|
283346
283408
|
}
|
|
283347
283409
|
if (status2 === 409) {
|
|
@@ -283351,33 +283413,33 @@ async function runPush(schema, args) {
|
|
|
283351
283413
|
(s) => s.shadowed != null
|
|
283352
283414
|
);
|
|
283353
283415
|
const lines = [
|
|
283354
|
-
|
|
283416
|
+
import_picocolors12.default.bold("Incompatible schema change \u2014 not safe to apply as-is."),
|
|
283355
283417
|
"",
|
|
283356
|
-
...unexecutable.map((u2) =>
|
|
283357
|
-
...warnings.map((w2) =>
|
|
283418
|
+
...unexecutable.map((u2) => import_picocolors12.default.red(fmtSignal(u2))),
|
|
283419
|
+
...warnings.map((w2) => import_picocolors12.default.yellow(fmtSignal(w2))),
|
|
283358
283420
|
"",
|
|
283359
283421
|
...hasShadowed ? [
|
|
283360
|
-
|
|
283422
|
+
import_picocolors12.default.dim(
|
|
283361
283423
|
" These models exist in the baseline above but not in your push. Sandbox readers"
|
|
283362
283424
|
),
|
|
283363
|
-
|
|
283425
|
+
import_picocolors12.default.dim(
|
|
283364
283426
|
" fall back to the production schema until you push your own, so applying this drops them."
|
|
283365
283427
|
),
|
|
283366
283428
|
""
|
|
283367
283429
|
] : [],
|
|
283368
|
-
|
|
283369
|
-
` Fix: ${
|
|
283430
|
+
import_picocolors12.default.dim(
|
|
283431
|
+
` Fix: ${import_picocolors12.default.bold("ablo push --force")} to apply anyway, or ${import_picocolors12.default.bold("--rename old:new")} if you renamed a model.`
|
|
283370
283432
|
)
|
|
283371
283433
|
];
|
|
283372
283434
|
return { ok: false, message: lines.join("\n") };
|
|
283373
283435
|
}
|
|
283374
283436
|
if (status2 === 403) {
|
|
283375
283437
|
const serverSays = body.message ?? body.reason;
|
|
283376
|
-
const hint = body.code === "database_role_cannot_enforce_rls" ? `Run ${
|
|
283438
|
+
const hint = body.code === "database_role_cannot_enforce_rls" ? `Run ${import_picocolors12.default.bold("npx ablo migrate")} \u2014 it creates the scoped role for you (your DB credential never leaves this machine).` : `Schema authoring needs a ${import_picocolors12.default.bold("sandbox")} key with ${import_picocolors12.default.bold("schema:push")} \u2014 manage keys at ${import_picocolors12.default.cyan("https://abloatai.com")}.`;
|
|
283377
283439
|
return {
|
|
283378
283440
|
ok: false,
|
|
283379
283441
|
message: `${serverSays ?? "This key can't author schema (missing schema:push scope)."}
|
|
283380
|
-
` +
|
|
283442
|
+
` + import_picocolors12.default.dim(hint)
|
|
283381
283443
|
};
|
|
283382
283444
|
}
|
|
283383
283445
|
return { ok: false, message: `Push failed (${status2}): ${body.message ?? body.reason ?? bodyText}` };
|
|
@@ -283387,25 +283449,25 @@ async function dev(argv) {
|
|
|
283387
283449
|
try {
|
|
283388
283450
|
args = parseDevArgs(argv);
|
|
283389
283451
|
} catch (err) {
|
|
283390
|
-
console.error(
|
|
283452
|
+
console.error(import_picocolors12.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
283391
283453
|
process.exit(1);
|
|
283392
283454
|
}
|
|
283393
283455
|
if (!args.apiKey) args.apiKey = resolveEffectiveApiKey("sandbox").key;
|
|
283394
283456
|
const key = classifyKey(args.apiKey, getMode());
|
|
283395
283457
|
if (!key.ok) {
|
|
283396
|
-
console.error(
|
|
283458
|
+
console.error(import_picocolors12.default.red(` ${key.reason}`));
|
|
283397
283459
|
process.exit(1);
|
|
283398
283460
|
}
|
|
283399
283461
|
console.log(`
|
|
283400
|
-
${brand("ablo")} ${
|
|
283462
|
+
${brand("ablo")} ${import_picocolors12.default.dim("push")} ${import_picocolors12.default.dim("(sandbox)")}
|
|
283401
283463
|
`);
|
|
283402
283464
|
const schema = await loadSchema(args.schemaPath, args.exportName);
|
|
283403
283465
|
const modelCount = Object.keys(schema.models).length;
|
|
283404
283466
|
console.log(
|
|
283405
|
-
` ${
|
|
283467
|
+
` ${import_picocolors12.default.dim("schema")} ${import_picocolors12.default.bold(args.schemaPath)} ${import_picocolors12.default.dim(`(${modelCount} models, hash ${(0, import_schema5.schemaHash)(schema)})`)}`
|
|
283406
283468
|
);
|
|
283407
|
-
console.log(` ${
|
|
283408
|
-
console.log(` ${
|
|
283469
|
+
console.log(` ${import_picocolors12.default.dim("key")} ${args.apiKey.slice(0, 12)}\u2026`);
|
|
283470
|
+
console.log(` ${import_picocolors12.default.dim("api")} ${args.url}
|
|
283409
283471
|
`);
|
|
283410
283472
|
const s = Y2();
|
|
283411
283473
|
s.start("Pushing schema definition (sandbox)");
|
|
@@ -283414,16 +283476,16 @@ async function dev(argv) {
|
|
|
283414
283476
|
if (!first.ok) process.exit(1);
|
|
283415
283477
|
if (process.env.ABLO_API_KEY) {
|
|
283416
283478
|
console.log(`
|
|
283417
|
-
${
|
|
283479
|
+
${import_picocolors12.default.green("\u2713")} ${import_picocolors12.default.bold("ABLO_API_KEY")} is set in this shell \u2014 the SDK reads it directly.`);
|
|
283418
283480
|
} else {
|
|
283419
283481
|
console.log(`
|
|
283420
|
-
${
|
|
283421
|
-
console.log(` ${
|
|
283482
|
+
${import_picocolors12.default.green("\u2713")} ${wireEnvLocal(args.apiKey)}`);
|
|
283483
|
+
console.log(` ${import_picocolors12.default.dim("Frameworks load it automatically; plain Node: node --env-file=.env.local app.ts")}`);
|
|
283422
283484
|
}
|
|
283423
283485
|
console.log(` Your app is wired for the sandbox.`);
|
|
283424
283486
|
if (!args.watch) return;
|
|
283425
283487
|
const abs = (0, import_path5.resolve)(process.cwd(), args.schemaPath);
|
|
283426
|
-
console.log(` ${
|
|
283488
|
+
console.log(` ${import_picocolors12.default.dim(`watching ${args.schemaPath} \u2026 (Ctrl-C to stop)`)}
|
|
283427
283489
|
`);
|
|
283428
283490
|
let timer2 = null;
|
|
283429
283491
|
let pushing = false;
|
|
@@ -283443,7 +283505,7 @@ async function dev(argv) {
|
|
|
283443
283505
|
const r2 = await runPush(next, args);
|
|
283444
283506
|
s2.stop(r2.message, r2.ok ? 0 : 1);
|
|
283445
283507
|
} catch (err) {
|
|
283446
|
-
s2.stop(
|
|
283508
|
+
s2.stop(import_picocolors12.default.red(`schema reload failed: ${err instanceof Error ? err.message : String(err)}`), 1);
|
|
283447
283509
|
} finally {
|
|
283448
283510
|
pushing = false;
|
|
283449
283511
|
}
|
|
@@ -283451,7 +283513,7 @@ async function dev(argv) {
|
|
|
283451
283513
|
const stop = () => {
|
|
283452
283514
|
watcher.close();
|
|
283453
283515
|
console.log(`
|
|
283454
|
-
${
|
|
283516
|
+
${import_picocolors12.default.dim("stopped.")}`);
|
|
283455
283517
|
process.exit(0);
|
|
283456
283518
|
};
|
|
283457
283519
|
process.on("SIGINT", stop);
|
|
@@ -283463,7 +283525,7 @@ async function dev(argv) {
|
|
|
283463
283525
|
// src/cli/login.ts
|
|
283464
283526
|
init_cjs_shims();
|
|
283465
283527
|
var import_child_process2 = require("child_process");
|
|
283466
|
-
var
|
|
283528
|
+
var import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
283467
283529
|
init_dist2();
|
|
283468
283530
|
init_config();
|
|
283469
283531
|
init_theme();
|
|
@@ -283524,9 +283586,9 @@ async function deviceLogin(argv, deps = {}) {
|
|
|
283524
283586
|
const code = await codeRes.json();
|
|
283525
283587
|
const approvePath = `/cli?user_code=${code.user_code}`;
|
|
283526
283588
|
const url = account === "signup" ? `${DASHBOARD_URL}/signup?next=${encodeURIComponent(approvePath)}` : `${DASHBOARD_URL}${approvePath}`;
|
|
283527
|
-
Me(`${
|
|
283589
|
+
Me(`${import_picocolors13.default.bold(code.user_code)}
|
|
283528
283590
|
|
|
283529
|
-
${
|
|
283591
|
+
${import_picocolors13.default.dim(url)}`, "Approve in your browser");
|
|
283530
283592
|
openUrl(url);
|
|
283531
283593
|
const s = Y2();
|
|
283532
283594
|
s.start("Waiting for approval\u2026");
|
|
@@ -283593,7 +283655,7 @@ ${import_picocolors12.default.dim(url)}`, "Approve in your browser");
|
|
|
283593
283655
|
if (reason) M2.error(reason);
|
|
283594
283656
|
else if (provRes) M2.error(`Key provisioning returned ${provRes.status} from ${DASHBOARD_URL}/api/cli/provision-key.`);
|
|
283595
283657
|
M2.error(
|
|
283596
|
-
`The browser approval succeeded but the key handoff failed. Try again, or grab a ${
|
|
283658
|
+
`The browser approval succeeded but the key handoff failed. Try again, or grab a ${import_picocolors13.default.bold("sk_test_")} key from the dashboard and set ${import_picocolors13.default.bold("ABLO_API_KEY")}.`
|
|
283597
283659
|
);
|
|
283598
283660
|
process.exit(1);
|
|
283599
283661
|
}
|
|
@@ -283613,9 +283675,9 @@ ${import_picocolors12.default.dim(url)}`, "Approve in your browser");
|
|
|
283613
283675
|
{ mode: "sandbox", activeProject: prov.project ?? void 0 }
|
|
283614
283676
|
);
|
|
283615
283677
|
s.stop(`Saved keys to ${path}`);
|
|
283616
|
-
const where = prov.project ? ` ${
|
|
283678
|
+
const where = prov.project ? ` ${import_picocolors13.default.dim(`(project ${prov.project.slug})`)}` : "";
|
|
283617
283679
|
Se(
|
|
283618
|
-
`${
|
|
283680
|
+
`${import_picocolors13.default.green("\u2713")} Logged in ${import_picocolors13.default.dim("(sandbox)")}${where}. Run ${import_picocolors13.default.bold("npx ablo push")} to push your schema.`
|
|
283619
283681
|
);
|
|
283620
283682
|
}
|
|
283621
283683
|
async function login(argv = [], deps = {}) {
|
|
@@ -283624,13 +283686,13 @@ async function login(argv = [], deps = {}) {
|
|
|
283624
283686
|
function logout() {
|
|
283625
283687
|
const removed = clearCredential();
|
|
283626
283688
|
if (removed) {
|
|
283627
|
-
console.log(` ${
|
|
283689
|
+
console.log(` ${import_picocolors13.default.green("\u2713")} Logged out ${import_picocolors13.default.dim(`(credentials removed from ${configDir()})`)}`);
|
|
283628
283690
|
} else {
|
|
283629
|
-
console.log(` ${
|
|
283691
|
+
console.log(` ${import_picocolors13.default.dim("\u25CB")} Not logged in \u2014 nothing to remove.`);
|
|
283630
283692
|
}
|
|
283631
283693
|
if (process.env.ABLO_API_KEY) {
|
|
283632
283694
|
console.log(
|
|
283633
|
-
|
|
283695
|
+
import_picocolors13.default.dim(` Note: ${import_picocolors13.default.bold("ABLO_API_KEY")} is still set in this shell and takes precedence.`)
|
|
283634
283696
|
);
|
|
283635
283697
|
}
|
|
283636
283698
|
}
|
|
@@ -283640,7 +283702,7 @@ init_config();
|
|
|
283640
283702
|
|
|
283641
283703
|
// src/cli/mode.ts
|
|
283642
283704
|
init_cjs_shims();
|
|
283643
|
-
var
|
|
283705
|
+
var import_picocolors14 = __toESM(require_picocolors(), 1);
|
|
283644
283706
|
init_dist2();
|
|
283645
283707
|
init_config();
|
|
283646
283708
|
var PREFIX = { sandbox: "sk_test_", production: "rk_live_" };
|
|
@@ -283652,10 +283714,10 @@ function hintFor(m2, current) {
|
|
|
283652
283714
|
}
|
|
283653
283715
|
function apply(m2) {
|
|
283654
283716
|
setMode(m2);
|
|
283655
|
-
console.log(` ${
|
|
283717
|
+
console.log(` ${import_picocolors14.default.green("\u2713")} now in ${import_picocolors14.default.bold(m2)}`);
|
|
283656
283718
|
if (!getKeyEntry(m2)) {
|
|
283657
283719
|
console.log(
|
|
283658
|
-
|
|
283720
|
+
import_picocolors14.default.dim(` No ${m2} key stored \u2014 run ${import_picocolors14.default.bold("ablo login")} or ${import_picocolors14.default.bold(`ablo login --api-key ${PREFIX[m2]}\u2026`)}.`)
|
|
283659
283721
|
);
|
|
283660
283722
|
}
|
|
283661
283723
|
}
|
|
@@ -283668,14 +283730,14 @@ async function mode(argv) {
|
|
|
283668
283730
|
}
|
|
283669
283731
|
if (arg) {
|
|
283670
283732
|
console.error(
|
|
283671
|
-
|
|
283733
|
+
import_picocolors14.default.red(` unknown mode: ${arg}`) + import_picocolors14.default.dim(` (expected ${import_picocolors14.default.bold("sandbox")} or ${import_picocolors14.default.bold("production")})`)
|
|
283672
283734
|
);
|
|
283673
283735
|
process.exit(1);
|
|
283674
283736
|
}
|
|
283675
283737
|
const current = getMode();
|
|
283676
283738
|
if (!process.stdin.isTTY || process.env.CI) {
|
|
283677
283739
|
console.error(
|
|
283678
|
-
|
|
283740
|
+
import_picocolors14.default.red(" `ablo mode` needs an argument without a TTY: ") + import_picocolors14.default.bold("ablo mode sandbox") + import_picocolors14.default.dim(" | ") + import_picocolors14.default.bold("ablo mode production") + import_picocolors14.default.dim(` (current: ${current})`)
|
|
283679
283741
|
);
|
|
283680
283742
|
process.exit(1);
|
|
283681
283743
|
}
|
|
@@ -283699,7 +283761,7 @@ init_projects();
|
|
|
283699
283761
|
|
|
283700
283762
|
// src/cli/status.ts
|
|
283701
283763
|
init_cjs_shims();
|
|
283702
|
-
var
|
|
283764
|
+
var import_picocolors15 = __toESM(require_picocolors(), 1);
|
|
283703
283765
|
init_config();
|
|
283704
283766
|
init_target();
|
|
283705
283767
|
init_theme();
|
|
@@ -283707,9 +283769,9 @@ init_push();
|
|
|
283707
283769
|
function expiryLabel(iso) {
|
|
283708
283770
|
const ms = Date.parse(iso) - Date.now();
|
|
283709
283771
|
if (Number.isNaN(ms)) return "";
|
|
283710
|
-
if (ms <= 0) return
|
|
283772
|
+
if (ms <= 0) return import_picocolors15.default.red("expired");
|
|
283711
283773
|
const days = Math.floor(ms / (24 * 60 * 60 * 1e3));
|
|
283712
|
-
return
|
|
283774
|
+
return import_picocolors15.default.dim(days > 0 ? `expires in ${days}d` : "expires <1d");
|
|
283713
283775
|
}
|
|
283714
283776
|
async function ping(apiUrl2) {
|
|
283715
283777
|
const ctrl = new AbortController();
|
|
@@ -283793,27 +283855,27 @@ function formatConflict(conflict) {
|
|
|
283793
283855
|
function printTargetLines(target, localProject) {
|
|
283794
283856
|
const confirmed = target?.confirmed ?? null;
|
|
283795
283857
|
if (confirmed?.organizationId) {
|
|
283796
|
-
console.log(` ${
|
|
283858
|
+
console.log(` ${import_picocolors15.default.dim("org")} ${import_picocolors15.default.dim(confirmed.organizationId)}`);
|
|
283797
283859
|
}
|
|
283798
283860
|
let projectLine;
|
|
283799
283861
|
if (confirmed?.project) {
|
|
283800
283862
|
const p2 = confirmed.project;
|
|
283801
|
-
projectLine = p2.isDefault ? `${
|
|
283863
|
+
projectLine = p2.isDefault ? `${import_picocolors15.default.bold("default")} ${import_picocolors15.default.dim("(org-default)")}` : `${import_picocolors15.default.bold(p2.slug)} ${import_picocolors15.default.dim(`(${p2.id})`)}`;
|
|
283802
283864
|
} else if (confirmed) {
|
|
283803
|
-
projectLine = `${
|
|
283865
|
+
projectLine = `${import_picocolors15.default.bold("default")} ${import_picocolors15.default.dim("(org-default)")}`;
|
|
283804
283866
|
} else if (localProject) {
|
|
283805
|
-
projectLine = `${
|
|
283867
|
+
projectLine = `${import_picocolors15.default.bold(localProject.slug)} ${import_picocolors15.default.dim(`(${localProject.id})`)} ${import_picocolors15.default.yellow("(unconfirmed)")}`;
|
|
283806
283868
|
} else {
|
|
283807
|
-
projectLine = `${
|
|
283869
|
+
projectLine = `${import_picocolors15.default.bold("default")} ${target ? import_picocolors15.default.yellow("(unconfirmed)") : import_picocolors15.default.dim("(org-default)")}`;
|
|
283808
283870
|
}
|
|
283809
|
-
console.log(` ${
|
|
283871
|
+
console.log(` ${import_picocolors15.default.dim("project")} ${projectLine}`);
|
|
283810
283872
|
const env = confirmed?.environment ?? target?.keyEnv ?? null;
|
|
283811
283873
|
if (env) {
|
|
283812
|
-
const suffix = confirmed ? "" : ` ${
|
|
283813
|
-
console.log(` ${
|
|
283874
|
+
const suffix = confirmed ? "" : ` ${import_picocolors15.default.yellow("(unconfirmed)")}`;
|
|
283875
|
+
console.log(` ${import_picocolors15.default.dim("env")} ${import_picocolors15.default.bold(env)}${suffix}`);
|
|
283814
283876
|
}
|
|
283815
283877
|
for (const m2 of target?.mismatches ?? []) {
|
|
283816
|
-
console.log(` ${
|
|
283878
|
+
console.log(` ${import_picocolors15.default.yellow("\u26A0")} ${import_picocolors15.default.yellow(describeMismatch(m2))}`);
|
|
283817
283879
|
}
|
|
283818
283880
|
}
|
|
283819
283881
|
async function status(args = []) {
|
|
@@ -283884,80 +283946,80 @@ async function status(args = []) {
|
|
|
283884
283946
|
return;
|
|
283885
283947
|
}
|
|
283886
283948
|
console.log(`
|
|
283887
|
-
${brand("ablo")} ${
|
|
283949
|
+
${brand("ablo")} ${import_picocolors15.default.dim("status")}
|
|
283888
283950
|
`);
|
|
283889
283951
|
if (effective.key && effective.source && effective.source !== "stored") {
|
|
283890
283952
|
const label = effective.source === "env" ? "ABLO_API_KEY env" : effective.source;
|
|
283891
283953
|
console.log(
|
|
283892
|
-
` ${
|
|
283954
|
+
` ${import_picocolors15.default.dim("key")} ${effective.key.slice(0, 12)}\u2026 ${import_picocolors15.default.dim(`(${label} \u2014 overrides stored)`)}`
|
|
283893
283955
|
);
|
|
283894
283956
|
} else if (!cfg) {
|
|
283895
|
-
console.log(` ${
|
|
283957
|
+
console.log(` ${import_picocolors15.default.yellow("!")} Not logged in \u2014 run ${import_picocolors15.default.bold("ablo login")}.`);
|
|
283896
283958
|
}
|
|
283897
|
-
console.log(` ${
|
|
283959
|
+
console.log(` ${import_picocolors15.default.dim("mode")} ${import_picocolors15.default.bold(mode2)}`);
|
|
283898
283960
|
const activeEntry = getKeyEntry(mode2);
|
|
283899
283961
|
const key = describeEffectiveKey(mode2, process.env.ABLO_API_KEY, activeEntry);
|
|
283900
283962
|
if (key.keyMismatch) {
|
|
283901
|
-
console.log(` ${
|
|
283963
|
+
console.log(` ${import_picocolors15.default.yellow("!")} ${import_picocolors15.default.yellow(key.keyMismatch.message)}`);
|
|
283902
283964
|
}
|
|
283903
283965
|
const activeProject = getActiveProject();
|
|
283904
283966
|
printTargetLines(target, activeProject);
|
|
283905
283967
|
for (const m2 of ["sandbox", "production"]) {
|
|
283906
283968
|
const entry = getKeyEntry(m2);
|
|
283907
|
-
const marker = m2 === mode2 ?
|
|
283969
|
+
const marker = m2 === mode2 ? import_picocolors15.default.green("\u25CF") : import_picocolors15.default.dim("\u25CB");
|
|
283908
283970
|
if (entry) {
|
|
283909
283971
|
const exp = entry.expiresAt ? ` ${expiryLabel(entry.expiresAt)}` : "";
|
|
283910
|
-
console.log(` ${marker} ${m2.padEnd(10)} ${
|
|
283972
|
+
console.log(` ${marker} ${m2.padEnd(10)} ${import_picocolors15.default.dim(`${entry.apiKey.slice(0, 12)}\u2026`)}${exp}`);
|
|
283911
283973
|
} else {
|
|
283912
|
-
console.log(` ${marker} ${m2.padEnd(10)} ${
|
|
283974
|
+
console.log(` ${marker} ${m2.padEnd(10)} ${import_picocolors15.default.dim("\u2014 no key")}`);
|
|
283913
283975
|
}
|
|
283914
283976
|
}
|
|
283915
283977
|
const org = target?.confirmed?.organizationId ?? activeEntry?.organizationId;
|
|
283916
283978
|
const plan = resolvePushPlan();
|
|
283917
283979
|
console.log(
|
|
283918
|
-
` ${
|
|
283980
|
+
` ${import_picocolors15.default.dim("push")} ${plan.apiKey ? `${import_picocolors15.default.bold(plan.flow)} ${import_picocolors15.default.dim(`with ${plan.apiKey.slice(0, 12)}\u2026 (${plan.source})`)}` : `${import_picocolors15.default.bold(plan.flow)} ${import_picocolors15.default.yellow("\u2014 no credential")} ${import_picocolors15.default.dim(`(run ${import_picocolors15.default.bold("ablo login")} or set ${import_picocolors15.default.bold("ABLO_API_KEY")})`)}`}`
|
|
283919
283981
|
);
|
|
283920
|
-
process.stdout.write(` ${
|
|
283982
|
+
process.stdout.write(` ${import_picocolors15.default.dim("api")} ${apiUrl2} `);
|
|
283921
283983
|
const reachable = await ping(apiUrl2);
|
|
283922
|
-
console.log(reachable ?
|
|
283984
|
+
console.log(reachable ? import_picocolors15.default.green("reachable") : import_picocolors15.default.red("unreachable"));
|
|
283923
283985
|
if (reachable) {
|
|
283924
283986
|
const introspectKey = effective.key;
|
|
283925
283987
|
const pushed = await fetchPushedSchema(apiUrl2, introspectKey);
|
|
283926
283988
|
if (pushed?.active) {
|
|
283927
|
-
const when = pushed.pushedAt ? ` ${
|
|
283928
|
-
const ver = pushed.version != null ? ` ${
|
|
283929
|
-
const hashLabel = pushed.hash ? ` ${
|
|
283930
|
-
console.log(` ${
|
|
283989
|
+
const when = pushed.pushedAt ? ` ${import_picocolors15.default.dim(`@ ${pushed.pushedAt.slice(0, 10)}`)}` : "";
|
|
283990
|
+
const ver = pushed.version != null ? ` ${import_picocolors15.default.dim(`(rev ${pushed.version})`)}` : "";
|
|
283991
|
+
const hashLabel = pushed.hash ? ` ${import_picocolors15.default.dim(`hash ${pushed.hash}`)}` : "";
|
|
283992
|
+
console.log(` ${import_picocolors15.default.dim("schema")} ${import_picocolors15.default.bold(`${pushed.models.length} models pushed`)}${ver}${hashLabel}${when}`);
|
|
283931
283993
|
for (const m2 of pushed.models) {
|
|
283932
|
-
const tn = m2.typename === m2.key ?
|
|
283994
|
+
const tn = m2.typename === m2.key ? import_picocolors15.default.dim(`typename=${m2.typename}`) : import_picocolors15.default.yellow(`typename=${m2.typename}`);
|
|
283933
283995
|
const conflict = formatConflict(m2.conflict);
|
|
283934
|
-
const conflictStr2 = conflict ? ` ${
|
|
283935
|
-
console.log(` ${
|
|
283996
|
+
const conflictStr2 = conflict ? ` ${import_picocolors15.default.dim(`conflict=${conflict}`)}` : "";
|
|
283997
|
+
console.log(` ${import_picocolors15.default.dim("\u2022")} ${m2.key.padEnd(14)} ${tn}${conflictStr2}`);
|
|
283936
283998
|
}
|
|
283937
283999
|
} else if (pushed && !pushed.active) {
|
|
283938
|
-
console.log(` ${
|
|
284000
|
+
console.log(` ${import_picocolors15.default.dim("schema")} ${import_picocolors15.default.yellow("none pushed")} ${import_picocolors15.default.dim(`(run ${import_picocolors15.default.bold("ablo push")} or ${import_picocolors15.default.bold("ablo dev")})`)}`);
|
|
283939
284001
|
}
|
|
283940
284002
|
const firstPushedModel = pushed?.active ? pushed.models[0] : void 0;
|
|
283941
284003
|
if (firstPushedModel !== void 0) {
|
|
283942
284004
|
const probe = await probeDataPlane(apiUrl2, introspectKey, firstPushedModel.typename);
|
|
283943
284005
|
if (probe.status === "no_database") {
|
|
283944
|
-
console.log(` ${
|
|
284006
|
+
console.log(` ${import_picocolors15.default.dim("data")} ${import_picocolors15.default.red("\u2717 no database registered")}${org ? import_picocolors15.default.dim(` for org ${org}`) : ""}`);
|
|
283945
284007
|
console.log(
|
|
283946
|
-
` ${
|
|
283947
|
-
`reads/writes will fail with ${
|
|
284008
|
+
` ${import_picocolors15.default.dim(
|
|
284009
|
+
`reads/writes will fail with ${import_picocolors15.default.bold("tenant_routing_failed")}. Connect one with ${import_picocolors15.default.bold("ablo connect")}, or point ${import_picocolors15.default.bold("ABLO_API_KEY")} at an org that has a database.`
|
|
283948
284010
|
)}`
|
|
283949
284011
|
);
|
|
283950
284012
|
} else if (probe.status === "intermittent") {
|
|
283951
|
-
console.log(` ${
|
|
284013
|
+
console.log(` ${import_picocolors15.default.dim("data")} ${import_picocolors15.default.red(`\u2717 database routing is intermittent`)} ${import_picocolors15.default.dim(`(${probe.ok} ok / ${probe.failed} failed of ${probe.ok + probe.failed})`)}${org ? import_picocolors15.default.dim(` for org ${org}`) : ""}`);
|
|
283952
284014
|
console.log(
|
|
283953
|
-
` ${
|
|
283954
|
-
`some reads/writes fail with ${
|
|
284015
|
+
` ${import_picocolors15.default.dim(
|
|
284016
|
+
`some reads/writes fail with ${import_picocolors15.default.bold("tenant_routing_failed")} \u2014 the registration is unstable. Re-establish it with ${import_picocolors15.default.bold("ablo connect")} (or check for a recent server redeploy).`
|
|
283955
284017
|
)}`
|
|
283956
284018
|
);
|
|
283957
284019
|
} else if (probe.status === "forbidden") {
|
|
283958
|
-
console.log(` ${
|
|
284020
|
+
console.log(` ${import_picocolors15.default.dim("data")} ${import_picocolors15.default.yellow("? key not authorized to read")}${probe.detail ? import_picocolors15.default.dim(` (${probe.detail})`) : ""}`);
|
|
283959
284021
|
} else if (probe.status === "unknown") {
|
|
283960
|
-
console.log(` ${
|
|
284022
|
+
console.log(` ${import_picocolors15.default.dim("data")} ${import_picocolors15.default.yellow(`? data-plane check inconclusive (${probe.detail})`)}`);
|
|
283961
284023
|
}
|
|
283962
284024
|
}
|
|
283963
284025
|
}
|
|
@@ -283967,7 +284029,7 @@ async function status(args = []) {
|
|
|
283967
284029
|
// src/cli/logs.ts
|
|
283968
284030
|
init_cjs_shims();
|
|
283969
284031
|
init_errors();
|
|
283970
|
-
var
|
|
284032
|
+
var import_picocolors16 = __toESM(require_picocolors(), 1);
|
|
283971
284033
|
init_config();
|
|
283972
284034
|
init_theme();
|
|
283973
284035
|
init_push();
|
|
@@ -284034,10 +284096,10 @@ function resolveSince(since) {
|
|
|
284034
284096
|
var sleep2 = (ms) => new Promise((r2) => setTimeout(r2, ms));
|
|
284035
284097
|
function colorOp(op) {
|
|
284036
284098
|
const label = op.padEnd(6);
|
|
284037
|
-
if (op === "create") return
|
|
284038
|
-
if (op === "update") return
|
|
284039
|
-
if (op === "delete") return
|
|
284040
|
-
return
|
|
284099
|
+
if (op === "create") return import_picocolors16.default.green(label);
|
|
284100
|
+
if (op === "update") return import_picocolors16.default.yellow(label);
|
|
284101
|
+
if (op === "delete") return import_picocolors16.default.red(label);
|
|
284102
|
+
return import_picocolors16.default.dim(label);
|
|
284041
284103
|
}
|
|
284042
284104
|
function render(e2, json) {
|
|
284043
284105
|
if (json) {
|
|
@@ -284046,21 +284108,21 @@ function render(e2, json) {
|
|
|
284046
284108
|
return;
|
|
284047
284109
|
}
|
|
284048
284110
|
const t = new Date(e2.at).toLocaleTimeString();
|
|
284049
|
-
const actor = e2.actor ?
|
|
284050
|
-
console.log(` ${
|
|
284111
|
+
const actor = e2.actor ? import_picocolors16.default.dim(` ${e2.actor}`) : "";
|
|
284112
|
+
console.log(` ${import_picocolors16.default.dim(t)} ${colorOp(e2.op)} ${import_picocolors16.default.bold(e2.model)} ${import_picocolors16.default.dim(e2.recordId)}${actor}`);
|
|
284051
284113
|
}
|
|
284052
284114
|
async function logs(argv) {
|
|
284053
284115
|
let args;
|
|
284054
284116
|
try {
|
|
284055
284117
|
args = parseLogsArgs(argv);
|
|
284056
284118
|
} catch (err) {
|
|
284057
|
-
console.error(
|
|
284119
|
+
console.error(import_picocolors16.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
284058
284120
|
process.exit(1);
|
|
284059
284121
|
}
|
|
284060
284122
|
const apiKey = resolveApiKey(args.mode);
|
|
284061
284123
|
if (!apiKey) {
|
|
284062
284124
|
console.error(
|
|
284063
|
-
|
|
284125
|
+
import_picocolors16.default.red(` No API key.`) + import_picocolors16.default.dim(` Run ${import_picocolors16.default.bold("ablo login")} or set ${import_picocolors16.default.bold("ABLO_API_KEY")}.`)
|
|
284064
284126
|
);
|
|
284065
284127
|
process.exit(1);
|
|
284066
284128
|
}
|
|
@@ -284074,7 +284136,7 @@ async function logs(argv) {
|
|
|
284074
284136
|
if (!res) return null;
|
|
284075
284137
|
if (!res.ok) {
|
|
284076
284138
|
const body = await res.json().catch(() => ({}));
|
|
284077
|
-
console.error(
|
|
284139
|
+
console.error(import_picocolors16.default.red(` logs failed (${res.status}): ${body.reason ?? body.message ?? ""}`));
|
|
284078
284140
|
process.exit(1);
|
|
284079
284141
|
}
|
|
284080
284142
|
const json = await res.json();
|
|
@@ -284085,7 +284147,7 @@ async function logs(argv) {
|
|
|
284085
284147
|
}
|
|
284086
284148
|
if (!args.json) {
|
|
284087
284149
|
console.log(`
|
|
284088
|
-
${brand("ablo")} ${
|
|
284150
|
+
${brand("ablo")} ${import_picocolors16.default.dim("logs")} ${import_picocolors16.default.dim(`(${args.mode ?? "active"} mode)`)}
|
|
284089
284151
|
`);
|
|
284090
284152
|
}
|
|
284091
284153
|
const initial = await fetchPage({
|
|
@@ -284095,13 +284157,13 @@ async function logs(argv) {
|
|
|
284095
284157
|
...args.op ? { op: args.op } : {}
|
|
284096
284158
|
});
|
|
284097
284159
|
if (!initial) {
|
|
284098
|
-
console.error(
|
|
284160
|
+
console.error(import_picocolors16.default.red(` Couldn't reach ${baseUrl2}.`));
|
|
284099
284161
|
process.exit(1);
|
|
284100
284162
|
}
|
|
284101
284163
|
for (const e2 of initial.events) render(e2, args.json);
|
|
284102
284164
|
let cursor = initial.cursor;
|
|
284103
284165
|
if (!args.follow) return;
|
|
284104
|
-
if (!args.json) console.log(` ${
|
|
284166
|
+
if (!args.json) console.log(` ${import_picocolors16.default.dim("watching for new activity \u2026 (Ctrl-C to stop)")}
|
|
284105
284167
|
`);
|
|
284106
284168
|
for (; ; ) {
|
|
284107
284169
|
await sleep2(1500);
|
|
@@ -284119,7 +284181,7 @@ async function logs(argv) {
|
|
|
284119
284181
|
// src/cli/webhooks.ts
|
|
284120
284182
|
init_cjs_shims();
|
|
284121
284183
|
var import_fs8 = require("fs");
|
|
284122
|
-
var
|
|
284184
|
+
var import_picocolors17 = __toESM(require_picocolors(), 1);
|
|
284123
284185
|
init_credentialPolicy();
|
|
284124
284186
|
init_config();
|
|
284125
284187
|
init_theme();
|
|
@@ -284152,12 +284214,12 @@ function requireKey2(mode2) {
|
|
|
284152
284214
|
const apiKey = resolveApiKey(mode2);
|
|
284153
284215
|
if (!apiKey) {
|
|
284154
284216
|
console.error(
|
|
284155
|
-
|
|
284217
|
+
import_picocolors17.default.red(" No API key.") + import_picocolors17.default.dim(` Run ${import_picocolors17.default.bold("ablo login")} or set ${import_picocolors17.default.bold("ABLO_API_KEY")}.`)
|
|
284156
284218
|
);
|
|
284157
284219
|
process.exit(1);
|
|
284158
284220
|
}
|
|
284159
284221
|
if (classifyCredentialKind(apiKey) !== "secret") {
|
|
284160
|
-
console.error(
|
|
284222
|
+
console.error(import_picocolors17.default.red(" Managing webhooks requires a secret key ") + import_picocolors17.default.dim("(sk_test_ / sk_live_)."));
|
|
284161
284223
|
process.exit(1);
|
|
284162
284224
|
}
|
|
284163
284225
|
return apiKey;
|
|
@@ -284173,12 +284235,12 @@ async function api(apiKey, method, path, body) {
|
|
|
284173
284235
|
...body ? { body: JSON.stringify(body) } : {}
|
|
284174
284236
|
}).catch(() => null);
|
|
284175
284237
|
if (!res) {
|
|
284176
|
-
console.error(
|
|
284238
|
+
console.error(import_picocolors17.default.red(` Couldn't reach ${baseUrl()}.`));
|
|
284177
284239
|
process.exit(1);
|
|
284178
284240
|
}
|
|
284179
284241
|
if (!res.ok) {
|
|
284180
284242
|
const err = await res.json().catch(() => ({}));
|
|
284181
|
-
console.error(
|
|
284243
|
+
console.error(import_picocolors17.default.red(` Request failed (${res.status}): ${err.message ?? err.reason ?? ""}`));
|
|
284182
284244
|
process.exit(1);
|
|
284183
284245
|
}
|
|
284184
284246
|
return await res.json();
|
|
@@ -284200,11 +284262,11 @@ ${line}
|
|
|
284200
284262
|
return file;
|
|
284201
284263
|
}
|
|
284202
284264
|
function printEndpoint(e2) {
|
|
284203
|
-
const dot = e2.status === "enabled" ?
|
|
284204
|
-
const health = e2.last_error ?
|
|
284205
|
-
console.log(` ${dot} ${
|
|
284265
|
+
const dot = e2.status === "enabled" ? import_picocolors17.default.green("\u25CF") : import_picocolors17.default.red("\u25CF");
|
|
284266
|
+
const health = e2.last_error ? import_picocolors17.default.red(` last error: ${e2.last_error}`) : "";
|
|
284267
|
+
console.log(` ${dot} ${import_picocolors17.default.bold(e2.id)} ${e2.url}`);
|
|
284206
284268
|
console.log(
|
|
284207
|
-
|
|
284269
|
+
import_picocolors17.default.dim(
|
|
284208
284270
|
` ${e2.status} \xB7 ${e2.environment} \xB7 events ${e2.enabled_events.join(",")} \xB7 cursor ${e2.cursor ?? "\u2014"}${health}`
|
|
284209
284271
|
)
|
|
284210
284272
|
);
|
|
@@ -284216,7 +284278,7 @@ async function webhooks(argv) {
|
|
|
284216
284278
|
if (sub === "create") {
|
|
284217
284279
|
const url = positional(rest);
|
|
284218
284280
|
if (!url) {
|
|
284219
|
-
console.error(
|
|
284281
|
+
console.error(import_picocolors17.default.red(" Usage: ") + brand("ablo webhooks create <url>"));
|
|
284220
284282
|
process.exit(1);
|
|
284221
284283
|
}
|
|
284222
284284
|
const apiKey = requireKey2(mode2);
|
|
@@ -284228,8 +284290,8 @@ async function webhooks(argv) {
|
|
|
284228
284290
|
});
|
|
284229
284291
|
const file = writeSecretToEnv(created.secret);
|
|
284230
284292
|
console.log(`
|
|
284231
|
-
${
|
|
284232
|
-
console.log(` ${
|
|
284293
|
+
${import_picocolors17.default.green("\u2713")} Registered ${import_picocolors17.default.bold(created.id)} \u2192 ${created.url}`);
|
|
284294
|
+
console.log(` ${import_picocolors17.default.green("\u2713")} Wrote ${import_picocolors17.default.bold(ENV_KEY)} to ${import_picocolors17.default.bold(file)} ${import_picocolors17.default.dim("(shown once)")}
|
|
284233
284295
|
`);
|
|
284234
284296
|
return;
|
|
284235
284297
|
}
|
|
@@ -284237,7 +284299,7 @@ async function webhooks(argv) {
|
|
|
284237
284299
|
const apiKey = requireKey2(mode2);
|
|
284238
284300
|
const { data } = await api(apiKey, "GET", "");
|
|
284239
284301
|
if (data.length === 0) {
|
|
284240
|
-
console.log(
|
|
284302
|
+
console.log(import_picocolors17.default.dim(" No webhook endpoints. ") + brand("ablo webhooks create <url>"));
|
|
284241
284303
|
return;
|
|
284242
284304
|
}
|
|
284243
284305
|
console.log();
|
|
@@ -284248,40 +284310,40 @@ async function webhooks(argv) {
|
|
|
284248
284310
|
if (sub === "roll") {
|
|
284249
284311
|
const id = positional(rest);
|
|
284250
284312
|
if (!id) {
|
|
284251
|
-
console.error(
|
|
284313
|
+
console.error(import_picocolors17.default.red(" Usage: ") + brand("ablo webhooks roll <id>"));
|
|
284252
284314
|
process.exit(1);
|
|
284253
284315
|
}
|
|
284254
284316
|
const apiKey = requireKey2(mode2);
|
|
284255
284317
|
const rolled = await api(apiKey, "POST", `/${id}/roll_secret`);
|
|
284256
284318
|
const file = writeSecretToEnv(rolled.secret);
|
|
284257
284319
|
console.log(`
|
|
284258
|
-
${
|
|
284320
|
+
${import_picocolors17.default.green("\u2713")} Rolled secret for ${import_picocolors17.default.bold(id)} \u2192 ${import_picocolors17.default.bold(file)} ${import_picocolors17.default.dim("(old secret now invalid)")}
|
|
284259
284321
|
`);
|
|
284260
284322
|
return;
|
|
284261
284323
|
}
|
|
284262
284324
|
if (sub === "enable") {
|
|
284263
284325
|
const id = positional(rest);
|
|
284264
284326
|
if (!id) {
|
|
284265
|
-
console.error(
|
|
284327
|
+
console.error(import_picocolors17.default.red(" Usage: ") + brand("ablo webhooks enable <id>"));
|
|
284266
284328
|
process.exit(1);
|
|
284267
284329
|
}
|
|
284268
284330
|
const apiKey = requireKey2(mode2);
|
|
284269
284331
|
const e2 = await api(apiKey, "POST", `/${id}/enable`);
|
|
284270
|
-
console.log(` ${
|
|
284332
|
+
console.log(` ${import_picocolors17.default.green("\u2713")} Re-enabled ${import_picocolors17.default.bold(e2.id)}`);
|
|
284271
284333
|
return;
|
|
284272
284334
|
}
|
|
284273
284335
|
if (sub === "rm" || sub === "delete") {
|
|
284274
284336
|
const id = positional(rest);
|
|
284275
284337
|
if (!id) {
|
|
284276
|
-
console.error(
|
|
284338
|
+
console.error(import_picocolors17.default.red(" Usage: ") + brand("ablo webhooks rm <id>"));
|
|
284277
284339
|
process.exit(1);
|
|
284278
284340
|
}
|
|
284279
284341
|
const apiKey = requireKey2(mode2);
|
|
284280
284342
|
await api(apiKey, "DELETE", `/${id}`);
|
|
284281
|
-
console.log(` ${
|
|
284343
|
+
console.log(` ${import_picocolors17.default.green("\u2713")} Removed ${import_picocolors17.default.bold(id)}`);
|
|
284282
284344
|
return;
|
|
284283
284345
|
}
|
|
284284
|
-
console.log(` ${
|
|
284346
|
+
console.log(` ${import_picocolors17.default.bold("Usage:")}`);
|
|
284285
284347
|
console.log(` ${brand("ablo webhooks create <url>")} Register an endpoint; writes ${ENV_KEY}`);
|
|
284286
284348
|
console.log(` ${brand("ablo webhooks list")} List endpoints + delivery health`);
|
|
284287
284349
|
console.log(` ${brand("ablo webhooks roll <id>")} Mint a fresh signing secret`);
|
|
@@ -284293,7 +284355,7 @@ async function webhooks(argv) {
|
|
|
284293
284355
|
// src/cli/check.ts
|
|
284294
284356
|
init_cjs_shims();
|
|
284295
284357
|
init_errors();
|
|
284296
|
-
var
|
|
284358
|
+
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
284297
284359
|
init_src();
|
|
284298
284360
|
var import_schema6 = require("@abloatai/ablo/schema");
|
|
284299
284361
|
init_push();
|
|
@@ -284331,13 +284393,13 @@ async function check(argv) {
|
|
|
284331
284393
|
try {
|
|
284332
284394
|
args = parseCheckArgs(argv);
|
|
284333
284395
|
} catch (err) {
|
|
284334
|
-
console.error(
|
|
284396
|
+
console.error(import_picocolors18.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
284335
284397
|
process.exit(1);
|
|
284336
284398
|
}
|
|
284337
284399
|
const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
|
|
284338
284400
|
if (!dbUrl) {
|
|
284339
284401
|
console.error(
|
|
284340
|
-
|
|
284402
|
+
import_picocolors18.default.red(` No database.`) + import_picocolors18.default.dim(` Set ${import_picocolors18.default.bold("DATABASE_URL")} to the Postgres you want Ablo to adopt.`)
|
|
284341
284403
|
);
|
|
284342
284404
|
process.exit(1);
|
|
284343
284405
|
}
|
|
@@ -284352,7 +284414,7 @@ async function check(argv) {
|
|
|
284352
284414
|
[args.appSchema]
|
|
284353
284415
|
);
|
|
284354
284416
|
} catch (err) {
|
|
284355
|
-
console.error(
|
|
284417
|
+
console.error(import_picocolors18.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
|
|
284356
284418
|
await sql.end({ timeout: 2 });
|
|
284357
284419
|
process.exit(1);
|
|
284358
284420
|
}
|
|
@@ -284367,7 +284429,7 @@ async function check(argv) {
|
|
|
284367
284429
|
set.add(r2.column_name);
|
|
284368
284430
|
}
|
|
284369
284431
|
console.log(`
|
|
284370
|
-
${brand("ablo")} ${
|
|
284432
|
+
${brand("ablo")} ${import_picocolors18.default.dim("check")} ${import_picocolors18.default.dim(`schema "${args.appSchema}"`)}
|
|
284371
284433
|
`);
|
|
284372
284434
|
const declaredTables = /* @__PURE__ */ new Set();
|
|
284373
284435
|
let errors = 0;
|
|
@@ -284377,7 +284439,7 @@ async function check(argv) {
|
|
|
284377
284439
|
declaredTables.add(table);
|
|
284378
284440
|
const present = colsByTable.get(table);
|
|
284379
284441
|
if (!present) {
|
|
284380
|
-
console.log(` ${
|
|
284442
|
+
console.log(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.bold(key)} ${import_picocolors18.default.dim("\u2192")} table ${import_picocolors18.default.bold(table)} ${import_picocolors18.default.red("not found")}`);
|
|
284381
284443
|
errors++;
|
|
284382
284444
|
continue;
|
|
284383
284445
|
}
|
|
@@ -284399,26 +284461,26 @@ async function check(argv) {
|
|
|
284399
284461
|
if (!present.has(col)) problems.push(`missing column "${col}" (field ${fieldName})`);
|
|
284400
284462
|
}
|
|
284401
284463
|
if (problems.length > 0) {
|
|
284402
|
-
console.log(` ${
|
|
284403
|
-
for (const p2 of problems) console.log(` ${
|
|
284404
|
-
for (const w2 of warns) console.log(` ${
|
|
284464
|
+
console.log(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.bold(key)} ${import_picocolors18.default.dim("\u2192")} ${table}`);
|
|
284465
|
+
for (const p2 of problems) console.log(` ${import_picocolors18.default.red("\u2022")} ${p2}`);
|
|
284466
|
+
for (const w2 of warns) console.log(` ${import_picocolors18.default.yellow("\u2022")} ${w2}`);
|
|
284405
284467
|
errors++;
|
|
284406
284468
|
} else if (warns.length > 0) {
|
|
284407
|
-
console.log(` ${
|
|
284408
|
-
for (const w2 of warns) console.log(` ${
|
|
284469
|
+
console.log(` ${import_picocolors18.default.yellow("!")} ${import_picocolors18.default.bold(key)} ${import_picocolors18.default.dim("\u2192")} ${table}`);
|
|
284470
|
+
for (const w2 of warns) console.log(` ${import_picocolors18.default.yellow("\u2022")} ${w2}`);
|
|
284409
284471
|
warnings++;
|
|
284410
284472
|
} else {
|
|
284411
|
-
console.log(` ${
|
|
284473
|
+
console.log(` ${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.bold(key)} ${import_picocolors18.default.dim(`\u2192 ${table} (id, ${orgCol ?? "no org"} ok)`)}`);
|
|
284412
284474
|
}
|
|
284413
284475
|
}
|
|
284414
284476
|
const modelCount = Object.keys(schemaJson.models).length;
|
|
284415
284477
|
const ignored = [...colsByTable.keys()].filter((t) => !declaredTables.has(t)).length;
|
|
284416
284478
|
console.log(
|
|
284417
284479
|
`
|
|
284418
|
-
${modelCount} model${modelCount === 1 ? "" : "s"} \xB7 ${
|
|
284480
|
+
${modelCount} model${modelCount === 1 ? "" : "s"} \xB7 ${import_picocolors18.default.green(`${modelCount - errors - warnings} ok`)}` + (warnings ? ` \xB7 ${import_picocolors18.default.yellow(`${warnings} warning${warnings === 1 ? "" : "s"}`)}` : "") + (errors ? ` \xB7 ${import_picocolors18.default.red(`${errors} error${errors === 1 ? "" : "s"}`)}` : "")
|
|
284419
284481
|
);
|
|
284420
284482
|
if (ignored > 0) {
|
|
284421
|
-
console.log(` ${
|
|
284483
|
+
console.log(` ${import_picocolors18.default.dim(`${ignored} other table${ignored === 1 ? "" : "s"} in your database \u2014 ignored by Ablo`)}`);
|
|
284422
284484
|
}
|
|
284423
284485
|
console.log();
|
|
284424
284486
|
process.exit(errors > 0 ? 1 : 0);
|
|
@@ -284426,7 +284488,7 @@ async function check(argv) {
|
|
|
284426
284488
|
|
|
284427
284489
|
// src/cli/upgrade.ts
|
|
284428
284490
|
init_cjs_shims();
|
|
284429
|
-
var
|
|
284491
|
+
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
284430
284492
|
var import_ts_morph = __toESM(require_ts_morph(), 1);
|
|
284431
284493
|
var DEFAULT_GLOBS = ["app/**/*.{ts,tsx}", "src/**/*.{ts,tsx}", "ablo/**/*.{ts,tsx}", "lib/**/*.{ts,tsx}"];
|
|
284432
284494
|
var VERB_ARGS = {
|
|
@@ -284504,7 +284566,7 @@ async function upgrade(argv) {
|
|
|
284504
284566
|
project.addSourceFilesAtPaths(globs.length > 0 ? globs : DEFAULT_GLOBS);
|
|
284505
284567
|
const files = project.getSourceFiles();
|
|
284506
284568
|
if (files.length === 0) {
|
|
284507
|
-
console.log(
|
|
284569
|
+
console.log(import_picocolors19.default.yellow(' No .ts/.tsx files found. Pass a glob, e.g. `ablo upgrade "src/**/*.tsx"`.'));
|
|
284508
284570
|
return;
|
|
284509
284571
|
}
|
|
284510
284572
|
const edits = [];
|
|
@@ -284578,39 +284640,39 @@ async function upgrade(argv) {
|
|
|
284578
284640
|
const rel = (f) => f.replace(cwd + "/", "");
|
|
284579
284641
|
console.log();
|
|
284580
284642
|
if (edits.length === 0 && manual.length === 0) {
|
|
284581
|
-
console.log(
|
|
284643
|
+
console.log(import_picocolors19.default.green(" \u2713 Nothing to migrate \u2014 your code is already on the current API."));
|
|
284582
284644
|
return;
|
|
284583
284645
|
}
|
|
284584
284646
|
if (edits.length > 0) {
|
|
284585
|
-
console.log(
|
|
284647
|
+
console.log(import_picocolors19.default.bold(` ${write ? "Applied" : "Would apply"} ${edits.length} change${edits.length === 1 ? "" : "s"}:`));
|
|
284586
284648
|
for (const e2 of edits) {
|
|
284587
|
-
console.log(` ${
|
|
284588
|
-
console.log(` ${
|
|
284589
|
-
console.log(` ${
|
|
284649
|
+
console.log(` ${import_picocolors19.default.dim(`${rel(e2.file)}:${e2.line}`)} ${import_picocolors19.default.cyan(e2.rule)}`);
|
|
284650
|
+
console.log(` ${import_picocolors19.default.red("-")} ${e2.before}`);
|
|
284651
|
+
console.log(` ${import_picocolors19.default.green("+")} ${e2.after}`);
|
|
284590
284652
|
}
|
|
284591
284653
|
}
|
|
284592
284654
|
if (manual.length > 0) {
|
|
284593
284655
|
console.log();
|
|
284594
|
-
console.log(
|
|
284656
|
+
console.log(import_picocolors19.default.bold(import_picocolors19.default.yellow(` ${manual.length} spot${manual.length === 1 ? "" : "s"} need manual review (structural):`)));
|
|
284595
284657
|
for (const m2 of manual) {
|
|
284596
|
-
console.log(` ${
|
|
284597
|
-
console.log(` ${
|
|
284658
|
+
console.log(` ${import_picocolors19.default.dim(`${rel(m2.file)}:${m2.line}`)} ${import_picocolors19.default.yellow(m2.rule)}`);
|
|
284659
|
+
console.log(` ${import_picocolors19.default.dim(m2.snippet)}`);
|
|
284598
284660
|
console.log(` \u2192 ${m2.hint}`);
|
|
284599
284661
|
}
|
|
284600
284662
|
}
|
|
284601
284663
|
console.log();
|
|
284602
284664
|
if (write) {
|
|
284603
284665
|
await project.save();
|
|
284604
|
-
console.log(
|
|
284666
|
+
console.log(import_picocolors19.default.green(` \u2713 Wrote ${edits.length} change${edits.length === 1 ? "" : "s"}. Review the diff, run your typecheck.`));
|
|
284605
284667
|
} else {
|
|
284606
|
-
console.log(
|
|
284668
|
+
console.log(import_picocolors19.default.dim(" Dry run. Re-run with `--write` to apply the auto-fixes above (manual items are never auto-written)."));
|
|
284607
284669
|
}
|
|
284608
284670
|
}
|
|
284609
284671
|
|
|
284610
284672
|
// src/cli/pull.ts
|
|
284611
284673
|
init_cjs_shims();
|
|
284612
284674
|
init_errors();
|
|
284613
|
-
var
|
|
284675
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
284614
284676
|
init_src();
|
|
284615
284677
|
var import_fs9 = require("fs");
|
|
284616
284678
|
init_theme();
|
|
@@ -284724,45 +284786,45 @@ async function pull(argv) {
|
|
|
284724
284786
|
try {
|
|
284725
284787
|
args = parsePullArgs(argv);
|
|
284726
284788
|
} catch (err) {
|
|
284727
|
-
console.error(
|
|
284789
|
+
console.error(import_picocolors20.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
284728
284790
|
process.exit(1);
|
|
284729
284791
|
}
|
|
284730
284792
|
const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
|
|
284731
284793
|
if (!dbUrl) {
|
|
284732
|
-
console.error(
|
|
284794
|
+
console.error(import_picocolors20.default.red(` No database.`) + import_picocolors20.default.dim(` Set ${import_picocolors20.default.bold("DATABASE_URL")} to the Postgres to pull from.`));
|
|
284733
284795
|
process.exit(1);
|
|
284734
284796
|
}
|
|
284735
284797
|
if ((0, import_fs9.existsSync)(args.out) && !args.force) {
|
|
284736
284798
|
console.error(
|
|
284737
|
-
|
|
284799
|
+
import_picocolors20.default.red(` ${args.out} already exists.`) + import_picocolors20.default.dim(` Re-run with ${import_picocolors20.default.bold("--force")} to overwrite.`)
|
|
284738
284800
|
);
|
|
284739
284801
|
process.exit(1);
|
|
284740
284802
|
}
|
|
284741
284803
|
console.log(`
|
|
284742
|
-
${brand("ablo")} ${
|
|
284804
|
+
${brand("ablo")} ${import_picocolors20.default.dim("pull")} ${import_picocolors20.default.dim(`schema "${args.appSchema}"`)}
|
|
284743
284805
|
`);
|
|
284744
284806
|
let result;
|
|
284745
284807
|
try {
|
|
284746
284808
|
result = await buildSchemaSourceFromDb({ dbUrl, appSchema: args.appSchema, importPath: args.importPath });
|
|
284747
284809
|
} catch (err) {
|
|
284748
|
-
console.error(
|
|
284810
|
+
console.error(import_picocolors20.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
|
|
284749
284811
|
process.exit(1);
|
|
284750
284812
|
}
|
|
284751
284813
|
if (result.models.length === 0) {
|
|
284752
284814
|
console.error(
|
|
284753
|
-
|
|
284815
|
+
import_picocolors20.default.yellow(` No adoptable tables found`) + import_picocolors20.default.dim(` (a model needs an ${import_picocolors20.default.bold("id")} + ${import_picocolors20.default.bold("organization_id")} column).`)
|
|
284754
284816
|
);
|
|
284755
284817
|
process.exit(1);
|
|
284756
284818
|
}
|
|
284757
284819
|
(0, import_fs9.writeFileSync)(args.out, result.source);
|
|
284758
|
-
console.log(` ${
|
|
284759
|
-
console.log(` ${
|
|
284820
|
+
console.log(` ${import_picocolors20.default.green("\u2713")} wrote ${import_picocolors20.default.bold(args.out)} ${import_picocolors20.default.dim(`(${result.models.length} models)`)}`);
|
|
284821
|
+
console.log(` ${import_picocolors20.default.dim(`models: ${result.models.join(", ")}`)}`);
|
|
284760
284822
|
if (result.skipped > 0) {
|
|
284761
|
-
console.log(` ${
|
|
284823
|
+
console.log(` ${import_picocolors20.default.dim(`${result.skipped} table(s) skipped \u2014 no id/organization_id`)}`);
|
|
284762
284824
|
}
|
|
284763
284825
|
console.log(
|
|
284764
284826
|
`
|
|
284765
|
-
${
|
|
284827
|
+
${import_picocolors20.default.dim("Introspection is lossy (enums, JSON shape, relations). Review the file, then")} ${import_picocolors20.default.bold("ablo check")}.
|
|
284766
284828
|
`
|
|
284767
284829
|
);
|
|
284768
284830
|
}
|
|
@@ -284770,7 +284832,7 @@ async function pull(argv) {
|
|
|
284770
284832
|
// src/cli/prismaPull.ts
|
|
284771
284833
|
init_cjs_shims();
|
|
284772
284834
|
init_errors();
|
|
284773
|
-
var
|
|
284835
|
+
var import_picocolors21 = __toESM(require_picocolors(), 1);
|
|
284774
284836
|
var import_fs10 = require("fs");
|
|
284775
284837
|
init_theme();
|
|
284776
284838
|
|
|
@@ -285055,55 +285117,55 @@ async function prismaPull(argv) {
|
|
|
285055
285117
|
try {
|
|
285056
285118
|
args = parsePrismaPullArgs(argv);
|
|
285057
285119
|
} catch (err) {
|
|
285058
|
-
console.error(
|
|
285120
|
+
console.error(import_picocolors21.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
285059
285121
|
process.exit(1);
|
|
285060
285122
|
}
|
|
285061
285123
|
if (!(0, import_fs10.existsSync)(args.schema)) {
|
|
285062
285124
|
console.error(
|
|
285063
|
-
|
|
285125
|
+
import_picocolors21.default.red(` No Prisma schema at ${import_picocolors21.default.bold(args.schema)}.`) + import_picocolors21.default.dim(` Pass a path: ${import_picocolors21.default.bold("ablo pull prisma <path>")}.`)
|
|
285064
285126
|
);
|
|
285065
285127
|
process.exit(1);
|
|
285066
285128
|
}
|
|
285067
285129
|
if ((0, import_fs10.existsSync)(args.out) && !args.force) {
|
|
285068
285130
|
console.error(
|
|
285069
|
-
|
|
285131
|
+
import_picocolors21.default.red(` ${args.out} already exists.`) + import_picocolors21.default.dim(` Re-run with ${import_picocolors21.default.bold("--force")} to overwrite.`)
|
|
285070
285132
|
);
|
|
285071
285133
|
process.exit(1);
|
|
285072
285134
|
}
|
|
285073
285135
|
console.log(`
|
|
285074
|
-
${brand("ablo")} ${
|
|
285136
|
+
${brand("ablo")} ${import_picocolors21.default.dim("pull prisma")} ${import_picocolors21.default.dim(args.schema)}
|
|
285075
285137
|
`);
|
|
285076
285138
|
let result;
|
|
285077
285139
|
try {
|
|
285078
285140
|
const src = (0, import_fs10.readFileSync)(args.schema, "utf8");
|
|
285079
285141
|
result = buildSchemaSourceFromPrisma({ src, importPath: args.importPath });
|
|
285080
285142
|
} catch (err) {
|
|
285081
|
-
console.error(
|
|
285143
|
+
console.error(import_picocolors21.default.red(` Couldn't parse the schema: ${err instanceof Error ? err.message : String(err)}`));
|
|
285082
285144
|
process.exit(1);
|
|
285083
285145
|
}
|
|
285084
285146
|
if (result.models.length === 0) {
|
|
285085
285147
|
console.error(
|
|
285086
|
-
|
|
285148
|
+
import_picocolors21.default.yellow(` No adoptable models found`) + import_picocolors21.default.dim(` (a model needs an ${import_picocolors21.default.bold("id")} + ${import_picocolors21.default.bold("organizationId")} / ${import_picocolors21.default.bold("organization_id")}).`)
|
|
285087
285149
|
);
|
|
285088
285150
|
process.exit(1);
|
|
285089
285151
|
}
|
|
285090
285152
|
(0, import_fs10.writeFileSync)(args.out, result.source);
|
|
285091
|
-
console.log(` ${
|
|
285092
|
-
console.log(` ${
|
|
285153
|
+
console.log(` ${import_picocolors21.default.green("\u2713")} wrote ${import_picocolors21.default.bold(args.out)} ${import_picocolors21.default.dim(`(${result.models.length} models)`)}`);
|
|
285154
|
+
console.log(` ${import_picocolors21.default.dim(`models: ${result.models.join(", ")}`)}`);
|
|
285093
285155
|
if (result.skipped.length > 0) {
|
|
285094
|
-
console.log(` ${
|
|
285095
|
-
for (const s of result.skipped) console.log(` ${
|
|
285156
|
+
console.log(` ${import_picocolors21.default.dim(`${result.skipped.length} model(s) skipped:`)}`);
|
|
285157
|
+
for (const s of result.skipped) console.log(` ${import_picocolors21.default.dim(`- ${s.name}: ${s.reason}`)}`);
|
|
285096
285158
|
}
|
|
285097
285159
|
console.log(
|
|
285098
285160
|
`
|
|
285099
|
-
${
|
|
285161
|
+
${import_picocolors21.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors21.default.bold("ablo check")}.
|
|
285100
285162
|
`
|
|
285101
285163
|
);
|
|
285102
285164
|
}
|
|
285103
285165
|
|
|
285104
285166
|
// src/cli/drizzlePull.ts
|
|
285105
285167
|
init_cjs_shims();
|
|
285106
|
-
var
|
|
285168
|
+
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
285107
285169
|
init_errors();
|
|
285108
285170
|
var import_fs11 = require("fs");
|
|
285109
285171
|
var import_path6 = require("path");
|
|
@@ -285242,27 +285304,27 @@ async function drizzlePull(argv) {
|
|
|
285242
285304
|
try {
|
|
285243
285305
|
args = parseDrizzlePullArgs(argv);
|
|
285244
285306
|
} catch (err) {
|
|
285245
|
-
console.error(
|
|
285307
|
+
console.error(import_picocolors22.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
285246
285308
|
process.exit(1);
|
|
285247
285309
|
}
|
|
285248
285310
|
if (!args.schema) {
|
|
285249
285311
|
console.error(
|
|
285250
|
-
|
|
285312
|
+
import_picocolors22.default.red(` No Drizzle schema given.`) + import_picocolors22.default.dim(` Pass the module: ${import_picocolors22.default.bold("ablo pull drizzle src/db/schema.ts")}.`)
|
|
285251
285313
|
);
|
|
285252
285314
|
process.exit(1);
|
|
285253
285315
|
}
|
|
285254
285316
|
if (!(0, import_fs11.existsSync)(args.schema)) {
|
|
285255
|
-
console.error(
|
|
285317
|
+
console.error(import_picocolors22.default.red(` No file at ${import_picocolors22.default.bold(args.schema)}.`));
|
|
285256
285318
|
process.exit(1);
|
|
285257
285319
|
}
|
|
285258
285320
|
if ((0, import_fs11.existsSync)(args.out) && !args.force) {
|
|
285259
285321
|
console.error(
|
|
285260
|
-
|
|
285322
|
+
import_picocolors22.default.red(` ${args.out} already exists.`) + import_picocolors22.default.dim(` Re-run with ${import_picocolors22.default.bold("--force")} to overwrite.`)
|
|
285261
285323
|
);
|
|
285262
285324
|
process.exit(1);
|
|
285263
285325
|
}
|
|
285264
285326
|
console.log(`
|
|
285265
|
-
${brand("ablo")} ${
|
|
285327
|
+
${brand("ablo")} ${import_picocolors22.default.dim("pull drizzle")} ${import_picocolors22.default.dim(args.schema)}
|
|
285266
285328
|
`);
|
|
285267
285329
|
let result;
|
|
285268
285330
|
try {
|
|
@@ -285270,26 +285332,26 @@ async function drizzlePull(argv) {
|
|
|
285270
285332
|
result = await buildSchemaSourceFromDrizzle({ mod, importPath: args.importPath });
|
|
285271
285333
|
} catch (err) {
|
|
285272
285334
|
const msg = err instanceof Error ? err.message : String(err);
|
|
285273
|
-
const hint = msg.includes("Cannot find package 'drizzle-orm'") ?
|
|
285274
|
-
console.error(
|
|
285335
|
+
const hint = msg.includes("Cannot find package 'drizzle-orm'") ? import_picocolors22.default.dim(` (install ${import_picocolors22.default.bold("drizzle-orm")} in this project)`) : "";
|
|
285336
|
+
console.error(import_picocolors22.default.red(` Couldn't load the schema: ${msg}`) + hint);
|
|
285275
285337
|
process.exit(1);
|
|
285276
285338
|
}
|
|
285277
285339
|
if (result.models.length === 0) {
|
|
285278
285340
|
console.error(
|
|
285279
|
-
|
|
285341
|
+
import_picocolors22.default.yellow(` No adoptable tables found`) + import_picocolors22.default.dim(` (a table needs an ${import_picocolors22.default.bold("id")} + ${import_picocolors22.default.bold("organization_id")} column).`)
|
|
285280
285342
|
);
|
|
285281
285343
|
process.exit(1);
|
|
285282
285344
|
}
|
|
285283
285345
|
(0, import_fs11.writeFileSync)(args.out, result.source);
|
|
285284
|
-
console.log(` ${
|
|
285285
|
-
console.log(` ${
|
|
285346
|
+
console.log(` ${import_picocolors22.default.green("\u2713")} wrote ${import_picocolors22.default.bold(args.out)} ${import_picocolors22.default.dim(`(${result.models.length} models)`)}`);
|
|
285347
|
+
console.log(` ${import_picocolors22.default.dim(`models: ${result.models.join(", ")}`)}`);
|
|
285286
285348
|
if (result.skipped.length > 0) {
|
|
285287
|
-
console.log(` ${
|
|
285288
|
-
for (const s of result.skipped) console.log(` ${
|
|
285349
|
+
console.log(` ${import_picocolors22.default.dim(`${result.skipped.length} table(s) skipped:`)}`);
|
|
285350
|
+
for (const s of result.skipped) console.log(` ${import_picocolors22.default.dim(`- ${s.name}: ${s.reason}`)}`);
|
|
285289
285351
|
}
|
|
285290
285352
|
console.log(
|
|
285291
285353
|
`
|
|
285292
|
-
${
|
|
285354
|
+
${import_picocolors22.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors22.default.bold("ablo check")}.
|
|
285293
285355
|
`
|
|
285294
285356
|
);
|
|
285295
285357
|
}
|
|
@@ -285299,7 +285361,7 @@ init_theme();
|
|
|
285299
285361
|
|
|
285300
285362
|
// src/cli/renderError.ts
|
|
285301
285363
|
init_cjs_shims();
|
|
285302
|
-
var
|
|
285364
|
+
var import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
285303
285365
|
init_errors();
|
|
285304
285366
|
init_theme();
|
|
285305
285367
|
var RECOVERY_HINT = {
|
|
@@ -285321,15 +285383,15 @@ function isStringArray(v2) {
|
|
|
285321
285383
|
function renderKnownDetails(details, line) {
|
|
285322
285384
|
if (!details) return;
|
|
285323
285385
|
const { retryAfterSeconds, missingIds, requiredCapability, unexecutable, errors } = details;
|
|
285324
|
-
if (typeof retryAfterSeconds === "number") line(` ${
|
|
285386
|
+
if (typeof retryAfterSeconds === "number") line(` ${import_picocolors23.default.dim("retry")} after ${retryAfterSeconds}s`);
|
|
285325
285387
|
if (isStringArray(missingIds) && missingIds.length > 0) {
|
|
285326
285388
|
const shown = missingIds.slice(0, 5).join(", ");
|
|
285327
285389
|
const more = missingIds.length > 5 ? ` (+${missingIds.length - 5} more)` : "";
|
|
285328
|
-
line(` ${
|
|
285390
|
+
line(` ${import_picocolors23.default.dim("missing")} ${shown}${more}`);
|
|
285329
285391
|
}
|
|
285330
|
-
if (typeof requiredCapability === "string") line(` ${
|
|
285392
|
+
if (typeof requiredCapability === "string") line(` ${import_picocolors23.default.dim("needs")} ${requiredCapability}`);
|
|
285331
285393
|
if (Array.isArray(unexecutable) && unexecutable.length > 0) {
|
|
285332
|
-
line(` ${
|
|
285394
|
+
line(` ${import_picocolors23.default.dim("blocked")} ${unexecutable.length} change(s) can't be applied \u2014 see \`unexecutable\` (--verbose)`);
|
|
285333
285395
|
}
|
|
285334
285396
|
if (Array.isArray(errors)) {
|
|
285335
285397
|
for (const e2 of errors.slice(0, 8)) {
|
|
@@ -285337,7 +285399,7 @@ function renderKnownDetails(details, line) {
|
|
|
285337
285399
|
const rec = e2;
|
|
285338
285400
|
const where = typeof rec.param === "string" ? `${rec.param}: ` : "";
|
|
285339
285401
|
const msg = typeof rec.message === "string" ? rec.message : "";
|
|
285340
|
-
if (msg) line(` ${
|
|
285402
|
+
if (msg) line(` ${import_picocolors23.default.dim("\xB7")} ${where}${msg}`);
|
|
285341
285403
|
}
|
|
285342
285404
|
}
|
|
285343
285405
|
}
|
|
@@ -285348,22 +285410,22 @@ function renderCliError(err, opts = {}) {
|
|
|
285348
285410
|
});
|
|
285349
285411
|
const verbose = opts.verbose ?? (process.argv.includes("--verbose") || process.env.ABLO_VERBOSE === "1");
|
|
285350
285412
|
if (err instanceof AbloError) {
|
|
285351
|
-
const codeTag = err.code ? ` ${
|
|
285413
|
+
const codeTag = err.code ? ` ${import_picocolors23.default.dim(`[${err.code}]`)}` : "";
|
|
285352
285414
|
line("");
|
|
285353
|
-
line(` ${brand("ablo")} ${
|
|
285415
|
+
line(` ${brand("ablo")} ${import_picocolors23.default.red("\u2717")} ${import_picocolors23.default.bold(titleForType(err.type))}${codeTag}`);
|
|
285354
285416
|
line("");
|
|
285355
285417
|
line(` ${err.message}`);
|
|
285356
|
-
if (err.param) line(` ${
|
|
285418
|
+
if (err.param) line(` ${import_picocolors23.default.dim("field")} ${err.param}`);
|
|
285357
285419
|
renderKnownDetails(err.details, line);
|
|
285358
285420
|
const hint = err.code ? RECOVERY_HINT[classifyRecovery(err.code)] : void 0;
|
|
285359
|
-
if (hint) line(` ${
|
|
285360
|
-
if (err.docUrl) line(` ${
|
|
285361
|
-
if (err.requestId) line(` ${
|
|
285421
|
+
if (hint) line(` ${import_picocolors23.default.dim(hint)}`);
|
|
285422
|
+
if (err.docUrl) line(` ${import_picocolors23.default.dim("docs")} ${err.docUrl}`);
|
|
285423
|
+
if (err.requestId) line(` ${import_picocolors23.default.dim("ref")} ${err.requestId}`);
|
|
285362
285424
|
if (verbose) {
|
|
285363
285425
|
if (err.details && Object.keys(err.details).length > 0) {
|
|
285364
|
-
line(` ${
|
|
285426
|
+
line(` ${import_picocolors23.default.dim("details")} ${JSON.stringify(err.details)}`);
|
|
285365
285427
|
}
|
|
285366
|
-
if (err.stack) line(
|
|
285428
|
+
if (err.stack) line(import_picocolors23.default.dim(err.stack));
|
|
285367
285429
|
}
|
|
285368
285430
|
line("");
|
|
285369
285431
|
process.exitCode = 1;
|
|
@@ -285371,16 +285433,16 @@ function renderCliError(err, opts = {}) {
|
|
|
285371
285433
|
}
|
|
285372
285434
|
const message = err instanceof Error ? err.message : String(err);
|
|
285373
285435
|
line("");
|
|
285374
|
-
line(` ${brand("ablo")} ${
|
|
285375
|
-
if (verbose && err instanceof Error && err.stack) line(
|
|
285376
|
-
else line(` ${
|
|
285436
|
+
line(` ${brand("ablo")} ${import_picocolors23.default.red("\u2717")} ${message}`);
|
|
285437
|
+
if (verbose && err instanceof Error && err.stack) line(import_picocolors23.default.dim(err.stack));
|
|
285438
|
+
else line(` ${import_picocolors23.default.dim("Run with --verbose for the full error.")}`);
|
|
285377
285439
|
line("");
|
|
285378
285440
|
process.exitCode = 1;
|
|
285379
285441
|
}
|
|
285380
285442
|
|
|
285381
285443
|
// src/cli/index.ts
|
|
285382
285444
|
var LOGO = `
|
|
285383
|
-
${brand("ablo")} ${
|
|
285445
|
+
${brand("ablo")} ${import_picocolors24.default.dim("sync engine")}
|
|
285384
285446
|
`;
|
|
285385
285447
|
var SUBCOMMAND_USAGE = {
|
|
285386
285448
|
connect: CONNECT_USAGE,
|
|
@@ -285415,7 +285477,7 @@ async function main() {
|
|
|
285415
285477
|
const devArgs = process.argv.slice(3);
|
|
285416
285478
|
const oneShot = devArgs.includes("--no-watch");
|
|
285417
285479
|
console.log(
|
|
285418
|
-
|
|
285480
|
+
import_picocolors24.default.dim(
|
|
285419
285481
|
oneShot ? " `ablo dev --no-watch` is `ablo push` (push once, no watcher) \u2014 running that." : " `ablo dev` is now `ablo push --watch` \u2014 running that."
|
|
285420
285482
|
)
|
|
285421
285483
|
);
|
|
@@ -285444,14 +285506,14 @@ async function main() {
|
|
|
285444
285506
|
const guard = guardActiveProjectKey();
|
|
285445
285507
|
if (!guard.ok && guard.available.length > 0 && !rest.includes("--url")) {
|
|
285446
285508
|
console.error(
|
|
285447
|
-
` ${
|
|
285509
|
+
` ${import_picocolors24.default.yellow("\u26A0")} active project ${import_picocolors24.default.bold(guard.activeProfile)} has no stored key ${import_picocolors24.default.dim(
|
|
285448
285510
|
`(you have keys for: ${guard.available.join(", ")})`
|
|
285449
285511
|
)}`
|
|
285450
285512
|
);
|
|
285451
285513
|
const loginCmd = guard.activeProfile === "default" ? "ablo login" : `ablo login --project ${guard.activeProfile}`;
|
|
285452
285514
|
console.error(
|
|
285453
|
-
|
|
285454
|
-
` Mint one with ${
|
|
285515
|
+
import_picocolors24.default.dim(
|
|
285516
|
+
` Mint one with ${import_picocolors24.default.bold(loginCmd)}, or switch with ${import_picocolors24.default.bold("ablo projects use <slug>")}.`
|
|
285455
285517
|
)
|
|
285456
285518
|
);
|
|
285457
285519
|
process.exitCode = 1;
|
|
@@ -285469,13 +285531,13 @@ async function main() {
|
|
|
285469
285531
|
await generate(process.argv.slice(3));
|
|
285470
285532
|
} else if (command === "schema") {
|
|
285471
285533
|
console.error(
|
|
285472
|
-
` ${
|
|
285534
|
+
` ${import_picocolors24.default.red("\u2717")} \`ablo schema push\` was renamed to \`${brand("ablo push")}\`.`
|
|
285473
285535
|
);
|
|
285474
285536
|
console.error(` Run \`ablo push${process.argv.slice(4).join(" ") ? " " + process.argv.slice(4).join(" ") : ""}\` instead.`);
|
|
285475
285537
|
process.exitCode = 1;
|
|
285476
285538
|
} else {
|
|
285477
285539
|
console.log(LOGO);
|
|
285478
|
-
console.log(` ${
|
|
285540
|
+
console.log(` ${import_picocolors24.default.bold("Usage:")}`);
|
|
285479
285541
|
console.log(` npx ablo init Scaffold ablo/ directory + starter schema`);
|
|
285480
285542
|
console.log(` npx ablo init --yes [--framework nextjs] Non-interactive (agents/CI): no prompts, flag-driven`);
|
|
285481
285543
|
console.log(` [--auth apikey] [--storage replication|endpoint] [--project <slug>] [--no-project]`);
|
|
@@ -285512,10 +285574,10 @@ async function main() {
|
|
|
285512
285574
|
console.log(` npx ablo generate Emit TypeScript types from your schema`);
|
|
285513
285575
|
console.log(` npx ablo generate --out path.ts Write generated types to a path`);
|
|
285514
285576
|
console.log();
|
|
285515
|
-
console.log(` ${
|
|
285577
|
+
console.log(` ${import_picocolors24.default.bold("Schema workflow:")}`);
|
|
285516
285578
|
console.log(` The server holds its own copy of your schema \u2014 edit ${brand("ablo/schema.ts")}, then`);
|
|
285517
285579
|
console.log(` run ${brand("ablo push")} (or keep ${brand("ablo dev")} running) before the server will accept`);
|
|
285518
|
-
console.log(` writes to new or changed models. Skip it and writes fail with ${
|
|
285580
|
+
console.log(` writes to new or changed models. Skip it and writes fail with ${import_picocolors24.default.yellow("server_execute_unknown_model")}.`);
|
|
285519
285581
|
console.log();
|
|
285520
285582
|
}
|
|
285521
285583
|
}
|
|
@@ -285566,7 +285628,7 @@ async function ensureInitProject(opts) {
|
|
|
285566
285628
|
const ensured = await ensureProject(slug);
|
|
285567
285629
|
if (ensured) {
|
|
285568
285630
|
console.log(
|
|
285569
|
-
` ${
|
|
285631
|
+
` ${import_picocolors24.default.green("\u2713")} ${ensured.created ? "Created" : "Using"} project ${import_picocolors24.default.bold(ensured.slug)} ${import_picocolors24.default.dim(`(${ensured.id})`)} \u2014 keys you mint for it are isolated from the org's other apps.`
|
|
285570
285632
|
);
|
|
285571
285633
|
}
|
|
285572
285634
|
}
|
|
@@ -285608,7 +285670,7 @@ async function chooseBool(flagValue, fallback, interactive, prompt) {
|
|
|
285608
285670
|
async function init(args = []) {
|
|
285609
285671
|
const opts = parseInitArgs(args);
|
|
285610
285672
|
const interactive = Boolean(process.stdin.isTTY) && !opts.yes && !process.env.CI;
|
|
285611
|
-
Ie(`${brand("ablo")} ${
|
|
285673
|
+
Ie(`${brand("ablo")} ${import_picocolors24.default.dim("sync engine")}`);
|
|
285612
285674
|
if (!(0, import_fs12.existsSync)("package.json")) {
|
|
285613
285675
|
xe("No package.json found. Run this from your project root.");
|
|
285614
285676
|
process.exit(1);
|
|
@@ -285686,7 +285748,7 @@ async function init(args = []) {
|
|
|
285686
285748
|
if (pullExisting) {
|
|
285687
285749
|
const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
|
|
285688
285750
|
if (!dbUrl) {
|
|
285689
|
-
schemaNote =
|
|
285751
|
+
schemaNote = import_picocolors24.default.dim(" (no DATABASE_URL \u2014 wrote starter; run `ablo pull` later)");
|
|
285690
285752
|
} else {
|
|
285691
285753
|
try {
|
|
285692
285754
|
const pulled = await buildSchemaSourceFromDb({
|
|
@@ -285696,12 +285758,12 @@ async function init(args = []) {
|
|
|
285696
285758
|
});
|
|
285697
285759
|
if (pulled.models.length > 0) {
|
|
285698
285760
|
schemaSource = pulled.source;
|
|
285699
|
-
schemaNote =
|
|
285761
|
+
schemaNote = import_picocolors24.default.dim(` (pulled ${pulled.models.length} models)`);
|
|
285700
285762
|
} else {
|
|
285701
|
-
schemaNote =
|
|
285763
|
+
schemaNote = import_picocolors24.default.dim(" (no adoptable tables \u2014 wrote starter)");
|
|
285702
285764
|
}
|
|
285703
285765
|
} catch {
|
|
285704
|
-
schemaNote =
|
|
285766
|
+
schemaNote = import_picocolors24.default.dim(" (pull failed \u2014 wrote starter)");
|
|
285705
285767
|
}
|
|
285706
285768
|
}
|
|
285707
285769
|
}
|
|
@@ -285727,14 +285789,14 @@ async function init(args = []) {
|
|
|
285727
285789
|
const existing = (0, import_fs12.readFileSync)(envFile, "utf-8");
|
|
285728
285790
|
if (!existing.includes("ABLO_")) {
|
|
285729
285791
|
(0, import_fs12.writeFileSync)(envFile, existing + "\n" + envBody);
|
|
285730
|
-
created.push(`${envFile} ${
|
|
285792
|
+
created.push(`${envFile} ${import_picocolors24.default.dim("(appended)")}`);
|
|
285731
285793
|
} else {
|
|
285732
|
-
created.push(`${envFile} ${
|
|
285794
|
+
created.push(`${envFile} ${import_picocolors24.default.dim("(already configured)")}`);
|
|
285733
285795
|
}
|
|
285734
285796
|
}
|
|
285735
285797
|
if (wireRealKey && resolvedKey) {
|
|
285736
285798
|
wireEnvLocal(resolvedKey);
|
|
285737
|
-
created.push(`.env.local ${
|
|
285799
|
+
created.push(`.env.local ${import_picocolors24.default.dim("(ABLO_API_KEY set from your login)")}`);
|
|
285738
285800
|
}
|
|
285739
285801
|
if (agent) {
|
|
285740
285802
|
(0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "agent.ts"), generateAgent());
|
|
@@ -285749,17 +285811,17 @@ async function init(args = []) {
|
|
|
285749
285811
|
}
|
|
285750
285812
|
const providersPath = (0, import_path7.join)(layout.appBase, "providers.tsx");
|
|
285751
285813
|
(0, import_fs12.writeFileSync)(providersPath, generateProviders());
|
|
285752
|
-
created.push(`${providersPath} ${
|
|
285814
|
+
created.push(`${providersPath} ${import_picocolors24.default.dim(`(wrap ${(0, import_path7.join)(layout.appBase, "layout.tsx")} in <Providers>)`)}`);
|
|
285753
285815
|
const sessionDir = (0, import_path7.join)(layout.appBase, "api", "ablo-session");
|
|
285754
285816
|
(0, import_fs12.mkdirSync)(sessionDir, { recursive: true });
|
|
285755
285817
|
(0, import_fs12.writeFileSync)((0, import_path7.join)(sessionDir, "route.ts"), generateSessionRoute());
|
|
285756
|
-
created.push(`${(0, import_path7.join)(sessionDir, "route.ts")} ${
|
|
285818
|
+
created.push(`${(0, import_path7.join)(sessionDir, "route.ts")} ${import_picocolors24.default.dim("(wire your auth)")}`);
|
|
285757
285819
|
}
|
|
285758
285820
|
if (framework !== "vanilla") {
|
|
285759
285821
|
(0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "TaskList.tsx"), generateComponent());
|
|
285760
285822
|
created.push(`${abloDir}/TaskList.tsx`);
|
|
285761
285823
|
}
|
|
285762
|
-
Me(created.map((f) => `${
|
|
285824
|
+
Me(created.map((f) => `${import_picocolors24.default.green("\u2713")} ${f}`).join("\n"), "Created");
|
|
285763
285825
|
const pm = detectPackageManager();
|
|
285764
285826
|
if (opts.install) {
|
|
285765
285827
|
const s = Y2();
|
|
@@ -285768,47 +285830,47 @@ async function init(args = []) {
|
|
|
285768
285830
|
(0, import_child_process3.execSync)(`${pm} add @abloatai/ablo`, { stdio: "ignore" });
|
|
285769
285831
|
s.stop("Installed @abloatai/ablo");
|
|
285770
285832
|
} catch {
|
|
285771
|
-
s.stop(`${
|
|
285833
|
+
s.stop(`${import_picocolors24.default.yellow("!")} Couldn't auto-install \u2014 run ${import_picocolors24.default.bold(`${pm} install @abloatai/ablo`)}`);
|
|
285772
285834
|
}
|
|
285773
285835
|
}
|
|
285774
285836
|
const steps = [
|
|
285775
|
-
`Get a ${
|
|
285776
|
-
`Run ${
|
|
285777
|
-
`Set ${
|
|
285778
|
-
`Run ${
|
|
285837
|
+
`Get a ${import_picocolors24.default.bold("sk_test_")} key at ${import_picocolors24.default.cyan("https://abloatai.com")}`,
|
|
285838
|
+
`Run ${import_picocolors24.default.bold("npx ablo login")} (or add ${import_picocolors24.default.bold("ABLO_API_KEY")} to ${import_picocolors24.default.bold(envFile)})`,
|
|
285839
|
+
`Set ${import_picocolors24.default.bold("DATABASE_URL")} in ${import_picocolors24.default.bold(envFile)} \u2014 your Postgres is the system of record; rows live there, never with Ablo`,
|
|
285840
|
+
`Run ${import_picocolors24.default.bold("npx ablo dev")} \u2014 pushes your schema definition and watches for changes`,
|
|
285779
285841
|
...storage === "replication" ? [
|
|
285780
|
-
`Connect your database \u2014 ${
|
|
285781
|
-
`Verify it \u2014 ${
|
|
285782
|
-
`Register it \u2014 ${
|
|
285842
|
+
`Connect your database \u2014 ${import_picocolors24.default.bold("npx ablo connect")} prints the one-time logical-replication setup SQL to run on your Postgres`,
|
|
285843
|
+
`Verify it \u2014 ${import_picocolors24.default.bold("npx ablo connect check")} walks wal_level, the publication, the role, and replica identity, with the exact fix for anything missing`,
|
|
285844
|
+
`Register it \u2014 ${import_picocolors24.default.bold("npx ablo connect register")} tells Ablo to start replicating; your app keeps writing through your own backend while Ablo tails the WAL`
|
|
285783
285845
|
] : [
|
|
285784
|
-
`Provision your DB: ${
|
|
285846
|
+
`Provision your DB: ${import_picocolors24.default.bold("npx ablo migrate")} (creates your Ablo-model tables + the adapter tables; keep your own migrations for everything else), then mount ${import_picocolors24.default.bold(`${abloDir}/data-source.ts`)} at ${import_picocolors24.default.bold("/api/ablo/source")}`
|
|
285785
285847
|
],
|
|
285786
285848
|
...framework === "nextjs" ? [
|
|
285787
|
-
`Wrap ${
|
|
285849
|
+
`Wrap ${import_picocolors24.default.bold((0, import_path7.join)(layout.appBase, "layout.tsx"))} in ${import_picocolors24.default.bold("<Providers>")} (${(0, import_path7.join)(layout.appBase, "providers.tsx")}) and add your auth to ${import_picocolors24.default.bold((0, import_path7.join)(layout.appBase, "api", "ablo-session", "route.ts"))}`
|
|
285788
285850
|
] : [],
|
|
285789
|
-
`Run ${
|
|
285851
|
+
`Run ${import_picocolors24.default.bold(`${pm} run dev`)} and open two browser tabs \u2014 changes sync in real-time`,
|
|
285790
285852
|
...agent ? [
|
|
285791
|
-
`Run ${
|
|
285792
|
-
`Run ${
|
|
285853
|
+
`Run ${import_picocolors24.default.bold(`npx tsx ${abloDir}/agent.ts`)} \u2014 an AI teammate edits the same tasks`,
|
|
285854
|
+
`Run ${import_picocolors24.default.bold("npx ablo logs")} to watch human + agent commits stream by`
|
|
285793
285855
|
] : []
|
|
285794
285856
|
];
|
|
285795
285857
|
Me(steps.map((s, i) => `${i + 1}. ${s}`).join("\n"), "Next steps");
|
|
285796
285858
|
const existingKey = resolveApiKey("sandbox");
|
|
285797
285859
|
if (existingKey) {
|
|
285798
285860
|
await ensureInitProject(opts);
|
|
285799
|
-
Se(`Already authorized ${
|
|
285861
|
+
Se(`Already authorized ${import_picocolors24.default.dim(`(${existingKey.slice(0, 11)}\u2026)`)} \u2014 run ${import_picocolors24.default.bold("npx ablo push")} next. ${import_picocolors24.default.dim("Docs:")} https://abloatai.com/docs`);
|
|
285800
285862
|
return;
|
|
285801
285863
|
}
|
|
285802
285864
|
if (interactive && opts.login) {
|
|
285803
285865
|
const loginNow = await ye({ message: "Log in now? (opens your browser)", initialValue: true });
|
|
285804
285866
|
if (!pD(loginNow) && loginNow) {
|
|
285805
|
-
Se(`${
|
|
285867
|
+
Se(`${import_picocolors24.default.dim("Docs:")} https://abloatai.com/docs`);
|
|
285806
285868
|
await login();
|
|
285807
285869
|
await ensureInitProject(opts);
|
|
285808
285870
|
return;
|
|
285809
285871
|
}
|
|
285810
285872
|
}
|
|
285811
|
-
Se(`Run ${
|
|
285873
|
+
Se(`Run ${import_picocolors24.default.bold("npx ablo login")} when ready. ${import_picocolors24.default.dim("Docs:")} https://abloatai.com/docs`);
|
|
285812
285874
|
}
|
|
285813
285875
|
function generateSchema() {
|
|
285814
285876
|
return `import { defineSchema, model, relation, z } from '@abloatai/ablo/schema';
|