@amerged/ohmyhost-cli 0.1.42 → 0.1.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-HBM2X2MV.js → chunk-5TO5I2HF.js} +77 -126
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/apps/product-cli/package.json +1 -1
- package/src/apps/product-cli/src/psql-session.ts +20 -32
- package/src/apps/product-cli/src/runtime-contract.ts +1 -13
- package/src/packages/contracts/src/credit-pricing.ts +0 -90
- package/src/packages/sdk-ts/package.json +1 -1
- package/src/packages/sdk-ts/src/generated/index.ts +0 -1
- package/src/packages/sdk-ts/src/generated/sdk.gen.ts +1 -1
- package/src/packages/sdk-ts/src/generated/types.gen.ts +0 -22
package/README.md
CHANGED
package/dist/bin.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire as __ohmyhostCreateRequire } from "node:module"; const require = __ohmyhostCreateRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
runProductionCli
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-5TO5I2HF.js";
|
|
6
6
|
import "./chunk-7D2RFLWM.js";
|
|
7
7
|
|
|
8
8
|
// apps/product-cli/dist/bin.js
|
|
@@ -40055,112 +40055,6 @@ function isSaasCnameTarget(value) {
|
|
|
40055
40055
|
return value === "customers.omh.st" || value === "customers.ohmyho.st";
|
|
40056
40056
|
}
|
|
40057
40057
|
|
|
40058
|
-
// packages/contracts/dist/credit-pricing.js
|
|
40059
|
-
function assertOrganizationCreditUsage(value) {
|
|
40060
|
-
const record7 = (input, keys) => {
|
|
40061
|
-
if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).sort().join() !== keys.split(",").sort().join())
|
|
40062
|
-
throw new TypeError("Invalid usage fields");
|
|
40063
|
-
return input;
|
|
40064
|
-
};
|
|
40065
|
-
const identifier2 = (input) => typeof input === "string" && /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(input);
|
|
40066
|
-
const amount = (input) => typeof input === "string" && /^(0|[1-9][0-9]{0,37})$/u.test(input);
|
|
40067
|
-
const instant2 = (input) => typeof input === "string" && Number.isFinite(Date.parse(input)) && new Date(input).toISOString() === input;
|
|
40068
|
-
const report = record7(value, "organization_id,as_of,unit,period,from,projects,days");
|
|
40069
|
-
if (!identifier2(report["organization_id"]) || !instant2(report["as_of"]) || !instant2(report["from"]) || report["unit"] !== "microcredits" || typeof report["period"] !== "string" || !/^20[0-9]{2}-(0[1-9]|1[0-2])$/u.test(report["period"]) || !Array.isArray(report["projects"]) || report["projects"].length > 1e3 || !Array.isArray(report["days"]) || report["days"].length > 15)
|
|
40070
|
-
throw new TypeError("Invalid usage report");
|
|
40071
|
-
let previous = "";
|
|
40072
|
-
for (const input of report["projects"]) {
|
|
40073
|
-
const project = record7(input, "project_id,used_micros");
|
|
40074
|
-
if (!identifier2(project["project_id"]) || String(project["project_id"]) <= previous || !amount(project["used_micros"]))
|
|
40075
|
-
throw new TypeError("Invalid usage project");
|
|
40076
|
-
previous = String(project["project_id"]);
|
|
40077
|
-
}
|
|
40078
|
-
let previousDay = "";
|
|
40079
|
-
for (const input of report["days"]) {
|
|
40080
|
-
const day = record7(input, "day,projects");
|
|
40081
|
-
if (typeof day["day"] !== "string" || !/^20[0-9]{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/u.test(day["day"]) || day["day"] <= previousDay || !Array.isArray(day["projects"]) || day["projects"].length > 1e3)
|
|
40082
|
-
throw new TypeError("Invalid usage day");
|
|
40083
|
-
previousDay = day["day"];
|
|
40084
|
-
previous = "";
|
|
40085
|
-
for (const projectInput of day["projects"]) {
|
|
40086
|
-
const project = record7(projectInput, "project_id,meters");
|
|
40087
|
-
if (!identifier2(project["project_id"]) || String(project["project_id"]) <= previous || !Array.isArray(project["meters"]) || project["meters"].length > 1e3)
|
|
40088
|
-
throw new TypeError("Invalid usage project");
|
|
40089
|
-
previous = String(project["project_id"]);
|
|
40090
|
-
for (const meterInput of project["meters"]) {
|
|
40091
|
-
const meter = record7(meterInput, "environment_id,meter,unit,rate_card_id,quantity,charged_micros");
|
|
40092
|
-
if (meter["environment_id"] !== null && !identifier2(meter["environment_id"]))
|
|
40093
|
-
throw new TypeError("Invalid usage environment");
|
|
40094
|
-
for (const key of ["meter", "unit", "rate_card_id"])
|
|
40095
|
-
if (typeof meter[key] !== "string" || !/^[A-Za-z0-9_.:/-]{1,128}$/u.test(meter[key]))
|
|
40096
|
-
throw new TypeError("Invalid usage meter");
|
|
40097
|
-
if (typeof meter["quantity"] !== "string" || !/^(0|[1-9][0-9]{0,29})(\.[0-9]{1,12})?$/u.test(meter["quantity"]) || !amount(meter["charged_micros"]))
|
|
40098
|
-
throw new TypeError("Invalid usage amount");
|
|
40099
|
-
}
|
|
40100
|
-
}
|
|
40101
|
-
}
|
|
40102
|
-
}
|
|
40103
|
-
function assertPublishedCreditRateCards(value) {
|
|
40104
|
-
if (!Array.isArray(value) || value.length > 4)
|
|
40105
|
-
throw new TypeError("Invalid published prices");
|
|
40106
|
-
const ids = /* @__PURE__ */ new Set();
|
|
40107
|
-
for (const input of value) {
|
|
40108
|
-
if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).sort().join() !== "currency,effective_from,id,published_at,rates")
|
|
40109
|
-
throw new TypeError("Invalid published price fields");
|
|
40110
|
-
const card = input;
|
|
40111
|
-
if (typeof card["id"] !== "string" || !/^[a-z0-9_.-]{1,128}$/u.test(card["id"]) || ids.has(card["id"]) || card["currency"] !== "USD")
|
|
40112
|
-
throw new TypeError("Invalid price identity");
|
|
40113
|
-
ids.add(card["id"]);
|
|
40114
|
-
for (const key of ["published_at", "effective_from"]) {
|
|
40115
|
-
const date2 = card[key];
|
|
40116
|
-
if (typeof date2 !== "string" || !Number.isFinite(Date.parse(date2)) || new Date(date2).toISOString() !== date2)
|
|
40117
|
-
throw new TypeError("Invalid price dates");
|
|
40118
|
-
}
|
|
40119
|
-
if (String(card["published_at"]) > String(card["effective_from"]) || !Array.isArray(card["rates"]) || card["rates"].length < 1 || card["rates"].length > 32)
|
|
40120
|
-
throw new TypeError("Invalid price publication");
|
|
40121
|
-
const meters = /* @__PURE__ */ new Set();
|
|
40122
|
-
for (const item of card["rates"]) {
|
|
40123
|
-
if (!item || typeof item !== "object" || Array.isArray(item) || Object.keys(item).sort().join() !== "credit_micros,meter,provider_cost_micros,unit,units_per_charge")
|
|
40124
|
-
throw new TypeError("Invalid rate fields");
|
|
40125
|
-
const rate = item;
|
|
40126
|
-
for (const key of ["meter", "unit"])
|
|
40127
|
-
if (typeof rate[key] !== "string" || !/^[a-z0-9_.-]{1,128}$/u.test(String(rate[key])))
|
|
40128
|
-
throw new TypeError("Invalid rate unit");
|
|
40129
|
-
const identity = String(rate["meter"]) + "/" + String(rate["unit"]);
|
|
40130
|
-
if (meters.has(identity))
|
|
40131
|
-
throw new TypeError("Duplicate rate");
|
|
40132
|
-
meters.add(identity);
|
|
40133
|
-
for (const key of ["units_per_charge", "provider_cost_micros", "credit_micros"]) {
|
|
40134
|
-
const amount = rate[key];
|
|
40135
|
-
if (key === "provider_cost_micros" && amount === null && rate["meter"] === "pgcf.memory.active")
|
|
40136
|
-
continue;
|
|
40137
|
-
if (typeof amount !== "string" || !/^(0|[1-9][0-9]{0,18})$/u.test(amount) || BigInt(amount) > 9223372036854775807n || key === "units_per_charge" && amount === "0")
|
|
40138
|
-
throw new TypeError("Invalid rate amount");
|
|
40139
|
-
}
|
|
40140
|
-
}
|
|
40141
|
-
}
|
|
40142
|
-
}
|
|
40143
|
-
function assertOrganizationAccount(value) {
|
|
40144
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "as_of,available_micros,monthly_micros,name,next_expiry,one_time_micros,organization_id,paid_until,plan,plan_source,reserved_micros,workos_organization_id")
|
|
40145
|
-
throw new TypeError("Invalid account fields");
|
|
40146
|
-
const v = value;
|
|
40147
|
-
if (typeof v["organization_id"] !== "string" || !/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(v["organization_id"]) || typeof v["name"] !== "string" || v["name"].length > 128 || typeof v["workos_organization_id"] !== "string" || !/^org_[A-Za-z0-9_]+$/u.test(v["workos_organization_id"]) || v["plan"] !== "paid" && v["plan"] !== "free" || !["free", "stripe", "granted", "manual"].includes(String(v["plan_source"])) || v["plan"] === "free" !== (v["plan_source"] === "free"))
|
|
40148
|
-
throw new TypeError("Invalid account scope");
|
|
40149
|
-
for (const field of ["available_micros", "monthly_micros", "one_time_micros", "reserved_micros"])
|
|
40150
|
-
if (typeof v[field] !== "string" || !/^(0|[1-9][0-9]{0,18})$/u.test(v[field]))
|
|
40151
|
-
throw new TypeError("Invalid account credits");
|
|
40152
|
-
const held = BigInt(String(v["reserved_micros"]));
|
|
40153
|
-
const balance = BigInt(String(v["monthly_micros"])) + BigInt(String(v["one_time_micros"]));
|
|
40154
|
-
if (BigInt(String(v["available_micros"])) !== (balance > held ? balance - held : 0n))
|
|
40155
|
-
throw new TypeError("Account credit buckets do not balance");
|
|
40156
|
-
for (const field of ["as_of", "paid_until", "next_expiry"]) {
|
|
40157
|
-
if (field !== "as_of" && v[field] === null)
|
|
40158
|
-
continue;
|
|
40159
|
-
if (typeof v[field] !== "string" || !Number.isFinite(Date.parse(v[field])) || new Date(v[field]).toISOString() !== v[field])
|
|
40160
|
-
throw new TypeError("Invalid account timestamp");
|
|
40161
|
-
}
|
|
40162
|
-
}
|
|
40163
|
-
|
|
40164
40058
|
// packages/contracts/dist/problem-policy.js
|
|
40165
40059
|
var PROBLEM_POLICY = {
|
|
40166
40060
|
source_generation_conflict: {
|
|
@@ -40906,19 +40800,12 @@ function parseOrganizationCredits(value) {
|
|
|
40906
40800
|
"available_micros",
|
|
40907
40801
|
"reserved_micros",
|
|
40908
40802
|
"grace_started_at",
|
|
40909
|
-
"grace_expires_at"
|
|
40910
|
-
"active_meters",
|
|
40911
|
-
"rate_cards"
|
|
40803
|
+
"grace_expires_at"
|
|
40912
40804
|
]);
|
|
40913
|
-
try {
|
|
40914
|
-
assertPublishedCreditRateCards(input["rate_cards"]);
|
|
40915
|
-
} catch {
|
|
40916
|
-
throw new ResponseContractError();
|
|
40917
|
-
}
|
|
40918
40805
|
const graceStart = input["grace_started_at"], graceEnd = input["grace_expires_at"];
|
|
40919
40806
|
if (!(graceStart === null && graceEnd === null) && !(isIsoInstant(graceStart) && isIsoInstant(graceEnd) && Date.parse(graceEnd) - Date.parse(graceStart) === 6048e5))
|
|
40920
40807
|
throw new ResponseContractError();
|
|
40921
|
-
if (!isUlid(input["organization_id"]) || input["unit"] !== "microcredits" || !isIsoInstant(input["as_of"])
|
|
40808
|
+
if (!isUlid(input["organization_id"]) || input["unit"] !== "microcredits" || !isIsoInstant(input["as_of"]))
|
|
40922
40809
|
throw new ResponseContractError();
|
|
40923
40810
|
for (const field of ["available_micros", "reserved_micros"]) {
|
|
40924
40811
|
const amount = input[field];
|
|
@@ -45312,6 +45199,72 @@ function exact3(value, keys) {
|
|
|
45312
45199
|
return Object.keys(value).sort().join(",") === [...keys].sort().join(",");
|
|
45313
45200
|
}
|
|
45314
45201
|
|
|
45202
|
+
// packages/contracts/dist/credit-pricing.js
|
|
45203
|
+
function assertOrganizationCreditUsage(value) {
|
|
45204
|
+
const record7 = (input, keys) => {
|
|
45205
|
+
if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).sort().join() !== keys.split(",").sort().join())
|
|
45206
|
+
throw new TypeError("Invalid usage fields");
|
|
45207
|
+
return input;
|
|
45208
|
+
};
|
|
45209
|
+
const identifier2 = (input) => typeof input === "string" && /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(input);
|
|
45210
|
+
const amount = (input) => typeof input === "string" && /^(0|[1-9][0-9]{0,37})$/u.test(input);
|
|
45211
|
+
const instant2 = (input) => typeof input === "string" && Number.isFinite(Date.parse(input)) && new Date(input).toISOString() === input;
|
|
45212
|
+
const report = record7(value, "organization_id,as_of,unit,period,from,projects,days");
|
|
45213
|
+
if (!identifier2(report["organization_id"]) || !instant2(report["as_of"]) || !instant2(report["from"]) || report["unit"] !== "microcredits" || typeof report["period"] !== "string" || !/^20[0-9]{2}-(0[1-9]|1[0-2])$/u.test(report["period"]) || !Array.isArray(report["projects"]) || report["projects"].length > 1e3 || !Array.isArray(report["days"]) || report["days"].length > 15)
|
|
45214
|
+
throw new TypeError("Invalid usage report");
|
|
45215
|
+
let previous = "";
|
|
45216
|
+
for (const input of report["projects"]) {
|
|
45217
|
+
const project = record7(input, "project_id,used_micros");
|
|
45218
|
+
if (!identifier2(project["project_id"]) || String(project["project_id"]) <= previous || !amount(project["used_micros"]))
|
|
45219
|
+
throw new TypeError("Invalid usage project");
|
|
45220
|
+
previous = String(project["project_id"]);
|
|
45221
|
+
}
|
|
45222
|
+
let previousDay = "";
|
|
45223
|
+
for (const input of report["days"]) {
|
|
45224
|
+
const day = record7(input, "day,projects");
|
|
45225
|
+
if (typeof day["day"] !== "string" || !/^20[0-9]{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/u.test(day["day"]) || day["day"] <= previousDay || !Array.isArray(day["projects"]) || day["projects"].length > 1e3)
|
|
45226
|
+
throw new TypeError("Invalid usage day");
|
|
45227
|
+
previousDay = day["day"];
|
|
45228
|
+
previous = "";
|
|
45229
|
+
for (const projectInput of day["projects"]) {
|
|
45230
|
+
const project = record7(projectInput, "project_id,meters");
|
|
45231
|
+
if (!identifier2(project["project_id"]) || String(project["project_id"]) <= previous || !Array.isArray(project["meters"]) || project["meters"].length > 1e3)
|
|
45232
|
+
throw new TypeError("Invalid usage project");
|
|
45233
|
+
previous = String(project["project_id"]);
|
|
45234
|
+
for (const meterInput of project["meters"]) {
|
|
45235
|
+
const meter = record7(meterInput, "environment_id,meter,unit,rate_card_id,quantity,charged_micros");
|
|
45236
|
+
if (meter["environment_id"] !== null && !identifier2(meter["environment_id"]))
|
|
45237
|
+
throw new TypeError("Invalid usage environment");
|
|
45238
|
+
for (const key of ["meter", "unit", "rate_card_id"])
|
|
45239
|
+
if (typeof meter[key] !== "string" || !/^[A-Za-z0-9_.:/-]{1,128}$/u.test(meter[key]))
|
|
45240
|
+
throw new TypeError("Invalid usage meter");
|
|
45241
|
+
if (typeof meter["quantity"] !== "string" || !/^(0|[1-9][0-9]{0,29})(\.[0-9]{1,12})?$/u.test(meter["quantity"]) || !amount(meter["charged_micros"]))
|
|
45242
|
+
throw new TypeError("Invalid usage amount");
|
|
45243
|
+
}
|
|
45244
|
+
}
|
|
45245
|
+
}
|
|
45246
|
+
}
|
|
45247
|
+
function assertOrganizationAccount(value) {
|
|
45248
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "as_of,available_micros,monthly_micros,name,next_expiry,one_time_micros,organization_id,paid_until,plan,plan_source,reserved_micros,workos_organization_id")
|
|
45249
|
+
throw new TypeError("Invalid account fields");
|
|
45250
|
+
const v = value;
|
|
45251
|
+
if (typeof v["organization_id"] !== "string" || !/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(v["organization_id"]) || typeof v["name"] !== "string" || v["name"].length > 128 || typeof v["workos_organization_id"] !== "string" || !/^org_[A-Za-z0-9_]+$/u.test(v["workos_organization_id"]) || v["plan"] !== "paid" && v["plan"] !== "free" || !["free", "stripe", "granted", "manual"].includes(String(v["plan_source"])) || v["plan"] === "free" !== (v["plan_source"] === "free"))
|
|
45252
|
+
throw new TypeError("Invalid account scope");
|
|
45253
|
+
for (const field of ["available_micros", "monthly_micros", "one_time_micros", "reserved_micros"])
|
|
45254
|
+
if (typeof v[field] !== "string" || !/^(0|[1-9][0-9]{0,18})$/u.test(v[field]))
|
|
45255
|
+
throw new TypeError("Invalid account credits");
|
|
45256
|
+
const held = BigInt(String(v["reserved_micros"]));
|
|
45257
|
+
const balance = BigInt(String(v["monthly_micros"])) + BigInt(String(v["one_time_micros"]));
|
|
45258
|
+
if (BigInt(String(v["available_micros"])) !== (balance > held ? balance - held : 0n))
|
|
45259
|
+
throw new TypeError("Account credit buckets do not balance");
|
|
45260
|
+
for (const field of ["as_of", "paid_until", "next_expiry"]) {
|
|
45261
|
+
if (field !== "as_of" && v[field] === null)
|
|
45262
|
+
continue;
|
|
45263
|
+
if (typeof v[field] !== "string" || !Number.isFinite(Date.parse(v[field])) || new Date(v[field]).toISOString() !== v[field])
|
|
45264
|
+
throw new TypeError("Invalid account timestamp");
|
|
45265
|
+
}
|
|
45266
|
+
}
|
|
45267
|
+
|
|
45315
45268
|
// packages/contracts/dist/database-compute.js
|
|
45316
45269
|
function assertDatabaseCompute(value) {
|
|
45317
45270
|
if (!record4(value) || !exact4(value, ["data_mode", "database", "environment", "observed_at", "project_id"]) || typeof value["project_id"] !== "string" || !/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(value["project_id"]) || !["dev", "prod"].includes(String(value["environment"])) || !["shared", "isolated"].includes(String(value["data_mode"])) || typeof value["observed_at"] !== "string" || !Number.isFinite(Date.parse(value["observed_at"])) || new Date(value["observed_at"]).toISOString() !== value["observed_at"])
|
|
@@ -45364,7 +45317,7 @@ function exact4(v, keys) {
|
|
|
45364
45317
|
// apps/product-cli/package.json
|
|
45365
45318
|
var package_default = {
|
|
45366
45319
|
name: "@ohmyhost/product-cli",
|
|
45367
|
-
version: "0.1.
|
|
45320
|
+
version: "0.1.43",
|
|
45368
45321
|
private: true,
|
|
45369
45322
|
ohmyhost: {
|
|
45370
45323
|
deployment: "production",
|
|
@@ -55906,9 +55859,9 @@ async function startBridge(raw, hooks = {}) {
|
|
|
55906
55859
|
// apps/product-cli/dist/psql-session.js
|
|
55907
55860
|
var runLocalPsql = async (input) => {
|
|
55908
55861
|
input.signal.throwIfAborted();
|
|
55909
|
-
if (input.websocketUrl !==
|
|
55862
|
+
if (input.websocketUrl !== `wss://${input.host}/`)
|
|
55910
55863
|
throw new Error("Invalid PGCF transport");
|
|
55911
|
-
const bridge =
|
|
55864
|
+
const bridge = await startBridge({
|
|
55912
55865
|
endpoint: input.websocketUrl,
|
|
55913
55866
|
database: input.database,
|
|
55914
55867
|
user: input.user,
|
|
@@ -55918,18 +55871,16 @@ var runLocalPsql = async (input) => {
|
|
|
55918
55871
|
input.signal.throwIfAborted();
|
|
55919
55872
|
const environment = Object.fromEntries(Object.entries(process.env).filter(([key]) => !/^PG[A-Z_]+$/iu.test(key)));
|
|
55920
55873
|
return await new Promise((resolve7, reject) => {
|
|
55921
|
-
const child = spawn("psql", ["-h", bridge
|
|
55874
|
+
const child = spawn("psql", ["-h", bridge.host, "-U", input.user, "-d", input.database], {
|
|
55922
55875
|
stdio: "inherit",
|
|
55923
55876
|
signal: input.signal,
|
|
55924
55877
|
env: {
|
|
55925
55878
|
...environment,
|
|
55926
55879
|
PGPASSWORD: input.password,
|
|
55927
|
-
PGSSLMODE:
|
|
55928
|
-
|
|
55929
|
-
|
|
55930
|
-
|
|
55931
|
-
PGGSSENCMODE: "disable"
|
|
55932
|
-
} : {}
|
|
55880
|
+
PGSSLMODE: "disable",
|
|
55881
|
+
PGPORT: String(bridge.port),
|
|
55882
|
+
PGCHANNELBINDING: "disable",
|
|
55883
|
+
PGGSSENCMODE: "disable"
|
|
55933
55884
|
}
|
|
55934
55885
|
});
|
|
55935
55886
|
child.once("error", (error) => {
|
|
@@ -55940,7 +55891,7 @@ var runLocalPsql = async (input) => {
|
|
|
55940
55891
|
});
|
|
55941
55892
|
});
|
|
55942
55893
|
} finally {
|
|
55943
|
-
await bridge
|
|
55894
|
+
await bridge.close();
|
|
55944
55895
|
}
|
|
55945
55896
|
};
|
|
55946
55897
|
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -9,21 +9,17 @@ export const runLocalPsql = async (input: {
|
|
|
9
9
|
readonly user: string;
|
|
10
10
|
readonly password: string;
|
|
11
11
|
readonly database: string;
|
|
12
|
-
readonly websocketUrl
|
|
12
|
+
readonly websocketUrl: string;
|
|
13
13
|
readonly signal: AbortSignal;
|
|
14
14
|
}): Promise<number> => {
|
|
15
15
|
input.signal.throwIfAborted();
|
|
16
|
-
if (input.websocketUrl !==
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
input.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
database: input.database,
|
|
24
|
-
user: input.user,
|
|
25
|
-
port: 0,
|
|
26
|
-
});
|
|
16
|
+
if (input.websocketUrl !== `wss://${input.host}/`) throw new Error("Invalid PGCF transport");
|
|
17
|
+
const bridge = await startBridge({
|
|
18
|
+
endpoint: input.websocketUrl,
|
|
19
|
+
database: input.database,
|
|
20
|
+
user: input.user,
|
|
21
|
+
port: 0,
|
|
22
|
+
});
|
|
27
23
|
try {
|
|
28
24
|
input.signal.throwIfAborted();
|
|
29
25
|
// A caller's libpq service/hostaddr settings must not redirect this temporary credential.
|
|
@@ -31,26 +27,18 @@ export const runLocalPsql = async (input: {
|
|
|
31
27
|
Object.entries(process.env).filter(([key]) => !/^PG[A-Z_]+$/iu.test(key)),
|
|
32
28
|
);
|
|
33
29
|
return await new Promise<number>((resolve, reject) => {
|
|
34
|
-
const child = spawn(
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
...(bridge
|
|
45
|
-
? {
|
|
46
|
-
PGPORT: String(bridge.port),
|
|
47
|
-
PGCHANNELBINDING: "disable",
|
|
48
|
-
PGGSSENCMODE: "disable",
|
|
49
|
-
}
|
|
50
|
-
: {}),
|
|
51
|
-
},
|
|
30
|
+
const child = spawn("psql", ["-h", bridge.host, "-U", input.user, "-d", input.database], {
|
|
31
|
+
stdio: "inherit",
|
|
32
|
+
signal: input.signal,
|
|
33
|
+
env: {
|
|
34
|
+
...environment,
|
|
35
|
+
PGPASSWORD: input.password,
|
|
36
|
+
PGSSLMODE: "disable",
|
|
37
|
+
PGPORT: String(bridge.port),
|
|
38
|
+
PGCHANNELBINDING: "disable",
|
|
39
|
+
PGGSSENCMODE: "disable",
|
|
52
40
|
},
|
|
53
|
-
);
|
|
41
|
+
});
|
|
54
42
|
child.once("error", (error: NodeJS.ErrnoException) => {
|
|
55
43
|
reject(error.code === "ENOENT" ? new PsqlUnavailableError() : error);
|
|
56
44
|
});
|
|
@@ -59,6 +47,6 @@ export const runLocalPsql = async (input: {
|
|
|
59
47
|
});
|
|
60
48
|
});
|
|
61
49
|
} finally {
|
|
62
|
-
await bridge
|
|
50
|
+
await bridge.close();
|
|
63
51
|
}
|
|
64
52
|
};
|
|
@@ -23,7 +23,6 @@ import type {
|
|
|
23
23
|
ProjectCreditBudget,
|
|
24
24
|
} from "@ohmyhost/sdk-ts";
|
|
25
25
|
import { isSaasCnameTarget, type SaasCnameTarget } from "@ohmyhost/contracts/platform-origins";
|
|
26
|
-
import { assertPublishedCreditRateCards } from "@ohmyhost/contracts/credit-pricing";
|
|
27
26
|
import {
|
|
28
27
|
publicOperationFailure,
|
|
29
28
|
publicOperationReconciliation,
|
|
@@ -554,14 +553,7 @@ export function parseOrganizationCredits(value: unknown): OrganizationCredits {
|
|
|
554
553
|
"reserved_micros",
|
|
555
554
|
"grace_started_at",
|
|
556
555
|
"grace_expires_at",
|
|
557
|
-
"active_meters",
|
|
558
|
-
"rate_cards",
|
|
559
556
|
]);
|
|
560
|
-
try {
|
|
561
|
-
assertPublishedCreditRateCards(input["rate_cards"]);
|
|
562
|
-
} catch {
|
|
563
|
-
throw new ResponseContractError();
|
|
564
|
-
}
|
|
565
557
|
const graceStart = input["grace_started_at"],
|
|
566
558
|
graceEnd = input["grace_expires_at"];
|
|
567
559
|
if (
|
|
@@ -576,11 +568,7 @@ export function parseOrganizationCredits(value: unknown): OrganizationCredits {
|
|
|
576
568
|
if (
|
|
577
569
|
!isUlid(input["organization_id"]) ||
|
|
578
570
|
input["unit"] !== "microcredits" ||
|
|
579
|
-
!isIsoInstant(input["as_of"])
|
|
580
|
-
!Array.isArray(input["active_meters"]) ||
|
|
581
|
-
input["active_meters"].some(
|
|
582
|
-
(m: unknown) => typeof m !== "string" || !/^[a-z0-9_.-]{1,128}$/u.test(m),
|
|
583
|
-
)
|
|
571
|
+
!isIsoInstant(input["as_of"])
|
|
584
572
|
)
|
|
585
573
|
throw new ResponseContractError();
|
|
586
574
|
for (const field of ["available_micros", "reserved_micros"]) {
|
|
@@ -116,96 +116,6 @@ export function assertOrganizationCreditUsage(
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
export interface PublishedCreditRateCard {
|
|
120
|
-
readonly id: string;
|
|
121
|
-
readonly currency: "USD";
|
|
122
|
-
readonly published_at: string;
|
|
123
|
-
readonly effective_from: string;
|
|
124
|
-
readonly rates: readonly {
|
|
125
|
-
readonly meter: string;
|
|
126
|
-
readonly unit: string;
|
|
127
|
-
readonly units_per_charge: string;
|
|
128
|
-
readonly provider_cost_micros: string | null;
|
|
129
|
-
readonly credit_micros: string;
|
|
130
|
-
}[];
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/** Shared wire validation only; clients never recompute accounting decisions. */
|
|
134
|
-
export function assertPublishedCreditRateCards(
|
|
135
|
-
value: unknown,
|
|
136
|
-
): asserts value is readonly PublishedCreditRateCard[] {
|
|
137
|
-
if (!Array.isArray(value) || value.length > 4) throw new TypeError("Invalid published prices");
|
|
138
|
-
const ids = new Set<string>();
|
|
139
|
-
for (const input of value) {
|
|
140
|
-
if (
|
|
141
|
-
!input ||
|
|
142
|
-
typeof input !== "object" ||
|
|
143
|
-
Array.isArray(input) ||
|
|
144
|
-
Object.keys(input).sort().join() !== "currency,effective_from,id,published_at,rates"
|
|
145
|
-
)
|
|
146
|
-
throw new TypeError("Invalid published price fields");
|
|
147
|
-
const card = input as Record<string, unknown>;
|
|
148
|
-
if (
|
|
149
|
-
typeof card["id"] !== "string" ||
|
|
150
|
-
!/^[a-z0-9_.-]{1,128}$/u.test(card["id"]) ||
|
|
151
|
-
ids.has(card["id"]) ||
|
|
152
|
-
card["currency"] !== "USD"
|
|
153
|
-
)
|
|
154
|
-
throw new TypeError("Invalid price identity");
|
|
155
|
-
ids.add(card["id"]);
|
|
156
|
-
for (const key of ["published_at", "effective_from"]) {
|
|
157
|
-
const date = card[key];
|
|
158
|
-
if (
|
|
159
|
-
typeof date !== "string" ||
|
|
160
|
-
!Number.isFinite(Date.parse(date)) ||
|
|
161
|
-
new Date(date).toISOString() !== date
|
|
162
|
-
)
|
|
163
|
-
throw new TypeError("Invalid price dates");
|
|
164
|
-
}
|
|
165
|
-
if (
|
|
166
|
-
String(card["published_at"]) > String(card["effective_from"]) ||
|
|
167
|
-
!Array.isArray(card["rates"]) ||
|
|
168
|
-
card["rates"].length < 1 ||
|
|
169
|
-
card["rates"].length > 32
|
|
170
|
-
)
|
|
171
|
-
throw new TypeError("Invalid price publication");
|
|
172
|
-
const meters = new Set<string>();
|
|
173
|
-
for (const item of card["rates"]) {
|
|
174
|
-
if (
|
|
175
|
-
!item ||
|
|
176
|
-
typeof item !== "object" ||
|
|
177
|
-
Array.isArray(item) ||
|
|
178
|
-
Object.keys(item).sort().join() !==
|
|
179
|
-
"credit_micros,meter,provider_cost_micros,unit,units_per_charge"
|
|
180
|
-
)
|
|
181
|
-
throw new TypeError("Invalid rate fields");
|
|
182
|
-
const rate = item as Record<string, unknown>;
|
|
183
|
-
for (const key of ["meter", "unit"])
|
|
184
|
-
if (typeof rate[key] !== "string" || !/^[a-z0-9_.-]{1,128}$/u.test(String(rate[key])))
|
|
185
|
-
throw new TypeError("Invalid rate unit");
|
|
186
|
-
const identity = String(rate["meter"]) + "/" + String(rate["unit"]);
|
|
187
|
-
if (meters.has(identity)) throw new TypeError("Duplicate rate");
|
|
188
|
-
meters.add(identity);
|
|
189
|
-
for (const key of ["units_per_charge", "provider_cost_micros", "credit_micros"]) {
|
|
190
|
-
const amount = rate[key];
|
|
191
|
-
if (
|
|
192
|
-
key === "provider_cost_micros" &&
|
|
193
|
-
amount === null &&
|
|
194
|
-
rate["meter"] === "pgcf.memory.active"
|
|
195
|
-
)
|
|
196
|
-
continue;
|
|
197
|
-
if (
|
|
198
|
-
typeof amount !== "string" ||
|
|
199
|
-
!/^(0|[1-9][0-9]{0,18})$/u.test(amount) ||
|
|
200
|
-
BigInt(amount) > 9223372036854775807n ||
|
|
201
|
-
(key === "units_per_charge" && amount === "0")
|
|
202
|
-
)
|
|
203
|
-
throw new TypeError("Invalid rate amount");
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
119
|
/** Effective feature access and the credit account: the monthly and one-time credit and what the open reservations hold. */
|
|
210
120
|
export function assertOrganizationAccount(value: unknown): void {
|
|
211
121
|
if (
|
|
@@ -2192,7 +2192,7 @@ export const configureBillingRecharge = <ThrowOnError extends boolean = true>(
|
|
|
2192
2192
|
/**
|
|
2193
2193
|
* Read the owner's shared organization credit pool
|
|
2194
2194
|
*
|
|
2195
|
-
* Returns the spendable credit and the open reservations, in microcredits (one credit is 1000000 microcredits). available_micros is the monthly credit plus the one-time credit minus the reservations, never below zero. Only the organization Owner may read this balance.
|
|
2195
|
+
* Returns the spendable credit and the open reservations, in microcredits (one credit is 1000000 microcredits). available_micros is the monthly credit plus the one-time credit minus the reservations, never below zero. Only the organization Owner may read this balance. It states what is left and never what the credit was spent on; read the recent usage from getOrganizationCreditUsage. When the credit is used up, grace_started_at and grace_expires_at name the seven days that keep existing services running. Remains available at zero credit.
|
|
2196
2196
|
*/
|
|
2197
2197
|
export const getOrganizationCredits = <ThrowOnError extends boolean = true>(
|
|
2198
2198
|
parameters: {
|
|
@@ -329,11 +329,6 @@ export type OrganizationCredits = {
|
|
|
329
329
|
* Seven days after the credit was used up. Existing services and domains are retained during this grace; no automatic data deletion. Explicit project stop budgets are separate.
|
|
330
330
|
*/
|
|
331
331
|
grace_expires_at: string | null;
|
|
332
|
-
active_meters: Array<string>;
|
|
333
|
-
/**
|
|
334
|
-
* Published integer prices, newest first. A published rate is not proof of enabled billing; only active_meters names currently billed sources. Builds retain their accepted quote. Provider cost is a list-price basis before platform allowances, not an invoice.
|
|
335
|
-
*/
|
|
336
|
-
rate_cards: Array<PublishedCreditRateCard>;
|
|
337
332
|
};
|
|
338
333
|
|
|
339
334
|
/**
|
|
@@ -379,23 +374,6 @@ export type OrganizationCreditUsage = {
|
|
|
379
374
|
}>;
|
|
380
375
|
};
|
|
381
376
|
|
|
382
|
-
export type PublishedCreditRateCard = {
|
|
383
|
-
id: string;
|
|
384
|
-
currency: "USD";
|
|
385
|
-
published_at: string;
|
|
386
|
-
effective_from: string;
|
|
387
|
-
rates: Array<{
|
|
388
|
-
meter: string;
|
|
389
|
-
unit: string;
|
|
390
|
-
units_per_charge: string;
|
|
391
|
-
/**
|
|
392
|
-
* Provider list-cost basis for units_per_charge, in USD micro-units (1000000 equals 1 USD), before shared allowances and discounts. Null for the PGCF RAM tariff means node infrastructure costs are tracked separately and are not allocated to this customer rate; null never means zero cost.
|
|
393
|
-
*/
|
|
394
|
-
provider_cost_micros: string | null;
|
|
395
|
-
credit_micros: CreditMicros;
|
|
396
|
-
}>;
|
|
397
|
-
};
|
|
398
|
-
|
|
399
377
|
export type SetProjectCreditBudgetRequest = {
|
|
400
378
|
/**
|
|
401
379
|
* The monthly budget, at most 9007199254740991 microcredits (about 9 billion credits); null removes it.
|