@astrale-os/sdk 0.6.0-beta.41 → 0.6.0-beta.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -2
- package/dist/application/action/context.d.ts +49 -18
- package/dist/application/action/index.d.ts +1 -1
- package/dist/application/error/error.d.ts +16 -1
- package/dist/application/error/error.js +39 -5
- package/dist/application/error/index.d.ts +1 -1
- package/dist/application/error/index.js +1 -1
- package/dist/application/mutation/authoring/builder.d.ts +81 -33
- package/dist/application/mutation/authoring/builder.js +100 -30
- package/dist/application/mutation/authoring/guard.d.ts +37 -0
- package/dist/application/mutation/authoring/guard.js +150 -0
- package/dist/application/mutation/authoring/index.d.ts +5 -2
- package/dist/application/mutation/authoring/index.js +2 -1
- package/dist/application/mutation/authoring/names.d.ts +19 -0
- package/dist/application/mutation/authoring/names.js +83 -0
- package/dist/application/mutation/authoring/transition.d.ts +3 -2
- package/dist/application/mutation/define.d.ts +24 -10
- package/dist/application/mutation/define.js +49 -53
- package/dist/application/mutation/execution/execute.d.ts +2 -2
- package/dist/application/mutation/execution/execute.js +78 -7
- package/dist/application/mutation/execution/executor.d.ts +4 -1
- package/dist/application/mutation/execution/executor.js +6 -1
- package/dist/application/mutation/failure.d.ts +11 -1
- package/dist/application/mutation/failure.js +18 -3
- package/dist/application/mutation/index.d.ts +8 -4
- package/dist/application/mutation/index.js +5 -2
- package/dist/application/mutation/legacy/v6/builder.d.ts +84 -0
- package/dist/application/mutation/legacy/v6/define.d.ts +18 -0
- package/dist/application/mutation/legacy/v6/define.js +56 -0
- package/dist/application/mutation/legacy/v6/mutation.d.ts +28 -0
- package/dist/application/mutation/legacy/v6/mutation.js +1 -0
- package/dist/application/mutation/{authoring → legacy/v6}/precondition.d.ts +4 -0
- package/dist/application/mutation/mutation.d.ts +30 -19
- package/dist/application/query/answer/answer.d.ts +117 -0
- package/dist/application/query/answer/answer.js +1 -0
- package/dist/application/query/answer/conformance.d.ts +4 -0
- package/dist/application/query/answer/conformance.js +27 -0
- package/dist/application/query/answer/facts.d.ts +17 -0
- package/dist/application/query/answer/facts.js +57 -0
- package/dist/application/query/composite/builder.d.ts +1 -1
- package/dist/application/query/composite/builder.js +1 -1
- package/dist/application/query/composite/plan.d.ts +1 -1
- package/dist/application/query/composite/plan.js +1 -1
- package/dist/application/query/composite/query.d.ts +1 -2
- package/dist/application/query/define.d.ts +2 -33
- package/dist/application/query/define.js +3 -130
- package/dist/application/query/definition.d.ts +12 -0
- package/dist/application/query/definition.js +47 -0
- package/dist/application/query/execution/bounds.d.ts +22 -0
- package/dist/application/query/execution/bounds.js +88 -0
- package/dist/application/query/execution/crossings.d.ts +25 -0
- package/dist/application/query/execution/crossings.js +192 -0
- package/dist/application/query/execution/error.d.ts +68 -0
- package/dist/application/query/execution/error.js +50 -0
- package/dist/application/query/execution/execute.d.ts +2 -7
- package/dist/application/query/execution/execute.js +3 -51
- package/dist/application/query/execution/recipe.d.ts +85 -0
- package/dist/application/query/execution/recipe.js +324 -0
- package/dist/application/query/execution/refusal.d.ts +37 -0
- package/dist/application/query/execution/refusal.js +80 -0
- package/dist/application/query/execution/rows.d.ts +42 -0
- package/dist/application/query/execution/rows.js +308 -0
- package/dist/application/query/execution/split.d.ts +24 -0
- package/dist/application/query/execution/split.js +190 -0
- package/dist/application/query/handles/build.d.ts +10 -0
- package/dist/application/query/handles/build.js +53 -0
- package/dist/application/query/handles/conditions.d.ts +94 -0
- package/dist/application/query/handles/conditions.js +1 -0
- package/dist/application/query/handles/domain.d.ts +157 -0
- package/dist/application/query/handles/domain.js +1 -0
- package/dist/application/query/handles/handles.d.ts +268 -0
- package/dist/application/query/handles/handles.js +1 -0
- package/dist/application/query/handles/model.d.ts +96 -0
- package/dist/application/query/handles/model.js +246 -0
- package/dist/application/query/handles/plan.d.ts +99 -0
- package/dist/application/query/handles/plan.js +37 -0
- package/dist/application/query/handles/reads.d.ts +106 -0
- package/dist/application/query/handles/reads.js +839 -0
- package/dist/application/query/handles/relations.d.ts +100 -0
- package/dist/application/query/handles/relations.js +1 -0
- package/dist/application/query/handles/rows.d.ts +94 -0
- package/dist/application/query/handles/rows.js +4 -0
- package/dist/application/query/handles/terms.d.ts +103 -0
- package/dist/application/query/handles/terms.js +282 -0
- package/dist/application/query/index.d.ts +10 -7
- package/dist/application/query/index.js +9 -4
- package/dist/application/query/language/index.d.ts +2 -0
- package/dist/application/query/language/index.js +2 -0
- package/dist/application/query/language/query.d.ts +323 -0
- package/dist/application/query/language/query.js +2 -0
- package/dist/application/query/{collection → legacy/v6/collection}/identity.d.ts +2 -2
- package/dist/application/query/{collection → legacy/v6/collection}/metadata.d.ts +1 -1
- package/dist/application/query/{collection → legacy/v6/collection}/union.d.ts +3 -3
- package/dist/application/query/{collection → legacy/v6/collection}/union.js +1 -1
- package/dist/application/query/{conformance.d.ts → legacy/v6/conformance.d.ts} +3 -3
- package/dist/application/query/{conformance.js → legacy/v6/conformance.js} +1 -1
- package/dist/application/query/legacy/v6/define.d.ts +37 -0
- package/dist/application/query/legacy/v6/define.js +90 -0
- package/dist/application/query/legacy/v6/execution/execute.d.ts +16 -0
- package/dist/application/query/legacy/v6/execution/execute.js +50 -0
- package/dist/application/query/legacy/v6/index.d.ts +6 -0
- package/dist/application/query/legacy/v6/index.js +3 -0
- package/dist/application/query/legacy/v6/kernel.d.ts +6 -0
- package/dist/application/query/legacy/v6/kernel.js +5 -0
- package/dist/application/query/{single → legacy/v6/single}/pagination.js +1 -1
- package/dist/application/query/{single → legacy/v6/single}/query.d.ts +2 -14
- package/dist/application/query/legacy/v6/single/query.js +1 -0
- package/dist/application/query/query.d.ts +16 -1
- package/dist/application/query/recipe/define.d.ts +44 -0
- package/dist/application/query/recipe/define.js +95 -0
- package/dist/application/query/recipe/recipe.d.ts +134 -0
- package/dist/application/query/recipe/recipe.js +1 -0
- package/dist/application/workflow/durable.d.ts +22 -8
- package/dist/deployment/build/build.d.ts +8 -1
- package/dist/deployment/build/material.js +3 -0
- package/dist/execution/invocation/capabilities/bind.d.ts +6 -4
- package/dist/execution/invocation/capabilities/bind.js +39 -3
- package/dist/execution/invocation/dispatch/action.js +4 -2
- package/dist/execution/invocation/dispatch/context.js +1 -1
- package/dist/execution/invocation/dispatch/workflow.js +4 -2
- package/dist/execution/invocation/result/index.d.ts +1 -0
- package/dist/execution/invocation/result/index.js +1 -0
- package/dist/execution/invocation/result/recipe.d.ts +6 -0
- package/dist/execution/invocation/result/recipe.js +17 -0
- package/dist/execution/service/create.js +1 -0
- package/dist/execution/workflows/failure.d.ts +23 -6
- package/dist/execution/workflows/failure.js +78 -18
- package/dist/execution/workflows/receiver.d.ts +3 -0
- package/dist/execution/workflows/receiver.js +1 -0
- package/dist/execution/workflows/run.js +13 -9
- package/dist/execution/workflows/steps.d.ts +3 -0
- package/dist/execution/workflows/steps.js +2 -2
- package/dist/platform/schema/kernel-compiled.d.ts +2 -0
- package/dist/platform/schema/kernel-compiled.js +2 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +1 -0
- package/dist/testing/recipe.d.ts +17 -0
- package/dist/testing/recipe.js +34 -0
- package/dist/tooling/cli/orchestrate.js +6 -2
- package/dist/tooling/linter/adapters/typescript/index.d.ts +2 -1
- package/dist/tooling/linter/adapters/typescript/index.js +2 -1
- package/dist/tooling/linter/adapters/typescript/query-observation.js +37 -12
- package/dist/tooling/linter/adapters/typescript/recipe-definitions.d.ts +34 -0
- package/dist/tooling/linter/adapters/typescript/recipe-definitions.js +88 -0
- package/dist/tooling/linter/adapters/typescript/type-analysis.d.ts +2 -0
- package/dist/tooling/linter/adapters/typescript/type-analysis.js +10 -0
- package/dist/tooling/linter/implementations/source/global.js +15 -9
- package/dist/tooling/linter/implementations/source/mutations.js +128 -19
- package/dist/tooling/linter/implementations/source/policy-edges.d.ts +2 -0
- package/dist/tooling/linter/implementations/source/policy-edges.js +113 -0
- package/dist/tooling/linter/implementations/source/recipes.d.ts +2 -0
- package/dist/tooling/linter/implementations/source/recipes.js +425 -0
- package/dist/tooling/linter/implementations/source/shared.d.ts +7 -1
- package/dist/tooling/linter/implementations/source/shared.js +31 -10
- package/dist/tooling/linter/implementations/source/verifiers.js +4 -0
- package/dist/tooling/linter/policy/generated.js +2 -2
- package/dist/tooling/linter/requirements/registry.js +4 -0
- package/docs/durable-workflows.md +48 -20
- package/docs/handler-dependencies.md +76 -4
- package/docs/legacy/query-v6.md +62 -0
- package/docs/queries.md +442 -0
- package/package.json +11 -8
- /package/dist/application/{query/single/query.js → mutation/legacy/v6/builder.js} +0 -0
- /package/dist/application/mutation/{authoring → legacy/v6}/precondition.js +0 -0
- /package/dist/application/query/{collection → legacy/v6/collection}/identity.js +0 -0
- /package/dist/application/query/{collection → legacy/v6/collection}/limits.d.ts +0 -0
- /package/dist/application/query/{collection → legacy/v6/collection}/limits.js +0 -0
- /package/dist/application/query/{collection → legacy/v6/collection}/metadata.js +0 -0
- /package/dist/application/query/{execution → legacy/v6/execution}/pages.d.ts +0 -0
- /package/dist/application/query/{execution → legacy/v6/execution}/pages.js +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/index.d.ts +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/index.js +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/node.d.ts +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/node.js +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/pagination.d.ts +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/selection.d.ts +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/selection.js +0 -0
package/docs/queries.md
ADDED
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
# Queries
|
|
2
|
+
|
|
3
|
+
A Query recipe is a typed business observation over the graph. Its guarantees:
|
|
4
|
+
|
|
5
|
+
- **Typed by the Domain.** Handles, conditions, rows and answers are typed from the Domain's
|
|
6
|
+
Schema. A Class, Property or relation the Domain cannot name does not compile.
|
|
7
|
+
- **Bounded before it reads.** Every verb carries its bound. Before anything is sent, the SDK
|
|
8
|
+
refuses a recipe past its bounds, and a relation that crosses a dependency Class the Domain does
|
|
9
|
+
not declare.
|
|
10
|
+
- **One snapshot per read.** Each read is one Kernel invocation at its own snapshot. Facets,
|
|
11
|
+
steps and pages of one recipe share none. A read whose root lists more than 32,768 ids in
|
|
12
|
+
`id.in` is the exception: the SDK sends it in consecutive parts, each at its own snapshot, and
|
|
13
|
+
merges their rows in the Kernel's order.
|
|
14
|
+
- **Authority-neutral.** A recipe names no authority; each call names the authority it reads
|
|
15
|
+
under.
|
|
16
|
+
- **One error per refusal.** A read the SDK or the Kernel refuses raises one `QueryError` by code.
|
|
17
|
+
A network failure, and a refusal that says when to retry, pass as they came. Business refusals
|
|
18
|
+
are the recipe's declared errors.
|
|
19
|
+
|
|
20
|
+
A recipe reads on a Host whose Kernel serves Query v7 and the Class of each row. A Kernel that
|
|
21
|
+
refuses Query v7 by name raises `QueryError` `UNSUPPORTED` with `feature: 'v7'`; a Runtime that
|
|
22
|
+
does not name that refusal answers `REFUSED`, with `kernelCode: 'INTERNAL_ERROR'`. An answer that
|
|
23
|
+
leaves out a row's Class raises `UNSUPPORTED` with `feature: 'classes'`. Query v7 is not served
|
|
24
|
+
in maintenance or repair sessions: the Kernel refuses it with `INSTANCE_MAINTENANCE`, which passes
|
|
25
|
+
as it came, and a migration that runs there reads with [Query V6](legacy/query-v6.md).
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import type { NodeId } from '@astrale-os/sdk/graph/node'
|
|
29
|
+
import { defineQuery } from '@astrale-os/sdk/query'
|
|
30
|
+
import { z } from 'zod'
|
|
31
|
+
|
|
32
|
+
import type { schema } from '#schema'
|
|
33
|
+
|
|
34
|
+
export const articleWithComments = defineQuery<typeof schema>()('publishing.article.with-comments', {
|
|
35
|
+
build: (article: NodeId, d) =>
|
|
36
|
+
d.Article.with((a) => ({
|
|
37
|
+
author: a.author.find(),
|
|
38
|
+
pinned: a
|
|
39
|
+
.via(d.comment_on, { direction: 'incoming' })
|
|
40
|
+
.where((_comment, edge) => edge.pinned.eq(true))
|
|
41
|
+
.orderBy((comment) => comment.postedAt.desc())
|
|
42
|
+
.first(3),
|
|
43
|
+
comments: a.via(d.comment_on, { direction: 'incoming' }).count({ upTo: 1_000 }),
|
|
44
|
+
})).find(article),
|
|
45
|
+
output: (row, article, { error }) => {
|
|
46
|
+
if (row === undefined) throw error('ARTICLE_NOT_FOUND', { article })
|
|
47
|
+
return row
|
|
48
|
+
},
|
|
49
|
+
errors: {
|
|
50
|
+
ARTICLE_NOT_FOUND: {
|
|
51
|
+
family: 'NOT_FOUND',
|
|
52
|
+
message: 'The article does not exist.',
|
|
53
|
+
details: z.strictObject({ article: z.string() }),
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// In an Action that declares `errors: { ...articleWithComments.errors }`
|
|
59
|
+
const article = await query(articleWithComments, input.article, { as: 'caller' })
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Handles
|
|
63
|
+
|
|
64
|
+
`build(input, d)` receives the handles of the Domain:
|
|
65
|
+
|
|
66
|
+
| Handle | What it reads |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `d.Article` | A Node Class of the Domain. An Edge Class handle (`d.comment_on`) reads Edges at the root. |
|
|
69
|
+
| `d.directory.Person` | A Class of the dependency the Schema aliases `directory`. |
|
|
70
|
+
| `d.kernel.<Class>` | A Kernel Class. |
|
|
71
|
+
| `d.core.<node>` | A Core node of the Domain, as a target: `get(d.core.settings)`, `rel.is(d.core.settings)`. |
|
|
72
|
+
| `d.origin` | The Domain's origin. |
|
|
73
|
+
| `d.document(query)` | One canonical v7 document; see [Canonical documents](#canonical-documents). |
|
|
74
|
+
|
|
75
|
+
A Class or alias named `kernel`, `core`, `origin` or `document` has no handle.
|
|
76
|
+
|
|
77
|
+
## Reads
|
|
78
|
+
|
|
79
|
+
A read starts from a Class handle, is narrowed with `where`, ordered with `orderBy`, carries
|
|
80
|
+
relations with `with`, and ends with the verb that says how much it reads:
|
|
81
|
+
|
|
82
|
+
| Verb | Answers | Refusals |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| `get(id?)` | Exactly one row | None: `NOT_FOUND`. A second: `INTEGRITY`. One the reader may not read: `NOT_READABLE`. |
|
|
85
|
+
| `find(id?)` | One row, or `undefined` | A second: `INTEGRITY`. One the reader may not read: `NOT_READABLE`. |
|
|
86
|
+
| `first()` | The first row, or `undefined` | |
|
|
87
|
+
| `first(n)` | Up to `n` rows | |
|
|
88
|
+
| `first(n, { more: true })` | `{ rows, more }`, reading `n + 1` rows | |
|
|
89
|
+
| `page({ max })` | `{ rows, next? }`; only as a recipe's single read | |
|
|
90
|
+
| `all({ max })` | Every row | Past `max`: `TOO_MANY`. |
|
|
91
|
+
| `exists(id?)` | `boolean` | |
|
|
92
|
+
| `count({ upTo })` | `{ count, more }`; `more` says the count reached `upTo` | |
|
|
93
|
+
|
|
94
|
+
`get` asserts that the row exists, so it reads the ids the Domain itself holds; for an id a caller
|
|
95
|
+
sends, use `find` and raise a declared error from `output` (see [Errors](#errors)).
|
|
96
|
+
|
|
97
|
+
`id` is a `NodeId`, a `NodePath` or a Core node. `.pick()` renders rows as references,
|
|
98
|
+
`{ id, $class }`. `.includeWithheld()` keeps the rows the reader may not read in a list, as
|
|
99
|
+
`{ id, $class, $withheld }` naming the values withheld. `get` and `find` raise `NOT_READABLE` on
|
|
100
|
+
such a row, since it exists. `d.A.union(d.B)` reads the Nodes of both Classes.
|
|
101
|
+
|
|
102
|
+
### Conditions
|
|
103
|
+
|
|
104
|
+
`where` takes one condition or a list, an implicit `and`. A falsy entry of the list is skipped, so
|
|
105
|
+
an optional filter is written in place:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
d.Article.where((a) => [
|
|
109
|
+
a.state.eq('published'),
|
|
110
|
+
filter.author && a.author.is(filter.author),
|
|
111
|
+
filter.since && a.publishedAt.gte(filter.since),
|
|
112
|
+
filter.featured !== undefined && a.featured.eq(filter.featured),
|
|
113
|
+
])
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
A filter whose value may be `false` or `0` is written `filter.x !== undefined && …` when it
|
|
117
|
+
applies whenever the caller sends it: with `filter.x && …`, those values would skip the entry. When
|
|
118
|
+
`false` means no filter, say so: `filter.x ? a.x.eq(true) : undefined`.
|
|
119
|
+
|
|
120
|
+
| Term | Meaning |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| `p.eq(v)` | Equal. `eq(null)` matches a stored null. |
|
|
123
|
+
| `p.in(values)` | One of at most 16 values. |
|
|
124
|
+
| `node.id.eq(id)`, `node.id.in(ids)` | The Node is this one, or one of these. `id.in` takes any length at a read's root; elsewhere a read carries at most 32,768 ids and values. |
|
|
125
|
+
| `node.path.eq(path)`, `node.path.in(paths)` | The Node is at this path, or at one of these. |
|
|
126
|
+
| `p.isSet()`, `p.isMissing()` | The value is present, or absent. |
|
|
127
|
+
| `p.gt`, `p.gte`, `p.lt`, `p.lte` | Ranges, on text, numbers and open values. |
|
|
128
|
+
| `p.between(lower, upper)` | `lower <= value < upper`. |
|
|
129
|
+
| `p.startsWith(prefix)` | A text prefix. |
|
|
130
|
+
| `node.is(Class)`, `node.isExactly(Class)` | The Node is of the Class, or a subclass for `is`. It narrows the handle and the rows. |
|
|
131
|
+
| `node.is(Class, (n) => conditions)` | The Node is of the Class and meets conditions written on the narrowed handle. |
|
|
132
|
+
| `c.and(…)`, `c.or(…)`, `c.not()` | Combinators on any condition. |
|
|
133
|
+
|
|
134
|
+
A relation is a condition too:
|
|
135
|
+
|
|
136
|
+
| Term | Meaning |
|
|
137
|
+
| --- | --- |
|
|
138
|
+
| `rel.is(target)` | The relation reaches this Node. |
|
|
139
|
+
| `rel.exists()` | It reaches any Node. |
|
|
140
|
+
| `rel.some(cond?)`, `rel.none(cond?)`, `rel.every(cond)` | Over the Nodes it reaches. |
|
|
141
|
+
| `rel.atLeast(n, cond?)`, `rel.atMost(n, cond?)`, `rel.exactly(n, cond?)` | How many it reaches. |
|
|
142
|
+
|
|
143
|
+
The callback of a relation condition receives `(node, edge)`: conditions on the far Node and on
|
|
144
|
+
the Edge's Properties.
|
|
145
|
+
|
|
146
|
+
### Order
|
|
147
|
+
|
|
148
|
+
`orderBy` takes one key or a list: `orderBy((a) => [a.publishedAt.desc(), a.title.asc()])`. Rows
|
|
149
|
+
without the value sort last; `asc({ missing: 'first' })` sorts them first. On a union whose
|
|
150
|
+
Classes map one name to different Properties, the name filters but does not order.
|
|
151
|
+
|
|
152
|
+
### Relations
|
|
153
|
+
|
|
154
|
+
A to-one end is named by its far role: `a.author` reads the author of an Article. Any Edge is read
|
|
155
|
+
with `.via(edge, { direction })`, where `direction` is `outgoing`, `incoming` or `incident`.
|
|
156
|
+
`.via([e1, e2])` reads several Edge Classes. `direction` is required when both ends accept the
|
|
157
|
+
Class, or when the Edges listed leave it in different directions.
|
|
158
|
+
|
|
159
|
+
A relation reads with the root verbs, per parent row: `get`, `find`, `first`, `all`, `count` and
|
|
160
|
+
`exists`. A relation's `get` that finds none, or a second row, raises `INTEGRITY` naming the
|
|
161
|
+
relation. A relation also takes `where`, `orderBy`, `with`, `pick`, `includeWithheld`, `distinct`
|
|
162
|
+
and `withEdge()`, which adds the Edge's Class and Properties to each row under `$edge`.
|
|
163
|
+
|
|
164
|
+
`.with((a) => ({ name: read }))` adds each relation's answer to the row under the name given. A
|
|
165
|
+
name a row already carries does not compile, and neither does a `.with` that reads another root:
|
|
166
|
+
read it as a facet of the recipe.
|
|
167
|
+
|
|
168
|
+
### Rows
|
|
169
|
+
|
|
170
|
+
A Node row is portable JSON: `id`, `$class`, and every Property by name. An optional Property may
|
|
171
|
+
be absent; `null` is a stored null. `$class` is typed as the Class or any subclass the Domain can
|
|
172
|
+
name. A Property whose name the SDK takes goes under `$prop` by its name: `id` on rows, a handle
|
|
173
|
+
member (`id`, `path`, `is`, `isExactly`, `via`) on handles, and an Edge's roles on an Edge read at
|
|
174
|
+
the root. A Property named `path` is `row.path` on a row and `a.$prop.path` on a handle. These
|
|
175
|
+
names are frozen, and no Property name starts with `$`, so a Property never moves. Two Properties of one name that two parent Classes declare, when the Class
|
|
176
|
+
declares none of its own, each go under `$` and their owner's name in lower camel case:
|
|
177
|
+
`row.$record.title`. The SDK's row keys (`$class`, `$edge`, `$source`, `$target`, `$withheld` and
|
|
178
|
+
`$prop`) are frozen too, and an owner group that would take one gets one more `$`: an owner named
|
|
179
|
+
`Class` groups under `$$class`.
|
|
180
|
+
|
|
181
|
+
An Edge read at the root answers rows of `{ $class, $source, $target }`, the Edge's Properties,
|
|
182
|
+
and both ends' rows under their roles. `edgeKey(row)` gives the `{ source, target, class }` a
|
|
183
|
+
Mutation or a guard names an Edge by. An Edge root's `get()`, `find()` and `exists()` take no id,
|
|
184
|
+
and its `first(n)` takes no `{ more }`.
|
|
185
|
+
|
|
186
|
+
## Recipes
|
|
187
|
+
|
|
188
|
+
`defineQuery<Schema>()(id, { build, output?, errors? })` defines a recipe. Its id is unique in the
|
|
189
|
+
Domain: dot-separated segments of lowercase letters, digits and `-`.
|
|
190
|
+
|
|
191
|
+
`build` returns one read, or a record of facets read together:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
build: (account: NodeId, d) => ({
|
|
195
|
+
account: d.Account.get(account),
|
|
196
|
+
invoices: d.Invoice.where((i) => i.account.is(account)).all({ max: 500 }),
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
A facet may be a read, a list of reads (`items.map((item) => d.Item.get(item))`), or `undefined`
|
|
201
|
+
for a read the input makes optional.
|
|
202
|
+
|
|
203
|
+
Reads that wait for others are chained with `steps`, `build`'s third parameter.
|
|
204
|
+
`steps(first, (earlier) => next, …)` takes two to eight steps and reads them in order. The first
|
|
205
|
+
is a record of facets; each next one is a function of the merged results of the steps before it,
|
|
206
|
+
and returns a record of facets none of them read. A facet name read again does not compile, and
|
|
207
|
+
throws a `TypeError` before the step reads. A recipe whose reads wait for none returns them
|
|
208
|
+
without `steps`. These steps order the reads of one call: they are not a Workflow's durable
|
|
209
|
+
Steps, and nothing of them is journaled or replayed.
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
build: (id: NodeId, { Article, Comment }, steps) =>
|
|
213
|
+
steps({ article: Article.find(id) }, ({ article }) => ({
|
|
214
|
+
comments:
|
|
215
|
+
article &&
|
|
216
|
+
Comment.where((c) => [
|
|
217
|
+
c.article.is(id),
|
|
218
|
+
article.publishedAt && c.postedAt.gte(article.publishedAt),
|
|
219
|
+
]).first(20),
|
|
220
|
+
})),
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`output(results, input, { error, is })` maps the results to the recipe's answer. It is
|
|
224
|
+
synchronous and pure, and returns portable JSON. Without it, the recipe answers what `build` read.
|
|
225
|
+
`is(row, classKey)` narrows a row to a Class or its subclasses. An expected case is a union in the
|
|
226
|
+
output, not an error.
|
|
227
|
+
|
|
228
|
+
## Calling a recipe
|
|
229
|
+
|
|
230
|
+
An Action calls `query(recipe, input, { as })`, where `as` is `executor` (the authority the handler
|
|
231
|
+
executes with) or `caller` (the incoming caller). An `auth: 'anonymous'` callable reads as
|
|
232
|
+
`executor` only. A recipe with no input is called with `undefined`.
|
|
233
|
+
|
|
234
|
+
A page recipe, whose single read is `.page({ max })`, takes the page size and the cursor at the
|
|
235
|
+
call and answers `{ rows, next? }`; its `output` keeps `rows` and `next`. `size` is a whole number
|
|
236
|
+
from 1 to `max`, `max` when omitted; any other value throws a `TypeError`:
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
const first = await query(articleTitles, undefined, { as: 'caller', size: 50 })
|
|
240
|
+
const second = first.next
|
|
241
|
+
? await query(articleTitles, undefined, { as: 'caller', size: 50, after: first.next })
|
|
242
|
+
: undefined
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
A durable Workflow reads in a Step, as its executor:
|
|
246
|
+
`step.query('load-article', articleById, input.article, { as: 'executor' })`. The output is
|
|
247
|
+
journaled, and a replay returns it. A `QueryError` other than `REFUSED`, or the `TypeError` of an
|
|
248
|
+
undeclared crossing (S12), fails the Step at once, and a replay throws it again. A `REFUSED` read
|
|
249
|
+
is a failed attempt like any Kernel refusal: a permanent one exhausts the Step at once, and a
|
|
250
|
+
stale snapshot spends no attempt. An inline Workflow calls `query(recipe, input, { as })` from its
|
|
251
|
+
context, as an Action does, inside a named `step.run`.
|
|
252
|
+
|
|
253
|
+
## Errors
|
|
254
|
+
|
|
255
|
+
A read that cannot be answered raises `QueryError`, with the recipe's id, a code and the details
|
|
256
|
+
of that code:
|
|
257
|
+
|
|
258
|
+
| Code | When | Details |
|
|
259
|
+
| --- | --- | --- |
|
|
260
|
+
| `NOT_FOUND` | A root `get` found nothing. | `class`; `target` when the read named one |
|
|
261
|
+
| `NOT_READABLE` | `get` or `find` met a fact the reader may not read. | `class`; `id`, a Node's id or an Edge's key |
|
|
262
|
+
| `INTEGRITY` | A relation's `get` found none, or a second row came where one is allowed. | `relation`; `parent` for a relation; `found: 0 \| 2` |
|
|
263
|
+
| `TOO_MANY` | Past a verb's `max`, a verb bound of 83,332, the recipe's 100,000 rows or its 32 reads. | `limit: 'max' \| 'rows' \| 'reads'`; `bound` |
|
|
264
|
+
| `REFUSED` | The Kernel refused the read. | `kernelCode`; `retryable`, whether a retry may succeed |
|
|
265
|
+
| `INPUT_INVALID` | The SDK or the Kernel refused the query the recipe built, before reading anything. | `kernelCode`, `phase` and `path` when the Kernel names them; `limit: 'in'` for an `in` past 16 values |
|
|
266
|
+
| `OUTDATED` | The Host's Registry does not match the Domain's build. | `kernelCode: 'QUERY_EXPECTATION_MISMATCH'` |
|
|
267
|
+
| `UNSUPPORTED` | The Kernel does not serve Query v7, or its answer leaves out a row's Class. | `feature: 'v7' \| 'classes'` |
|
|
268
|
+
|
|
269
|
+
`isQueryError(error, code?)` recognizes one.
|
|
270
|
+
|
|
271
|
+
A refusal the caller handles by code is declared in `errors`, as on Actions, and raised by
|
|
272
|
+
`output`:
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
const articleNotFound = {
|
|
276
|
+
family: 'NOT_FOUND',
|
|
277
|
+
message: 'The article does not exist.',
|
|
278
|
+
details: z.strictObject({ article: z.string() }),
|
|
279
|
+
} as const
|
|
280
|
+
|
|
281
|
+
export const articleAuthor = defineQuery<typeof schema>()('publishing.article.author', {
|
|
282
|
+
build: (article: NodeId, d) => d.Article.with((a) => ({ author: a.author.find() })).find(article),
|
|
283
|
+
output: (row, article, { error }) => {
|
|
284
|
+
if (row === undefined) throw error('ARTICLE_NOT_FOUND', { article })
|
|
285
|
+
return row.author === undefined
|
|
286
|
+
? { kind: 'unassigned' as const }
|
|
287
|
+
: { kind: 'assigned' as const, author: row.author.id }
|
|
288
|
+
},
|
|
289
|
+
errors: { ARTICLE_NOT_FOUND: articleNotFound },
|
|
290
|
+
})
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
A Function lets a recipe's error reach its own caller by declaring it again:
|
|
294
|
+
`{ errors: { ...articleAuthor.errors } }`.
|
|
295
|
+
|
|
296
|
+
## Bounds and checks
|
|
297
|
+
|
|
298
|
+
Before a step sends anything, the SDK checks:
|
|
299
|
+
|
|
300
|
+
- **Bounds (S11).** A recipe makes at most 32 reads and reads at most 100,000 rows, counted from
|
|
301
|
+
its verbs' bounds and its relations per parent row. A verb's bound is at most 83,332. Past one,
|
|
302
|
+
the read raises `TOO_MANY`. An `in` takes at most 16 values; past them, it raises
|
|
303
|
+
`INPUT_INVALID`. Fewer than 2 or more than 8 steps do not compile, and `steps` throws a
|
|
304
|
+
`TypeError` when `build` calls it.
|
|
305
|
+
- **Requirements (S12).** A relation that crosses a Class of a direct dependency needs `read` and
|
|
306
|
+
`traverse` on that Class in the Domain's `requirements({ classes })`. A requirement on a parent
|
|
307
|
+
covers its children. Otherwise the call throws a `TypeError` naming the recipe, the relation and
|
|
308
|
+
the requirement to add. Roots, Kernel Classes and the Domain's own Classes need nothing. A
|
|
309
|
+
relation an empty list folds away is checked as if the list were not empty. A condition the
|
|
310
|
+
input leaves unbuilt, such as a falsy `where` entry or an `undefined` facet, is not checked.
|
|
311
|
+
|
|
312
|
+
`checkRecipe(recipe, sampleInput, { domain })` from `@astrale-os/sdk/testing` runs both checks in
|
|
313
|
+
a test, without a Kernel, on the first step it builds for its sample input. Give it an input that
|
|
314
|
+
builds every optional condition, and test later steps through a call. Given the Domain's
|
|
315
|
+
definition it checks S12; given the Schema alone it skips S12.
|
|
316
|
+
|
|
317
|
+
The Domain linter also reads recipes:
|
|
318
|
+
|
|
319
|
+
- `QRY-RECIPE-CLOCK` refuses a clock read in `build` or `output`. Take now in the input.
|
|
320
|
+
- `QRY-RECIPE-ROWS` warns when a recipe's constant bounds multiply past 10,000 rows.
|
|
321
|
+
- `QRY-RECIPE-FALSY` warns on a condition entry `x && p.eq(x)` or `x ? p.eq(x) : undefined`
|
|
322
|
+
whose `x` may be `false` or `0`, wherever falsy entries are skipped. Write
|
|
323
|
+
`x !== undefined && p.eq(x)` to compare whenever `x` is set, or say that `false` or `0` means no
|
|
324
|
+
filter: `x ? p.eq(true) : undefined` for a flag, `x !== undefined && x !== 0 && p.gte(x)` for a
|
|
325
|
+
number.
|
|
326
|
+
|
|
327
|
+
## Beyond v7
|
|
328
|
+
|
|
329
|
+
What v7 cannot express is bridged inside the recipe, with a step or in `output`. A bridge's bound
|
|
330
|
+
and steps count toward the recipe's bounds, and its callers never see it. When the Kernel serves
|
|
331
|
+
the construct, the bridge moves into the read and the output type does not change. Beyond a long
|
|
332
|
+
root `id.in`, the SDK never splits a read behind its author.
|
|
333
|
+
|
|
334
|
+
| The business asks | Bridge inside the recipe |
|
|
335
|
+
| --- | --- |
|
|
336
|
+
| A value compared with a parent's | A first step reads the parent and the next compares with its value; or read both and filter in `output`. |
|
|
337
|
+
| Two parts of a pattern meeting at one Node | Read both with `.with`, compare in `output`. |
|
|
338
|
+
| Ancestors, a sub-tree at any depth | One step per level reads the next with `id.in`, up to 8 steps. |
|
|
339
|
+
| Sums, averages, groups | Read the values with `all({ max })`, compute in `output`. |
|
|
340
|
+
| Case-insensitive search, locale sorting | A normalized Property the Domain writes, or filter and sort in `output`. |
|
|
341
|
+
| Several unrelated lists | Facets of one recipe, each read at its own snapshot. |
|
|
342
|
+
|
|
343
|
+
A filter in `output` runs after the Kernel applied the verb's bound.
|
|
344
|
+
|
|
345
|
+
## Canonical documents
|
|
346
|
+
|
|
347
|
+
`d.document(query)` reads one canonical v7 document whose lists carry a `limit`. Its rows are typed
|
|
348
|
+
`unknown`, so `output` validates them:
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
import { z } from 'zod'
|
|
352
|
+
|
|
353
|
+
export const recentArticles = defineQuery<typeof schema>()('publishing.article.recent', {
|
|
354
|
+
build: (_: undefined, d) =>
|
|
355
|
+
d.document({
|
|
356
|
+
query: 'astrale.query/7',
|
|
357
|
+
list: { nodes: ['publishing.example:class.Article'], select: 'value', limit: 20 },
|
|
358
|
+
}),
|
|
359
|
+
output: (rows) => z.array(z.looseObject({ id: z.string() })).parse(rows),
|
|
360
|
+
})
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Mutation recipes
|
|
364
|
+
|
|
365
|
+
A Mutation takes the same form, over the same handles:
|
|
366
|
+
`defineMutation<Schema>()(id, { build, output?, reject?, errors? })`. `build(input, d, mutation)`
|
|
367
|
+
is synchronous; each write names its Class by its handle, and a write names one Class:
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
// The Schema declares the key: no two Workspaces share a slug.
|
|
371
|
+
const Workspace = nodeClass({ properties: { slug: property(z.string()) }, unique: [['slug']], … })
|
|
372
|
+
|
|
373
|
+
export const createWorkspace = defineMutation<typeof schema>()('workspace.create', {
|
|
374
|
+
build: (input: { readonly slug: string }, { Workspace }, mutation) => {
|
|
375
|
+
mutation.createNode({ as: 'workspace', class: Workspace, props: { slug: input.slug } })
|
|
376
|
+
},
|
|
377
|
+
reject: (failure, input, { error }) => {
|
|
378
|
+
if (failure.code !== 'MUTATION_POST_STATE_INVALID' || failure.rule !== 'duplicate-key') {
|
|
379
|
+
throw failure
|
|
380
|
+
}
|
|
381
|
+
throw error('SLUG_TAKEN', { slug: input.slug })
|
|
382
|
+
},
|
|
383
|
+
errors: { SLUG_TAKEN: slugTaken },
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
await mutate(createWorkspace, { slug }, { as: 'caller' })
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
`output(result, input)` maps the committed `MutationResult`, which the recipe answers without it.
|
|
390
|
+
`reject(failure, input, { error, is })` maps an expected refusal to the same output, or raises a
|
|
391
|
+
declared error: here the Kernel refuses a second Workspace with the slug, `duplicate-key`, on every
|
|
392
|
+
write that could make one, and the recipe names that refusal. A Mutation recipe shares the Query
|
|
393
|
+
recipes' ids, and every call names its authority: `mutate(recipe, input, { as })`,
|
|
394
|
+
`step.mutate(id, recipe, input, { as: 'executor' })`. The closure form,
|
|
395
|
+
`defineMutation<Schema>()((domain) => ({ id, build, project? }))`, is deprecated.
|
|
396
|
+
|
|
397
|
+
## Guards
|
|
398
|
+
|
|
399
|
+
A Mutation guards its write on a range: a Class handle after `where`, `union` and `orderBy`, or a
|
|
400
|
+
canonical v7 Query. `mutation.expect.none(range)`, `expect.exists(range)`,
|
|
401
|
+
`expect.count(range, n | { atLeast?, atMost? })` or `expect.unchanged(range, ids)`. The Kernel
|
|
402
|
+
decides the guard with the Mutation, on the range's Classes and `where`, and for `unchanged` its
|
|
403
|
+
`orderBy`. A read (`first`, `exists`, `count`…) and a rendered set (`with`, `pick`) are refused:
|
|
404
|
+
their verb, size or rendering would not reach the guard. Each verb takes `{ name }` last, and a
|
|
405
|
+
refusal carries the failed precondition's name as `failure.precondition`.
|
|
406
|
+
|
|
407
|
+
A guard states what the Schema cannot: a condition across Nodes at commit, a count that no
|
|
408
|
+
cardinality states (at most 100 Tasks in a Project), an unchanged list. What the Schema declares is
|
|
409
|
+
never a guard: a key (`unique`), a cardinality, the endpoints an Edge accepts, a state machine's
|
|
410
|
+
transitions. The Kernel checks those on every write, where a guard costs a read and holds only for
|
|
411
|
+
the Mutations that carry it. Closing a Project is its state machine's transition; that none of its
|
|
412
|
+
Tasks is open is a guard:
|
|
413
|
+
|
|
414
|
+
```ts
|
|
415
|
+
export const closeProject = defineMutation<typeof schema>()('project.close', {
|
|
416
|
+
build: (input: { readonly project: NodeId }, { Project, Task }, mutation) => {
|
|
417
|
+
mutation.expect.none(
|
|
418
|
+
Task.where((t) => [t.project.is(input.project), t.status.eq('open')]),
|
|
419
|
+
{ name: 'open-tasks' },
|
|
420
|
+
)
|
|
421
|
+
mutation.transition({
|
|
422
|
+
node: input.project,
|
|
423
|
+
class: Project,
|
|
424
|
+
property: 'status',
|
|
425
|
+
from: 'open',
|
|
426
|
+
event: 'close',
|
|
427
|
+
})
|
|
428
|
+
},
|
|
429
|
+
reject: (failure, input, { error }) => {
|
|
430
|
+
if (failure.code !== 'MUTATION_PRECONDITION_FAILED' || failure.precondition !== 'open-tasks') {
|
|
431
|
+
throw failure
|
|
432
|
+
}
|
|
433
|
+
throw error('OPEN_TASKS', { project: input.project })
|
|
434
|
+
},
|
|
435
|
+
errors: { OPEN_TASKS: openTasks },
|
|
436
|
+
})
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
## Query V6
|
|
440
|
+
|
|
441
|
+
Query V6 definitions keep their meaning and are deprecated. See
|
|
442
|
+
[Query V6 and the move to v7](legacy/query-v6.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrale-os/sdk",
|
|
3
|
-
"version": "0.6.0-beta.
|
|
3
|
+
"version": "0.6.0-beta.43",
|
|
4
4
|
"description": "Schema-first SDK for defining, composing, and deploying Astrale domains",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astrale",
|
|
@@ -258,11 +258,11 @@
|
|
|
258
258
|
"registry": "https://registry.npmjs.org/"
|
|
259
259
|
},
|
|
260
260
|
"dependencies": {
|
|
261
|
-
"@astrale-os/kernel-client": "1.0.0-beta.
|
|
262
|
-
"@astrale-os/kernel-core": "1.0.0-beta.
|
|
263
|
-
"@astrale-os/kernel-dsl": "1.0.0-beta.
|
|
264
|
-
"@astrale-os/kernel-protocol": "1.0.0-beta.
|
|
265
|
-
"@astrale-os/kernel-server": "1.0.0-beta.
|
|
261
|
+
"@astrale-os/kernel-client": "1.0.0-beta.27",
|
|
262
|
+
"@astrale-os/kernel-core": "1.0.0-beta.24",
|
|
263
|
+
"@astrale-os/kernel-dsl": "1.0.0-beta.17",
|
|
264
|
+
"@astrale-os/kernel-protocol": "1.0.0-beta.24",
|
|
265
|
+
"@astrale-os/kernel-server": "1.0.0-beta.24",
|
|
266
266
|
"@lucide/icons": "1.34.0",
|
|
267
267
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
268
268
|
"cross-spawn": "7.0.6",
|
|
@@ -339,13 +339,14 @@
|
|
|
339
339
|
"e2e": "node __e2e__/flagship/runner.mjs",
|
|
340
340
|
"typecheck": "tsgo --noEmit && pnpm run typecheck:workers && pnpm run typecheck:specs && pnpm run typecheck:types && pnpm run typecheck:tests && pnpm run typecheck:e2e && pnpm run typecheck:knowledge",
|
|
341
341
|
"typecheck:workers": "tsgo --noEmit -p tsconfig.workers.json",
|
|
342
|
-
"typecheck:specs": "tsgo --noEmit -p src/application/query/.spec/tsconfig.json && tsgo --noEmit -p src/platform/schema/.spec/tsconfig.json && tsgo --noEmit -p src/application/mutation/.spec/tsconfig.json && tsgo --noEmit -p src/tooling/cli/.spec/tsconfig.json",
|
|
342
|
+
"typecheck:specs": "tsgo --noEmit -p src/application/query/.spec/tsconfig.json && tsgo --noEmit -p src/application/query/legacy/v6/.spec/tsconfig.json && tsgo --noEmit -p src/platform/schema/.spec/tsconfig.json && tsgo --noEmit -p src/application/mutation/.spec/tsconfig.json && tsgo --noEmit -p src/tooling/cli/.spec/tsconfig.json",
|
|
343
343
|
"typecheck:types": "tsgo --noEmit -p __tests__/tsconfig.types.json",
|
|
344
344
|
"typecheck:tests": "tsgo --noEmit -p tsconfig.test.json",
|
|
345
345
|
"typecheck:e2e": "tsgo --noEmit -p __e2e__/flagship/fixtures/tsconfig.v1.json && tsgo --noEmit -p __e2e__/flagship/fixtures/tsconfig.json",
|
|
346
346
|
"typecheck:knowledge": "tsgo --noEmit -p knowledge/cli/tsconfig.json",
|
|
347
|
+
"typecheck:budgets": "node scripts/type-budget/run.mjs",
|
|
347
348
|
"test": "pnpm run test:checks && pnpm run test:file && pnpm run test:scripts",
|
|
348
|
-
"test:checks": "pnpm run check:knowledge && pnpm run check:linter-policy && pnpm run check:issues && pnpm run test:node-server-v1",
|
|
349
|
+
"test:checks": "pnpm run check:knowledge && pnpm run check:linter-policy && pnpm run check:kernel-schema && pnpm run check:issues && pnpm run test:node-server-v1",
|
|
349
350
|
"test:file": "vitest run --config __tests__/vitest.config.ts",
|
|
350
351
|
"test:scripts": "node --test scripts/*.test.mjs actions/domain/*.test.mjs",
|
|
351
352
|
"test:node-server-v1": "vitest run --config __tests__/vitest.node-server-v1.config.ts",
|
|
@@ -358,10 +359,12 @@
|
|
|
358
359
|
"format:check": "pnpm exec oxfmt --check .",
|
|
359
360
|
"check:knowledge": "node knowledge/cli/main.mjs check knowledge && bash knowledge/cli/test.sh",
|
|
360
361
|
"check:linter-policy": "node scripts/sync-domain-policy.mjs --check",
|
|
362
|
+
"check:kernel-schema": "node scripts/sync-kernel-schema.mjs --check",
|
|
361
363
|
"check:issues": "issues/.cli/issues check && node --test issues/.cli/history.test.mjs && node --test issues/.cli/view-server.test.mjs && node --test issues/.view/contract.test.mjs",
|
|
362
364
|
"qualify:publications": "node scripts/qualify-publications.mjs",
|
|
363
365
|
"qualify": "node scripts/qualify.mjs",
|
|
364
366
|
"sync:linter-policy": "node scripts/sync-domain-policy.mjs",
|
|
367
|
+
"sync:kernel-schema": "node scripts/sync-kernel-schema.mjs",
|
|
365
368
|
"setup": "bash scripts/setup/setup.sh",
|
|
366
369
|
"setup:codex": "AGENT_HARNESSES=codex bash scripts/setup/setup.sh",
|
|
367
370
|
"setup:claude": "AGENT_HARNESSES=claude bash scripts/setup/setup.sh",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|