@telorun/sqlite 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ # SUSTAINABLE USE LICENSE (Fair-code)
2
+
3
+ Copyright (c) 2026 CodeNet Sp. z o.o.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, modify, and distribute the Software for any purpose—including commercial purposes—subject to the following conditions:
6
+
7
+ 1. ANTI-COMPETITION RESTRICTION: The Software may not be provided to third parties as a managed service, commercial SaaS (Software-as-a-Service), PaaS (Platform-as-a-Service), BaaS (Backend-as-a-Service), or similar offering where the primary value provided to the user is the functionality of the Software itself, without a separate commercial license from the copyright holder.
8
+
9
+ 2. PERMITTED COMMERCIAL USE: You are free to use the Software to build, host, and monetize your own commercial applications, products, and services, provided such use does not violate Clause 1.
10
+
11
+ 3. ATTRIBUTION: This copyright notice and license must be included in all copies or substantial portions of the Software.
12
+
13
+ 4. CONTRIBUTIONS: Contributions to the Software are welcome and encouraged. By contributing, you agree that your contributions may be incorporated into the Software and distributed under this license.
14
+
15
+ 5. DISCLAIMER: The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the Software or the use or other dealings in the Software.
16
+
17
+ For commercial licensing, managed hosting exemptions, or enterprise inquiries, please contact <contact@codenet.pl>.
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/telorun/telo/main/assets/telo.png" alt="Telo" width="200" />
3
+ </p>
4
+
5
+ <h1 align="center">Telo</h1>
6
+
7
+ <p align="center">Runtime for declarative backends.</p>
8
+
9
+ <p align="center">
10
+ <a href="https://github.com/telorun/telo/actions/workflows/test.yml"><img alt="Tests" src="https://github.com/telorun/telo/actions/workflows/test.yml/badge.svg" /></a>
11
+ <a href="https://www.npmjs.com/package/@telorun/cli"><img alt="node" src="https://img.shields.io/node/v/@telorun/cli" /></a>
12
+ <br />
13
+ <a href="https://github.com/telorun/telo/commits/main"><img alt="Last commit" src="https://img.shields.io/github/last-commit/telorun/telo" /></a>
14
+ <a href="https://github.com/telorun/telo/issues"><img alt="Issues" src="https://img.shields.io/github/issues/telorun/telo" /></a>
15
+ <a href="https://github.com/telorun/telo/pulls"><img alt="Pull requests" src="https://img.shields.io/github/issues-pr/telorun/telo" /></a>
16
+ <br />
17
+ <img alt="Changesets" src="https://img.shields.io/badge/maintained%20with-changesets-176de3" />
18
+ </p>
19
+
20
+ Telo is an execution engine (Micro-Kernel) that runs logic defined entirely in YAML manifests. Instead of writing imperative backend code, you define your routes, databases, schemas, and AI workflows as atomic, interconnected YAML documents. Telo takes those manifests and runs them.
21
+
22
+ Built to be language-agnostic and infinitely extensible.
23
+
24
+ ```bash
25
+ # Reconcile your manifest into a running backend
26
+ $ telo ./examples/todo-app
27
+
28
+ {"level":30,"time":1771610393008,"pid":1310178,"hostname":"dev","msg":"Server listening at http://127.0.0.1:8077"}
29
+ ```
30
+
31
+ ## Why use Telo?
32
+
33
+ - **Open Standards:** Built on YAML, JSON Schema, and CEL — no proprietary DSL.
34
+ - **Static Analysis:** CEL type checking, reference validation, and IDE diagnostics catch errors before runtime.
35
+ - **Micro-Kernel Architecture:** Telo itself knows nothing about HTTP or SQL. Everything is a module you import, scope, and compose with typed variable and secret contracts.
36
+ - **Language Agnostic:** Available as a Node.js runtime today, with a shared YAML runtime contract that allows for future Rust or Go implementations without changing your manifests.
37
+
38
+ ## What It Does
39
+
40
+ - **Loads** YAML resources and compiles CEL expressions (`${{ }}`) into an in-memory registry.
41
+ - **Resolves** resource dependencies via a multi-pass init loop, handling ordering automatically.
42
+ - **Indexes** resources by Kind and Name for constant-time lookup.
43
+ - **Dispatches** execution to the controller that owns each Kind.
44
+
45
+ ## Example manifest
46
+
47
+ See [examples/](./examples/) for a list of working applications.
48
+
49
+ ## Status
50
+
51
+ Telo is under heavy development. While it is pre-1.0, breaking changes ship in **minor** releases — manifest shapes, kind schemas, and APIs can change between versions. Pin your imports and expect to update manifests when you upgrade. The core runtime, module system, and standard library are functional, but Telo is not yet recommended for production use.
52
+
53
+ **1.0 lands when the Rust kernel reaches feature parity with the Node.js implementation** — that is the milestone that freezes the manifest contract across runtimes.
54
+
55
+ ## The Meaning of Telo
56
+
57
+ The name Telo is derived from the Greek root Telos - meaning the "end goal", "purpose", or "final state". That is exactly the philosophy behind this runtime. In standard imperative programming, you have to write thousands of lines of code to tell a server exactly how to start. With Telo, you simply declare your desired final state.
58
+
59
+ You define the end state. Telo makes it real.
60
+
61
+ ## Philosophy
62
+
63
+ Modern platforms often spend disproportionate effort on technical mechanics-wiring frameworks, managing infrastructure, and negotiating toolchains-while the original business problem gets delayed or diluted. Telo pushes in the opposite direction: it treats kernel execution as a stable, predictable host so teams can concentrate on the **business logic and outcomes** instead of the plumbing.
64
+
65
+ By separating "what the system should do" from "how it is hosted", the runtime reduces friction for domain‑level changes. Teams can move faster on product requirements, experiment more safely, and keep conversations centered on value delivered rather than implementation trivia.
66
+
67
+ Telo also aims to **join forces across all programming language communities**, so the best ideas, patterns, and implementations can converge into a shared kernel truth without forcing everyone into a single stack.
68
+
69
+ YAML also makes the system more **AI‑friendly** than traditional programming languages: it is explicit, structured, and easier for tools to generate, review, and transform without losing intent.
70
+
71
+ ## Modularity
72
+
73
+ Telo is built around **modules** that own specific resource kinds. A module is loaded from a manifest, declares which kinds it implements, and then receives only the resources of those kinds. This keeps concerns isolated and lets teams compose systems from focused building blocks rather than monolithic services.
74
+
75
+ At kernel execution time, execution is always routed by **Kind.Name**. The kernel resolves the Kind to its owning module and hands off execution. Modules can call back into the kernel to execute other resources, enabling composition without tight coupling.
76
+
77
+ ## Architecture
78
+
79
+ The architecture is inspired by Kubernetes-style manifests: declarative resources, explicit kinds, and a control plane that routes work based on those definitions.
80
+ Those manifests were taken to the next level by allowing them to run inside a standalone runtime host.
81
+
82
+ ## See more at
83
+
84
+ - [Telo Kernel](./kernel/README.md)
85
+ - [Telo SDK for module authors](sdk/README.md)
86
+ - [Modules](modules/README.md)
87
+
88
+ ## License
89
+
90
+ See [LICENSE](https://github.com/telorun/telo/blob/main/LICENSE).
91
+
92
+ ## Contribution Note
93
+
94
+ By contributing, you agree that code and examples in this repository may be translated or re‑implemented in other programming languages (including by AI systems) to support the project’s polyglot goals.
@@ -0,0 +1,22 @@
1
+ import type { ResourceContext } from "@telorun/sdk";
2
+ import { SqlConnectionBase } from "@telorun/sql";
3
+ import { Kysely } from "kysely";
4
+ import type { SqliteDb } from "./sqlite-driver-interface.js";
5
+ interface SqliteConnectionManifest {
6
+ metadata: {
7
+ name: string;
8
+ module: string;
9
+ };
10
+ /** File path, or omitted / `:memory:` for an in-memory database. */
11
+ file?: string;
12
+ }
13
+ declare class SqliteConnection extends SqlConnectionBase {
14
+ private readonly sqlite;
15
+ constructor(db: Kysely<any>, sqlite: SqliteDb, ctx: ResourceContext);
16
+ /** The driver's native multi-statement entry point — kysely binds one
17
+ * statement per call. */
18
+ executeScript(sql: string): Promise<void>;
19
+ }
20
+ export declare function register(): void;
21
+ export declare function create(resource: SqliteConnectionManifest, ctx: ResourceContext): Promise<SqliteConnection>;
22
+ export {};
@@ -0,0 +1,67 @@
1
+ import { quoteAnsiIdentifier, SqlConnectionBase } from "@telorun/sql";
2
+ import { Kysely, SqliteAdapter, SqliteDialect } from "kysely";
3
+ const sqliteDialect = {
4
+ placeholderStyle: "qmark",
5
+ quoteIdentifier: quoteAnsiIdentifier,
6
+ // SQLite has no array type, so set membership expands to one placeholder per
7
+ // element.
8
+ renderIn(column, values, addParam) {
9
+ return `${column} IN (${values.map((value) => addParam(value)).join(", ")})`;
10
+ },
11
+ };
12
+ class SqliteConnection extends SqlConnectionBase {
13
+ sqlite;
14
+ constructor(db, sqlite, ctx) {
15
+ super(db, sqliteDialect, ctx);
16
+ this.sqlite = sqlite;
17
+ }
18
+ /** The driver's native multi-statement entry point — kysely binds one
19
+ * statement per call. */
20
+ async executeScript(sql) {
21
+ this.sqlite.exec(sql);
22
+ }
23
+ }
24
+ // Kysely's stock SQLite adapter reports `supportsTransactionalDdl = false`, so
25
+ // its Migrator runs migrations without a transaction. SQLite does support
26
+ // transactional DDL, so we flip the flag — letting the Migrator wrap the whole
27
+ // migration batch in a single transaction, matching PostgreSQL.
28
+ class TransactionalSqliteAdapter extends SqliteAdapter {
29
+ get supportsTransactionalDdl() {
30
+ return true;
31
+ }
32
+ }
33
+ class TransactionalSqliteDialect extends SqliteDialect {
34
+ createAdapter() {
35
+ return new TransactionalSqliteAdapter();
36
+ }
37
+ }
38
+ async function openSqliteDatabase(file = ":memory:") {
39
+ // Auto-create the parent directory for file-backed databases. SQLite
40
+ // drivers fail-fast when the directory doesn't exist; mirroring `mkdir
41
+ // -p` here lets manifests use paths like `./tmp/foo.sqlite` without a
42
+ // separate filesystem-prep step. `:memory:` skips filesystem entirely.
43
+ if (file !== ":memory:") {
44
+ const { mkdir } = await import("node:fs/promises");
45
+ const { dirname } = await import("node:path");
46
+ const dir = dirname(file);
47
+ if (dir && dir !== "." && dir !== "/") {
48
+ await mkdir(dir, { recursive: true });
49
+ }
50
+ }
51
+ // Route through this package's own `./sqlite-driver` subpath export so the
52
+ // resolver selects the driver per runtime (Bun → bun:sqlite, Node →
53
+ // better-sqlite3). A manual `typeof Bun` check with relative imports gets
54
+ // flattened by the controller bundler into an unconditional top-level
55
+ // `import "bun:sqlite"`, which Node's ESM loader rejects before the guard
56
+ // runs; an external `@telorun/*` specifier stays a deferred dynamic import.
57
+ const { openDatabase } = await import("@telorun/sqlite/sqlite-driver");
58
+ return openDatabase(file);
59
+ }
60
+ export function register() { }
61
+ export async function create(resource, ctx) {
62
+ const sqlite = await openSqliteDatabase(resource.file ?? ":memory:");
63
+ const db = new Kysely({
64
+ dialect: new TransactionalSqliteDialect({ database: sqlite }),
65
+ });
66
+ return new SqliteConnection(db, sqlite, ctx);
67
+ }
@@ -0,0 +1,37 @@
1
+ import type { ResourceContext, ResourceInstance } from "@telorun/sdk";
2
+ import { type MigrationMap, type ReclaimPolicy, type SqlConnection } from "@telorun/sql";
3
+ import type { SqliteTableResource } from "./table-controller.js";
4
+ interface SqliteSchemaManifest {
5
+ metadata: {
6
+ name: string;
7
+ module: string;
8
+ };
9
+ connection: SqlConnection;
10
+ version?: string;
11
+ ledger?: string;
12
+ tables?: SqliteTableResource[];
13
+ beforeMigrations?: MigrationMap;
14
+ migrations?: MigrationMap;
15
+ reclaim?: ReclaimPolicy;
16
+ }
17
+ /**
18
+ * `SQLite.Schema` — the single schema-change kind: declared tables and
19
+ * imperative migrations reconciled in one boot pass, under one clock.
20
+ *
21
+ * SQLite has exactly one namespace, so unlike the PostgreSQL kind there is no
22
+ * `schema:` field to name and nothing to create.
23
+ */
24
+ declare class SqliteSchemaResource implements ResourceInstance {
25
+ private readonly manifest;
26
+ private readonly ctx;
27
+ constructor(manifest: SqliteSchemaManifest, ctx: ResourceContext);
28
+ /** Configured state is pulled, observed state is pushed — everything this
29
+ * resource knows is learned while running, so the snapshot is empty and the
30
+ * whole report arrives through `setStatus`. It still has to exist: a
31
+ * resource that publishes nothing is absent from the `resources` scope. */
32
+ snapshot(): Record<string, unknown>;
33
+ run(): Promise<void>;
34
+ }
35
+ export declare function register(): void;
36
+ export declare function create(resource: SqliteSchemaManifest, ctx: ResourceContext): Promise<SqliteSchemaResource>;
37
+ export {};
@@ -0,0 +1,49 @@
1
+ import { resolveSqlConnection, runSchemaPass, } from "@telorun/sql";
2
+ import { SqliteSchemaDriver } from "./sqlite-schema-driver.js";
3
+ /**
4
+ * `SQLite.Schema` — the single schema-change kind: declared tables and
5
+ * imperative migrations reconciled in one boot pass, under one clock.
6
+ *
7
+ * SQLite has exactly one namespace, so unlike the PostgreSQL kind there is no
8
+ * `schema:` field to name and nothing to create.
9
+ */
10
+ class SqliteSchemaResource {
11
+ manifest;
12
+ ctx;
13
+ constructor(manifest, ctx) {
14
+ this.manifest = manifest;
15
+ this.ctx = ctx;
16
+ }
17
+ /** Configured state is pulled, observed state is pushed — everything this
18
+ * resource knows is learned while running, so the snapshot is empty and the
19
+ * whole report arrives through `setStatus`. It still has to exist: a
20
+ * resource that publishes nothing is absent from the `resources` scope. */
21
+ snapshot() {
22
+ return {};
23
+ }
24
+ async run() {
25
+ const connection = resolveSqlConnection(this.manifest.connection, this.ctx, () => `SQLite.Schema "${this.manifest.metadata.name}": 'connection'`);
26
+ if (!connection) {
27
+ throw new Error(`SQLite.Schema "${this.manifest.metadata.name}": missing connection`);
28
+ }
29
+ const status = await runSchemaPass(new SqliteSchemaDriver(connection), this.ctx, {
30
+ schema: "main",
31
+ ledger: this.manifest.ledger,
32
+ version: this.manifest.version,
33
+ tables: (this.manifest.tables ?? []).map((table) => table.declaration),
34
+ beforeMigrations: this.manifest.beforeMigrations ?? {},
35
+ migrations: this.manifest.migrations ?? {},
36
+ reclaim: this.manifest.reclaim,
37
+ });
38
+ for (const key of status.orphanedMigrations) {
39
+ // Deleting decade-old migrations from a manifest is normal, so an applied
40
+ // key with no declaration is reported and never an error.
41
+ this.ctx.log.info("Applied migration has no declaration", { "sql.migration.name": key });
42
+ }
43
+ this.ctx.setStatus({ ...status });
44
+ }
45
+ }
46
+ export function register() { }
47
+ export async function create(resource, ctx) {
48
+ return new SqliteSchemaResource(resource, ctx);
49
+ }
@@ -0,0 +1,70 @@
1
+ import { type ChangeSafety, type DeclaredColumn, type DeclaredForeignKey, type DeclaredIndex, type DeclaredTable, type LiveColumn, type SchemaObjectId, type LiveTable, type LedgerTables, type LiveForeignKey, type SchemaDriver, type SqlConnection } from "@telorun/sql";
2
+ /**
3
+ * SQLite's half of declarative schema.
4
+ *
5
+ * The vocabulary is honestly smaller than PostgreSQL's because the engine is:
6
+ * five storage classes, no namespaces, no `ALTER COLUMN`, and foreign keys that
7
+ * exist only as part of the table they were created with. Nothing here pretends
8
+ * otherwise — a change SQLite cannot make in place is refused with the reason,
9
+ * which is what sends the author to a `migrations:` entry that rebuilds the
10
+ * table rather than leaving them with a silently unapplied declaration.
11
+ */
12
+ /** SQLite storage classes. There is no date, boolean or UUID type — those are
13
+ * conventions over these five, and inventing names for them here would be the
14
+ * lowest-common-denominator type vocabulary this design rejects. */
15
+ export declare const SQLITE_TYPES: readonly ["integer", "real", "text", "blob", "numeric"];
16
+ export type SqliteType = (typeof SQLITE_TYPES)[number];
17
+ export declare class SqliteSchemaDriver implements SchemaDriver {
18
+ #private;
19
+ readonly connection: SqlConnection;
20
+ constructor(connection: SqlConnection);
21
+ quote(name: string): string;
22
+ /** SQLite has exactly one namespace, so a table is named on its own. */
23
+ qualify(_schema: string, table: string): string;
24
+ /**
25
+ * SQLite has no advisory lock, so the contract is met by a weaker mechanism
26
+ * and this says which.
27
+ *
28
+ * The engine serializes WRITERS, so no two passes interleave a write; each
29
+ * group `runAtomically` submits is a transaction. What is NOT excluded is two
30
+ * passes running concurrently against one database file and interleaving
31
+ * between groups. That is survivable rather than merely unlikely: every step
32
+ * is derived from live state and re-derivable, the DDL is `IF NOT EXISTS`, and
33
+ * the ledger writes are last in their groups — so two racing passes converge
34
+ * on the same schema instead of diverging.
35
+ *
36
+ * What it does not buy is exclusion for the destructive phase: two passes
37
+ * could both find a tombstone eligible, and the second's `DROP … IF EXISTS`
38
+ * is then a no-op. Acceptable because the outcome is identical; a genuine
39
+ * lock would need `BEGIN IMMEDIATE` held across the whole pass, which cannot
40
+ * nest with the per-group transactions.
41
+ */
42
+ withLock<T>(_schema: string, body: () => Promise<T>): Promise<T>;
43
+ ensureNamespaceStatements(): string[];
44
+ ledgerStatements(_schema: string, tables: LedgerTables): string[];
45
+ now(): Promise<string>;
46
+ runAtomically(statements: readonly string[]): Promise<void>;
47
+ introspect(_schema: string, tables: readonly string[]): Promise<LiveTable[]>;
48
+ typeSignature(column: DeclaredColumn): string;
49
+ /** An index is dropped and recreated, which SQLite does support. */
50
+ classifyIndexChange(): ChangeSafety;
51
+ /** A foreign key exists only as part of the table it was created with, so
52
+ * changing one means rebuilding the table. */
53
+ classifyForeignKeyChange(live: LiveForeignKey): ChangeSafety;
54
+ classifyAlter(live: LiveColumn, declared: DeclaredColumn): ChangeSafety;
55
+ classifyCopy(live: LiveColumn, target: DeclaredColumn): ChangeSafety;
56
+ createTable(schema: string, table: DeclaredTable): string[];
57
+ addColumn(schema: string, table: string, column: DeclaredColumn): string[];
58
+ /** Unreachable: `classifyAlter` refuses every in-place column change SQLite
59
+ * cannot make, which is all of them. */
60
+ alterColumn(_schema: string, table: string, _live: LiveColumn, column: DeclaredColumn): string[];
61
+ copyColumn(schema: string, table: string, from: string, to: string): string[];
62
+ createIndex(schema: string, table: string, index: DeclaredIndex): string[];
63
+ dropIndex(_schema: string, _table: string, index: string): string[];
64
+ addForeignKey(_schema: string, table: string, fk: DeclaredForeignKey): string[];
65
+ /** Unreachable: `canReclaim` refuses a foreign key before the drop is planned. */
66
+ dropForeignKey(_schema: string, table: string, name: string): string[];
67
+ canReclaim(id: SchemaObjectId): ChangeSafety;
68
+ dropColumn(schema: string, table: string, column: string): string[];
69
+ dropTable(schema: string, table: string): string[];
70
+ }