Skip to content

Commit bbc20f3

Browse files
claude[bot]claude
andcommitted
docs(readme): make the published README TypeScript examples compile
Splits PR #18751's census by publication (a block is published when its document is inside its package's `files[]`) and corrects the published half: 43 of 44 syntactically-valid-and-wrong blocks across 20 package READMEs. Internal documents (ADVANCED_FEATURES.md, PHASE2_IMPLEMENTATION.md, V3_MIGRATION_GUIDE.md, ARCHITECTURE.md, …) are untouched, and no gate, ratchet or CI wiring is added — #18715 ruling F. Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk Co-authored-by: Claude <noreply@anthropic.com>
1 parent 16cb493 commit bbc20f3

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)