Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .changeset/runtime-readme-unread-call-site-audit.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
---
"@objectstack/runtime": patch
---

Repair six false API claims in the published `@objectstack/runtime` README
(#10368). The README is in the package's `files` array, so it is the page npm
renders — a reader following it wrote code that could not compile.

Found by hand-adjudicating every call site in that document that
`check:published-readme-exports` reports under `NOT read:` — receivers built
from free variables, parameters and globals, which neither the gate nor a human
reader can type by looking. 30 sites on 17 receivers were read; the repairs below
are what came out.

- `engine.update('user', user.id, { name: 'Jane' })` → `engine.update('user',
{ id: user.id, name: 'Jane' })`. `IDataEngine.update` is
`(objectName, data, options?)`; there is no `id` parameter. A by-id update is
identified by a truthy scalar `data.id` (or `options.where.id`) — the rule
`resolveEngineUpdateDispatch` in `@objectstack/metadata-core` defines.
- `engine.delete('user', user.id)` → `engine.delete('user', { where: { id: user.id } })`.
`IDataEngine.delete` is `(objectName, options?)`; the id belongs in
`options.where.id` (`assertEngineDeleteDispatch`). Passing it positionally
landed the id in the options bag.
- The **Interface Methods** bullet list restated both wrong signatures, so it is
corrected in the same edit — a repaired example beside a bullet list that still
contradicts it is not a repair.
- `reply.code(429).send({ retryAfterMs })` in the rate-limiting recipe →
`res.status(429).json({ retryAfterMs })`. `reply.code()` is Fastify; this
package's HTTP contract is `IHttpResponse`, which spells the step
`status(code)` and whose `send` takes `string | Uint8Array | ArrayBuffer`, not
an object. The `docs/HARDENING.md` recipe the same section links to already
answers 429 through the framework's own JSON responder.
- `status: res.statusCode` in the middleware example → dropped.
`IHttpResponse` has no `statusCode`; a response's status is observed through
`IHttpServer.afterResponse` (`HttpResponseObservation.status`), not read off
the response inside middleware.
- The `PluginContext` interface block declared `logger: Console` and
`getKernel?(): any`. The real contract (`@objectstack/core`) is
`logger: Logger` and a required `getKernel(): ObjectKernel`.

Documentation only — no runtime, type or export change.
17 changes: 9 additions & 8 deletions packages/runtime/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -203,17 +203,19 @@ class MyBusinessPlugin implements Plugin {
// CRUD operations - works with any data layer
const user = await engine.insert('user', { name: 'John' });
const users = await engine.find('user', { filter: { active: true } });
await engine.update('user', user.id, { name: 'Jane' });
await engine.delete('user', user.id);
await engine.update('user', { id: user.id, name: 'Jane' });
await engine.delete('user', { where: { id: user.id } });
}
}
```

**Interface Methods:**
- `insert(objectName, data)` - Create a record
- `find(objectName, query?)` - Query records
- `update(objectName, id, data)` - Update a record
- `delete(objectName, id)` - Delete a record
- `update(objectName, data, options?)` - Update a record (one row when `data.id` is a
truthy scalar, or `options.where.id` is; `options.multi` for a bulk update)
- `delete(objectName, options?)` - Delete a record (`options.where.id` for one row,
`options.multi` for a bulk delete)

### ObjectKernel

Expand DownExpand Up@@ -247,8 +249,8 @@ interface PluginContext {
getService<T>(name: string): T;
hook(name: string, handler: Function): void;
trigger(name: string, ...args: any[]): Promise<void>;
logger: Console;
getKernel?(): any;
logger: Logger;
getKernel(): ObjectKernel;
}
```

Expand DownExpand Up@@ -481,7 +483,6 @@ export class LoggingMiddleware implements Plugin {
ctx.logger.info('Response', {
method: req.method,
path: req.path,
status: res.statusCode,
duration
});
});
Expand DownExpand Up@@ -607,7 +608,7 @@ import { RateLimiter, DEFAULT_RATE_LIMITS } from '@objectstack/runtime';

const limiter = new RateLimiter(DEFAULT_RATE_LIMITS.auth);
const decision = limiter.consume(`ip:${ip}`);
if (!decision.allowed) reply.code(429).send({ retryAfterMs: decision.retryAfterMs });
if (!decision.allowed) res.status(429).json({ retryAfterMs: decision.retryAfterMs });
```

### Observability (opt-in adapters)
Expand Down
Loading