New:Socket for Asana Is Now Available.Learn more
Sign In

@ultimat3/cli

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ultimat3/cli - npm Package Compare versions

Comparing version
12.0.0
to
13.0.0
+152
src/framework-schema.ts
// The framework's OWN tables, as one table: which package declares each, which relations it
// creates, and the DDL. One list, read by the one applier, so "installed in dev and not in
// production" is not a state this framework can be in — `startQueue` is on every boot path there
// is, `ROLE=migrate` included.
//
// A framework table an app has to install by hand is a table that will be missing in production on
// the one code path that needs it, and it surfaces as a Postgres `42P01` from inside a worker.
import { SQL_AUDIT_TABLE, SQL_IDEMPOTENCY_TABLE } from '@ultimat3/action';
import { AUTH_TABLE_NAMES, AUTH_TABLES, SQL_AUTH_LIMIT_TABLES } from '@ultimat3/auth';
import { SQL_RATE_LIMIT_TABLE } from '@ultimat3/http';
import { SQL_JOBS_TABLE } from '@ultimat3/jobs';
import { SQL_NOTIFY_DELIVERIES_TABLE, SQL_NOTIFY_INBOX_TABLE } from '@ultimat3/notify';
import { FrameworkSchemaFailedError } from './schema-errors';
export interface FrameworkSchema {
/** The package whose source declares the DDL — where to look when a column is wrong. */
readonly pkg: string;
/**
* Every relation this entry creates. Read by the refusal below, so an operator learns which
* tables were being installed rather than only which statement failed — and pinned against the
* DDL text by `framework-schema.test.ts`, so a row cannot claim a table its SQL never creates.
*/
readonly tables: readonly string[];
/** One or more statements each; `;` separates. */
readonly ddl: readonly string[];
}
/**
* Applied unconditionally, whether or not this boot installs a store behind it.
*
* `create table if not exists` on an unused table costs one round trip at boot. The alternative
* costs a request: a store installed later must never be the thing that discovers the schema was
* never applied, and several of these are installed AFTER this runs — `defineAuth` builds its
* limiter when the app's modules import, and `setNotifyStores` is an app's boot line.
*
* Ordered so a foreign key never precedes its target. Only `AUTH_TABLES` has any, and they are
* internal to that entry, which is why it ships as an ordered list rather than one string.
*/
export const FRAMEWORK_SCHEMA: readonly FrameworkSchema[] = Object.freeze([
Object.freeze({
pkg: '@ultimat3/jobs',
tables: Object.freeze([
'x_jobs',
'x_job_steps',
'x_backfills',
'x_outbox',
'x_scheduler_state',
'x_scheduler_leader',
'x_job_leases',
'x_job_events',
]),
ddl: Object.freeze([SQL_JOBS_TABLE]),
}),
Object.freeze({
pkg: '@ultimat3/action',
tables: Object.freeze(['x_idempotency']),
ddl: Object.freeze([SQL_IDEMPOTENCY_TABLE]),
}),
// The DDL only, and deliberately NO `setAuditSink`: there is no default audit sink on purpose,
// so `X_AUDIT_SINK_MISSING` keeps firing at boot for an app that declares `audit: true` and
// installs none.
Object.freeze({
pkg: '@ultimat3/action',
tables: Object.freeze(['x_audit']),
ddl: Object.freeze([SQL_AUDIT_TABLE]),
}),
Object.freeze({
pkg: '@ultimat3/http',
tables: Object.freeze(['x_rate_limit']),
ddl: Object.freeze([SQL_RATE_LIMIT_TABLE]),
}),
Object.freeze({
pkg: '@ultimat3/auth',
tables: Object.freeze(['x_auth_failures', 'x_auth_lockouts']),
ddl: Object.freeze([SQL_AUTH_LIMIT_TABLES]),
}),
/**
* The five tables `BuiltinAdapter` reads, and the oldest hole in this list.
*
* `packages/auth/src/tables.ts` exports them "so an app can paste them into a migration", and
* nothing in the framework has ever applied them — while `x db gen` diffs `describeEntities()`
* and these are not `entity()` declarations, so neither half was a file anybody could
* hand-write. `examples/dummy/CLAUDE.md` records the consequence in its own words: nobody can
* hold a session in the reference app. Applied here on exactly the rule the rate-limit and audit
* rows already follow.
*
* `AUTH_TABLE_NAMES` rather than five literals: @ultimat3/auth already publishes the list, and a
* second copy is a second thing to keep right when a table is added.
*/
Object.freeze({
pkg: '@ultimat3/auth',
tables: AUTH_TABLE_NAMES,
ddl: AUTH_TABLES,
}),
/**
* The delivery ledger is what stops a replayed notifier job sending twice, and it is the entry
* whose absence is least visible: without the table the ledger's first `claim` raises `42P01`
* from inside a worker, which reads as a dead-lettered notification rather than as a missing
* schema. Installed whether or not this boot calls `setNotifyStores`, for the same reason as
* every row above it — that call is an APP's boot line and runs after this one.
*/
Object.freeze({
pkg: '@ultimat3/notify',
tables: Object.freeze(['x_notify_deliveries']),
ddl: Object.freeze([SQL_NOTIFY_DELIVERIES_TABLE]),
}),
Object.freeze({
pkg: '@ultimat3/notify',
tables: Object.freeze(['x_notify_inbox']),
ddl: Object.freeze([SQL_NOTIFY_INBOX_TABLE]),
}),
]);
/** Every relation this boot creates, flattened. */
export const frameworkTableNames = (): readonly string[] =>
FRAMEWORK_SCHEMA.flatMap((entry) => [...entry.tables]);
/**
* PGlite speaks the extended protocol, which carries one statement per round trip, so the DDL is
* applied statement by statement. Safe to split on `;`: every constant is fixed, with no semicolon
* inside a literal, and each package's own SQL test is where that stays true.
*/
export const schemaStatements = (ddl: readonly string[]): readonly string[] =>
ddl.flatMap((text) => text.split(';')).filter((statement) => statement.trim().length > 0);
/** One statement, executed. The caller owns the connection; this file owns no database import. */
export type SchemaExecutor = (statement: string) => Promise<unknown>;
/**
* Apply every entry, in order, and answer what was created.
*
* The refusal is the point of the `pkg`/`tables` columns: a raw `permission denied for schema
* public` names neither the framework table it was creating nor the package that wants it, and a
* boot failure is read by an operator who has no source tree open.
*/
export async function applyFrameworkSchema(execute: SchemaExecutor): Promise<readonly string[]> {
for (const entry of FRAMEWORK_SCHEMA) {
for (const statement of schemaStatements(entry.ddl)) {
try {
await execute(statement);
} catch (error) {
throw new FrameworkSchemaFailedError({
pkg: entry.pkg,
tables: entry.tables,
cause: error,
});
}
}
}
return frameworkTableNames();
}
// The one code raised while installing the framework's own tables. Apart from `errors.ts` for the
// reason `packages/jobs/src/backfill-errors.ts` is apart from that package's: one file, one job,
// and `errors.ts` is at 486 lines against the 500 the `filesize` step enforces. The code, its
// title and its registration stay in `error-codes.ts`, where every other CLI code lives.
import { renderThrowable, UltimateError } from '@ultimat3/core';
/**
* A statement in `FRAMEWORK_SCHEMA` did not apply. Raised in place of the driver's own rejection,
* which names neither the framework table being created nor the package that wants it — and a boot
* failure is read by an operator with no source tree open.
*
* `renderThrowable` and never `${cause}`: a `catch` binding is annotated by nobody, and a pool
* rejection is routinely an object whose `toString` throws.
*/
export class FrameworkSchemaFailedError extends UltimateError {
constructor(input: { pkg: string; tables: readonly string[]; cause: unknown }) {
super({
code: 'X_FRAMEWORK_SCHEMA_FAILED',
cause: `${input.pkg} could not create ${input.tables.join(', ')}: ${renderThrowable(input.cause)}`,
// An EDIT plus the command that CONFIRMS it, which is the house shape for a repair the gate
// cannot perform: the two real causes are a role without `create`, and a relation of that
// name already present with an incompatible shape.
fix: `grant create on schema public to the role in DATABASE_URL, then re-run: x db migrate`,
meta: { pkg: input.pkg, tables: [...input.tables] },
});
}
}
+29
-28
{
"name": "@ultimat3/cli",
"version": "12.0.0",
"version": "13.0.0",
"description": "The `x` binary: new, dev, build, verify, generate, db, mcp, doctor, deploy",

@@ -40,31 +40,32 @@ "license": "MIT",

"@babel/core": "^7.28.4",
"@ultimat3/action": "12.0.0",
"@ultimat3/admin": "12.0.0",
"@ultimat3/ai": "12.0.0",
"@ultimat3/auth": "12.0.0",
"@ultimat3/cache": "12.0.0",
"@ultimat3/core": "12.0.0",
"@ultimat3/db": "12.0.0",
"@ultimat3/entity": "12.0.0",
"@ultimat3/flags": "12.0.0",
"@ultimat3/http": "12.0.0",
"@ultimat3/i18n": "12.0.0",
"@ultimat3/jobs": "12.0.0",
"@ultimat3/mail": "12.0.0",
"@ultimat3/manifest": "12.0.0",
"@ultimat3/mcp": "12.0.0",
"@ultimat3/money": "12.0.0",
"@ultimat3/policy": "12.0.0",
"@ultimat3/pwa": "12.0.0",
"@ultimat3/query": "12.0.0",
"@ultimat3/realtime": "12.0.0",
"@ultimat3/render": "12.0.0",
"@ultimat3/schema": "12.0.0",
"@ultimat3/scraping": "12.0.0",
"@ultimat3/seo": "12.0.0",
"@ultimat3/storage": "12.0.0",
"@ultimat3/testing": "12.0.0",
"@ultimat3/time": "12.0.0",
"@ultimat3/action": "13.0.0",
"@ultimat3/admin": "13.0.0",
"@ultimat3/ai": "13.0.0",
"@ultimat3/auth": "13.0.0",
"@ultimat3/cache": "13.0.0",
"@ultimat3/core": "13.0.0",
"@ultimat3/db": "13.0.0",
"@ultimat3/entity": "13.0.0",
"@ultimat3/flags": "13.0.0",
"@ultimat3/http": "13.0.0",
"@ultimat3/i18n": "13.0.0",
"@ultimat3/jobs": "13.0.0",
"@ultimat3/mail": "13.0.0",
"@ultimat3/manifest": "13.0.0",
"@ultimat3/mcp": "13.0.0",
"@ultimat3/money": "13.0.0",
"@ultimat3/notify": "13.0.0",
"@ultimat3/policy": "13.0.0",
"@ultimat3/pwa": "13.0.0",
"@ultimat3/query": "13.0.0",
"@ultimat3/realtime": "13.0.0",
"@ultimat3/render": "13.0.0",
"@ultimat3/schema": "13.0.0",
"@ultimat3/scraping": "13.0.0",
"@ultimat3/seo": "13.0.0",
"@ultimat3/storage": "13.0.0",
"@ultimat3/testing": "13.0.0",
"@ultimat3/time": "13.0.0",
"babel-preset-solid": "^1.9.15"
}
}

@@ -11,7 +11,4 @@ // The database and the job queue, started together and released together. Split from

resetIdempotency,
SQL_AUDIT_TABLE,
SQL_IDEMPOTENCY_TABLE,
setIdempotencyStore,
} from '@ultimat3/action';
import { SQL_AUTH_LIMIT_TABLES } from '@ultimat3/auth';
import type { DbClient, PgliteClient, PostgresClient, SqlFragment } from '@ultimat3/db';

@@ -27,3 +24,2 @@ import {

import type { Tx } from '@ultimat3/entity';
import { SQL_RATE_LIMIT_TABLE } from '@ultimat3/http';
import type { EventBus, JobDriver, OutboxStore, PgExecutor } from '@ultimat3/jobs';

@@ -37,3 +33,2 @@ import {

resetJobsFacade,
SQL_JOBS_TABLE,
setEventBus,

@@ -45,2 +40,3 @@ setJobDriver,

import type { DevServices } from './dev-services';
import { applyFrameworkSchema } from './framework-schema';
import type { RuntimeOverrides } from './runtime-overrides';

@@ -120,36 +116,14 @@

*
* PGlite speaks the extended protocol, which carries one statement per round trip, so the DDL is
* applied statement by statement. Safe to split on `;`: every constant is fixed, with no semicolon
* inside a literal, and each package's own SQL test is where that stays true.
* The LIST is `FRAMEWORK_SCHEMA` and lives in `framework-schema.ts`, not here: this function is on
* every boot path the framework has — `x dev`, each served role, `x jobs`, `x db backfill`,
* `x mcp serve` and `ROLE=migrate` all reach it through `startQueue` — so the list it reads is the
* one place a framework table can be forgotten, and it is worth being a table somebody can read
* rather than an array literal inside a boot function.
*
* `SQL_IDEMPOTENCY_TABLE`, `SQL_RATE_LIMIT_TABLE` and `SQL_AUTH_LIMIT_TABLES` are here and not in
* `@ultimat3/action`, `@ultimat3/http` or `@ultimat3/auth` because a package that holds no
* database dependency cannot apply its own schema
* — the same reason `SQL_JOBS_TABLE` is applied here. Each one absent is the same failure at a
* different door: a retried `POST /api/payments/charge` charges the card twice, and the FIRST
* request a `rateLimitStore` deployment serves dies on a missing `x_rate_limit` relation. The
* table is installed whether or not this boot passes `runtime.rateLimitStore` — `create table if
* not exists` on an unused table costs one round trip at boot, and a store installed later must
* not be the thing that discovers the schema was never applied. The auth pair is the strongest
* case for that rule: `defineAuth` builds its limiter when the APP's modules import, which is
* after this, so the first failed sign-in would otherwise be what discovers the missing relation.
* Each package's DDL is here and not in `@ultimat3/action`, `@ultimat3/http`, `@ultimat3/auth` or
* `@ultimat3/notify` because a package that holds no database dependency cannot apply its own
* schema — the same reason `SQL_JOBS_TABLE` is applied by the boot.
*/
async function applySchema(client: DevDbClient): Promise<void> {
for (const ddl of [
SQL_JOBS_TABLE,
SQL_IDEMPOTENCY_TABLE,
// The DDL only, and deliberately NO `setAuditSink` beside `setIdempotencyStore` below: there
// is no default audit sink on purpose, so `X_AUDIT_SINK_MISSING` keeps firing at boot for an
// app that declares `audit: true` and installs none. Applying the table without installing a
// sink is the same call `SQL_RATE_LIMIT_TABLE` already makes — one round trip at boot on a
// possibly-unused table, against `postgresAuditSink` failing its first write with
// `relation "x_audit" does not exist`.
SQL_AUDIT_TABLE,
SQL_RATE_LIMIT_TABLE,
SQL_AUTH_LIMIT_TABLES,
]) {
for (const statement of ddl.split(';')) {
if (statement.trim().length > 0) await client.execute(raw(statement));
}
}
await applyFrameworkSchema((statement) => client.execute(raw(statement)));
}

@@ -156,0 +130,0 @@

@@ -33,2 +33,3 @@ // Every framework package's error codes, present in this process before `x errors` answers.

'@ultimat3/money',
'@ultimat3/notify',
'@ultimat3/policy',

@@ -35,0 +36,0 @@ '@ultimat3/pwa',

@@ -52,2 +52,6 @@ // The X_* codes owned by @ultimat3/cli, and nothing else: the two lists, their titles, the one

'X_RELEASE_VERSION_SKEW',
// The framework's own tables, refused by name rather than by the driver's rejection: a raw
// `permission denied for schema public` says which statement failed and neither which framework
// table it was creating nor which package wants it.
'X_FRAMEWORK_SCHEMA_FAILED',
'X_STORAGE_UNWRITABLE',

@@ -202,2 +206,3 @@ 'X_STORAGE_SECRET_DEV',

X_ERROR_CODE_UNRESOLVED: 'an error code is written as a name this repository cannot resolve',
X_FRAMEWORK_SCHEMA_FAILED: 'a framework table could not be created at boot',
X_STORAGE_UNWRITABLE: 'the storage disk this process needs cannot be written to',

@@ -204,0 +209,0 @@ X_STORAGE_SECRET_DEV: 'upload grants would be signed with the shipped development key',

@@ -199,2 +199,11 @@ // Public API of @ultimat3/cli. Explicit re-exports only: create-ultimate and the test suite build

export { checkFlagReads, declaredFlags, readsFlag } from './flag-reads';
export type { FrameworkSchema, SchemaExecutor } from './framework-schema';
// The framework's own tables, as data. Exported so `scripts/` can read the applier's list without
// re-deriving it — the shape a ratchet over declared-but-never-applied DDL needs.
export {
applyFrameworkSchema,
FRAMEWORK_SCHEMA,
frameworkTableNames,
schemaStatements,
} from './framework-schema';
export type { Guard } from './guards';

@@ -243,2 +252,3 @@ export { findingProblem, GUARD_DIR, guardFindings, guardPaths } from './guards';

export { COMMANDS, cliVersion, commandFor, SPECS } from './registry';
export { FrameworkSchemaFailedError } from './schema-errors';
export type { MigratedApp, ServedApp, ServeOptions, StartedApp } from './serve';

@@ -245,0 +255,0 @@ export {

@@ -99,2 +99,5 @@ // `errors.explain`: one runnable command per error code. Its own file because the CLI's fix table

// the one that inspects the binding rather than guessing between a volume and a bucket.
// `x db migrate --json` and not `x doctor`: this fires from inside `startQueue`, so the command
// that re-runs exactly the failing step is the migrate role, and it reports what it applied.
X_FRAMEWORK_SCHEMA_FAILED: 'x db migrate --json',
X_STORAGE_UNWRITABLE: 'x doctor --json',

@@ -101,0 +104,0 @@ X_STORAGE_SECRET_DEV: 'export STORAGE_SIGNING_SECRET="$(openssl rand -hex 32)"',