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
2 changes: 1 addition & 1 deletion .agents/skills/verify-interface/expected-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const STAGED = {
"after": {
"query": [
true,
true,
false,
true
],
"iterator": true,
Expand Down
18 changes: 4 additions & 14 deletions .agents/skills/verify-interface/expected-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,30 +113,20 @@ export const EXPECTED = {
},
"query-execution": {
"lazy": 0,
"viaAwait": [
{
"title": "A"
}
],
"awaitedIsQuery": true,
"awaitRuns": 0,
"viaRun": [
{
"title": "A"
}
],
"viaThen": [
{
"title": "A"
}
],
"viaThenChain": 1,
"runs": 7,
"runs": 3,
"sameStages": true,
"rejected": "Error: provider refused",
"caught": "handled:provider refused",
"onRejected": "caught:provider refused",
"methods": [
"function",
"function",
"undefined",
"function"
],
"iterator": "function"
Expand Down
6 changes: 3 additions & 3 deletions .agents/skills/verify-interface/features/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ One file per feature. `covers:` lists the source globs it owns, `docs:` the page
| [expressions-dates](expressions-dates.md) | Inside a query callback a date is tested against a range, broken into calendar parts in an optional timezone, or turned into epoch seconds. | expression-dates |
| [expressions-arrays](expressions-arrays.md) | Inside a query callback a list is indexed, searched, sliced, mapped, filtered, counted and reduced. | expression-arrays |
| [atomic-requests](atomic-requests.md) | A module asks for one conditional change of one record: update or delete under a revision, or delete when one field still equals what was observed. | atomic-requests |
| [atomic-execution](atomic-execution.md) | Awaiting an atomic mutation resolves to `applied`, `not-applied` or `unknown`; any other provider answer is an `AtomicMutationUnsupportedError`; cursors are refused; nothing is retried. | atomic-execution |
| [execution](execution.md) | A query is lazy; `await`, `run()` and `then()` run it once, `cursor()` and `for await` read it one result at a time and close the cursor when the consumer stops. | query-execution, query-cursor |
| [atomic-execution](atomic-execution.md) | Running an atomic mutation resolves to `applied`, `not-applied` or `unknown`; any other provider answer is an `AtomicMutationUnsupportedError`; cursors are refused; nothing is retried. | atomic-execution |
| [execution](execution.md) | A query is lazy; `run()` runs it once and `await` alone runs nothing, `cursor()` and `for await` read it one result at a time and close the cursor when the consumer stops. | query-execution, query-cursor |
| [duplicate-key](duplicate-key.md) | A write that repeats a unique key rejects with a `DuplicateKeyError` naming the index and its fields; `IsDuplicateKeyError` also recognises the error of another copy of the interface. | duplicate-key-error |
| [kinds](kinds.md) | `IsQuery`, `IsStream`, `IsDatum` and `IsValueProxy` tell what a value is, including a value built by another copy of the interface, which `instanceof` cannot. | query-kinds |
| [staged-query](staged-query.md) | A build tool imports `@antelopejs/interface-database/staged-query`, builds queries and reads `build()`; the entry point never loads `interface-core` or `node:async_hooks`, its queries cannot run, and the root entry later gives the same classes `run`, `then` and `cursor`. | staged-isolation |
| [staged-query](staged-query.md) | A build tool imports `@antelopejs/interface-database/staged-query`, builds queries and reads `build()`; the entry point never loads `interface-core` or `node:async_hooks`, its queries cannot run, and the root entry later gives the same classes `run` and `cursor`. | staged-isolation |
| [entry-points](entry-points.md) | A module imports from the root entry or from one of the subpaths the exports map lists; `typesVersions` lists the same subpaths for older resolvers. | subpaths |
| [types](types.md) | A TypeScript module sees the documented result type of each call, the table names of its schema, and the values `getAll`, `between`, `insert` and `atomicMutation` accept and refuse. | usage-types |
4 changes: 2 additions & 2 deletions .agents/skills/verify-interface/features/atomic-execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ journeys:

# Atomic execution

Awaiting an atomic mutation resolves to `applied`, `not-applied` or `unknown`; any other provider answer is an `AtomicMutationUnsupportedError`; cursors are refused; nothing is retried.
Running an atomic mutation resolves to `applied`, `not-applied` or `unknown`; any other provider answer is an `AtomicMutationUnsupportedError`; cursors are refused; nothing is retried.

## Sub-features

- `atomic-execution`: an atomic mutation resolves to applied, not-applied or unknown, refuses any other answer, and is never retried.

## How to get to it (user POV)

- `const outcome = await table.atomicMutation(id, request)`.
- `const outcome = await table.atomicMutation(id, request).run()`.

## Driving it with antelopejs-verify

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ A module asks for one conditional change of one record: update or delete under a

## How to get to it (user POV)

- `await table.atomicMutation(id, { type: "delete", revisionField: "revision", expectedRevision: observed })`.
- `await table.atomicMutation(id, { type: "delete", revisionField: "revision", expectedRevision: observed }).run()`.

## Driving it with antelopejs-verify

Expand Down
4 changes: 2 additions & 2 deletions .agents/skills/verify-interface/features/datums.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ A single document or value is read, a field is picked, defaulted, transformed wi
## Sub-features

- `datum-stages`: key, default, do, lookup, pluck and cast on one document record docs/1.query_types/8.datum.md.
- `datum-run`: awaiting a document or one of its fields returns the provider's answer, null included.
- `datum-run`: running a document or one of its fields returns the provider's answer, null included.

## How to get to it (user POV)

- `await users.get("u").key("address").key("city")`.
- `await users.get("u").key("address").key("city").run()`.

## Driving it with antelopejs-verify

Expand Down
4 changes: 2 additions & 2 deletions .agents/skills/verify-interface/features/execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ journeys:

# Execution

A query is lazy; `await`, `run()` and `then()` run it once, `cursor()` and `for await` read it one result at a time and close the cursor when the consumer stops.
A query is lazy; `run()` runs it once and `await` alone runs nothing, `cursor()` and `for await` read it one result at a time and close the cursor when the consumer stops.

## Sub-features

- `query-execution`: await, run and then run a query once each, lazily, and pass the provider's failure on.
- `query-execution`: run runs a query once, lazily, and passes the provider's failure on; awaiting a query runs nothing.
- `query-cursor`: cursor and for await read the provider one result at a time and close the cursor when the consumer stops.

## How to get to it (user POV)
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/verify-interface/features/joins.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ A stream is joined to another (left or inner), looked up through a foreign key,

## How to get to it (user POV)

- `await users.joinInner(orders, (u, o) => o.key("userId").eq(u.key("_id")), (u, o) => ({ amount: o.key("amount") }))`.
- `await users.joinInner(orders, (u, o) => o.key("userId").eq(u.key("_id")), (u, o) => ({ amount: o.key("amount") })).run()`.

## Driving it with antelopejs-verify

Expand Down
4 changes: 2 additions & 2 deletions .agents/skills/verify-interface/features/schemas.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ A module defines a `Schema`, which registers with the bound provider, then picks

- `schema-registry`: a Schema registers with the provider, and Schema.get and Schema.list find it.
- `schema-stages`: instance, createInstance, destroyInstance and listInstances record the stages docs/3.results/5.database_changes.md names.
- `schema-run`: awaiting a schema call runs it once, and building one runs nothing.
- `schema-run`: running a schema call runs it once, and building one runs nothing.

## How to get to it (user POV)

- `const schema = new Schema("myapp", { users: { fields: {}, indexes: {} } }); await schema.createInstance("tenant-1");`
- `const schema = new Schema("myapp", { users: { fields: {}, indexes: {} } }); await schema.createInstance("tenant-1").run();`

## Driving it with antelopejs-verify

Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/verify-interface/features/staged-query.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ journeys:

# Staged query

A build tool imports `@antelopejs/interface-database/staged-query`, builds queries and reads `build()`; the entry point never loads `interface-core` or `node:async_hooks`, its queries cannot run, and the root entry later gives the same classes `run`, `then` and `cursor`.
A build tool imports `@antelopejs/interface-database/staged-query`, builds queries and reads `build()`; the entry point never loads `interface-core` or `node:async_hooks`, its queries cannot run, and the root entry later gives the same classes `run` and `cursor`.

## Sub-features

Expand Down
4 changes: 2 additions & 2 deletions .agents/skills/verify-interface/features/streams.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ A stream is mapped, filtered, plucked, ordered, sliced and aggregated. This feat
- `stream-shape`: key, default, map, filter, pluck, without and union record their stages in order.
- `stream-order`: orderBy, slice and nth record index, direction and bounds, and never change the stream they extend.
- `stream-aggregates`: count, sum, avg, min, max, distinct and changes record the field they work on.
- `stream-run`: awaiting a stream hands the provider the stages and returns its answer.
- `stream-run`: running a stream hands the provider the stages and returns its answer.

## How to get to it (user POV)

- `await users.filter((u) => u.key("age").ge(18)).orderBy("name").slice(0, 10)`.
- `await users.filter((u) => u.key("age").ge(18)).orderBy("name").slice(0, 10).run()`.

## Driving it with antelopejs-verify

Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/verify-interface/features/tables-read.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ A table is read whole, by primary key (`get`), by secondary index (`getAll`) or

## How to get to it (user POV)

- `await users.getAll("admin", "role")` and `await users.between("createdAt", from, to)`.
- `await users.getAll("admin", "role").run()` and `await users.between("createdAt", from, to).run()`.

## Driving it with antelopejs-verify

Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/verify-interface/features/tables-write.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ A module inserts (with `conflict: "update"` or `"replace"`), updates with a part

## How to get to it (user POV)

- `await users.insert({ _id: "u" }, { conflict: "update" })` and `await users.get("u").delete()`.
- `await users.insert({ _id: "u" }, { conflict: "update" }).run()` and `await users.get("u").delete().run()`.

## Driving it with antelopejs-verify

Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/verify-interface/features/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ A TypeScript module sees the documented result type of each call, the table name

## How to get to it (user POV)

- `const ids: string[] = await users.insert({ name: "A" })`.
- `const ids: string[] = await users.insert({ name: "A" }).run()`.

## Driving it with antelopejs-verify

Expand Down
49 changes: 25 additions & 24 deletions .agents/skills/verify-interface/probes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,30 @@ import type * as QueryApi from "$REPO/dist/query";
interface User { _id: string; name: string; age: number; createdAt: Date; tags: string[]; address: { city: string } }
declare const schema: Api.Schema<{ users: User; orders: { _id: string; total: number } }>;
const users = schema.instance().table("users");
type Await<Q> = Q extends PromiseLike<infer R> ? R : never;
type Result<Q extends { run(): unknown }> = Awaited<ReturnType<Q["run"]>>;
type Instance = ReturnType<typeof schema.instance>;
async function usage() {
const ids: string[] = await users.insert({ name: "A" }, { conflict: "update" });
const manyIds: string[] = await users.insert([{ name: "A" }, { name: "B" }]);
const user: User = await users.get("id");
const all: User[] = await users;
const some: User[] = await users.getAll(["a", "b"], "name");
const range: User[] = await users.between("createdAt", new Date(0), new Date(1));
const compound: User[] = await users.between("tags", ["a", "b"], ["c", "d"]);
const total: number = await users.count();
const names: string[] = await users.key("name");
const cities: string[] = await users.map((u) => u.key("address").key("city"));
const sorted: User[] = await users.orderBy("name", "desc").slice(0, 10);
const first: User | null = await users.orderBy("name").nth(0);
const updated: number = await users.get("id").update({ age: 1 });
const computed: number = await users.getAll("a", "name").update((u) => ({ age: u.key("age").add(1) }));
const replaced: number = await users.get("id").replace({ _id: "id", name: "N" });
const removed: number = await users.filter((u) => u.key("age").lt(5)).delete();
const outcome: "applied" | "not-applied" | "unknown" = await users.atomicMutation("id", { type: "delete", revisionField: "name", expectedRevision: "r1" });
const instanceId: string = await schema.createInstance("tenant");
const destroyed: void = await schema.destroyInstance("tenant");
const listed: string[] = await schema.listInstances();
const feed: Common.Changes<User>[] = await users.changes();
const ids: string[] = await users.insert({ name: "A" }, { conflict: "update" }).run();
const manyIds: string[] = await users.insert([{ name: "A" }, { name: "B" }]).run();
const user: User = await users.get("id").run();
const all: User[] = await users.run();
const some: User[] = await users.getAll(["a", "b"], "name").run();
const range: User[] = await users.between("createdAt", new Date(0), new Date(1)).run();
const compound: User[] = await users.between("tags", ["a", "b"], ["c", "d"]).run();
const total: number = await users.count().run();
const names: string[] = await users.key("name").run();
const cities: string[] = await users.map((u) => u.key("address").key("city")).run();
const sorted: User[] = await users.orderBy("name", "desc").slice(0, 10).run();
const first: User | null = await users.orderBy("name").nth(0).run();
const updated: number = await users.get("id").update({ age: 1 }).run();
const computed: number = await users.getAll("a", "name").update((u) => ({ age: u.key("age").add(1) })).run();
const replaced: number = await users.get("id").replace({ _id: "id", name: "N" }).run();
const removed: number = await users.filter((u) => u.key("age").lt(5)).delete().run();
const outcome: "applied" | "not-applied" | "unknown" = await users.atomicMutation("id", { type: "delete", revisionField: "name", expectedRevision: "r1" }).run();
const instanceId: string = await schema.createInstance("tenant").run();
const destroyed: void = await schema.destroyInstance("tenant").run();
const listed: string[] = await schema.listInstances().run();
const feed: Common.Changes<User>[] = await users.changes().run();
for await (const each of users) { const typed: User = each; void typed; }
for await (const change of users.get("id").changes()) { const kind: "added" | "removed" | "modified" = change.changeType; void kind; }
void [ids, manyIds, user, all, some, range, compound, total, names, cities, sorted, first, updated, computed, replaced, removed, outcome, instanceId, destroyed, listed, feed];
Expand All @@ -51,6 +51,7 @@ type _BoundDate = Assert<Accepts<Common.IndexBound, Date>>;
type _BoundNull = Assert<Accepts<Common.IndexBound, null>>;
type _BoundList = Assert<Accepts<Common.IndexBound, (string | number | Date | null)[]>>;
type _NoBoundObject = Assert<Not<Accepts<Common.IndexBound, { a: 1 }>>>;
type _Reads = Assert<Same<Await<ReturnType<typeof users.get>>, User>>;
type _ExecutionMembers = Assert<Same<keyof QueryApi.QueryExecution<number>, "run" | "then" | "cursor" | typeof Symbol.asyncIterator>>;
type _Reads = Assert<Same<Result<ReturnType<typeof users.get>>, User>>;
type _ExecutionMembers = Assert<Same<keyof QueryApi.QueryExecution<number>, "run" | "cursor" | typeof Symbol.asyncIterator>>;
type _NotThenable = Assert<Not<Accepts<PromiseLike<unknown>, typeof users>>>;
`;
20 changes: 9 additions & 11 deletions .agents/skills/verify-interface/scenarios-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,24 +80,24 @@ export const ATOMIC_BODIES = {
const outcomes = {};
for (const reply of ["applied", "not-applied", "unknown"]) {
answer = reply;
outcomes[reply] = await table.atomicMutation("n-1", drop);
outcomes[reply] = await table.atomicMutation("n-1", drop).run();
}
const bad = {};
for (const [label, reply] of [["undefined", undefined], ["zero", 0], ["count", 1], ["null", null], ["text", "done"], ["object", { applied: true }]]) {
answer = reply;
bad[label] = await fails(() => table.atomicMutation("n-1", drop));
bad[label] = await fails(() => table.atomicMutation("n-1", drop).run());
}
answer = "applied";
const viaRun = await table.atomicMutation("n-1", drop).run();
const cursor = await fails(() => table.atomicMutation("n-1", drop).cursor());
const iterate = await fails(async () => { for await (const item of table.atomicMutation("n-1", drop)) return item; });
seen.runs.length = 0;
answer = new Error("link lost");
const lost = await fails(() => table.atomicMutation("n-1", drop));
const lost = await fails(() => table.atomicMutation("n-1", drop).run());
const callsAfterLoss = seen.runs.length;
answer = "applied";
seen.runs.length = 0;
await table.atomicMutation("n-1", drop);
await table.atomicMutation("n-1", drop).run();
const terminal = seen.runs[0];
return {
outcomes, bad, viaRun, cursor, iterate, lost, callsAfterLoss,
Expand All @@ -107,22 +107,20 @@ export const ATOMIC_BODIES = {
`,
} satisfies { [id: string]: string };

/** Query execution: await, run, then, cursor and async iteration reach the provider as documented. */
/** Query execution: run, cursor and async iteration reach the provider as documented; awaiting a query runs nothing. */
export const EXECUTION_BODIES = {
"query-execution": `${NOTES}
answer = [{ title: "A" }];
const built = table.filter((n) => n.key("title").eq("A"));
const lazy = seen.runs.length;
const viaAwait = await built;
const awaited = await built;
const awaitRuns = seen.runs.length;
const viaRun = await built.run();
const viaThen = await new Promise((resolve) => built.then(resolve));
const viaThenChain = await built.then((rows) => rows.length);
const sameStages = seen.runs.every((stages) => JSON.stringify(show(stages)) === JSON.stringify(show(built)));
answer = new Error("provider refused");
const rejected = await fails(() => built);
const caught = await built.then(undefined, (error) => "handled:" + error.message);
const rejected = await fails(() => built.run());
const onRejected = await built.run().catch((error) => "caught:" + error.message);
return { lazy, viaAwait, viaRun, viaThen, viaThenChain, runs: seen.runs.length, sameStages, rejected, caught, onRejected, methods: ["run", "then", "cursor"].map((name) => typeof built[name]), iterator: typeof built[Symbol.asyncIterator] };
return { lazy, awaitedIsQuery: awaited === built, awaitRuns, viaRun, runs: seen.runs.length, sameStages, rejected, onRejected, methods: ["run", "then", "cursor"].map((name) => typeof built[name]), iterator: typeof built[Symbol.asyncIterator] };
`,
"query-cursor": `${NOTES}
readAnswers = [{ done: false, value: { n: 1 } }, { done: false, value: { n: 2 } }, { done: true, value: undefined }];
Expand Down
10 changes: 5 additions & 5 deletions .agents/skills/verify-interface/scenarios-streams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ export const STREAM_BODIES = {
`,
"stream-run": `${STORE}
answer = [{ _id: "u1" }, { _id: "u2" }];
const direct = await users.orderBy("name");
const direct = await users.orderBy("name").run();
answer = 7;
const counted = await users.count();
const counted = await users.count().run();
return { direct, counted, runs: seen.runs.map((stages) => show(stages).slice(3)) };
`,
} satisfies { [id: string]: string };
Expand Down Expand Up @@ -98,11 +98,11 @@ export const DATUM_BODIES = {
`,
"datum-run": `${STORE}
answer = { _id: "user-123", name: "Ada" };
const doc = await users.get("user-123");
const doc = await users.get("user-123").run();
answer = "Ada";
const name = await users.get("user-123").key("name");
const name = await users.get("user-123").key("name").run();
answer = null;
const missing = await users.get("nobody");
const missing = await users.get("nobody").run();
return { doc, name, missing, runs: seen.runs.map((stages) => show(stages).slice(3)) };
`,
} satisfies { [id: string]: string };
Loading
Loading