From eae3459a5c3227cd129ac899a5897c455c6aebef Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Sun, 28 Jun 2026 16:34:29 +0800 Subject: [PATCH 1/2] =?UTF-8?q?docs(examples):=20add=20embed-objectql=20?= =?UTF-8?q?=E2=80=94=20using=20the=20lean=20@objectstack/objectql/core=20e?= =?UTF-8?q?ntry=20(ADR-0076)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A minimal, runnable example (the deferred ADR-0076 P4/P5) showing how a thin host embeds the ObjectQL engine as a plain library via `@objectstack/objectql/core` — no kernel, no ObjectQLPlugin, no @objectstack/metadata-protocol. The object is an ordinary `ObjectSchema.create({...})` (the same shape shipped to a full backend): one object model, two hosts, only the capability set differs. Doubles as a CI smoke (examples/* are in `turbo run test`): asserts CRUD round-trips via the lean entry and that the entry exposes ObjectQL but not ObjectQLPlugin / ObjectStackProtocolImplementation. Co-Authored-By: Claude Opus 4.8 --- examples/embed-objectql/README.md | 32 ++++++++++ examples/embed-objectql/package.json | 22 +++++++ examples/embed-objectql/src/index.ts | 71 ++++++++++++++++++++++ examples/embed-objectql/test/embed.test.ts | 19 ++++++ examples/embed-objectql/tsconfig.json | 9 +++ pnpm-lock.yaml | 22 +++++++ 6 files changed, 175 insertions(+) create mode 100644 examples/embed-objectql/README.md create mode 100644 examples/embed-objectql/package.json create mode 100644 examples/embed-objectql/src/index.ts create mode 100644 examples/embed-objectql/test/embed.test.ts create mode 100644 examples/embed-objectql/tsconfig.json diff --git a/examples/embed-objectql/README.md b/examples/embed-objectql/README.md new file mode 100644 index 0000000000..fe9602b1a1 --- /dev/null +++ b/examples/embed-objectql/README.md @@ -0,0 +1,32 @@ +# Embedding ObjectQL as a library (`@objectstack/objectql/core`) + +Minimal example of using the **ObjectQL data engine on its own** — no kernel, no +plugins, no metadata-management protocol. This is the path for a thin host (e.g. +a gateway, an edge worker, a CLI tool) that wants the query/CRUD engine and the +*same* object definitions as a full ObjectStack backend, without the platform. + +## The point (ADR-0076) + +Import from the **lean entry**: + +```ts +import { ObjectQL } from '@objectstack/objectql/core'; +``` + +`@objectstack/objectql/core` exposes the engine, registry, hooks, and validation +only. It does **not** pull in `ObjectQLPlugin`, the kernel factory, or +`@objectstack/metadata-protocol` (the 268KB metadata-management layer), so none +of that lands in your bundle. (The batteries-included `@objectstack/objectql` +entry still re-exports everything for full hosts.) + +The object here is an ordinary `ObjectSchema.create({...})` — identical to what +you would ship in a `*.object.ts` to a full backend. **One object model, two +hosts; only the installed capability set differs.** + +## Run + +```bash +pnpm --filter @objectstack/example-embed-objectql test # smoke +``` + +See [`src/index.ts`](./src/index.ts) for the ~40-line embed. diff --git a/examples/embed-objectql/package.json b/examples/embed-objectql/package.json new file mode 100644 index 0000000000..f0c3bdf48c --- /dev/null +++ b/examples/embed-objectql/package.json @@ -0,0 +1,22 @@ +{ + "name": "@objectstack/example-embed-objectql", + "version": "0.0.0", + "private": true, + "description": "Embed the ObjectQL engine as a plain library via @objectstack/objectql/core — no kernel, no plugins, no metadata protocol (ADR-0076).", + "type": "module", + "scripts": { + "demo": "vitest run", + "test": "vitest run", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@objectstack/objectql": "workspace:*", + "@objectstack/driver-memory": "workspace:*", + "@objectstack/spec": "workspace:*" + }, + "devDependencies": { + "@types/node": "^26.0.0", + "typescript": "^6.0.3", + "vitest": "^4.1.9" + } +} diff --git a/examples/embed-objectql/src/index.ts b/examples/embed-objectql/src/index.ts new file mode 100644 index 0000000000..9cb8530f3e --- /dev/null +++ b/examples/embed-objectql/src/index.ts @@ -0,0 +1,71 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. +// +// Embedding the ObjectQL engine as a plain library (ADR-0076). +// +// This imports from `@objectstack/objectql/core` — the LEAN entry. It pulls the +// data engine (query/CRUD/hooks/validation) only: NO kernel, NO ObjectQLPlugin, +// and NOT `@objectstack/metadata-protocol` (the 268KB metadata-management layer). +// Ideal for a thin, latency-sensitive host (e.g. a gateway) that wants the +// engine and the *same* object definitions as the full platform, without the +// platform itself. +// +// The object below is an ordinary `ObjectSchema.create({...})` — the exact same +// shape you would ship in a `*.object.ts` to a full ObjectStack backend. One +// object model, two hosts; only the installed capability set differs. + +import { ObjectQL } from '@objectstack/objectql/core'; +import { InMemoryDriver } from '@objectstack/driver-memory'; +import { ObjectSchema, Field, type ServiceObject } from '@objectstack/spec/data'; + +export const Account = ObjectSchema.create({ + name: 'account', + label: 'Account', + pluralLabel: 'Accounts', + fields: { + name: Field.text({ label: 'Name', required: true }), + industry: Field.text({ label: 'Industry' }), + active: Field.boolean({ label: 'Active' }), + }, +}); + +export interface AccountRow { + id: string; + name: string; + industry?: string; + active?: boolean; +} + +/** Boot a standalone engine, register one object, do CRUD, return active rows. */ +export async function runEmbeddedEngine(): Promise { + const engine = new ObjectQL(); + engine.registerDriver(new InMemoryDriver({ persistence: false }), true); + await engine.init(); + + // Register the object directly — the registry lives in the core engine, so no + // kernel/plugin/metadata-protocol is involved. (`ObjectSchema.create` is the + // authoring shape; `registerObject` takes the canonical `ServiceObject`.) + engine.registry.registerObject(Account as ServiceObject, 'example-embed'); + + await engine.insert('account', { name: 'Acme', industry: 'Manufacturing', active: true }); + await engine.insert('account', { name: 'Globex', industry: 'Energy', active: false }); + await engine.insert('account', { name: 'Initech', industry: 'Software', active: true }); + + return engine.find('account', { + where: { active: true }, + orderBy: [{ field: 'name', order: 'asc' }], + }) as Promise; +} + +// Allow `node`/`tsx`-style direct execution to print the result. +if (import.meta.url === `file://${process.argv[1]}`) { + runEmbeddedEngine() + .then((rows) => { + // eslint-disable-next-line no-console + console.log(`Active accounts (${rows.length}):`, rows.map((r) => r.name).join(', ')); + }) + .catch((err) => { + // eslint-disable-next-line no-console + console.error(err); + process.exit(1); + }); +} diff --git a/examples/embed-objectql/test/embed.test.ts b/examples/embed-objectql/test/embed.test.ts new file mode 100644 index 0000000000..2b9e117c70 --- /dev/null +++ b/examples/embed-objectql/test/embed.test.ts @@ -0,0 +1,19 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +import { describe, it, expect } from 'vitest'; +import { runEmbeddedEngine } from '../src/index.js'; + +describe('embed @objectstack/objectql/core (ADR-0076)', () => { + it('runs the engine standalone and round-trips CRUD via the lean entry', async () => { + const active = await runEmbeddedEngine(); + expect(active.map((r) => r.name)).toEqual(['Acme', 'Initech']); + expect(active.every((r) => r.active === true)).toBe(true); + }); + + it('the lean entry exposes the engine but not the kernel plugin / protocol', async () => { + const core: Record = await import('@objectstack/objectql/core'); + expect(typeof core.ObjectQL).toBe('function'); + expect(core.ObjectQLPlugin).toBeUndefined(); + expect(core.ObjectStackProtocolImplementation).toBeUndefined(); + }); +}); diff --git a/examples/embed-objectql/tsconfig.json b/examples/embed-objectql/tsconfig.json new file mode 100644 index 0000000000..f9a5e26624 --- /dev/null +++ b/examples/embed-objectql/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*", "test/**/*"], + "exclude": ["node_modules", "dist"], + "compilerOptions": { + "noEmit": true, + "types": ["node"] + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 14be2822bd..c8dc729e28 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -209,6 +209,28 @@ importers: specifier: ^4.1.9 version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.2)(msw@2.14.6(@types/node@26.0.0)(typescript@6.0.3))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + examples/embed-objectql: + dependencies: + '@objectstack/driver-memory': + specifier: workspace:* + version: link:../../packages/plugins/driver-memory + '@objectstack/objectql': + specifier: workspace:* + version: link:../../packages/objectql + '@objectstack/spec': + specifier: workspace:* + version: link:../../packages/spec + devDependencies: + '@types/node': + specifier: ^26.0.0 + version: 26.0.0 + typescript: + specifier: ^6.0.3 + version: 6.0.3 + vitest: + specifier: ^4.1.9 + version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.2)(msw@2.14.6(@types/node@26.0.0)(typescript@6.0.3))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + packages/adapters/hono: dependencies: '@objectstack/plugin-hono-server': From df71726665b8e75a6bf97d2b92107ad2dada90c4 Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Sun, 28 Jun 2026 17:34:42 +0800 Subject: [PATCH 2/2] chore(changeset): empty changeset for private embed-objectql example (no release) Co-Authored-By: Claude Opus 4.8 --- .changeset/embed-objectql-example.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .changeset/embed-objectql-example.md diff --git a/.changeset/embed-objectql-example.md b/.changeset/embed-objectql-example.md new file mode 100644 index 0000000000..f631da433e --- /dev/null +++ b/.changeset/embed-objectql-example.md @@ -0,0 +1,4 @@ +--- +--- + +docs(examples): add `embed-objectql` lean-core usage example. Private package — no release impact.