typion 0.1.0 → 0.1.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/dist/auth/better-auth.d.ts +2 -4
- package/dist/auth/better-auth.js +1 -1
- package/dist/auth/better-auth.js.map +1 -1
- package/dist/cli/index.js +63 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/{context-CpLQy7OI.d.ts → context-B4e2eREP.d.ts} +6 -4
- package/dist/contract/index.d.ts +1 -1
- package/dist/db/pglite.d.ts +1 -1
- package/dist/{handler-D03-rJ-t.d.ts → handler-BWVU8TCR.d.ts} +2 -2
- package/dist/hono/index.d.ts +1 -1
- package/dist/hono/index.js +1 -1
- package/dist/http/index.d.ts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/next/index.d.ts +2 -2
- package/dist/next/index.js +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/seed/index.d.ts +1 -1
- package/dist/testing/index.d.ts +2 -4
- package/dist/testing/index.js +1 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/{typion-TJWDkHGh.js → typion-Dh2BCSRz.js} +3 -2
- package/dist/{typion-TJWDkHGh.js.map → typion-Dh2BCSRz.js.map} +1 -1
- package/dist/validation/zod.d.ts +1 -1
- package/docs/README.md +4 -4
- package/docs/access.md +2 -0
- package/docs/auth.md +73 -18
- package/docs/collections.md +4 -0
- package/docs/database.md +3 -1
- package/docs/features/multi-tenant.md +5 -4
- package/docs/http.md +4 -0
- package/docs/queries.md +3 -0
- package/docs/testing.md +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Gt as ContextFactoryInput, N as AnyDb, Wt as ContextFactory, a as MaybePromise } from "../context-B4e2eREP.js";
|
|
2
2
|
//#region src/auth/better-auth.d.ts
|
|
3
3
|
export type SessionProvider = {
|
|
4
4
|
api: {
|
|
@@ -8,8 +8,6 @@ export type SessionProvider = {
|
|
|
8
8
|
};
|
|
9
9
|
};
|
|
10
10
|
export type SessionOf<TAuth extends SessionProvider> = Awaited<ReturnType<TAuth['api']['getSession']>>;
|
|
11
|
-
export declare function betterAuthContext<TAuth extends SessionProvider, TCtx extends object>(auth: TAuth, map: (session: SessionOf<TAuth>, input:
|
|
12
|
-
system: SystemTypion;
|
|
13
|
-
}) => MaybePromise<TCtx>): ContextFactory<TCtx>;
|
|
11
|
+
export declare function betterAuthContext<TAuth extends SessionProvider, TCtx extends object>(auth: TAuth | ((db: AnyDb) => TAuth), map: (session: SessionOf<TAuth>, input: ContextFactoryInput) => MaybePromise<TCtx>): ContextFactory<TCtx>;
|
|
14
12
|
//#endregion
|
|
15
13
|
//# sourceMappingURL=better-auth.d.ts.map
|
package/dist/auth/better-auth.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
function betterAuthContext(auth, map) {
|
|
3
3
|
return async (input) => {
|
|
4
4
|
const headers = input.headers ?? input.request?.headers ?? new Headers();
|
|
5
|
-
return map(await auth.api.getSession({ headers }), input);
|
|
5
|
+
return map(await ("api" in auth ? auth : auth(input.db)).api.getSession({ headers }), input);
|
|
6
6
|
};
|
|
7
7
|
}
|
|
8
8
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"better-auth.js","names":[],"sources":["../../src/auth/better-auth.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"better-auth.js","names":[],"sources":["../../src/auth/better-auth.ts"],"sourcesContent":["import type { MaybePromise } from '../types/context.js'\nimport type { ContextFactory, ContextFactoryInput } from '../types/collection.js'\nimport type { AnyDb } from '../types/runtime.js'\n\n// Structural on purpose: the engine never imports better-auth, so any auth library exposing the same\n// `api.getSession({ headers })` shape works, and better-auth stays an optional peer.\nexport type SessionProvider = {\n api: { getSession(options: { headers: Headers }): Promise<unknown> }\n}\n\nexport type SessionOf<TAuth extends SessionProvider> = Awaited<\n ReturnType<TAuth['api']['getSession']>\n>\n\n// The library says who signed in; `map` says who that is to the application. It gets the factory's input with\n// `system`, which crosses tenants, so it can look the caller up: their membership, their role (auth.md).\n// `auth` is the provider, or what builds it on the instance's connection: a provider per instance reads a test's\n// sessions from the test's database, and the config never has to reach the instance itself (ADR-092).\nexport function betterAuthContext<TAuth extends SessionProvider, TCtx extends object>(\n auth: TAuth | ((db: AnyDb) => TAuth),\n map: (session: SessionOf<TAuth>, input: ContextFactoryInput) => MaybePromise<TCtx>,\n): ContextFactory<TCtx> {\n return async (input) => {\n const headers = input.headers ?? input.request?.headers ?? new Headers()\n const provider = 'api' in auth ? (auth as TAuth) : auth(input.db)\n const session = (await provider.api.getSession({ headers })) as SessionOf<TAuth>\n return map(session, input)\n }\n}\n"],"mappings":";AAkBA,SAAgB,kBACd,MACA,KACsB;CACtB,OAAO,OAAO,UAAU;EACtB,MAAM,UAAU,MAAM,WAAW,MAAM,SAAS,WAAW,IAAI,QAAQ;EAGvE,OAAO,IAAI,OAFM,SAAS,OAAQ,OAAiB,KAAK,MAAM,EAAE,EAAA,CAChC,IAAI,WAAW,EAAE,QAAQ,CAAC,GACtC,KAAK;CAC3B;AACF"}
|
package/dist/cli/index.js
CHANGED
|
@@ -281,7 +281,7 @@ async function seed(loaded, io, options) {
|
|
|
281
281
|
}
|
|
282
282
|
const file = findSeedFile(loaded, options.file);
|
|
283
283
|
if (file === null) throw new CliError("No seed file found (typion/seed.ts). Write one, point at it with --file, or use --random.");
|
|
284
|
-
const { getTypion } = await import("../typion-
|
|
284
|
+
const { getTypion } = await import("../typion-Dh2BCSRz.js").then((n) => n.i);
|
|
285
285
|
const typion = await getTypion(loaded.config);
|
|
286
286
|
const seedModule = await import(pathToFileURL(file).href);
|
|
287
287
|
const run = seedModule.seed ?? seedModule.default;
|
|
@@ -404,6 +404,7 @@ function writeAgentNotes(root, io) {
|
|
|
404
404
|
//#endregion
|
|
405
405
|
//#region src/cli/init.ts
|
|
406
406
|
const DRIZZLE = "1.0.0-rc.4";
|
|
407
|
+
const TRANSPORTS = ["@typion/rest", "@typion/rpc"];
|
|
407
408
|
function packageManager(root) {
|
|
408
409
|
if (existsSync(join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
409
410
|
if (existsSync(join(root, "yarn.lock"))) return "yarn";
|
|
@@ -547,6 +548,57 @@ function allowTsImports(root, io) {
|
|
|
547
548
|
writeFileSync(path, `${JSON.stringify(json, null, 2)}\n`);
|
|
548
549
|
io.out(`Set ${flag} in tsconfig.json: Typion's imports end in .ts.`);
|
|
549
550
|
}
|
|
551
|
+
function pnpmMajor(root) {
|
|
552
|
+
const declared = /^pnpm@(\d+)/.exec(String(readJson(join(root, "package.json"))?.packageManager));
|
|
553
|
+
const agent = /^pnpm\/(\d+)/.exec(process.env.npm_config_user_agent ?? "");
|
|
554
|
+
const version = declared?.[1] ?? agent?.[1] ?? /^(\d+)\./.exec(spawnSync("pnpm", ["--version"], {
|
|
555
|
+
cwd: root,
|
|
556
|
+
encoding: "utf8"
|
|
557
|
+
}).stdout ?? "")?.[1];
|
|
558
|
+
return version === void 0 ? null : Number(version);
|
|
559
|
+
}
|
|
560
|
+
function workspaceFile(root) {
|
|
561
|
+
for (let dir = root; dirname(dir) !== dir; dir = dirname(dir)) if (existsSync(join(dir, "pnpm-workspace.yaml"))) return join(dir, "pnpm-workspace.yaml");
|
|
562
|
+
return join(root, "pnpm-workspace.yaml");
|
|
563
|
+
}
|
|
564
|
+
function allowEsbuild(root, io) {
|
|
565
|
+
const major = pnpmMajor(root);
|
|
566
|
+
if (major === null || major < 11) return;
|
|
567
|
+
const path = workspaceFile(root);
|
|
568
|
+
const name = relative(process.cwd(), path);
|
|
569
|
+
const why = `pnpm ${major} fails an install on a build script it was not told to run, and drizzle-kit brings esbuild's`;
|
|
570
|
+
if (!existsSync(path)) {
|
|
571
|
+
writeFileSync(path, "allowBuilds:\n esbuild: true\n");
|
|
572
|
+
io.out(`Created ${name} to allow esbuild's build script: ${why}.`);
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
const text = readFileSync(path, "utf8");
|
|
576
|
+
const eol = text.includes("\r\n") ? "\r\n" : "\n";
|
|
577
|
+
const lines = text.split(/\r?\n/);
|
|
578
|
+
const valueOf = (line) => line.slice(line.indexOf(":") + 1).replace(/(^|\s)#.*$/, "").trim();
|
|
579
|
+
const key = lines.findIndex((line) => /^allowBuilds\s*:/.test(line));
|
|
580
|
+
if (key === -1) {
|
|
581
|
+
if (lines.at(-1) === "") lines.pop();
|
|
582
|
+
lines.push(...lines.length === 0 ? [] : [""], "allowBuilds:", " esbuild: true", "");
|
|
583
|
+
} else if (valueOf(lines[key]) !== "") {
|
|
584
|
+
if (!/\besbuild\b/.test(lines[key])) io.out(`Add esbuild: true to allowBuilds in ${name}: ${why}.`);
|
|
585
|
+
return;
|
|
586
|
+
} else {
|
|
587
|
+
let end = key + 1;
|
|
588
|
+
while (end < lines.length && /^(\s|#|$)/.test(lines[end])) end++;
|
|
589
|
+
const block = lines.slice(key + 1, end);
|
|
590
|
+
const entry = block.findIndex((line) => /^\s+(['"]?)esbuild\1\s*:/.test(line));
|
|
591
|
+
if (entry === -1) {
|
|
592
|
+
const indent = block.map((line) => /^(\s+)[^\s#]/.exec(line)?.[1]).find(Boolean) ?? " ";
|
|
593
|
+
lines.splice(key + 1, 0, `${indent}esbuild: true`);
|
|
594
|
+
} else {
|
|
595
|
+
if (/^(true|false)$/i.test(valueOf(block[entry]))) return;
|
|
596
|
+
lines[key + 1 + entry] = `${/^\s+/.exec(block[entry])[0]}esbuild: true`;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
writeFileSync(path, lines.join(eol));
|
|
600
|
+
io.out(`Set allowBuilds: esbuild: true in ${name}: ${why}.`);
|
|
601
|
+
}
|
|
550
602
|
const ROUTE = `import { rest } from '@typion/rest'
|
|
551
603
|
import { rpc } from '@typion/rpc'
|
|
552
604
|
import { routeHandlers } from 'typion/next'
|
|
@@ -565,30 +617,34 @@ async function init(cwd, io, options) {
|
|
|
565
617
|
allowTsImports(project.root, io);
|
|
566
618
|
if (project.framework === "next" && project.appDir !== null) write(join(project.appDir, "api", "[...path]", "route.ts"), ROUTE, io);
|
|
567
619
|
writeAgentNotes(project.root, io);
|
|
568
|
-
|
|
620
|
+
const manager = packageManager(project.root);
|
|
621
|
+
if (manager === "pnpm") allowEsbuild(project.root, io);
|
|
622
|
+
await installDependencies(project, manager, driver, io, options);
|
|
569
623
|
io.out("");
|
|
570
624
|
io.out("Next:");
|
|
571
625
|
io.out(" 1. turn on what you need: `softDelete` or `versions` on a collection, `localization`, `multiTenant` or `plugins` in `defineSetup`");
|
|
572
626
|
io.out(existsSync(join(project.root, "typion", "migrations")) ? " 2. `typion db migrate` to apply the migrations, then `typion db seed`" : " 2. `typion db push` to create the tables, then `typion db seed`");
|
|
573
627
|
if (project.framework === "next") io.out(" 3. read from a server component with `const app = await getApp(config)` (typion/next)");
|
|
574
628
|
if (project.framework === "hono") {
|
|
575
|
-
io.out(" 3.
|
|
576
|
-
|
|
629
|
+
io.out(" 3. bind the caller in your own routes with `typionMiddleware(config)`: `c.var.typion`");
|
|
630
|
+
const transports = TRANSPORTS.filter((name) => !project.dependencies.has(name));
|
|
631
|
+
const add = installCommand(manager, transports, false);
|
|
632
|
+
const install = transports.length === 0 ? "" : `\`${add.command} ${add.args.join(" ")}\`, then `;
|
|
633
|
+
io.out(` 4. to serve the collections over HTTP: ${install}\`.all('/api/*', typionRoutes(config, { transports: [rest(), rpc()] }))\` (typion/hono)`);
|
|
577
634
|
}
|
|
578
635
|
}
|
|
579
|
-
async function installDependencies(project, driver, io, options) {
|
|
636
|
+
async function installDependencies(project, manager, driver, io, options) {
|
|
580
637
|
const deps = [
|
|
581
638
|
"typion",
|
|
582
639
|
"drizzle-orm",
|
|
583
640
|
driver === "pglite" ? "@electric-sql/pglite" : "pg",
|
|
584
|
-
...project.framework === "
|
|
641
|
+
...project.framework === "next" ? TRANSPORTS : []
|
|
585
642
|
];
|
|
586
643
|
const dev = ["drizzle-kit"];
|
|
587
644
|
const spec = (name) => name.startsWith("drizzle-") ? `${name}@${DRIZZLE}` : name;
|
|
588
645
|
const missing = deps.filter((name) => !project.dependencies.has(name)).map(spec);
|
|
589
646
|
const missingDev = dev.filter((name) => !project.dependencies.has(name)).map(spec);
|
|
590
647
|
if (missing.length === 0 && missingDev.length === 0) return;
|
|
591
|
-
const manager = packageManager(project.root);
|
|
592
648
|
const lines = [...missing.length === 0 ? [] : [installCommand(manager, missing, false)], ...missingDev.length === 0 ? [] : [installCommand(manager, missingDev, true)]];
|
|
593
649
|
io.out("");
|
|
594
650
|
io.out("Missing packages:");
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["run","db.push","db.generate","db.migrate","db.seed","db.status","db.studio"],"sources":["../../src/cli/output.ts","../../src/cli/config.ts","../../src/cli/db.ts","../../src/cli/agents.ts","../../src/cli/init.ts","../../src/cli/make.ts","../../src/cli/index.ts"],"sourcesContent":["// A failure the user can act on: the CLI prints its message without a stack.\nexport class CliError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'CliError'\n }\n}\n\nexport type Io = {\n out(line: string): void\n err(line: string): void\n}\n\nexport const consoleIo: Io = {\n out: (line) => console.log(line),\n err: (line) => console.error(line),\n}\n","import { existsSync } from 'node:fs'\nimport { dirname, isAbsolute, join, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { TypionConfig } from '../types/collection.js'\nimport { CliError } from './output.js'\n\nconst NAMES = ['typion.config.ts', 'typion.config.js', 'typion.config.mts', 'typion.config.mjs']\nconst DIRS = ['', 'src', 'app', 'config']\n\nexport type LoadedConfig = {\n readonly config: TypionConfig<any>\n readonly path: string\n readonly dir: string\n}\n\nexport function findConfig(cwd: string, given?: string): string {\n if (given !== undefined) {\n const path = isAbsolute(given) ? given : resolve(cwd, given)\n if (!existsSync(path)) throw new CliError(`No config at ${path}`)\n return path\n }\n let dir = resolve(cwd)\n for (;;) {\n for (const sub of DIRS) {\n for (const name of NAMES) {\n const candidate = join(dir, sub, name)\n if (existsSync(candidate)) return candidate\n }\n }\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n throw new CliError(\n `No typion.config.ts found from ${cwd}. Pass --config <path>, or run \\`typion init\\`.`,\n )\n}\n\n// Node runs TypeScript directly (type stripping), which covers a config written with relative imports. A\n// project whose config uses path aliases (`@/…`) needs jiti, so it is used when the project has it.\nasync function importConfig(path: string): Promise<Record<string, unknown>> {\n try {\n return await withoutTypelessWarning(\n async () => (await import(pathToFileURL(path).href)) as Record<string, unknown>,\n )\n } catch (error) {\n const jiti = await loadJiti(path)\n if (jiti === null) throw error\n return (await jiti.import(path, {})) as Record<string, unknown>\n }\n}\n\n// A project without `\"type\": \"module\"` (a Next.js app) makes Node warn, once per command, that it parsed the config\n// twice to find it is a module: true, and nothing the user can act on without changing how their app is built.\nasync function withoutTypelessWarning<T>(load: () => Promise<T>): Promise<T> {\n const emit = process.emitWarning\n process.emitWarning = ((warning: string | Error, ...rest: unknown[]) => {\n const code = (rest[0] as { code?: string } | undefined)?.code ?? rest[1]\n if (code === 'MODULE_TYPELESS_PACKAGE_JSON') return\n ;(emit as (...args: unknown[]) => void).call(process, warning, ...rest)\n }) as typeof process.emitWarning\n try {\n return await load()\n } finally {\n process.emitWarning = emit\n }\n}\n\ntype Jiti = { import(id: string, opts: object): unknown }\n\nasync function loadJiti(path: string): Promise<Jiti | null> {\n try {\n // Optional, and resolved through a variable so it is not a dependency of the build: a project whose\n // config needs path aliases usually has jiti already (drizzle-kit ships it).\n const id = 'jiti'\n const mod = (await import(id)) as { createJiti: (from: string, options?: object) => Jiti }\n return mod.createJiti(pathToFileURL(path).href, { interopDefault: true })\n } catch {\n return null\n }\n}\n\nfunction pickConfig(exports: Record<string, unknown>, path: string): TypionConfig<any> {\n const candidates = [exports.config, exports.default, ...Object.values(exports)]\n for (const value of candidates) {\n if (\n typeof value === 'object' &&\n value !== null &&\n 'definition' in value &&\n 'schema' in value &&\n typeof (value as { definition?: unknown }).definition === 'object'\n ) {\n return value as TypionConfig<any>\n }\n }\n throw new CliError(\n `${path} exports no Typion config (expected \\`export const config = defineConfig(…)\\`)`,\n )\n}\n\n// A config reads `process.env.DATABASE_URL`, and Next.js, Vite and drizzle-kit read `.env` files for it: the CLI does\n// too, `.env.local` first so that it wins, and never over what the shell already set.\nfunction loadEnv(dir: string): void {\n for (const name of ['.env.local', '.env']) {\n const path = join(dir, name)\n if (existsSync(path)) process.loadEnvFile(path)\n }\n}\n\nexport async function loadConfig(cwd: string, given?: string): Promise<LoadedConfig> {\n loadEnv(cwd)\n const path = findConfig(cwd, given)\n const exports = await importConfig(path)\n return { config: pickConfig(exports, path), path, dir: dirname(path) }\n}\n","import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, isAbsolute, join, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { sql } from 'drizzle-orm'\nimport type { PgAsyncDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core'\nimport { getTableConfig, PgTable } from 'drizzle-orm/pg-core'\nimport { is } from 'drizzle-orm'\nimport type { LoadedConfig } from './config.js'\nimport { CliError, type Io } from './output.js'\n\nconst BREAKPOINT = '--> statement-breakpoint'\nconst DEFAULT_DIR = 'typion/migrations'\nconst DEFAULT_TABLE = 'typion_migrations'\n\ntype Journal = { version: 1; entries: { idx: number; tag: string; createdAt: string }[] }\n\ntype Api = typeof import('drizzle-kit/api-postgres')\n\nasync function api(): Promise<Api> {\n try {\n return (await import('drizzle-kit/api-postgres')) as Api\n } catch {\n throw new CliError(\n 'The database commands need drizzle-kit installed (`pnpm add -D drizzle-kit`)',\n )\n }\n}\n\nfunction schemaOf(loaded: LoadedConfig): Record<string, unknown> {\n return loaded.config.schema as Record<string, unknown>\n}\n\n// Migrations belong to the project, not to the folder the config happens to sit in, so they are resolved\n// from the nearest package.json above it.\nfunction projectRoot(loaded: LoadedConfig): string {\n let dir = loaded.dir\n for (;;) {\n if (existsSync(join(dir, 'package.json'))) return dir\n const parent = dirname(dir)\n if (parent === dir) return loaded.dir\n dir = parent\n }\n}\n\nfunction migrationsDir(loaded: LoadedConfig): string {\n const dir = loaded.config.migrations?.dir ?? DEFAULT_DIR\n return isAbsolute(dir) ? dir : resolve(projectRoot(loaded), dir)\n}\n\nfunction migrationsTable(loaded: LoadedConfig): { schema: string; table: string } {\n return {\n schema: loaded.config.migrations?.schema ?? 'public',\n table: loaded.config.migrations?.table ?? DEFAULT_TABLE,\n }\n}\n\nfunction readJournal(dir: string): Journal {\n try {\n return JSON.parse(readFileSync(join(dir, 'meta', 'journal.json'), 'utf8')) as Journal\n } catch {\n return { version: 1, entries: [] }\n }\n}\n\nfunction writeJournal(dir: string, journal: Journal): void {\n mkdirSync(join(dir, 'meta'), { recursive: true })\n writeFileSync(join(dir, 'meta', 'journal.json'), `${JSON.stringify(journal, null, 2)}\\n`)\n}\n\nfunction connect(loaded: LoadedConfig): PgAsyncDatabase<PgQueryResultHKT, any> {\n const adapter = loaded.config.db\n if (adapter === undefined) {\n throw new CliError('The config has no database adapter (`db` in defineConfig)')\n }\n adapter.check?.()\n return adapter.connect(loaded.config.definition.relations)\n}\n\nexport async function push(loaded: LoadedConfig, io: Io, options: { force?: boolean }) {\n const { pushSchema } = await api()\n const db = connect(loaded)\n const { sqlStatements, hints, apply } = await pushSchema(schemaOf(loaded), db as never)\n if (sqlStatements.length === 0) {\n io.out('The database already matches the config.')\n return\n }\n for (const statement of sqlStatements) io.out(statement)\n if (hints.length > 0) {\n for (const hint of hints) io.err(`! ${hint.hint}`)\n if (options.force !== true) {\n throw new CliError('These changes can lose data: run again with --force to apply them.')\n }\n }\n await apply()\n io.out(`Applied ${sqlStatements.length} statement(s).`)\n await loaded.config.db?.close?.()\n}\n\nexport async function generate(loaded: LoadedConfig, io: Io, options: { name?: string }) {\n const { generateDrizzleJson, generateMigration } = await api()\n const dir = migrationsDir(loaded)\n const journal = readJournal(dir)\n const last = journal.entries.at(-1)\n const previous =\n last === undefined\n ? await generateDrizzleJson({})\n : (JSON.parse(\n readFileSync(\n join(dir, 'meta', `${String(last.idx).padStart(4, '0')}_snapshot.json`),\n 'utf8',\n ),\n ) as Awaited<ReturnType<typeof generateDrizzleJson>>)\n const current = await generateDrizzleJson(schemaOf(loaded), previous.id)\n const statements = await generateMigration(previous, current)\n if (statements.length === 0) {\n io.out('No changes: the migrations already describe the config.')\n return\n }\n const idx = (last?.idx ?? -1) + 1\n const tag = `${String(idx).padStart(4, '0')}_${(options.name ?? 'change').replace(/[^a-z0-9_-]+/gi, '_')}`\n mkdirSync(join(dir, 'meta'), { recursive: true })\n writeFileSync(join(dir, `${tag}.sql`), `${statements.join(`\\n${BREAKPOINT}\\n`)}\\n`)\n writeFileSync(\n join(dir, 'meta', `${String(idx).padStart(4, '0')}_snapshot.json`),\n `${JSON.stringify(current, null, 2)}\\n`,\n )\n journal.entries.push({ idx, tag, createdAt: new Date().toISOString() })\n writeJournal(dir, journal)\n io.out(`Wrote ${tag}.sql with ${statements.length} statement(s).`)\n}\n\nasync function applied(\n db: PgAsyncDatabase<PgQueryResultHKT, any>,\n where: { schema: string; table: string },\n): Promise<Set<string>> {\n if (where.schema !== 'public') {\n await db.execute(sql.raw(`create schema if not exists \"${where.schema}\"`))\n }\n await db.execute(\n sql.raw(\n `create table if not exists \"${where.schema}\".\"${where.table}\" (` +\n 'tag text primary key, applied_at timestamptz not null default now())',\n ),\n )\n const rows = await db.execute(sql.raw(`select tag from \"${where.schema}\".\"${where.table}\"`))\n const list =\n (rows as unknown as { rows?: { tag: string }[] }).rows ?? (rows as unknown as { tag: string }[])\n return new Set(list.map((row) => row.tag))\n}\n\nexport async function migrate(loaded: LoadedConfig, io: Io) {\n const dir = migrationsDir(loaded)\n const journal = readJournal(dir)\n if (journal.entries.length === 0) {\n io.out('No migrations to apply: run `typion db generate` first.')\n return\n }\n const db = connect(loaded)\n const where = migrationsTable(loaded)\n const done = await applied(db, where)\n const pending = journal.entries.filter((entry) => !done.has(entry.tag))\n if (pending.length === 0) {\n io.out('The database is up to date.')\n await loaded.config.db?.close?.()\n return\n }\n for (const entry of pending) {\n const statements = readFileSync(join(dir, `${entry.tag}.sql`), 'utf8')\n .split(BREAKPOINT)\n .map((statement) => statement.trim())\n .filter((statement) => statement !== '')\n await db.transaction(async (tx) => {\n for (const statement of statements) await tx.execute(sql.raw(statement))\n await tx.execute(\n sql.raw(`insert into \"${where.schema}\".\"${where.table}\" (tag) values ('${entry.tag}')`),\n )\n })\n io.out(`Applied ${entry.tag} (${statements.length} statement(s)).`)\n }\n await loaded.config.db?.close?.()\n}\n\nconst SEED_FILES = ['typion/seed.ts', 'typion/seed.js', 'seed.ts', 'seed.js']\n\nfunction findSeedFile(loaded: LoadedConfig, given?: string): string | null {\n if (given !== undefined) {\n const path = isAbsolute(given) ? given : resolve(process.cwd(), given)\n if (!existsSync(path)) throw new CliError(`No seed file at ${path}`)\n return path\n }\n for (const dir of [projectRoot(loaded), loaded.dir]) {\n for (const name of SEED_FILES) {\n const candidate = join(dir, name)\n if (existsSync(candidate)) return candidate\n }\n }\n return null\n}\n\n// Two ways to fill a database, and they answer different needs: the project's own seed writes meaningful rows\n// through the engine (hooks, versions and validation included), `--random` asks drizzle-seed for plausible\n// ones straight into the tables.\nasync function anyRows(loaded: LoadedConfig): Promise<boolean> {\n const db = connect(loaded)\n for (const entity of Object.values(loaded.config.definition.entities)) {\n if (entity.owner !== 'user') continue\n const where = `\"${entity.pgSchema ?? 'public'}\".\"${entity.tableName}\"`\n const rows = await db.execute(sql.raw(`select 1 from ${where} limit 1`))\n const list = (rows as unknown as { rows?: unknown[] }).rows ?? (rows as unknown as unknown[])\n if (list.length > 0) return true\n }\n return false\n}\n\nexport async function seed(\n loaded: LoadedConfig,\n io: Io,\n options: {\n file?: string\n random?: boolean\n count?: number\n seed?: number\n reset?: boolean\n ifEmpty?: boolean\n },\n) {\n if (options.ifEmpty === true && (await anyRows(loaded))) {\n io.out('The collections already hold rows: nothing seeded.')\n await loaded.config.db?.close?.()\n return\n }\n const { resetTables, seedCollections } = await import('../seed/index.js')\n if (options.reset === true) {\n await resetTables(loaded.config)\n io.out('Emptied every table of the config.')\n }\n if (options.random === true) {\n await seedCollections(loaded.config, { count: options.count, seed: options.seed })\n io.out(`Seeded every collection with ${options.count ?? 10} rows.`)\n await loaded.config.db?.close?.()\n return\n }\n const file = findSeedFile(loaded, options.file)\n if (file === null) {\n throw new CliError(\n 'No seed file found (typion/seed.ts). Write one, point at it with --file, or use --random.',\n )\n }\n const { getTypion } = await import('../runtime/typion.js')\n const typion = await getTypion(loaded.config)\n const seedModule = (await import(pathToFileURL(file).href)) as Record<string, unknown>\n const run = (seedModule.seed ?? seedModule.default) as\n ((args: { typion: unknown; config: unknown }) => unknown) | undefined\n if (typeof run !== 'function') {\n throw new CliError(\n `${file} exports no seed function (\\`export async function seed({ typion })\\`)`,\n )\n }\n await run({ typion, config: loaded.config })\n io.out(`Ran ${file}.`)\n await loaded.config.db?.close?.()\n}\n\nexport async function status(loaded: LoadedConfig, io: Io) {\n const dir = migrationsDir(loaded)\n const journal = readJournal(dir)\n const db = connect(loaded)\n const done = await applied(db, migrationsTable(loaded))\n if (journal.entries.length === 0) io.out('No migrations yet.')\n for (const entry of journal.entries) {\n io.out(`${done.has(entry.tag) ? 'applied' : 'pending'} ${entry.tag}`)\n }\n const tables = Object.values(schemaOf(loaded)).filter((value) => is(value, PgTable)) as PgTable[]\n io.out(\n `${tables.length} table(s) in the config: ${tables.map((t) => getTableConfig(t).name).join(', ')}`,\n )\n await loaded.config.db?.close?.()\n}\n\nexport async function studio(loaded: LoadedConfig, io: Io, options: { port?: number }) {\n const { startStudioServer } = await api()\n const credentials = loaded.config.db?.credentials?.()\n if (credentials === undefined) {\n throw new CliError(\n 'This database adapter cannot open Studio: pass `url` to dbAdapter(), or use typion/db/pglite.',\n )\n }\n io.out(`Studio on https://local.drizzle.studio (port ${options.port ?? 4983})`)\n await startStudioServer(schemaOf(loaded), credentials as never, { port: options.port })\n}\n\nexport function listMigrations(dir: string): string[] {\n try {\n return readdirSync(dir).filter((name) => name.endsWith('.sql'))\n } catch {\n return []\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join, relative } from 'node:path'\nimport type { Io } from './output.js'\n\n// Agents do not know Typion from training, so the application's agent notes carry the rules that must always hold\n// and point at the guide shipped with the installed version. The block is delimited so that it can be rewritten\n// without touching the rest of the file, the way Next.js maintains its own (`next dev`).\nexport const START_MARKER = '<!-- BEGIN:typion-agent-rules -->'\nexport const END_MARKER = '<!-- END:typion-agent-rules -->'\n\nexport const AGENT_RULES = `${START_MARKER}\n\n# Typion\n\nThis application's data layer is Typion, which is newer than your training data. Before you add or change a\ncollection, an access rule, a hook, a query or an API route, read \\`node_modules/typion/docs/README.md\\` (resolved\nfrom this file's directory) and the page it points to for the task.\n\n- Read and write the application's data through Typion, bound to the caller: \\`getApp(config)\\` on Next.js,\n \\`c.var.typion\\` on Hono, \\`typion.$fromRequest(request)\\` elsewhere, \\`typion.$as(ctx)\\`. Never through\n \\`typion.$db\\`, Drizzle or SQL: they skip the access rules, tenants, soft delete, drafts, hooks and the history.\n- \\`typion.$system({ reason })\\` skips the access rules: for seeds, jobs and webhooks, never to answer a user's\n request.\n- Authorization lives in each collection's \\`access\\` rules, deny by default, not in pages, routes or server\n actions.\n- Every schema change ships a migration: \\`typion db generate --name <change>\\`.\n\nThis block is written by \\`typion init\\`; run it again after upgrading Typion.\n\n${END_MARKER}`\n\nconst SKILL_MARKER =\n '<!-- Written by `typion init`, which rewrites it: keep project notes in AGENTS.md. -->'\n\nexport const SKILL = `---\nname: typion\ndescription: Use for any change to this application's data layer with Typion — collections, access rules, hooks, queries and descriptors, API routes, seeds, migrations, or tests that touch typion.config.ts or the typion/ folder next to it.\n---\n\n${SKILL_MARKER}\n\nTypion is newer than your training data. Its guide ships with the installed version, in \\`node_modules/typion/docs/\\`.\n\n1. Read \\`node_modules/typion/docs/README.md\\`: the model, the rules, where things go.\n2. Read the page for the task:\n - \\`collections.md\\`: a collection, its columns and relations, a many-to-many\n - \\`access.md\\`: who reads and writes what, hidden columns, what a caller sees\n - \\`auth.md\\`: signing in, who the caller is\n - \\`hooks.md\\`: reacting to writes\n - \\`queries.md\\`: reads, writes, descriptors, transactions, errors\n - \\`http.md\\`: Next.js pages and server actions, Hono, the routes, the browser client, business operations\n - \\`database.md\\`: drivers, migrations, seed\n - \\`testing.md\\`: tests\n - \\`features/\\`: localization, multi-tenancy, soft delete, versions and drafts\n3. Before you call the change done: \\`tsc\\` passes, every new collection has its rules, a test covers a caller the\n rules allow and one they refuse, and a schema change has its migration.\n`\n\ntype Outcome = 'created' | 'updated' | 'unchanged' | 'kept'\n\nfunction upsert(existing: string): string {\n const eol = existing.includes('\\r\\n') ? '\\r\\n' : '\\n'\n const block = AGENT_RULES.replace(/\\n/g, eol)\n const start = existing.indexOf(START_MARKER)\n const end = existing.indexOf(END_MARKER, start)\n if (start !== -1 && end !== -1) {\n return existing.slice(0, start) + block + existing.slice(end + END_MARKER.length)\n }\n if (existing.length === 0) return `${block}${eol}`\n const body = existing.endsWith(eol) ? existing : `${existing}${eol}`\n return `${body}${eol}${block}${eol}`\n}\n\nfunction writeFile(path: string, next: string, previous: string | null): Outcome {\n if (previous === next) return 'unchanged'\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, next)\n return previous === null ? 'created' : 'updated'\n}\n\nfunction read(path: string): string | null {\n return existsSync(path) ? readFileSync(path, 'utf8') : null\n}\n\n// The block goes where the project keeps its notes: AGENTS.md, or CLAUDE.md when only that one exists; a project\n// with neither gets both, CLAUDE.md importing AGENTS.md. The skill is rewritten only while it is still Typion's.\nexport function writeAgentNotes(root: string, io: Io): void {\n const agentsPath = join(root, 'AGENTS.md')\n const claudePath = join(root, 'CLAUDE.md')\n const agents = read(agentsPath)\n const claude = read(claudePath)\n const report = (path: string, outcome: Outcome) => {\n const verb = { created: 'Created', updated: 'Updated', kept: 'Kept', unchanged: null }[outcome]\n if (verb !== null) io.out(`${verb} ${relative(process.cwd(), path)}.`)\n }\n\n const claudeHostsBlock = claude?.includes(START_MARKER) ?? false\n if (agents !== null && (agents.includes(START_MARKER) || !claudeHostsBlock)) {\n report(agentsPath, writeFile(agentsPath, upsert(agents), agents))\n } else if (claude !== null) {\n report(claudePath, writeFile(claudePath, upsert(claude), claude))\n } else {\n report(agentsPath, writeFile(agentsPath, upsert(''), null))\n report(claudePath, writeFile(claudePath, '@AGENTS.md\\n', null))\n }\n\n const skillPath = join(root, '.claude', 'skills', 'typion', 'SKILL.md')\n const skill = read(skillPath)\n if (skill !== null && !skill.includes(SKILL_MARKER)) {\n io.out(`Kept ${relative(process.cwd(), skillPath)} (it is not Typion's any more).`)\n } else {\n report(skillPath, writeFile(skillPath, SKILL, skill))\n }\n}\n","import { spawnSync } from 'node:child_process'\nimport { createInterface } from 'node:readline/promises'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join, relative } from 'node:path'\nimport { writeAgentNotes } from './agents.js'\nimport { CliError, type Io } from './output.js'\n\ntype PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun'\n\n// npm's `latest` Drizzle is 0.x, and a later release candidate can break the engine (rc.5 does): the version the\n// engine is built on, which its `package.json` names as a peer.\nconst DRIZZLE = '1.0.0-rc.4'\n\nfunction packageManager(root: string): PackageManager {\n if (existsSync(join(root, 'pnpm-lock.yaml'))) return 'pnpm'\n if (existsSync(join(root, 'yarn.lock'))) return 'yarn'\n if (existsSync(join(root, 'bun.lock')) || existsSync(join(root, 'bun.lockb'))) return 'bun'\n if (existsSync(join(root, 'package-lock.json'))) return 'npm'\n const agent = process.env.npm_config_user_agent ?? ''\n if (agent.startsWith('yarn')) return 'yarn'\n if (agent.startsWith('bun')) return 'bun'\n if (agent.startsWith('pnpm')) return 'pnpm'\n return 'npm'\n}\n\nfunction installCommand(\n manager: PackageManager,\n packages: readonly string[],\n dev: boolean,\n): { command: string; args: string[] } {\n const flag = dev ? (manager === 'npm' ? '--save-dev' : '-D') : ''\n const verb = manager === 'npm' ? 'install' : 'add'\n return {\n command: manager,\n args: [verb, ...(flag === '' ? [] : [flag]), ...packages],\n }\n}\n\nasync function confirm(question: string, io: Io): Promise<boolean> {\n if (!process.stdin.isTTY) {\n io.out(`${question} — not a terminal, so nothing was installed. Re-run with --yes.`)\n return false\n }\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n try {\n const answer = (await rl.question(`${question} [Y/n] `)).trim().toLowerCase()\n return answer === '' || answer === 'y' || answer === 'yes'\n } finally {\n rl.close()\n }\n}\n\nfunction run(command: string, args: readonly string[], cwd: string, io: Io): void {\n io.out(`> ${command} ${args.join(' ')}`)\n const result = spawnSync(command, [...args], { cwd, stdio: 'inherit' })\n if (result.error !== undefined || (result.status !== null && result.status !== 0)) {\n throw new CliError(`\\`${command} ${args.join(' ')}\\` failed; install them yourself and re-run.`)\n }\n}\n\ntype Project = {\n readonly root: string\n readonly src: string\n readonly framework: 'next' | 'hono' | 'none'\n readonly dependencies: ReadonlySet<string>\n readonly appDir: string | null\n}\n\nfunction readJson(path: string): Record<string, any> | null {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as Record<string, any>\n } catch {\n return null\n }\n}\n\nfunction inspect(cwd: string): Project {\n const pkg = readJson(join(cwd, 'package.json'))\n if (pkg === null) throw new CliError(`No package.json in ${cwd}: run this inside your project.`)\n const deps = { ...pkg.dependencies, ...pkg.devDependencies } as Record<string, string>\n const src = existsSync(join(cwd, 'src')) ? join(cwd, 'src') : cwd\n const appDir = [join(src, 'app'), join(cwd, 'app')].find((dir) => existsSync(dir)) ?? null\n return {\n root: cwd,\n src,\n framework: deps.next !== undefined ? 'next' : deps.hono !== undefined ? 'hono' : 'none',\n dependencies: new Set(Object.keys(deps)),\n appDir,\n }\n}\n\nfunction write(path: string, contents: string, io: Io): boolean {\n if (existsSync(path)) {\n io.out(`Kept ${relative(process.cwd(), path)} (it exists already).`)\n return false\n }\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, contents)\n io.out(`Created ${relative(process.cwd(), path)}.`)\n return true\n}\n\nconst COLLECTION = `import { text } from 'drizzle-orm/pg-core'\nimport { collection } from 'typion'\n\nexport const pages = collection('pages', {\n columns: {\n title: text().notNull(),\n slug: text().notNull().unique(),\n },\n public: true,\n})\n`\n\nfunction configFile(driver: 'pglite' | 'postgres'): string {\n const db =\n driver === 'pglite'\n ? ` db: pglite({ dataDir: '.pglite' }),`\n : ` db: dbAdapter((relations) => drizzle(process.env.DATABASE_URL!, { relations }), {\\n url: process.env.DATABASE_URL,\\n }),`\n const imports =\n driver === 'pglite'\n ? `import { defineConfig, defineSetup } from 'typion'\\nimport { pglite } from 'typion/db/pglite'`\n : `import { drizzle } from 'drizzle-orm/node-postgres'\\nimport { dbAdapter, defineConfig, defineSetup } from 'typion'`\n return `${imports}\nimport { pages } from './typion/collections/pages.ts'\n\n// What the application knows about a caller: rules, hooks and field rules read it. Registered below as well, since\n// field rules are typed before this config exists.\nexport type AppContext = {\n actorId: string | null\n}\n\n// A constant of its own: the collections read it to type their options and their columns, which happens before\n// this config exists. It takes the features (\\`localization\\`, \\`multiTenant\\`) and the plugins (\\`plugins: […]\\`).\nexport const setup = defineSetup({})\n\nexport const config = defineConfig({\n setup,\n collections: [pages],\n${db}\n // Who is calling. Return your own shape; declare the return type, the body may query \\`system\\`.\n context: async ({ headers }): Promise<AppContext> => {\n void headers\n return { actorId: null }\n },\n})\n\ndeclare module 'typion' {\n interface Register {\n setup: typeof setup\n config: typeof config\n context: AppContext\n }\n}\n`\n}\n\n// Every import Typion writes ends in `.ts`, as Node needs to load the config the way the CLI does. TypeScript takes\n// them with `allowImportingTsExtensions` where nothing is emitted (Next.js), and with\n// `rewriteRelativeImportExtensions` where `tsc` builds the project.\nfunction allowTsImports(root: string, io: Io): void {\n const path = join(root, 'tsconfig.json')\n if (!existsSync(path)) return\n const json = readJson(path)\n const options = (json?.compilerOptions ?? {}) as Record<string, unknown>\n if (\n options.allowImportingTsExtensions === true ||\n options.rewriteRelativeImportExtensions === true\n ) {\n return\n }\n const flag =\n options.noEmit === true ? 'allowImportingTsExtensions' : 'rewriteRelativeImportExtensions'\n if (json === null) {\n io.out(\n `Add \"${flag}\": true to the compilerOptions of tsconfig.json: Typion's imports end in .ts.`,\n )\n return\n }\n json.compilerOptions = { ...options, [flag]: true }\n writeFileSync(path, `${JSON.stringify(json, null, 2)}\\n`)\n io.out(`Set ${flag} in tsconfig.json: Typion's imports end in .ts.`)\n}\n\nconst ROUTE = `import { rest } from '@typion/rest'\nimport { rpc } from '@typion/rpc'\nimport { routeHandlers } from 'typion/next'\nimport { config } from '@/typion.config.ts'\n\n// What the app serves over HTTP: a resource per collection, and the endpoint the browser client posts to. Leave\n// out what you do not use.\nexport const { GET, POST, PATCH, PUT, DELETE, OPTIONS } = await routeHandlers(config, {\n transports: [rest(), rpc()],\n})\n`\n\nexport async function init(\n cwd: string,\n io: Io,\n options: { driver?: string; yes?: boolean; skipInstall?: boolean },\n): Promise<void> {\n const project = inspect(cwd)\n const driver = options.driver === 'postgres' ? 'postgres' : 'pglite'\n\n // The sample collection comes with a new config only: a run after an upgrade refreshes the notes and leaves the\n // project's collections as they are.\n if (write(join(project.src, 'typion.config.ts'), configFile(driver), io)) {\n write(join(project.src, 'typion', 'collections', 'pages.ts'), COLLECTION, io)\n }\n allowTsImports(project.root, io)\n\n if (project.framework === 'next' && project.appDir !== null) {\n write(join(project.appDir, 'api', '[...path]', 'route.ts'), ROUTE, io)\n }\n writeAgentNotes(project.root, io)\n\n await installDependencies(project, driver, io, options)\n\n io.out('')\n io.out('Next:')\n io.out(\n ' 1. turn on what you need: `softDelete` or `versions` on a collection, `localization`, `multiTenant` or `plugins` in `defineSetup`',\n )\n io.out(\n existsSync(join(project.root, 'typion', 'migrations'))\n ? ' 2. `typion db migrate` to apply the migrations, then `typion db seed`'\n : ' 2. `typion db push` to create the tables, then `typion db seed`',\n )\n if (project.framework === 'next') {\n io.out(\n ' 3. read from a server component with `const app = await getApp(config)` (typion/next)',\n )\n }\n // The app's entry is the developer's own file: what to add to it is said, not written.\n if (project.framework === 'hono') {\n io.out(\n \" 3. mount the transports in your Hono app: `.all('/api/*', typionRoutes(config, { transports: [rest(), rpc()] }))` (typion/hono)\",\n )\n io.out(\n ' 4. bind the caller in your own routes with `typionMiddleware(config)`: `c.var.typion`',\n )\n }\n}\n\nasync function installDependencies(\n project: Project,\n driver: 'pglite' | 'postgres',\n io: Io,\n options: { yes?: boolean; skipInstall?: boolean },\n): Promise<void> {\n const deps = [\n 'typion',\n 'drizzle-orm',\n driver === 'pglite' ? '@electric-sql/pglite' : 'pg',\n ...(project.framework === 'none' ? [] : ['@typion/rest', '@typion/rpc']),\n ]\n const dev = ['drizzle-kit']\n const spec = (name: string) => (name.startsWith('drizzle-') ? `${name}@${DRIZZLE}` : name)\n const missing = deps.filter((name) => !project.dependencies.has(name)).map(spec)\n const missingDev = dev.filter((name) => !project.dependencies.has(name)).map(spec)\n if (missing.length === 0 && missingDev.length === 0) return\n\n const manager = packageManager(project.root)\n const lines = [\n ...(missing.length === 0 ? [] : [installCommand(manager, missing, false)]),\n ...(missingDev.length === 0 ? [] : [installCommand(manager, missingDev, true)]),\n ]\n io.out('')\n io.out('Missing packages:')\n for (const line of lines) io.out(` ${line.command} ${line.args.join(' ')}`)\n\n if (options.skipInstall === true) return\n const ok = options.yes === true || (await confirm(`Install them with ${manager}?`, io))\n if (!ok) return\n for (const line of lines) run(line.command, line.args, project.root, io)\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport type { LoadedConfig } from './config.js'\nimport { CliError, type Io } from './output.js'\n\nfunction pascal(name: string): string {\n return name.replace(/(^|[-_ ])(\\w)/g, (_, __, char: string) => char.toUpperCase())\n}\n\nfunction camel(name: string): string {\n const upper = pascal(name)\n return upper.charAt(0).toLowerCase() + upper.slice(1)\n}\n\n// Where the collections of this project live: next to the ones already there, else beside the config.\nfunction collectionsDir(loaded: LoadedConfig, given?: string): string {\n if (given !== undefined) return isAbsolute(given) ? given : resolve(process.cwd(), given)\n const candidates = [join(loaded.dir, 'typion', 'collections'), join(loaded.dir, 'collections')]\n return candidates.find((dir) => existsSync(dir)) ?? candidates[0]!\n}\n\n// No rule is written for the developer: a generated `read: true` would serve every row to anyone, anonymous callers\n// included, until someone noticed. In a multi-tenant application the rows belong to the caller's tenant.\nfunction template(name: string, tenant: boolean): string {\n return `import { text } from 'drizzle-orm/pg-core'\nimport { collection } from 'typion'\n\nexport const ${camel(name)} = collection('${camel(name)}', {\n columns: {\n // Plain Drizzle columns; the features and plugins add their own options next to \\`columns\\`.\n title: text().notNull(),\n },${tenant ? '\\n tenant: true,' : ''}\n}).define({\n // Deny by default: an operation without a rule is refused. Give each one the rule it needs, for example\n // \\`read: ({ ctx }) => ctx.actorId !== null\\`.\n access: {},\n})\n`\n}\n\nfunction importPath(from: string, to: string): string {\n const path = relative(from, to).replaceAll('\\\\', '/')\n return path.startsWith('.') ? path : `./${path}`\n}\n\n// Best effort: adds the import and the entry to `collections: [...]`. When the config does not match the\n// shape the CLI understands, it prints the two lines instead of guessing.\nfunction register(loaded: LoadedConfig, name: string, file: string, io: Io): void {\n const source = readFileSync(loaded.path, 'utf8')\n const specifier = importPath(loaded.dir, file).replace(/\\.ts$/, '.ts')\n const line = `import { ${camel(name)} } from '${specifier}'`\n const list = /collections:\\s*\\[([^\\]]*)\\]/.exec(source)\n if (source.includes(line) || list === null) {\n io.out('Add it to the config:')\n io.out(` ${line}`)\n io.out(` collections: [ …, ${camel(name)} ]`)\n return\n }\n const entries = list[1]!.trim()\n const updated = source\n .replace(/(^import .*\\n)(?![\\s\\S]*^import )/m, `$1${line}\\n`)\n .replace(\n /collections:\\s*\\[([^\\]]*)\\]/,\n `collections: [${entries === '' ? '' : `${entries.replace(/,\\s*$/, '')}, `}${camel(name)}]`,\n )\n writeFileSync(loaded.path, updated)\n io.out(`Registered it in ${relative(process.cwd(), loaded.path)}.`)\n}\n\nfunction pluginTemplate(name: string): string {\n const id = camel(name)\n const Kinds = `${pascal(name)}Kinds`\n const Options = `${pascal(name)}Options`\n return `import { definePlugin } from 'typion'\nimport type { CollectionKinds } from 'typion'\n\n// What this plugin adds to every collection. A collection that does not set the option gets nothing.\nexport type ${Options} = { ${id}?: boolean }\n\n// How the option turns into columns and into engine options, read from \\`this['options']\\`.\ninterface ${Kinds} extends CollectionKinds {\n readonly columns: this['options'] extends { ${id}: true } ? {} : {}\n readonly entity: this['options'] extends { ${id}: true } ? {} : {}\n}\n\nexport function ${id}() {\n return definePlugin({\n id: '${id}',\n collection: {\n $options: {} as ${Options},\n $kinds: {} as ${Kinds},\n // Option keys this plugin reads, so they never reach the engine as entity options.\n consumes: ['${id}'],\n columns: (options) => (options.${id} === true ? {} : {}),\n entity: (options) => (options.${id} === true ? {} : {}),\n },\n // Tables, relations, entities, hooks, api, routes, errors and context go here too; see @typion/audit.\n })\n}\n`\n}\n\nexport function makePlugin(\n loaded: LoadedConfig,\n io: Io,\n name: string,\n options: { dir?: string },\n): void {\n if (!/^[a-z][a-z0-9_-]*$/i.test(name)) {\n throw new CliError(`'${name}' is not a plugin name (letters, digits, dash, underscore)`)\n }\n const dir =\n options.dir === undefined\n ? join(loaded.dir, 'typion', 'plugins')\n : isAbsolute(options.dir)\n ? options.dir\n : resolve(process.cwd(), options.dir)\n const file = join(dir, `${camel(name)}.ts`)\n if (existsSync(file)) throw new CliError(`${relative(process.cwd(), file)} already exists`)\n mkdirSync(dir, { recursive: true })\n writeFileSync(file, pluginTemplate(name))\n io.out(`Created ${relative(process.cwd(), file)}.`)\n io.out('Install it in the config:')\n io.out(` import { ${camel(name)} } from './${relative(loaded.dir, file).replaceAll('\\\\', '/')}'`)\n io.out(` export const setup = defineSetup({ plugins: [ …, ${camel(name)}() ] })`)\n}\n\nexport function makeCollection(\n loaded: LoadedConfig,\n io: Io,\n name: string,\n options: { dir?: string },\n): void {\n if (!/^[a-z][a-z0-9_-]*$/i.test(name)) {\n throw new CliError(`'${name}' is not a collection name (letters, digits, dash, underscore)`)\n }\n const dir = collectionsDir(loaded, options.dir)\n const file = join(dir, `${camel(name)}.ts`)\n if (existsSync(file)) throw new CliError(`${relative(process.cwd(), file)} already exists`)\n mkdirSync(dirname(file), { recursive: true })\n const tenant = loaded.config.definition.plugins.some((plugin) => plugin.id === 'multiTenant')\n writeFileSync(file, template(name, tenant))\n io.out(`Created ${relative(process.cwd(), file)}.`)\n register(loaded, name, file, io)\n io.out('Then `typion db push` (or `typion db generate` + `typion db migrate`).')\n}\n","import { parseArgs } from 'node:util'\nimport { ConfigError } from '../errors.js'\nimport { loadConfig } from './config.js'\nimport * as db from './db.js'\nimport { init } from './init.js'\nimport { makeCollection, makePlugin } from './make.js'\nimport { CliError, consoleIo, type Io } from './output.js'\n\nconst HELP = `typion — the CLI of a Typion project\n\nUsage\n typion <group> <command> [options]\n\nDatabase (everything that touches the schema, through drizzle-kit)\n typion db push [--force] make the database match the config (development)\n typion db generate [--name x] write the SQL of what changed, with its snapshot\n typion db migrate apply the migrations the database has not applied\n typion db seed [--file path] run the project's seed through the engine\n [--random] ask drizzle-seed for rows instead (--count, --seed)\n [--reset] empty every table of the config first\n [--if-empty] do nothing when a collection already holds rows\n typion db status what is applied, what is pending\n typion db studio [--port n] open Drizzle Studio on this config\n\nProject\n typion make collection <name> [--dir path] a new collection, registered in the config\n typion make plugin <name> [--dir path] a plugin skeleton for this project\n typion init [--driver pglite|postgres] [--yes] [--skip-install]\n set Typion up in a project that exists, with the\n notes its agents read (run it again after upgrading)\n\nOptions\n --config <path> the config to read; by default the nearest typion.config.ts\n --help this text (also: typion db --help)\n`\n\nconst DB_HELP = `typion db — the database of a Typion config\n\n push [--force] make the database match the config (development)\n generate [--name x] write the SQL of what changed, with its snapshot\n migrate apply the migrations the database has not applied\n seed [--file|--random] fill the database: the project's seed, or drizzle-seed rows\n status what is applied, what is pending\n studio [--port n] open Drizzle Studio on this config\n`\n\nconst OPTIONS = {\n config: { type: 'string' },\n name: { type: 'string' },\n dir: { type: 'string' },\n driver: { type: 'string' },\n port: { type: 'string' },\n file: { type: 'string' },\n count: { type: 'string' },\n seed: { type: 'string' },\n force: { type: 'boolean' },\n random: { type: 'boolean' },\n reset: { type: 'boolean' },\n 'if-empty': { type: 'boolean' },\n 'skip-install': { type: 'boolean' },\n yes: { type: 'boolean' },\n help: { type: 'boolean' },\n} as const\n\nfunction port(raw: string | undefined): number | undefined {\n if (raw === undefined) return undefined\n if (!/^\\d+$/.test(raw)) throw new CliError('--port must be a number')\n return Number(raw)\n}\n\nexport async function run(argv: readonly string[], io: Io = consoleIo, cwd = process.cwd()) {\n const { values, positionals } = parseArgs({\n args: [...argv],\n options: OPTIONS,\n allowPositionals: true,\n strict: true,\n })\n const [group, command, argument] = positionals\n\n if (group === undefined || values.help === true) {\n io.out(group === 'db' ? DB_HELP : HELP)\n return\n }\n\n const config = async () => loadConfig(cwd, values.config)\n\n switch (`${group} ${command ?? ''}`.trim()) {\n case 'db push':\n return db.push(await config(), io, { force: values.force })\n case 'db generate':\n return db.generate(await config(), io, { name: values.name })\n case 'db migrate':\n return db.migrate(await config(), io)\n case 'db seed':\n return db.seed(await config(), io, {\n file: values.file,\n random: values.random,\n count: values.count === undefined ? undefined : Number(values.count),\n seed: values.seed === undefined ? undefined : Number(values.seed),\n reset: values.reset,\n ifEmpty: values['if-empty'],\n })\n case 'db status':\n return db.status(await config(), io)\n case 'db studio':\n return db.studio(await config(), io, { port: port(values.port) })\n case 'db':\n io.out(DB_HELP)\n return\n case 'make collection': {\n if (argument === undefined) throw new CliError('Usage: typion make collection <name>')\n return makeCollection(await config(), io, argument, { dir: values.dir })\n }\n case 'make plugin': {\n if (argument === undefined) throw new CliError('Usage: typion make plugin <name>')\n return makePlugin(await config(), io, argument, { dir: values.dir })\n }\n case 'make':\n throw new CliError('Usage: typion make collection|plugin <name>')\n case 'init':\n return init(cwd, io, {\n driver: values.driver,\n yes: values.yes,\n skipInstall: values['skip-install'],\n })\n default:\n throw new CliError(\n `Unknown command '${[group, command].filter(Boolean).join(' ')}'. Try \\`typion --help\\`.`,\n )\n }\n}\n\nexport async function main(argv: readonly string[]): Promise<number> {\n try {\n await run(argv)\n return 0\n } catch (error) {\n // What the user has to change, in a sentence: a stack trace would bury it.\n if (error instanceof CliError || error instanceof ConfigError) {\n console.error(error.message)\n return 1\n }\n console.error(error)\n return 1\n }\n}\n\nexport { CliError }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["run","db.push","db.generate","db.migrate","db.seed","db.status","db.studio"],"sources":["../../src/cli/output.ts","../../src/cli/config.ts","../../src/cli/db.ts","../../src/cli/agents.ts","../../src/cli/init.ts","../../src/cli/make.ts","../../src/cli/index.ts"],"sourcesContent":["// A failure the user can act on: the CLI prints its message without a stack.\nexport class CliError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'CliError'\n }\n}\n\nexport type Io = {\n out(line: string): void\n err(line: string): void\n}\n\nexport const consoleIo: Io = {\n out: (line) => console.log(line),\n err: (line) => console.error(line),\n}\n","import { existsSync } from 'node:fs'\nimport { dirname, isAbsolute, join, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { TypionConfig } from '../types/collection.js'\nimport { CliError } from './output.js'\n\nconst NAMES = ['typion.config.ts', 'typion.config.js', 'typion.config.mts', 'typion.config.mjs']\nconst DIRS = ['', 'src', 'app', 'config']\n\nexport type LoadedConfig = {\n readonly config: TypionConfig<any>\n readonly path: string\n readonly dir: string\n}\n\nexport function findConfig(cwd: string, given?: string): string {\n if (given !== undefined) {\n const path = isAbsolute(given) ? given : resolve(cwd, given)\n if (!existsSync(path)) throw new CliError(`No config at ${path}`)\n return path\n }\n let dir = resolve(cwd)\n for (;;) {\n for (const sub of DIRS) {\n for (const name of NAMES) {\n const candidate = join(dir, sub, name)\n if (existsSync(candidate)) return candidate\n }\n }\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n throw new CliError(\n `No typion.config.ts found from ${cwd}. Pass --config <path>, or run \\`typion init\\`.`,\n )\n}\n\n// Node runs TypeScript directly (type stripping), which covers a config written with relative imports. A\n// project whose config uses path aliases (`@/…`) needs jiti, so it is used when the project has it.\nasync function importConfig(path: string): Promise<Record<string, unknown>> {\n try {\n return await withoutTypelessWarning(\n async () => (await import(pathToFileURL(path).href)) as Record<string, unknown>,\n )\n } catch (error) {\n const jiti = await loadJiti(path)\n if (jiti === null) throw error\n return (await jiti.import(path, {})) as Record<string, unknown>\n }\n}\n\n// A project without `\"type\": \"module\"` (a Next.js app) makes Node warn, once per command, that it parsed the config\n// twice to find it is a module: true, and nothing the user can act on without changing how their app is built.\nasync function withoutTypelessWarning<T>(load: () => Promise<T>): Promise<T> {\n const emit = process.emitWarning\n process.emitWarning = ((warning: string | Error, ...rest: unknown[]) => {\n const code = (rest[0] as { code?: string } | undefined)?.code ?? rest[1]\n if (code === 'MODULE_TYPELESS_PACKAGE_JSON') return\n ;(emit as (...args: unknown[]) => void).call(process, warning, ...rest)\n }) as typeof process.emitWarning\n try {\n return await load()\n } finally {\n process.emitWarning = emit\n }\n}\n\ntype Jiti = { import(id: string, opts: object): unknown }\n\nasync function loadJiti(path: string): Promise<Jiti | null> {\n try {\n // Optional, and resolved through a variable so it is not a dependency of the build: a project whose\n // config needs path aliases usually has jiti already (drizzle-kit ships it).\n const id = 'jiti'\n const mod = (await import(id)) as { createJiti: (from: string, options?: object) => Jiti }\n return mod.createJiti(pathToFileURL(path).href, { interopDefault: true })\n } catch {\n return null\n }\n}\n\nfunction pickConfig(exports: Record<string, unknown>, path: string): TypionConfig<any> {\n const candidates = [exports.config, exports.default, ...Object.values(exports)]\n for (const value of candidates) {\n if (\n typeof value === 'object' &&\n value !== null &&\n 'definition' in value &&\n 'schema' in value &&\n typeof (value as { definition?: unknown }).definition === 'object'\n ) {\n return value as TypionConfig<any>\n }\n }\n throw new CliError(\n `${path} exports no Typion config (expected \\`export const config = defineConfig(…)\\`)`,\n )\n}\n\n// A config reads `process.env.DATABASE_URL`, and Next.js, Vite and drizzle-kit read `.env` files for it: the CLI does\n// too, `.env.local` first so that it wins, and never over what the shell already set.\nfunction loadEnv(dir: string): void {\n for (const name of ['.env.local', '.env']) {\n const path = join(dir, name)\n if (existsSync(path)) process.loadEnvFile(path)\n }\n}\n\nexport async function loadConfig(cwd: string, given?: string): Promise<LoadedConfig> {\n loadEnv(cwd)\n const path = findConfig(cwd, given)\n const exports = await importConfig(path)\n return { config: pickConfig(exports, path), path, dir: dirname(path) }\n}\n","import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, isAbsolute, join, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { sql } from 'drizzle-orm'\nimport type { PgAsyncDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core'\nimport { getTableConfig, PgTable } from 'drizzle-orm/pg-core'\nimport { is } from 'drizzle-orm'\nimport type { LoadedConfig } from './config.js'\nimport { CliError, type Io } from './output.js'\n\nconst BREAKPOINT = '--> statement-breakpoint'\nconst DEFAULT_DIR = 'typion/migrations'\nconst DEFAULT_TABLE = 'typion_migrations'\n\ntype Journal = { version: 1; entries: { idx: number; tag: string; createdAt: string }[] }\n\ntype Api = typeof import('drizzle-kit/api-postgres')\n\nasync function api(): Promise<Api> {\n try {\n return (await import('drizzle-kit/api-postgres')) as Api\n } catch {\n throw new CliError(\n 'The database commands need drizzle-kit installed (`pnpm add -D drizzle-kit`)',\n )\n }\n}\n\nfunction schemaOf(loaded: LoadedConfig): Record<string, unknown> {\n return loaded.config.schema as Record<string, unknown>\n}\n\n// Migrations belong to the project, not to the folder the config happens to sit in, so they are resolved\n// from the nearest package.json above it.\nfunction projectRoot(loaded: LoadedConfig): string {\n let dir = loaded.dir\n for (;;) {\n if (existsSync(join(dir, 'package.json'))) return dir\n const parent = dirname(dir)\n if (parent === dir) return loaded.dir\n dir = parent\n }\n}\n\nfunction migrationsDir(loaded: LoadedConfig): string {\n const dir = loaded.config.migrations?.dir ?? DEFAULT_DIR\n return isAbsolute(dir) ? dir : resolve(projectRoot(loaded), dir)\n}\n\nfunction migrationsTable(loaded: LoadedConfig): { schema: string; table: string } {\n return {\n schema: loaded.config.migrations?.schema ?? 'public',\n table: loaded.config.migrations?.table ?? DEFAULT_TABLE,\n }\n}\n\nfunction readJournal(dir: string): Journal {\n try {\n return JSON.parse(readFileSync(join(dir, 'meta', 'journal.json'), 'utf8')) as Journal\n } catch {\n return { version: 1, entries: [] }\n }\n}\n\nfunction writeJournal(dir: string, journal: Journal): void {\n mkdirSync(join(dir, 'meta'), { recursive: true })\n writeFileSync(join(dir, 'meta', 'journal.json'), `${JSON.stringify(journal, null, 2)}\\n`)\n}\n\nfunction connect(loaded: LoadedConfig): PgAsyncDatabase<PgQueryResultHKT, any> {\n const adapter = loaded.config.db\n if (adapter === undefined) {\n throw new CliError('The config has no database adapter (`db` in defineConfig)')\n }\n adapter.check?.()\n return adapter.connect(loaded.config.definition.relations)\n}\n\nexport async function push(loaded: LoadedConfig, io: Io, options: { force?: boolean }) {\n const { pushSchema } = await api()\n const db = connect(loaded)\n const { sqlStatements, hints, apply } = await pushSchema(schemaOf(loaded), db as never)\n if (sqlStatements.length === 0) {\n io.out('The database already matches the config.')\n return\n }\n for (const statement of sqlStatements) io.out(statement)\n if (hints.length > 0) {\n for (const hint of hints) io.err(`! ${hint.hint}`)\n if (options.force !== true) {\n throw new CliError('These changes can lose data: run again with --force to apply them.')\n }\n }\n await apply()\n io.out(`Applied ${sqlStatements.length} statement(s).`)\n await loaded.config.db?.close?.()\n}\n\nexport async function generate(loaded: LoadedConfig, io: Io, options: { name?: string }) {\n const { generateDrizzleJson, generateMigration } = await api()\n const dir = migrationsDir(loaded)\n const journal = readJournal(dir)\n const last = journal.entries.at(-1)\n const previous =\n last === undefined\n ? await generateDrizzleJson({})\n : (JSON.parse(\n readFileSync(\n join(dir, 'meta', `${String(last.idx).padStart(4, '0')}_snapshot.json`),\n 'utf8',\n ),\n ) as Awaited<ReturnType<typeof generateDrizzleJson>>)\n const current = await generateDrizzleJson(schemaOf(loaded), previous.id)\n const statements = await generateMigration(previous, current)\n if (statements.length === 0) {\n io.out('No changes: the migrations already describe the config.')\n return\n }\n const idx = (last?.idx ?? -1) + 1\n const tag = `${String(idx).padStart(4, '0')}_${(options.name ?? 'change').replace(/[^a-z0-9_-]+/gi, '_')}`\n mkdirSync(join(dir, 'meta'), { recursive: true })\n writeFileSync(join(dir, `${tag}.sql`), `${statements.join(`\\n${BREAKPOINT}\\n`)}\\n`)\n writeFileSync(\n join(dir, 'meta', `${String(idx).padStart(4, '0')}_snapshot.json`),\n `${JSON.stringify(current, null, 2)}\\n`,\n )\n journal.entries.push({ idx, tag, createdAt: new Date().toISOString() })\n writeJournal(dir, journal)\n io.out(`Wrote ${tag}.sql with ${statements.length} statement(s).`)\n}\n\nasync function applied(\n db: PgAsyncDatabase<PgQueryResultHKT, any>,\n where: { schema: string; table: string },\n): Promise<Set<string>> {\n if (where.schema !== 'public') {\n await db.execute(sql.raw(`create schema if not exists \"${where.schema}\"`))\n }\n await db.execute(\n sql.raw(\n `create table if not exists \"${where.schema}\".\"${where.table}\" (` +\n 'tag text primary key, applied_at timestamptz not null default now())',\n ),\n )\n const rows = await db.execute(sql.raw(`select tag from \"${where.schema}\".\"${where.table}\"`))\n const list =\n (rows as unknown as { rows?: { tag: string }[] }).rows ?? (rows as unknown as { tag: string }[])\n return new Set(list.map((row) => row.tag))\n}\n\nexport async function migrate(loaded: LoadedConfig, io: Io) {\n const dir = migrationsDir(loaded)\n const journal = readJournal(dir)\n if (journal.entries.length === 0) {\n io.out('No migrations to apply: run `typion db generate` first.')\n return\n }\n const db = connect(loaded)\n const where = migrationsTable(loaded)\n const done = await applied(db, where)\n const pending = journal.entries.filter((entry) => !done.has(entry.tag))\n if (pending.length === 0) {\n io.out('The database is up to date.')\n await loaded.config.db?.close?.()\n return\n }\n for (const entry of pending) {\n const statements = readFileSync(join(dir, `${entry.tag}.sql`), 'utf8')\n .split(BREAKPOINT)\n .map((statement) => statement.trim())\n .filter((statement) => statement !== '')\n await db.transaction(async (tx) => {\n for (const statement of statements) await tx.execute(sql.raw(statement))\n await tx.execute(\n sql.raw(`insert into \"${where.schema}\".\"${where.table}\" (tag) values ('${entry.tag}')`),\n )\n })\n io.out(`Applied ${entry.tag} (${statements.length} statement(s)).`)\n }\n await loaded.config.db?.close?.()\n}\n\nconst SEED_FILES = ['typion/seed.ts', 'typion/seed.js', 'seed.ts', 'seed.js']\n\nfunction findSeedFile(loaded: LoadedConfig, given?: string): string | null {\n if (given !== undefined) {\n const path = isAbsolute(given) ? given : resolve(process.cwd(), given)\n if (!existsSync(path)) throw new CliError(`No seed file at ${path}`)\n return path\n }\n for (const dir of [projectRoot(loaded), loaded.dir]) {\n for (const name of SEED_FILES) {\n const candidate = join(dir, name)\n if (existsSync(candidate)) return candidate\n }\n }\n return null\n}\n\n// Two ways to fill a database, and they answer different needs: the project's own seed writes meaningful rows\n// through the engine (hooks, versions and validation included), `--random` asks drizzle-seed for plausible\n// ones straight into the tables.\nasync function anyRows(loaded: LoadedConfig): Promise<boolean> {\n const db = connect(loaded)\n for (const entity of Object.values(loaded.config.definition.entities)) {\n if (entity.owner !== 'user') continue\n const where = `\"${entity.pgSchema ?? 'public'}\".\"${entity.tableName}\"`\n const rows = await db.execute(sql.raw(`select 1 from ${where} limit 1`))\n const list = (rows as unknown as { rows?: unknown[] }).rows ?? (rows as unknown as unknown[])\n if (list.length > 0) return true\n }\n return false\n}\n\nexport async function seed(\n loaded: LoadedConfig,\n io: Io,\n options: {\n file?: string\n random?: boolean\n count?: number\n seed?: number\n reset?: boolean\n ifEmpty?: boolean\n },\n) {\n if (options.ifEmpty === true && (await anyRows(loaded))) {\n io.out('The collections already hold rows: nothing seeded.')\n await loaded.config.db?.close?.()\n return\n }\n const { resetTables, seedCollections } = await import('../seed/index.js')\n if (options.reset === true) {\n await resetTables(loaded.config)\n io.out('Emptied every table of the config.')\n }\n if (options.random === true) {\n await seedCollections(loaded.config, { count: options.count, seed: options.seed })\n io.out(`Seeded every collection with ${options.count ?? 10} rows.`)\n await loaded.config.db?.close?.()\n return\n }\n const file = findSeedFile(loaded, options.file)\n if (file === null) {\n throw new CliError(\n 'No seed file found (typion/seed.ts). Write one, point at it with --file, or use --random.',\n )\n }\n const { getTypion } = await import('../runtime/typion.js')\n const typion = await getTypion(loaded.config)\n const seedModule = (await import(pathToFileURL(file).href)) as Record<string, unknown>\n const run = (seedModule.seed ?? seedModule.default) as\n ((args: { typion: unknown; config: unknown }) => unknown) | undefined\n if (typeof run !== 'function') {\n throw new CliError(\n `${file} exports no seed function (\\`export async function seed({ typion })\\`)`,\n )\n }\n await run({ typion, config: loaded.config })\n io.out(`Ran ${file}.`)\n await loaded.config.db?.close?.()\n}\n\nexport async function status(loaded: LoadedConfig, io: Io) {\n const dir = migrationsDir(loaded)\n const journal = readJournal(dir)\n const db = connect(loaded)\n const done = await applied(db, migrationsTable(loaded))\n if (journal.entries.length === 0) io.out('No migrations yet.')\n for (const entry of journal.entries) {\n io.out(`${done.has(entry.tag) ? 'applied' : 'pending'} ${entry.tag}`)\n }\n const tables = Object.values(schemaOf(loaded)).filter((value) => is(value, PgTable)) as PgTable[]\n io.out(\n `${tables.length} table(s) in the config: ${tables.map((t) => getTableConfig(t).name).join(', ')}`,\n )\n await loaded.config.db?.close?.()\n}\n\nexport async function studio(loaded: LoadedConfig, io: Io, options: { port?: number }) {\n const { startStudioServer } = await api()\n const credentials = loaded.config.db?.credentials?.()\n if (credentials === undefined) {\n throw new CliError(\n 'This database adapter cannot open Studio: pass `url` to dbAdapter(), or use typion/db/pglite.',\n )\n }\n io.out(`Studio on https://local.drizzle.studio (port ${options.port ?? 4983})`)\n await startStudioServer(schemaOf(loaded), credentials as never, { port: options.port })\n}\n\nexport function listMigrations(dir: string): string[] {\n try {\n return readdirSync(dir).filter((name) => name.endsWith('.sql'))\n } catch {\n return []\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join, relative } from 'node:path'\nimport type { Io } from './output.js'\n\n// Agents do not know Typion from training, so the application's agent notes carry the rules that must always hold\n// and point at the guide shipped with the installed version. The block is delimited so that it can be rewritten\n// without touching the rest of the file, the way Next.js maintains its own (`next dev`).\nexport const START_MARKER = '<!-- BEGIN:typion-agent-rules -->'\nexport const END_MARKER = '<!-- END:typion-agent-rules -->'\n\nexport const AGENT_RULES = `${START_MARKER}\n\n# Typion\n\nThis application's data layer is Typion, which is newer than your training data. Before you add or change a\ncollection, an access rule, a hook, a query or an API route, read \\`node_modules/typion/docs/README.md\\` (resolved\nfrom this file's directory) and the page it points to for the task.\n\n- Read and write the application's data through Typion, bound to the caller: \\`getApp(config)\\` on Next.js,\n \\`c.var.typion\\` on Hono, \\`typion.$fromRequest(request)\\` elsewhere, \\`typion.$as(ctx)\\`. Never through\n \\`typion.$db\\`, Drizzle or SQL: they skip the access rules, tenants, soft delete, drafts, hooks and the history.\n- \\`typion.$system({ reason })\\` skips the access rules: for seeds, jobs and webhooks, never to answer a user's\n request.\n- Authorization lives in each collection's \\`access\\` rules, deny by default, not in pages, routes or server\n actions.\n- Every schema change ships a migration: \\`typion db generate --name <change>\\`.\n\nThis block is written by \\`typion init\\`; run it again after upgrading Typion.\n\n${END_MARKER}`\n\nconst SKILL_MARKER =\n '<!-- Written by `typion init`, which rewrites it: keep project notes in AGENTS.md. -->'\n\nexport const SKILL = `---\nname: typion\ndescription: Use for any change to this application's data layer with Typion — collections, access rules, hooks, queries and descriptors, API routes, seeds, migrations, or tests that touch typion.config.ts or the typion/ folder next to it.\n---\n\n${SKILL_MARKER}\n\nTypion is newer than your training data. Its guide ships with the installed version, in \\`node_modules/typion/docs/\\`.\n\n1. Read \\`node_modules/typion/docs/README.md\\`: the model, the rules, where things go.\n2. Read the page for the task:\n - \\`collections.md\\`: a collection, its columns and relations, a many-to-many\n - \\`access.md\\`: who reads and writes what, hidden columns, what a caller sees\n - \\`auth.md\\`: signing in, who the caller is\n - \\`hooks.md\\`: reacting to writes\n - \\`queries.md\\`: reads, writes, descriptors, transactions, errors\n - \\`http.md\\`: Next.js pages and server actions, Hono, the routes, the browser client, business operations\n - \\`database.md\\`: drivers, migrations, seed\n - \\`testing.md\\`: tests\n - \\`features/\\`: localization, multi-tenancy, soft delete, versions and drafts\n3. Before you call the change done: \\`tsc\\` passes, every new collection has its rules, a test covers a caller the\n rules allow and one they refuse, and a schema change has its migration.\n`\n\ntype Outcome = 'created' | 'updated' | 'unchanged' | 'kept'\n\nfunction upsert(existing: string): string {\n const eol = existing.includes('\\r\\n') ? '\\r\\n' : '\\n'\n const block = AGENT_RULES.replace(/\\n/g, eol)\n const start = existing.indexOf(START_MARKER)\n const end = existing.indexOf(END_MARKER, start)\n if (start !== -1 && end !== -1) {\n return existing.slice(0, start) + block + existing.slice(end + END_MARKER.length)\n }\n if (existing.length === 0) return `${block}${eol}`\n const body = existing.endsWith(eol) ? existing : `${existing}${eol}`\n return `${body}${eol}${block}${eol}`\n}\n\nfunction writeFile(path: string, next: string, previous: string | null): Outcome {\n if (previous === next) return 'unchanged'\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, next)\n return previous === null ? 'created' : 'updated'\n}\n\nfunction read(path: string): string | null {\n return existsSync(path) ? readFileSync(path, 'utf8') : null\n}\n\n// The block goes where the project keeps its notes: AGENTS.md, or CLAUDE.md when only that one exists; a project\n// with neither gets both, CLAUDE.md importing AGENTS.md. The skill is rewritten only while it is still Typion's.\nexport function writeAgentNotes(root: string, io: Io): void {\n const agentsPath = join(root, 'AGENTS.md')\n const claudePath = join(root, 'CLAUDE.md')\n const agents = read(agentsPath)\n const claude = read(claudePath)\n const report = (path: string, outcome: Outcome) => {\n const verb = { created: 'Created', updated: 'Updated', kept: 'Kept', unchanged: null }[outcome]\n if (verb !== null) io.out(`${verb} ${relative(process.cwd(), path)}.`)\n }\n\n const claudeHostsBlock = claude?.includes(START_MARKER) ?? false\n if (agents !== null && (agents.includes(START_MARKER) || !claudeHostsBlock)) {\n report(agentsPath, writeFile(agentsPath, upsert(agents), agents))\n } else if (claude !== null) {\n report(claudePath, writeFile(claudePath, upsert(claude), claude))\n } else {\n report(agentsPath, writeFile(agentsPath, upsert(''), null))\n report(claudePath, writeFile(claudePath, '@AGENTS.md\\n', null))\n }\n\n const skillPath = join(root, '.claude', 'skills', 'typion', 'SKILL.md')\n const skill = read(skillPath)\n if (skill !== null && !skill.includes(SKILL_MARKER)) {\n io.out(`Kept ${relative(process.cwd(), skillPath)} (it is not Typion's any more).`)\n } else {\n report(skillPath, writeFile(skillPath, SKILL, skill))\n }\n}\n","import { spawnSync } from 'node:child_process'\nimport { createInterface } from 'node:readline/promises'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join, relative } from 'node:path'\nimport { writeAgentNotes } from './agents.js'\nimport { CliError, type Io } from './output.js'\n\ntype PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun'\n\n// npm's `latest` Drizzle is 0.x, and a later release candidate can break the engine (rc.5 does): the version the\n// engine is built on, which its `package.json` names as a peer.\nconst DRIZZLE = '1.0.0-rc.4'\n\nconst TRANSPORTS = ['@typion/rest', '@typion/rpc']\n\nfunction packageManager(root: string): PackageManager {\n if (existsSync(join(root, 'pnpm-lock.yaml'))) return 'pnpm'\n if (existsSync(join(root, 'yarn.lock'))) return 'yarn'\n if (existsSync(join(root, 'bun.lock')) || existsSync(join(root, 'bun.lockb'))) return 'bun'\n if (existsSync(join(root, 'package-lock.json'))) return 'npm'\n const agent = process.env.npm_config_user_agent ?? ''\n if (agent.startsWith('yarn')) return 'yarn'\n if (agent.startsWith('bun')) return 'bun'\n if (agent.startsWith('pnpm')) return 'pnpm'\n return 'npm'\n}\n\nfunction installCommand(\n manager: PackageManager,\n packages: readonly string[],\n dev: boolean,\n): { command: string; args: string[] } {\n const flag = dev ? (manager === 'npm' ? '--save-dev' : '-D') : ''\n const verb = manager === 'npm' ? 'install' : 'add'\n return {\n command: manager,\n args: [verb, ...(flag === '' ? [] : [flag]), ...packages],\n }\n}\n\nasync function confirm(question: string, io: Io): Promise<boolean> {\n if (!process.stdin.isTTY) {\n io.out(`${question} — not a terminal, so nothing was installed. Re-run with --yes.`)\n return false\n }\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n try {\n const answer = (await rl.question(`${question} [Y/n] `)).trim().toLowerCase()\n return answer === '' || answer === 'y' || answer === 'yes'\n } finally {\n rl.close()\n }\n}\n\nfunction run(command: string, args: readonly string[], cwd: string, io: Io): void {\n io.out(`> ${command} ${args.join(' ')}`)\n const result = spawnSync(command, [...args], { cwd, stdio: 'inherit' })\n if (result.error !== undefined || (result.status !== null && result.status !== 0)) {\n throw new CliError(`\\`${command} ${args.join(' ')}\\` failed; install them yourself and re-run.`)\n }\n}\n\ntype Project = {\n readonly root: string\n readonly src: string\n readonly framework: 'next' | 'hono' | 'none'\n readonly dependencies: ReadonlySet<string>\n readonly appDir: string | null\n}\n\nfunction readJson(path: string): Record<string, any> | null {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as Record<string, any>\n } catch {\n return null\n }\n}\n\nfunction inspect(cwd: string): Project {\n const pkg = readJson(join(cwd, 'package.json'))\n if (pkg === null) throw new CliError(`No package.json in ${cwd}: run this inside your project.`)\n const deps = { ...pkg.dependencies, ...pkg.devDependencies } as Record<string, string>\n const src = existsSync(join(cwd, 'src')) ? join(cwd, 'src') : cwd\n const appDir = [join(src, 'app'), join(cwd, 'app')].find((dir) => existsSync(dir)) ?? null\n return {\n root: cwd,\n src,\n framework: deps.next !== undefined ? 'next' : deps.hono !== undefined ? 'hono' : 'none',\n dependencies: new Set(Object.keys(deps)),\n appDir,\n }\n}\n\nfunction write(path: string, contents: string, io: Io): boolean {\n if (existsSync(path)) {\n io.out(`Kept ${relative(process.cwd(), path)} (it exists already).`)\n return false\n }\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, contents)\n io.out(`Created ${relative(process.cwd(), path)}.`)\n return true\n}\n\nconst COLLECTION = `import { text } from 'drizzle-orm/pg-core'\nimport { collection } from 'typion'\n\nexport const pages = collection('pages', {\n columns: {\n title: text().notNull(),\n slug: text().notNull().unique(),\n },\n public: true,\n})\n`\n\nfunction configFile(driver: 'pglite' | 'postgres'): string {\n const db =\n driver === 'pglite'\n ? ` db: pglite({ dataDir: '.pglite' }),`\n : ` db: dbAdapter((relations) => drizzle(process.env.DATABASE_URL!, { relations }), {\\n url: process.env.DATABASE_URL,\\n }),`\n const imports =\n driver === 'pglite'\n ? `import { defineConfig, defineSetup } from 'typion'\\nimport { pglite } from 'typion/db/pglite'`\n : `import { drizzle } from 'drizzle-orm/node-postgres'\\nimport { dbAdapter, defineConfig, defineSetup } from 'typion'`\n return `${imports}\nimport { pages } from './typion/collections/pages.ts'\n\n// What the application knows about a caller: rules, hooks and field rules read it. Registered below as well, since\n// field rules are typed before this config exists.\nexport type AppContext = {\n actorId: string | null\n}\n\n// A constant of its own: the collections read it to type their options and their columns, which happens before\n// this config exists. It takes the features (\\`localization\\`, \\`multiTenant\\`) and the plugins (\\`plugins: […]\\`).\nexport const setup = defineSetup({})\n\nexport const config = defineConfig({\n setup,\n collections: [pages],\n${db}\n // Who is calling. Return your own shape; declare the return type, the body may query \\`system\\`.\n context: async ({ headers }): Promise<AppContext> => {\n void headers\n return { actorId: null }\n },\n})\n\ndeclare module 'typion' {\n interface Register {\n setup: typeof setup\n config: typeof config\n context: AppContext\n }\n}\n`\n}\n\n// Every import Typion writes ends in `.ts`, as Node needs to load the config the way the CLI does. TypeScript takes\n// them with `allowImportingTsExtensions` where nothing is emitted (Next.js), and with\n// `rewriteRelativeImportExtensions` where `tsc` builds the project.\nfunction allowTsImports(root: string, io: Io): void {\n const path = join(root, 'tsconfig.json')\n if (!existsSync(path)) return\n const json = readJson(path)\n const options = (json?.compilerOptions ?? {}) as Record<string, unknown>\n if (\n options.allowImportingTsExtensions === true ||\n options.rewriteRelativeImportExtensions === true\n ) {\n return\n }\n const flag =\n options.noEmit === true ? 'allowImportingTsExtensions' : 'rewriteRelativeImportExtensions'\n if (json === null) {\n io.out(\n `Add \"${flag}\": true to the compilerOptions of tsconfig.json: Typion's imports end in .ts.`,\n )\n return\n }\n json.compilerOptions = { ...options, [flag]: true }\n writeFileSync(path, `${JSON.stringify(json, null, 2)}\\n`)\n io.out(`Set ${flag} in tsconfig.json: Typion's imports end in .ts.`)\n}\n\n// The pnpm that will install: the version the project declares, which pnpm switches to; else the one running\n// `init`; else the one on the PATH.\nfunction pnpmMajor(root: string): number | null {\n const declared = /^pnpm@(\\d+)/.exec(String(readJson(join(root, 'package.json'))?.packageManager))\n const agent = /^pnpm\\/(\\d+)/.exec(process.env.npm_config_user_agent ?? '')\n const version =\n declared?.[1] ??\n agent?.[1] ??\n /^(\\d+)\\./.exec(\n spawnSync('pnpm', ['--version'], { cwd: root, encoding: 'utf8' }).stdout ?? '',\n )?.[1]\n return version === undefined ? null : Number(version)\n}\n\n// pnpm reads its settings from the workspace's root, the nearest pnpm-workspace.yaml up the tree.\nfunction workspaceFile(root: string): string {\n for (let dir = root; dirname(dir) !== dir; dir = dirname(dir)) {\n if (existsSync(join(dir, 'pnpm-workspace.yaml'))) return join(dir, 'pnpm-workspace.yaml')\n }\n return join(root, 'pnpm-workspace.yaml')\n}\n\n// From pnpm 11 a build script that is neither allowed nor denied fails the install, and drizzle-kit brings\n// esbuild's: `init` would stop at its own install. pnpm then leaves a placeholder under `allowBuilds`, which fails\n// every install after it, and a second `allowBuilds` added by hand is a duplicated key.\nfunction allowEsbuild(root: string, io: Io): void {\n const major = pnpmMajor(root)\n if (major === null || major < 11) return\n const path = workspaceFile(root)\n const name = relative(process.cwd(), path)\n const why = `pnpm ${major} fails an install on a build script it was not told to run, and drizzle-kit brings esbuild's`\n if (!existsSync(path)) {\n writeFileSync(path, 'allowBuilds:\\n esbuild: true\\n')\n io.out(`Created ${name} to allow esbuild's build script: ${why}.`)\n return\n }\n const text = readFileSync(path, 'utf8')\n const eol = text.includes('\\r\\n') ? '\\r\\n' : '\\n'\n const lines = text.split(/\\r?\\n/)\n const valueOf = (line: string) =>\n line\n .slice(line.indexOf(':') + 1)\n .replace(/(^|\\s)#.*$/, '')\n .trim()\n const key = lines.findIndex((line) => /^allowBuilds\\s*:/.test(line))\n if (key === -1) {\n if (lines.at(-1) === '') lines.pop()\n lines.push(...(lines.length === 0 ? [] : ['']), 'allowBuilds:', ' esbuild: true', '')\n } else if (valueOf(lines[key]!) !== '') {\n // A flow mapping, `allowBuilds: { … }`, is the project's to edit.\n if (!/\\besbuild\\b/.test(lines[key]!))\n io.out(`Add esbuild: true to allowBuilds in ${name}: ${why}.`)\n return\n } else {\n let end = key + 1\n while (end < lines.length && /^(\\s|#|$)/.test(lines[end]!)) end++\n const block = lines.slice(key + 1, end)\n const entry = block.findIndex((line) => /^\\s+(['\"]?)esbuild\\1\\s*:/.test(line))\n if (entry === -1) {\n const indent = block.map((line) => /^(\\s+)[^\\s#]/.exec(line)?.[1]).find(Boolean) ?? ' '\n lines.splice(key + 1, 0, `${indent}esbuild: true`)\n } else {\n // A value the project chose stays; anything else is pnpm's placeholder.\n if (/^(true|false)$/i.test(valueOf(block[entry]!))) return\n lines[key + 1 + entry] = `${/^\\s+/.exec(block[entry]!)![0]}esbuild: true`\n }\n }\n writeFileSync(path, lines.join(eol))\n io.out(`Set allowBuilds: esbuild: true in ${name}: ${why}.`)\n}\n\nconst ROUTE = `import { rest } from '@typion/rest'\nimport { rpc } from '@typion/rpc'\nimport { routeHandlers } from 'typion/next'\nimport { config } from '@/typion.config.ts'\n\n// What the app serves over HTTP: a resource per collection, and the endpoint the browser client posts to. Leave\n// out what you do not use.\nexport const { GET, POST, PATCH, PUT, DELETE, OPTIONS } = await routeHandlers(config, {\n transports: [rest(), rpc()],\n})\n`\n\nexport async function init(\n cwd: string,\n io: Io,\n options: { driver?: string; yes?: boolean; skipInstall?: boolean },\n): Promise<void> {\n const project = inspect(cwd)\n const driver = options.driver === 'postgres' ? 'postgres' : 'pglite'\n\n // The sample collection comes with a new config only: a run after an upgrade refreshes the notes and leaves the\n // project's collections as they are.\n if (write(join(project.src, 'typion.config.ts'), configFile(driver), io)) {\n write(join(project.src, 'typion', 'collections', 'pages.ts'), COLLECTION, io)\n }\n allowTsImports(project.root, io)\n\n if (project.framework === 'next' && project.appDir !== null) {\n write(join(project.appDir, 'api', '[...path]', 'route.ts'), ROUTE, io)\n }\n writeAgentNotes(project.root, io)\n\n const manager = packageManager(project.root)\n if (manager === 'pnpm') allowEsbuild(project.root, io)\n await installDependencies(project, manager, driver, io, options)\n\n io.out('')\n io.out('Next:')\n io.out(\n ' 1. turn on what you need: `softDelete` or `versions` on a collection, `localization`, `multiTenant` or `plugins` in `defineSetup`',\n )\n io.out(\n existsSync(join(project.root, 'typion', 'migrations'))\n ? ' 2. `typion db migrate` to apply the migrations, then `typion db seed`'\n : ' 2. `typion db push` to create the tables, then `typion db seed`',\n )\n if (project.framework === 'next') {\n io.out(\n ' 3. read from a server component with `const app = await getApp(config)` (typion/next)',\n )\n }\n // The app's entry is the developer's own file: what to add to it is said, not written. It may serve only routes\n // of its own, so the transports come with the step that mounts them rather than with what every app installs.\n if (project.framework === 'hono') {\n io.out(\n ' 3. bind the caller in your own routes with `typionMiddleware(config)`: `c.var.typion`',\n )\n const transports = TRANSPORTS.filter((name) => !project.dependencies.has(name))\n const add = installCommand(manager, transports, false)\n const install = transports.length === 0 ? '' : `\\`${add.command} ${add.args.join(' ')}\\`, then `\n io.out(\n ` 4. to serve the collections over HTTP: ${install}\\`.all('/api/*', typionRoutes(config, { transports: [rest(), rpc()] }))\\` (typion/hono)`,\n )\n }\n}\n\nasync function installDependencies(\n project: Project,\n manager: PackageManager,\n driver: 'pglite' | 'postgres',\n io: Io,\n options: { yes?: boolean; skipInstall?: boolean },\n): Promise<void> {\n const deps = [\n 'typion',\n 'drizzle-orm',\n driver === 'pglite' ? '@electric-sql/pglite' : 'pg',\n // The route `init` writes on Next.js imports them.\n ...(project.framework === 'next' ? TRANSPORTS : []),\n ]\n const dev = ['drizzle-kit']\n const spec = (name: string) => (name.startsWith('drizzle-') ? `${name}@${DRIZZLE}` : name)\n const missing = deps.filter((name) => !project.dependencies.has(name)).map(spec)\n const missingDev = dev.filter((name) => !project.dependencies.has(name)).map(spec)\n if (missing.length === 0 && missingDev.length === 0) return\n\n const lines = [\n ...(missing.length === 0 ? [] : [installCommand(manager, missing, false)]),\n ...(missingDev.length === 0 ? [] : [installCommand(manager, missingDev, true)]),\n ]\n io.out('')\n io.out('Missing packages:')\n for (const line of lines) io.out(` ${line.command} ${line.args.join(' ')}`)\n\n if (options.skipInstall === true) return\n const ok = options.yes === true || (await confirm(`Install them with ${manager}?`, io))\n if (!ok) return\n for (const line of lines) run(line.command, line.args, project.root, io)\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport type { LoadedConfig } from './config.js'\nimport { CliError, type Io } from './output.js'\n\nfunction pascal(name: string): string {\n return name.replace(/(^|[-_ ])(\\w)/g, (_, __, char: string) => char.toUpperCase())\n}\n\nfunction camel(name: string): string {\n const upper = pascal(name)\n return upper.charAt(0).toLowerCase() + upper.slice(1)\n}\n\n// Where the collections of this project live: next to the ones already there, else beside the config.\nfunction collectionsDir(loaded: LoadedConfig, given?: string): string {\n if (given !== undefined) return isAbsolute(given) ? given : resolve(process.cwd(), given)\n const candidates = [join(loaded.dir, 'typion', 'collections'), join(loaded.dir, 'collections')]\n return candidates.find((dir) => existsSync(dir)) ?? candidates[0]!\n}\n\n// No rule is written for the developer: a generated `read: true` would serve every row to anyone, anonymous callers\n// included, until someone noticed. In a multi-tenant application the rows belong to the caller's tenant.\nfunction template(name: string, tenant: boolean): string {\n return `import { text } from 'drizzle-orm/pg-core'\nimport { collection } from 'typion'\n\nexport const ${camel(name)} = collection('${camel(name)}', {\n columns: {\n // Plain Drizzle columns; the features and plugins add their own options next to \\`columns\\`.\n title: text().notNull(),\n },${tenant ? '\\n tenant: true,' : ''}\n}).define({\n // Deny by default: an operation without a rule is refused. Give each one the rule it needs, for example\n // \\`read: ({ ctx }) => ctx.actorId !== null\\`.\n access: {},\n})\n`\n}\n\nfunction importPath(from: string, to: string): string {\n const path = relative(from, to).replaceAll('\\\\', '/')\n return path.startsWith('.') ? path : `./${path}`\n}\n\n// Best effort: adds the import and the entry to `collections: [...]`. When the config does not match the\n// shape the CLI understands, it prints the two lines instead of guessing.\nfunction register(loaded: LoadedConfig, name: string, file: string, io: Io): void {\n const source = readFileSync(loaded.path, 'utf8')\n const specifier = importPath(loaded.dir, file).replace(/\\.ts$/, '.ts')\n const line = `import { ${camel(name)} } from '${specifier}'`\n const list = /collections:\\s*\\[([^\\]]*)\\]/.exec(source)\n if (source.includes(line) || list === null) {\n io.out('Add it to the config:')\n io.out(` ${line}`)\n io.out(` collections: [ …, ${camel(name)} ]`)\n return\n }\n const entries = list[1]!.trim()\n const updated = source\n .replace(/(^import .*\\n)(?![\\s\\S]*^import )/m, `$1${line}\\n`)\n .replace(\n /collections:\\s*\\[([^\\]]*)\\]/,\n `collections: [${entries === '' ? '' : `${entries.replace(/,\\s*$/, '')}, `}${camel(name)}]`,\n )\n writeFileSync(loaded.path, updated)\n io.out(`Registered it in ${relative(process.cwd(), loaded.path)}.`)\n}\n\nfunction pluginTemplate(name: string): string {\n const id = camel(name)\n const Kinds = `${pascal(name)}Kinds`\n const Options = `${pascal(name)}Options`\n return `import { definePlugin } from 'typion'\nimport type { CollectionKinds } from 'typion'\n\n// What this plugin adds to every collection. A collection that does not set the option gets nothing.\nexport type ${Options} = { ${id}?: boolean }\n\n// How the option turns into columns and into engine options, read from \\`this['options']\\`.\ninterface ${Kinds} extends CollectionKinds {\n readonly columns: this['options'] extends { ${id}: true } ? {} : {}\n readonly entity: this['options'] extends { ${id}: true } ? {} : {}\n}\n\nexport function ${id}() {\n return definePlugin({\n id: '${id}',\n collection: {\n $options: {} as ${Options},\n $kinds: {} as ${Kinds},\n // Option keys this plugin reads, so they never reach the engine as entity options.\n consumes: ['${id}'],\n columns: (options) => (options.${id} === true ? {} : {}),\n entity: (options) => (options.${id} === true ? {} : {}),\n },\n // Tables, relations, entities, hooks, api, routes, errors and context go here too; see @typion/audit.\n })\n}\n`\n}\n\nexport function makePlugin(\n loaded: LoadedConfig,\n io: Io,\n name: string,\n options: { dir?: string },\n): void {\n if (!/^[a-z][a-z0-9_-]*$/i.test(name)) {\n throw new CliError(`'${name}' is not a plugin name (letters, digits, dash, underscore)`)\n }\n const dir =\n options.dir === undefined\n ? join(loaded.dir, 'typion', 'plugins')\n : isAbsolute(options.dir)\n ? options.dir\n : resolve(process.cwd(), options.dir)\n const file = join(dir, `${camel(name)}.ts`)\n if (existsSync(file)) throw new CliError(`${relative(process.cwd(), file)} already exists`)\n mkdirSync(dir, { recursive: true })\n writeFileSync(file, pluginTemplate(name))\n io.out(`Created ${relative(process.cwd(), file)}.`)\n io.out('Install it in the config:')\n io.out(` import { ${camel(name)} } from './${relative(loaded.dir, file).replaceAll('\\\\', '/')}'`)\n io.out(` export const setup = defineSetup({ plugins: [ …, ${camel(name)}() ] })`)\n}\n\nexport function makeCollection(\n loaded: LoadedConfig,\n io: Io,\n name: string,\n options: { dir?: string },\n): void {\n if (!/^[a-z][a-z0-9_-]*$/i.test(name)) {\n throw new CliError(`'${name}' is not a collection name (letters, digits, dash, underscore)`)\n }\n const dir = collectionsDir(loaded, options.dir)\n const file = join(dir, `${camel(name)}.ts`)\n if (existsSync(file)) throw new CliError(`${relative(process.cwd(), file)} already exists`)\n mkdirSync(dirname(file), { recursive: true })\n const tenant = loaded.config.definition.plugins.some((plugin) => plugin.id === 'multiTenant')\n writeFileSync(file, template(name, tenant))\n io.out(`Created ${relative(process.cwd(), file)}.`)\n register(loaded, name, file, io)\n io.out('Then `typion db push` (or `typion db generate` + `typion db migrate`).')\n}\n","import { parseArgs } from 'node:util'\nimport { ConfigError } from '../errors.js'\nimport { loadConfig } from './config.js'\nimport * as db from './db.js'\nimport { init } from './init.js'\nimport { makeCollection, makePlugin } from './make.js'\nimport { CliError, consoleIo, type Io } from './output.js'\n\nconst HELP = `typion — the CLI of a Typion project\n\nUsage\n typion <group> <command> [options]\n\nDatabase (everything that touches the schema, through drizzle-kit)\n typion db push [--force] make the database match the config (development)\n typion db generate [--name x] write the SQL of what changed, with its snapshot\n typion db migrate apply the migrations the database has not applied\n typion db seed [--file path] run the project's seed through the engine\n [--random] ask drizzle-seed for rows instead (--count, --seed)\n [--reset] empty every table of the config first\n [--if-empty] do nothing when a collection already holds rows\n typion db status what is applied, what is pending\n typion db studio [--port n] open Drizzle Studio on this config\n\nProject\n typion make collection <name> [--dir path] a new collection, registered in the config\n typion make plugin <name> [--dir path] a plugin skeleton for this project\n typion init [--driver pglite|postgres] [--yes] [--skip-install]\n set Typion up in a project that exists, with the\n notes its agents read (run it again after upgrading)\n\nOptions\n --config <path> the config to read; by default the nearest typion.config.ts\n --help this text (also: typion db --help)\n`\n\nconst DB_HELP = `typion db — the database of a Typion config\n\n push [--force] make the database match the config (development)\n generate [--name x] write the SQL of what changed, with its snapshot\n migrate apply the migrations the database has not applied\n seed [--file|--random] fill the database: the project's seed, or drizzle-seed rows\n status what is applied, what is pending\n studio [--port n] open Drizzle Studio on this config\n`\n\nconst OPTIONS = {\n config: { type: 'string' },\n name: { type: 'string' },\n dir: { type: 'string' },\n driver: { type: 'string' },\n port: { type: 'string' },\n file: { type: 'string' },\n count: { type: 'string' },\n seed: { type: 'string' },\n force: { type: 'boolean' },\n random: { type: 'boolean' },\n reset: { type: 'boolean' },\n 'if-empty': { type: 'boolean' },\n 'skip-install': { type: 'boolean' },\n yes: { type: 'boolean' },\n help: { type: 'boolean' },\n} as const\n\nfunction port(raw: string | undefined): number | undefined {\n if (raw === undefined) return undefined\n if (!/^\\d+$/.test(raw)) throw new CliError('--port must be a number')\n return Number(raw)\n}\n\nexport async function run(argv: readonly string[], io: Io = consoleIo, cwd = process.cwd()) {\n const { values, positionals } = parseArgs({\n args: [...argv],\n options: OPTIONS,\n allowPositionals: true,\n strict: true,\n })\n const [group, command, argument] = positionals\n\n if (group === undefined || values.help === true) {\n io.out(group === 'db' ? DB_HELP : HELP)\n return\n }\n\n const config = async () => loadConfig(cwd, values.config)\n\n switch (`${group} ${command ?? ''}`.trim()) {\n case 'db push':\n return db.push(await config(), io, { force: values.force })\n case 'db generate':\n return db.generate(await config(), io, { name: values.name })\n case 'db migrate':\n return db.migrate(await config(), io)\n case 'db seed':\n return db.seed(await config(), io, {\n file: values.file,\n random: values.random,\n count: values.count === undefined ? undefined : Number(values.count),\n seed: values.seed === undefined ? undefined : Number(values.seed),\n reset: values.reset,\n ifEmpty: values['if-empty'],\n })\n case 'db status':\n return db.status(await config(), io)\n case 'db studio':\n return db.studio(await config(), io, { port: port(values.port) })\n case 'db':\n io.out(DB_HELP)\n return\n case 'make collection': {\n if (argument === undefined) throw new CliError('Usage: typion make collection <name>')\n return makeCollection(await config(), io, argument, { dir: values.dir })\n }\n case 'make plugin': {\n if (argument === undefined) throw new CliError('Usage: typion make plugin <name>')\n return makePlugin(await config(), io, argument, { dir: values.dir })\n }\n case 'make':\n throw new CliError('Usage: typion make collection|plugin <name>')\n case 'init':\n return init(cwd, io, {\n driver: values.driver,\n yes: values.yes,\n skipInstall: values['skip-install'],\n })\n default:\n throw new CliError(\n `Unknown command '${[group, command].filter(Boolean).join(' ')}'. Try \\`typion --help\\`.`,\n )\n }\n}\n\nexport async function main(argv: readonly string[]): Promise<number> {\n try {\n await run(argv)\n return 0\n } catch (error) {\n // What the user has to change, in a sentence: a stack trace would bury it.\n if (error instanceof CliError || error instanceof ConfigError) {\n console.error(error.message)\n return 1\n }\n console.error(error)\n return 1\n }\n}\n\nexport { CliError }\n"],"mappings":"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"}
|
|
@@ -1436,9 +1436,11 @@ interface HookTypion extends RegisteredHook {}
|
|
|
1436
1436
|
type RegisteredHook = [Registered] extends [never] ? {} : Omit<TypionApp<RegisteredTypes, 'user'>, '$mode'> & {
|
|
1437
1437
|
readonly $mode: TypionMode;
|
|
1438
1438
|
};
|
|
1439
|
-
type
|
|
1439
|
+
type ContextFactoryInput = ContextInput & {
|
|
1440
1440
|
system: SystemTypion;
|
|
1441
|
-
|
|
1441
|
+
db: AnyDb;
|
|
1442
|
+
};
|
|
1443
|
+
type ContextFactory<TCtx> = (input: ContextFactoryInput) => MaybePromise<TCtx>;
|
|
1442
1444
|
type TypionConfig<TE extends TypionTypes> = {
|
|
1443
1445
|
readonly '~types'?: TE;
|
|
1444
1446
|
readonly definition: TypionDefinition<TE>;
|
|
@@ -2094,5 +2096,5 @@ type ContextInput = {
|
|
|
2094
2096
|
type Ctx<TE extends TypionTypes> = BaseContext & TE['ctx'] & PluginContext<TE['plugins']>;
|
|
2095
2097
|
type FullCtx<TCtx extends object, P extends readonly AnyPlugin[]> = BaseContext & TCtx & PluginContext<P>;
|
|
2096
2098
|
//#endregion
|
|
2097
|
-
export { AfterChangeArgs as $,
|
|
2098
|
-
//# sourceMappingURL=context-
|
|
2099
|
+
export { AfterChangeArgs as $, InferItem as $a, PluginTables as $i, MultiTenantOptions as $n, NoneIfEmpty as $r, OneSpec as $t, Dialect as A, BadRequestError as Aa, CollectionKinds as Ai, TypionSetup as An, ColumnKeys as Ar, VersionsTraits as At, EntityMeta as B, NotFoundError as Ba, PluginCollectionOptions as Bi, SoftDeleteEntityOptions as Bn, FieldOptions as Br, ConfigOptions as Bt, WireBatchEnvelope as C, ReadModeFn as Ca, VS as Ci, defineConfig as Cn, collection as Cr, VersionsEntityOptions as Ct, WireErrorCode as D, WriteModeFn as Da, AnyFeatureTables as Di, SetupLocalization as Dn, TraitOptions as Dr, VersionsOption as Dt, WireError as E, ValidationMode as Ea, VirtualTable as Ei, Setup as En, timestamps as Er, VersionsListOptions as Et, AnyTx as F, ConflictKind as Fa, FeatureTables as Fi, VersionsPlugin as Fn, EntityApi as Fr, ByName as Ft, AllowAll as G, TypionErrorOptions as Ga, PluginErrorDef as Gi, SoftDeleteTraits as Gn, Insert as Gr, ContextFactoryInput as Gt, RelationMeta as H, PluginErrorOptions as Ha, PluginEntities as Hi, SoftDeletePlugin as Hn, FieldRuleArgs as Hr, ConfigTables as Ht, AnyTypion as I, ForbiddenError as Ia, MergeAll as Ii, versionsTable as In, EntityDef as Ir, Collection as It, OperationMeta as J, ValidationIssue as Ja, PluginKinds as Ji, IntegerTenantColumn as Jn, LinkMethods as Jr, HandlerOptionsOf as Jt, AnyAccessRules as K, UnsupportedError as Ka, PluginErrorFactories as Ki, softDeleteColumn as Kn, LinkApi as Kr, DbAdapter as Kt, AnyTypionApp as L, ForbiddenErrorOptions as La, MergeConfigs as Li, SoftDeleteApi as Ln, EntityOptions as Lr, CollectionDraft as Lt, AfterCommitQueue as M, ConfigErrorKind as Ma, ColumnKind as Mi, DefaultFeaturePlugins as Mn, CreateOptions as Mr, AfterCommitErrorHandler as Mt, AnyDb as N, ConflictError as Na, DependenciesError as Ni, FeaturePlugins as Nn, DeleteOptions as Nr, AnyCollection as Nt, WireErrorEnvelope as O, WriteModeOf as Oa, AnyPlugin as Oi, SetupOptions as On, TraitQueries as Or, VersionsReadOptions as Ot, AnyRow as P, ConflictErrorOptions as Pa, FeatureId as Pi, FeaturesOptions as Pn, EntitiesOf$1 as Pr, Behaviour as Pt, RuleSchema as Q, Descriptor as Qa, PluginSpec as Qi, MultiTenantKinds as Qn, NoExtraKeys as Qr, NoUnknownKeys as Qt, ValidationAdapter as R, InternalError as Ra, Plugin as Ri, SoftDeleteCollectionOptions as Rn, EntitySchemas as Rr, CollectionRuntime as Rt, ErrorStatus as S, PluginSystemOptions as Sa, HiddenReadKeys as Si, dbAdapter as Sn, definePlugin as Sr, VersionsCreateOptions as St, WireEnvelope as T, TableMode as Ta, VirtualRelation as Ti, DefaultSetup as Tn, defaultIdColumn as Tr, VersionsKinds as Tt, TypionDefinition as U, TypionError as Ua, PluginEntityDefiner as Ui, SoftDeleteQueries as Un, HiddenWriteKeys as Ur, ConfigTypes as Ut, GeneratedTables as V, PluginError as Va, PluginContext as Vi, SoftDeleteKinds as Vn, FieldRefinements as Vr, ConfigSeedOptions as Vt, AccessRules as W, TypionErrorCode as Wa, PluginEntityOptions as Wi, SoftDeleteReadOptions as Wn, Id$1 as Wr, ContextFactory as Wt, RuleArgs as X, isTypionError as Xa, PluginRoute as Xi, MultiTenantContext as Xn, LinkQueryMethods as Xr, ManySpec as Xt, RawWhere as Y, attempt as Ya, PluginNeedsContext as Yi, MultiTenantCollectionOptions as Yn, LinkQueries as Yr, HookTypion as Yt, RuleData as Z, AnyDescriptor as Za, PluginRuntime as Zi, MultiTenantEntityOptions as Zn, ManagedKeys as Zr, MigrationsOptions as Zt, TransportCall as _, Kinded as _a, ReadOptions as _i, structuralValidation as _n, localizedIndex as _r, Version as _t, MaybePromise as a, COLUMN_KIND as aa, UpdateInput as ai, RelationSpec as an, descriptor as ao, TenantIdOf as ar, HookBase as at, TransportSchema as b, PluginContextOptions as ba, DynamicReadKeys as bi, AfterCommitErrorInfo as bn, DefinedPlugin as br, VersionState as bt, CorsOptions as c, ColumnKindTypes as ca, WriteOptions as ci, TableOf as cn, tenantColumn as cr, TypionHookApp as ct, HandlerOptions as d, InputModeOf as da, Extend as di, Typion as dn, LocalizationOptions as dr, DraftsApi as dt, RequirementsError as ea, NullableTimestampKeys as ei, RegisteredContext as en, InferResult as eo, MultiTenantPlugin as er, ChangeArgs as et, TypionApp as f, KeyedShape as fa, FindConfig as fi, TypionAppOf as fn, LocalizationPlugin as fr, DraftsQueries as ft, Transport as g, KindWriteOptions as ga, Page as gi, StructuralValidationAdapter as gn, localized as gr, PendingDraftPolicy as gt, CheckedDescriptor as h, KindWrite as ha, OrderBy as hi, StructuralSchema as hn, Localized as hr, HistoryQueries as ht, FullCtx as i, TableShape as ia, Select as ii, RelationHelpers as in, WriteOp as io, TenantContext as ir, GlobalHooks as it, SchemaDrift as j, ConfigError as ja, CollectionSpec as ji, defineSetup as jn, CreateInput as jr, VersionsUpdateOptions as jt, WireIssue as k, Attempt as ka, AnyPluginSpec as ki, SetupPlugins as kn, AnyEntities as kr, VersionsRestoreOptions as kt, DescriptorApi as l, ColumnKindsSpec as la, ByIdConfig as li, ThroughSpec as ln, LocaleReadMode as lr, TypionHookEntityApi as lt, TypionTypes as m, KindRead as ma, InputSchema as mi, Register as mn, LocalizationWriteMode as mr, HistoryApi as mt, ContextInput as n, SeedGenerators as na, ReferenceOptions as ni, RegisteredSetup as nn, RESULT as no, MultiTenantTraits as nr, CreateInput_ as nt, TypionMode as o, ColumnKindRuntime as oa, UpdateManyOptions as oi, StructureInput as on, TextTenantColumn as or, Insert_ as ot, TypionEngine as p, KindOf as pa, FirstConfig as pi, TypionConfig as pn, LocalizationReadMode as pr, FieldDiff as pt, Operation as q, ValidationError as qa, PluginErrorInput as qi, softUnique as qn, LinkInput as qr, FoldKinds as qt, Ctx as r, SeedSpec as ra, Row as ri, RegisteredTypes as rn, ReadOp as ro, TenantColumnOf as rr, EntityHooks as rt, BindOptions as s, ColumnKindSpec as sa, UpdateOptions as si, SystemTypion as sn, UuidTenantColumn as sr, Select_ as st, BaseContext as t, SeedColumnsInput as ta, ReadRow as ti, RegisteredPlugins as tn, Op as to, MultiTenantSystemOptions as tr, ChangeOp as tt, EntityName as u, ColumnKindsTypes as ua, CountConfig as ui, TypesOf as un, LocalizationColumnKinds as ur, UpdateInput_ as ut, TransportEntity as v, KindedData as va, Result as vi, GetTypionOptions as vn, localizedUnique as vr, VersionOperation as vt, WireDataEnvelope as w, ReadModeOf as wa, VirtualColumn as wi, AnySetup as wn, Jsonified as wr, VersionsHookArgs as wt, ERROR_STATUS as x, PluginReadOptions as xa, Governed as xi, allowAll as xn, PluginSpecInput as xr, VersionsCollectionOptions as xt, TransportOperation as y, OptionType as ya, Where as yi, getTypion as yn, localizedValue as yr, VersionRef as yt, ColumnMeta as z, InvalidStateError as za, PluginApis as zi, SoftDeleteDeleteOptions as zn, EntityValidation as zr, ColumnsOf as zt };
|
|
2100
|
+
//# sourceMappingURL=context-B4e2eREP.d.ts.map
|
package/dist/contract/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $a as
|
|
1
|
+
import { $a as InferItem, $r as NoneIfEmpty, C as WireBatchEnvelope, D as WireErrorCode, Dr as TraitOptions, E as WireError, Mr as CreateOptions, Nr as DeleteOptions, O as WireErrorEnvelope, Or as TraitQueries, Qa as Descriptor, Qr as NoExtraKeys, S as ErrorStatus, T as WireEnvelope, U as TypionDefinition, Wa as TypionErrorCode, Wr as Id, Yr as LinkQueries, Za as AnyDescriptor, ai as UpdateInput, ao as descriptor, eo as InferResult, gi as Page, io as WriteOp, jr as CreateInput, k as WireIssue, m as TypionTypes, mi as InputSchema, no as RESULT, oi as UpdateManyOptions, ri as Row, ro as ReadOp, si as UpdateOptions, ti as ReadRow, to as Op, u as EntityName, vi as Result, w as WireDataEnvelope, wr as Jsonified, x as ERROR_STATUS, xa as PluginReadOptions } from "../context-B4e2eREP.js";
|
|
2
2
|
import { AnyRelations, GetTableViewFieldSelection, KnownKeysOnly } from "drizzle-orm";
|
|
3
3
|
//#region src/contract/wire.d.ts
|
|
4
4
|
type ColData<C> = C extends {
|
package/dist/db/pglite.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as HandlerOptions, m as TypionTypes, p as TypionEngine } from "./context-
|
|
1
|
+
import { d as HandlerOptions, m as TypionTypes, p as TypionEngine } from "./context-B4e2eREP.js";
|
|
2
2
|
//#region src/http/handler.d.ts
|
|
3
3
|
declare function typionErrorResponse(error: unknown, options?: {
|
|
4
4
|
debug?: boolean;
|
|
@@ -20,4 +20,4 @@ type RouteHandlers = {
|
|
|
20
20
|
declare function toRouteHandlers(handler: (request: Request) => Promise<Response>): RouteHandlers;
|
|
21
21
|
//#endregion
|
|
22
22
|
export { typionErrorResponse as i, createHandler as n, toRouteHandlers as r, RouteHandlers as t };
|
|
23
|
-
//# sourceMappingURL=handler-
|
|
23
|
+
//# sourceMappingURL=handler-BWVU8TCR.d.ts.map
|
package/dist/hono/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Jt as HandlerOptionsOf, ba as PluginContextOptions, fn as TypionAppOf, pn as TypionConfig, un as TypesOf } from "../context-B4e2eREP.js";
|
|
2
2
|
import { Context, MiddlewareHandler } from "hono";
|
|
3
3
|
//#region src/hono/index.d.ts
|
|
4
4
|
type ContextOptionsOf<C> = PluginContextOptions<TypesOf<C>['plugins']>;
|
package/dist/hono/index.js
CHANGED