Skip to content

Commit a484966

Browse files
os-try-charlesclaude[bot]claude
authored
docs(readme): make the published README TypeScript examples compile (#18968)
Part of #18915 Clause-②: no Executes maintainer decision batch #156 item 2 — ruling F on #18715. No gate, no ratchet, no CI wiring is added: this is the user-facing half of PR #18751's census, corrected. ## Act 1 — the split, re-derived PR #18751's instrument re-run on a fresh `origin/main` (`node scripts/measure-markdown-ts-blocks.mjs --json`, workspace built first, all three controls behaving: GREEN clean, FIRING reports TS2341, UNPUBLISHED_SUBPATH reports a counted TS2307 on each of its three specifiers). The card's numbers reproduce exactly: ``` handwritten stratum blocks 282 files 54 raw 195 tolerant 76 well-formed-and-wrong 58 changelog stratum blocks 720 (out of scope) ``` Split by publication — a block is *published* when its document is inside its package's `files[]`: | reading | total | published | internal | |:---|---:|---:|---:| | tolerant failures | 76 | **54** | 22 | | syntactically valid and wrong | 58 | **44** | 14 | The split is unambiguous in this repo: every non-private package's `files[]` is `["dist","README.md","CHANGELOG.md"]` (only `@objectstack/spec` lists more), so **the published package-root Markdown is exactly `README.md`**, and every other package-root document — `ADVANCED_FEATURES.md`, `PHASE2_IMPLEMENTATION.md`, `V3_MIGRATION_GUIDE.md`, `ARCHITECTURE.md`, `DEVELOPMENT_PLAN.md`, `PLUGIN_STANDARDS.md`, `REST_API_PLUGIN.md`, `ZOD_SCHEMA_AUDIT_REPORT.md`, `STACKBLITZ.md`, `CLIENT_SPEC_COMPLIANCE.md`, `ROADMAP.md`, plus the private `packages/qa/*` READMEs — is internal. Confirmed against the packer rather than asserted from `package.json`, with a control from the same population that must read the other way: ``` npm pack --dry-run --json, packages/core README.md in tarball: True # positive control ADVANCED_FEATURES.md in tarball: False # same population, must be absent PHASE2_IMPLEMENTATION.md in tarball: False ``` No count in the split is zero, so no zero needed pairing. **`packages/spec/liveness/README.md`** (PR #18938's surface) measures as **published** — `liveness` is a `files[]` entry, and `npm pack` puts `liveness/README.md` and `liveness/state-counts.md` in the tarball. It is nonetheless **not in this card's population**: the census population is Markdown at a *package root* (a directory carrying a `package.json`), `packages/spec/liveness` is not one, and the file therefore contributes none of the 76/58. No overlap with #18938, and nothing of theirs is touched here. ## Act 2 — the published corrections **43 of the 44** published syntactically-valid-and-wrong blocks now compile; 20 READMEs changed. Re-measured on the same instrument: | reading | before | after | |:---|---:|---:| | published, syntactically valid and wrong | 44 | **1** | | published, tolerant failures | 54 | 11 | | internal, syntactically valid and wrong | 14 | 14 (untouched) | | internal, tolerant failures | 22 | 22 (untouched) | The 11 remaining published tolerant failures are **10 syntax-only blocks** — bare type-signature fragments in `service-automation`, `trigger-record-change`, `trigger-schedule` and `service-job`, the `needs-explicit-partial-tag` class the instrument's own forward convention describes — plus the one block below. Syntax-only blocks are outside act 2's mandate, which is the syntactically-valid-and-wrong set. What was wrong, by class: - **Legacy option vocabulary.** `@objectstack/client-react`'s hooks take `fields` / `orderBy` / `limit` / `where`; the README still wrote `select` / `sort` / `top` / `filters`, and read `PaginatedResult.value` where the member is `records`. Also `timeout` to `timeoutMs` (`service-job`), `attempts` to `maxAttempts` (`service-queue`), `filter` to `where` (`IDataEngine.find`). - **An async API used as a chainable one.** `ObjectKernel.use()` is async and resolves to the kernel, so `kernel.use(a).use(b)` does not type-check at all; and `ObjectKernelConfig` has no `plugins` member. - **Interfaces implemented but never imported.** Four plugin examples wrote `implements Plugin` with no import — which silently bound to the DOM's `Plugin` — and three omitted the required `init`. `PluginContext.getService` is declared with a type parameter that has no default, so every example that read a service back left it `unknown`. - **Removed or never-existing API, rewritten rather than left as a fossil.** `@objectstack/driver-memory`'s default export is a legacy `onEnable` object that `kernel.use()` refuses on both the type and the boot path — the quick start now registers through `DriverPlugin`, and the "Key Exports" row that called it a drop-in plugin is corrected with it. Its persistence adapters take an options bag and hang under `persistence.adapter`. `defineStack` has no `driver` key. `@objectstack/rest`'s `RestServer` takes the host `IHttpServer` as its first argument and `registerRoutes()` takes none; `RouteManager` is constructed on a server. `ObjectSchema.parse()` returns the value — the `{ success, data }` envelope belongs to `safeParse`. `useMutation` has no `onMutate` and no mutation context, so the "Optimistic Updates" example was rebuilt on the options it does have. - **Untyped parameters under `--strict`** in React and handler examples, annotated. Two of the 44 (`packages/cli`, `packages/mcp`) were **measurement artefacts worth stating plainly**: `objects: Object.values(objects)` over an elided `./src/objects` barrel. The forgiven TS2307 leaves the namespace `any`, and `Object.values` then infers its type parameter from the union-shaped contextual type, producing a mismatch a reader's own resolvable barrel would not produce. Both now name the objects they import, which is typed and clearer either way. Beyond the counted blocks, the same defect class was corrected in three further `client-react` blocks (Master-Detail, Search with Debounce, and the Type Safety comment) that the census does not flag only because they import nothing and so type-check as `any`. Leaving `data.value` and `select:` standing one section below a corrected copy of themselves was not defensible; this is called out because it is work outside the measured set. ## The one block deliberately left, and why `packages/plugins/knowledge-ragflow/README.md` writes `source.options.datasetId`. That is what the shipped adapter reads (`extractRagflowOptions` casts the source to a shape carrying an optional `options` record, and its error text names `source.options.datasetId`), and it is **not** what `KnowledgeSourceSchema` declares — the declared key is `adapterConfig`, and the schema is a plain `z.object`, so a parse would strip `options` outright. Correcting the document to `adapterConfig` would make it compile and stop working. Correcting the adapter is a runtime change, out of this card's scope, and picks a winner between two live spellings. Contract-first says the defect is upstream, so the block is left as it stands and the conflict is reported for the maintainer instead of being papered over in a docs PR. That is why this PR says `Part of #18915` and not `Fixes`. ## Changeset — measured for this diff, not inherited The house `skip-changeset` argument for docs cards is "no package's `files[]` reaches `content/docs/**`". **It inverts here.** `README.md` is listed in `files[]` for every one of the 20 packages touched, so the bytes this PR changes are inside the published tarball — measured above with `npm pack --dry-run` and a same-population control that reads the other way. AGENTS.md: `skip-changeset` "is for a diff that publishes nothing from any released package". This diff publishes changed bytes from twenty released packages, and those bytes are what an upgrading agent reads. So this PR carries a **`patch`** changeset naming all twenty, and ⛔ no `skip-changeset` label. ## Scope - Touched: `packages/*/README.md` only, plus the changeset. ⛔ No internal document, ⛔ no `CHANGELOG.md`, ⛔ no `content/docs/**`, ⛔ no runtime code, ⛔ no gate or CI wiring. - The changeset file is the one path outside the claim's declared file surface (`packages/**/README.md`); it is the companion artefact the measurement above obliges, and it is named here rather than slipped in. ## Acceptance notes - `packages/client/README.md` documents `data.find()`'s legacy vocabulary (`select` / `filters` / `sort` / `top`). Unlike the `client-react` case this **compiles** — `QueryOptions` still accepts it — so it is out of this card's set, but `find` itself carries `@deprecated` and `data.query()` is the canonical call. Noted, not filed. - The `check:undeclared-dep-imports` family is not affected: no `package.json` moved. ## Verification Tree: `a8b75f978` (`origin/main` merged in, workspace rebuilt, `pnpm install --frozen-lockfile` after the lockfile moved). Every number below is from that tree. **The census, final run.** `node scripts/measure-markdown-ts-blocks.mjs --json`, exit 0, all three controls behaving (`green=clean firing=fires unpublished-subpath=fires`): ``` handwritten blocks 284 files 54 raw 172 tolerant 33 well-formed-and-wrong 15 published tolerant 11 well-formed-and-wrong 1 internal tolerant 22 well-formed-and-wrong 14 ``` 284 rather than 282 because the `observability` wiring block, which redeclared `metrics` four times in one fence, is now three fences — one per deployment, which is how a reader picks between them. **Gate families**, derived in-worktree from this tree with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no stale-tree warning after the merge): **63 derived, 63 run, all exit 0**, reconciled back through `--ran` with each command's exit code captured before any pipe — "63 derived familt(ies) accounted for — 63 run, 0 NOT-MEASURED (a DERIVED zero — all 63 recorded an exit code and none of them is 3)". `check:pm-dispatch-gates` is not among the derived families for this change set. That reconciliation answers one link only; it is not a complete account of CI. **Tests.** The diff changes no TypeScript, so no package's `tsc` program or vitest source set moves. Two suites do read a README this PR edits, found by grepping every test file in `packages/` for `README.md` (19 hits, triaged by the path each one reads), and both were run: ``` pnpm --filter @objectstack/client exec vitest run --maxWorkers=2 src/readme-package-install-example.test.ts Test Files 1 passed (1) · Tests 6 passed (6) · CLIENT_README_EXIT=0 pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/api/package-api.test.ts src/kernel/plugin-structure.test.ts Test Files 2 passed (2) · Tests 81 passed (81) · SPEC_TARGETED_EXIT=0 ``` The first one parses `packages/client/README.md` with the TypeScript parser and validates the manifest it finds against the install contract — it is the pin that a README edit in that package could break. **Lint.** Zero files in this diff are in eslint's population, measured from eslint's own config rather than assumed, with a control from the same tree that must read the other way: ``` ESLint#calculateConfigForFile packages/types/README.md rules: 0 ignored: true .changeset/18915-published-readme-examples-compile.md rules: 0 ignored: true packages/types/src/index.ts rules: 6 ignored: false # control ``` `eslint.config.mjs` scopes every block to `{ts,tsx,mts,cts,js,jsx,mjs,cjs}`, so no configuration in this diff can move an untouched file's verdict either. **Control bytes.** `grep -naP` for the C0 range over every changed path: no hits; a fixture carrying one byte in that range hits, so the scan is live. `pnpm check:nul-bytes` is among the 63 green gates. Authored by Claude Code, session `session_017ef78bLdybu3AffehKkhfk`. --- _Generated by [Claude Code](https://claude.ai/code)_ Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2265bb0 commit a484966

21 files changed

Lines changed: 276 additions & 161 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
"@objectstack/cli": patch
3+
"@objectstack/client": patch
4+
"@objectstack/client-react": patch
5+
"@objectstack/driver-memory": patch
6+
"@objectstack/driver-mongodb": patch
7+
"@objectstack/driver-turso": patch
8+
"@objectstack/mcp": patch
9+
"@objectstack/observability": patch
10+
"@objectstack/plugin-auth": patch
11+
"@objectstack/rest": patch
12+
"@objectstack/runtime": patch
13+
"@objectstack/service-cache": patch
14+
"@objectstack/service-i18n": patch
15+
"@objectstack/service-job": patch
16+
"@objectstack/service-package": patch
17+
"@objectstack/service-queue": patch
18+
"@objectstack/service-realtime": patch
19+
"@objectstack/service-storage": patch
20+
"@objectstack/spec": patch
21+
"@objectstack/types": patch
22+
---
23+
24+
The TypeScript examples in these packages' **published** `README.md` now compile against the package they document — 43 of the 44 blocks the `measure-markdown-ts-blocks` census reported as syntactically valid and wrong, in documents that ship inside the npm tarball.
25+
26+
`README.md` is listed in every one of these packages' `files[]`, so these bytes are the artefact a consumer — or a consumer's AI — reads and copies. What the census counted was not style: the examples named options the packages no longer accept, chained a method that returns a promise, and implemented interfaces they never imported.
27+
28+
The corrections, by class:
29+
30+
- **Legacy option vocabulary.** `@objectstack/client-react`'s hooks take `fields` / `orderBy` / `limit` / `where`, not `select` / `sort` / `top` / `filters`, and `PaginatedResult` carries `records`, not `value`. `@objectstack/service-job` takes `timeoutMs`, `@objectstack/service-queue` takes `maxAttempts`, and `IDataEngine.find` takes `where`.
31+
- **Async registration used synchronously.** `ObjectKernel.use()` returns `Promise<this>`, so `kernel.use(a).use(b)` does not chain; the examples now `await` each registration. `ObjectKernelConfig` has no `plugins` member.
32+
- **Interfaces implemented but never imported.** Several plugin examples wrote `implements Plugin` with no import, which bound to the DOM's `Plugin`; they now import `Plugin` / `PluginContext` and declare the required `init`. `PluginContext.getService<T>()` has no default type argument, so the examples that read a service now name its contract.
33+
- **Removed or never-existing API.** `@objectstack/driver-memory`'s default export is a legacy `onEnable` object that `kernel.use()` refuses — the quick start now registers through `DriverPlugin`; its persistence adapters take an options bag under `persistence.adapter`. `defineStack` has no `driver` key. `@objectstack/rest`'s `RestServer` takes the host `IHttpServer` first and `registerRoutes()` takes no arguments; `RouteManager` is constructed on a server. `@objectstack/spec`'s `ObjectSchema.parse()` returns the value — the `{ success, data }` envelope is `safeParse`'s. `useMutation` has no `onMutate` / mutation context.
34+
35+
No runtime code changed and no gate was added (#18715 ruling F). One block is deliberately left: `@objectstack/knowledge-ragflow`'s README writes `source.options.datasetId`, which is what the shipped adapter reads and what `KnowledgeSourceSchema` does not declare — correcting the document either way would contradict one of the two, so the conflict is reported rather than papered over.

‎packages/cli/README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ The CLI looks for `objectstack.config.ts` (or `.js`, `.mjs`) in the current dire
131131

132132
```typescript
133133
import { defineStack } from '@objectstack/spec';
134-
import * as objects from './src/objects';
134+
import { project, task } from './src/objects';
135135

136136
export default defineStack({
137137
manifest: {
@@ -141,7 +141,7 @@ export default defineStack({
141141
type: 'app',
142142
name: 'My App',
143143
},
144-
objects: Object.values(objects),
144+
objects: [project, task],
145145
});
146146
```
147147

‎packages/client-react/README.md‎

Lines changed: 58 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,17 @@ import { useQuery } from '@objectstack/client-react';
5151

5252
function TaskList() {
5353
const { data, isLoading, error, refetch } = useQuery('todo_task', {
54-
select: ['id', 'subject', 'priority'],
55-
sort: ['-created_at'],
56-
top: 20
54+
fields: ['id', 'subject', 'priority'],
55+
orderBy: ['-created_at'],
56+
limit: 20
5757
});
5858

5959
if (isLoading) return <div>Loading...</div>;
6060
if (error) return <div>Error: {error.message}</div>;
6161

6262
return (
6363
<div>
64-
{data?.value.map(task => (
64+
{data?.records.map(task => (
6565
<div key={task.id}>{task.subject}</div>
6666
))}
6767
<button onClick={refetch}>Refresh</button>
@@ -73,6 +73,7 @@ function TaskList() {
7373
#### Mutate Data
7474

7575
```tsx
76+
import type { FormEvent } from 'react';
7677
import { useMutation } from '@objectstack/client-react';
7778

7879
function CreateTaskForm() {
@@ -82,7 +83,7 @@ function CreateTaskForm() {
8283
}
8384
});
8485

85-
const handleSubmit = (e) => {
86+
const handleSubmit = (e: FormEvent) => {
8687
e.preventDefault();
8788
mutate({
8889
subject: 'New Task',
@@ -119,12 +120,12 @@ function PaginatedTaskList() {
119120
hasPreviousPage
120121
} = usePagination('todo_task', {
121122
pageSize: 10,
122-
sort: ['-created_at']
123+
orderBy: ['-created_at']
123124
});
124125

125126
return (
126127
<div>
127-
{data?.value.map(task => (
128+
{data?.records.map(task => (
128129
<div key={task.id}>{task.subject}</div>
129130
))}
130131
<div className="pagination">
@@ -155,7 +156,7 @@ function InfiniteTaskList() {
155156
isFetchingNextPage
156157
} = useInfiniteQuery('todo_task', {
157158
pageSize: 20,
158-
sort: ['-created_at']
159+
orderBy: ['-created_at']
159160
});
160161

161162
return (
@@ -180,7 +181,7 @@ function InfiniteTaskList() {
180181
```tsx
181182
import { useObject } from '@objectstack/client-react';
182183

183-
function ObjectSchemaViewer({ objectName }) {
184+
function ObjectSchemaViewer({ objectName }: { objectName: string }) {
184185
const { data: schema, isLoading } = useObject(objectName);
185186

186187
if (isLoading) return <div>Loading schema...</div>;
@@ -199,7 +200,7 @@ function ObjectSchemaViewer({ objectName }) {
199200
```tsx
200201
import { useView } from '@objectstack/client-react';
201202

202-
function ViewConfiguration({ objectName }) {
203+
function ViewConfiguration({ objectName }: { objectName: string }) {
203204
const { data: view, isLoading } = useView(objectName, 'list');
204205

205206
if (isLoading) return <div>Loading view...</div>;
@@ -218,7 +219,7 @@ function ViewConfiguration({ objectName }) {
218219
```tsx
219220
import { useFields } from '@objectstack/client-react';
220221

221-
function FieldList({ objectName }) {
222+
function FieldList({ objectName }: { objectName: string }) {
222223
const { data: fields, isLoading } = useFields(objectName);
223224

224225
if (isLoading) return <div>Loading fields...</div>;
@@ -269,7 +270,7 @@ interface Task {
269270
}
270271

271272
const { data } = useQuery<Task>('todo_task');
272-
// data.value is typed as Task[]
273+
// data.records is typed as Task[]
273274

274275
const { mutate } = useMutation<Task, Partial<Task>>('todo_task', 'create');
275276
// mutate expects Partial<Task>
@@ -280,23 +281,26 @@ const { mutate } = useMutation<Task, Partial<Task>>('todo_task', 'create');
280281
### Master-Detail View
281282

282283
```tsx
284+
import { useState } from 'react';
285+
import { useQuery } from '@objectstack/client-react';
286+
283287
function TaskList() {
284288
const [selectedId, setSelectedId] = useState<string | null>(null);
285-
289+
286290
const { data: tasks } = useQuery('todo_task', {
287-
select: ['id', 'subject'],
288-
sort: ['-created_at']
291+
fields: ['id', 'subject'],
292+
orderBy: ['-created_at']
289293
});
290-
294+
291295
const { data: selectedTask } = useQuery('todo_task', {
292-
filters: ['id', '=', selectedId],
296+
where: { id: selectedId },
293297
enabled: !!selectedId // Only fetch when ID is selected
294298
});
295-
299+
296300
return (
297301
<div className="flex">
298-
<TaskListPanel tasks={tasks?.value} onSelect={setSelectedId} />
299-
<TaskDetail task={selectedTask?.value?.[0]} />
302+
<TaskListPanel tasks={tasks?.records} onSelect={setSelectedId} />
303+
<TaskDetail task={selectedTask?.records?.[0]} />
300304
</div>
301305
);
302306
}
@@ -305,26 +309,31 @@ function TaskList() {
305309
### Optimistic Updates
306310

307311
```tsx
308-
function TaskToggle({ taskId, completed }) {
312+
import { useState } from 'react';
313+
import { useMutation } from '@objectstack/client-react';
314+
315+
function TaskToggle({ taskId, completed }: { taskId: string; completed: boolean }) {
316+
// `useMutation` has no mutation-context hook, so the optimistic value is held
317+
// locally and rolled back from `onError`.
318+
const [checked, setChecked] = useState(completed);
319+
309320
const { mutate } = useMutation('todo_task', 'update', {
310-
onMutate: async (variables) => {
311-
// Optimistically update UI
312-
return { previousValue: completed };
313-
},
314-
onError: (error, variables, context) => {
315-
// Revert on error
316-
console.error('Update failed, reverting', context.previousValue);
317-
},
318-
onSuccess: () => {
319-
// Refetch to ensure data consistency
320-
queryClient.invalidateQueries(['todo_task']);
321+
onError: (error: Error) => {
322+
console.error('Update failed, reverting', error);
323+
setChecked(completed);
321324
}
322325
});
323-
326+
324327
return (
325-
<Checkbox
326-
checked={completed}
327-
onChange={(e) => mutate({ id: taskId, is_completed: e.target.checked })}
328+
<input
329+
type="checkbox"
330+
checked={checked}
331+
onChange={(e) => {
332+
const next = e.target.checked;
333+
setChecked(next);
334+
// The `update` operation takes `{ id, data }`.
335+
mutate({ id: taskId, data: { is_completed: next } });
336+
}}
328337
/>
329338
);
330339
}
@@ -333,22 +342,24 @@ function TaskToggle({ taskId, completed }) {
333342
### Dependent Queries
334343

335344
```tsx
336-
function ProjectTasks({ projectId }) {
345+
import { useQuery } from '@objectstack/client-react';
346+
347+
function ProjectTasks({ projectId }: { projectId: string }) {
337348
// First, get project details
338349
const { data: project } = useQuery('project', {
339-
filters: ['id', '=', projectId]
350+
where: { id: projectId }
340351
});
341-
352+
342353
// Then, get tasks for this project
343354
const { data: tasks } = useQuery('todo_task', {
344-
filters: ['project_id', '=', projectId],
355+
where: { project_id: projectId },
345356
enabled: !!project // Only fetch when project is loaded
346357
});
347-
358+
348359
return (
349360
<div>
350-
<h2>{project?.value?.[0]?.name}</h2>
351-
<TaskList tasks={tasks?.value} />
361+
<h2>{project?.records?.[0]?.name}</h2>
362+
<TaskList tasks={tasks?.records} />
352363
</div>
353364
);
354365
}
@@ -357,14 +368,15 @@ function ProjectTasks({ projectId }) {
357368
### Search with Debounce
358369

359370
```tsx
360-
import { useDeferredValue } from 'react';
371+
import { useDeferredValue, useState } from 'react';
372+
import { useQuery } from '@objectstack/client-react';
361373

362374
function TaskSearch() {
363375
const [searchTerm, setSearchTerm] = useState('');
364376
const deferredSearch = useDeferredValue(searchTerm);
365377

366378
const { data, isLoading } = useQuery('todo_task', {
367-
filters: ['subject', 'contains', deferredSearch],
379+
where: { subject: { $contains: deferredSearch } },
368380
enabled: deferredSearch.length >= 3 // Only search with 3+ chars
369381
});
370382

@@ -377,7 +389,7 @@ function TaskSearch() {
377389
placeholder="Search tasks..."
378390
/>
379391
{isLoading && <Spinner />}
380-
<TaskList tasks={data?.value} />
392+
<TaskList tasks={data?.records} />
381393
</div>
382394
);
383395
}

‎packages/client/README.md‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ async function main() {
4646
// 2. Connect (Fetches system capabilities)
4747
await client.connect();
4848

49-
// 3. Metadata Access
50-
const todoSchema = await client.meta.getItem('object', 'todo_task');
49+
// 3. Metadata Access — the document is carried under `item`
50+
const { item } = await client.meta.getItem('object', 'todo_task');
51+
const todoSchema = item as { fields: Record<string, unknown> };
5152
console.log('Fields:', todoSchema.fields);
5253

5354
// Save Metadata (New Feature)
@@ -143,9 +144,12 @@ Batch operations support the following options:
143144
The client provides standardized error handling with machine-readable error codes:
144145

145146
```typescript
147+
import type { StandardError } from '@objectstack/client';
148+
146149
try {
147150
await client.data.create('todo_task', { subject: '' });
148-
} catch (error) {
151+
} catch (caught) {
152+
const error = caught as Error & Partial<StandardError>;
149153
console.error('Error code:', error.code); // e.g., 'validation_error'
150154
console.error('Category:', error.category); // e.g., 'validation'
151155
console.error('HTTP status:', error.httpStatus); // e.g., 400
@@ -288,7 +292,7 @@ const cubes = await client.analytics.meta('sales');
288292
console.log(cubes[0].name);
289293

290294
// Automation
291-
const run = await client.automation.trigger('send_welcome_email', { userId });
295+
const run = await client.automation.trigger('send_welcome_email', { userId: 'usr_123' });
292296
console.log(run.status);
293297

294298
// File Storage

‎packages/drivers/driver-memory/README.md‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ pnpm add @objectstack/driver-memory
1919

2020
```typescript
2121
import { ObjectKernel } from '@objectstack/core';
22-
import memoryPlugin from '@objectstack/driver-memory';
22+
import { DriverPlugin } from '@objectstack/runtime';
23+
import { InMemoryDriver } from '@objectstack/driver-memory';
2324

2425
const kernel = new ObjectKernel();
25-
kernel.use(memoryPlugin); // default plugin
26+
await kernel.use(new DriverPlugin(new InMemoryDriver(), 'memory'));
2627
await kernel.bootstrap();
2728
```
2829

@@ -41,7 +42,7 @@ await driver.connect();
4142
import { InMemoryDriver, FileSystemPersistenceAdapter } from '@objectstack/driver-memory';
4243

4344
const driver = new InMemoryDriver({
44-
persistence: new FileSystemPersistenceAdapter('./data/snapshot.json'),
45+
persistence: { adapter: new FileSystemPersistenceAdapter({ path: './data/snapshot.json' }) },
4546
});
4647
await driver.connect();
4748
```
@@ -52,15 +53,15 @@ await driver.connect();
5253
import { InMemoryDriver, LocalStoragePersistenceAdapter } from '@objectstack/driver-memory';
5354

5455
const driver = new InMemoryDriver({
55-
persistence: new LocalStoragePersistenceAdapter('objectstack:dev'),
56+
persistence: { adapter: new LocalStoragePersistenceAdapter({ key: 'objectstack:dev' }) },
5657
});
5758
```
5859

5960
## Key Exports
6061

6162
| Export | Kind | Description |
6263
|:---|:---|:---|
63-
| `default` | kernel plugin | Drop-in plugin. |
64+
| `default` | legacy plugin object | Legacy `onEnable` shape — not a kernel `Plugin`; register through `DriverPlugin`. |
6465
| `InMemoryDriver` | class | Driver instance for direct use. |
6566
| `InMemoryStrategy` | class | Query execution strategy used by ObjectQL. |
6667
| `FileSystemPersistenceAdapter` | class | Node-only persistence. |

‎packages/drivers/driver-mongodb/README.md‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,19 @@ explicit override (recognised values: `mongodb`, `mongo`).
3636

3737
```typescript
3838
import { defineStack } from '@objectstack/spec';
39+
import { DriverPlugin } from '@objectstack/runtime';
3940
import { MongoDBDriver } from '@objectstack/driver-mongodb';
4041

4142
export default defineStack({
42-
driver: new MongoDBDriver({
43-
url: 'mongodb://localhost:27017/myapp',
44-
database: 'myapp', // Optional: overrides URI database
45-
maxPoolSize: 10, // Optional: connection pool size (default: 10)
46-
minPoolSize: 1, // Optional: minimum pool (default: 1)
47-
connectTimeoutMS: 10000, // Optional: connection timeout
48-
}),
43+
plugins: [
44+
new DriverPlugin(new MongoDBDriver({
45+
url: 'mongodb://localhost:27017/myapp',
46+
database: 'myapp', // Optional: overrides URI database
47+
maxPoolSize: 10, // Optional: connection pool size (default: 10)
48+
minPoolSize: 1, // Optional: minimum pool (default: 1)
49+
connectTimeoutMS: 10000, // Optional: connection timeout
50+
}), 'mongodb'),
51+
],
4952
});
5053
```
5154

0 commit comments

Comments
 (0)