Skip to content

Commit 53ec0b1

Browse files
os-billclaude
andauthored
feat(spec)!: FlowEdgeSchema.condition is an evaluated slot — composes EvaluatedExpressionInputSchema; structuralConditionRefusal drops the ast-only admission (#15807) (#17267)
* wip(spec): FlowEdgeSchema.condition composes EvaluatedExpressionInputSchema; structuralConditionRefusal drops the ast-only admission (#15807) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH * wip: consumer readings (service-automation, lint) and the CLI-path pin (#15807) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH * chore(spec): regenerate api-surface, export-origins, declaration-map, json-schema manifest and docs for EvaluatedExpressionInputSchema Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH * chore: changeset for the evaluated edge condition (#15807) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH * test(dogfood): list EvaluatedExpressionInputSchema in the expression-surface discovery roster so FlowEdgeSchema.condition stays discovered (#15807) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH * fix(spec): register the ADR-0087 D3 semantic entry for the evaluated edge condition, and state the stored-flow blast radius (#15807) Contract-review REWORK on this branch. No code change: the schema, the three doors, the pins and the roster are untouched. F1 — the changeset's remedy sentence was true for an AUTHOR and false for a row already in `sys_metadata`. `applyConversionsToStoredItem` is deliberately not applied to `flow` (conversions/stored.ts; same skip in the database loader's `rowToData`), so the read path never re-validates a stored flow; the door is `registerFlow` -> `canonicalizeStoredFlow` -> `FlowSchema.parse`, and all three boot paths in service-automation's plugin wrap it in try/catch, warn and continue. A stored flow carrying a refused edge condition therefore stops being registered ENTIRELY -- trigger never armed, whole flow dark, announced by one warn line. Stated as its own paragraph. F2 — `no-migration-prescription` is refused by a body carrying a migration prescription (ADR-0087), and the "What an author does" paragraph is one. D2 is genuinely impossible (an `ast`-only envelope has no `source` to derive; dropping a blank condition flips the edge from never-fires to always-fires), which is the argument FOR a D3 semantic TODO rather than for the exemption. Registered in the shape of the three-day-old precedent on this lane, #16041's `analytics-time-dimension-date-range-vocabulary-closed`, which also had no D2 available and registered anyway. Marker flipped to `registered`. The entry is authored as a FILE under migrations/entries/semantic/ and the registry regions regenerated -- those regions are generated (#7297/#6957) and a hand edit between the markers is reverted by the next gen run. Both projections correctly show NO diff: they fold only up to PROTOCOL_MAJOR (17) and this is a major-18 entry -- controlled by the precedent entry being absent from them too, against a lit control of a major-17 id that is present. F3 — `EvaluatedExpressionSchema`'s "exactly one issue, at `source`, for every refused shape" is true only for a direct caller; composed through `EvaluatedExpressionInputSchema` the `ast`-only and blank-bare-string shapes surface as one `invalid_union` at the slot. One sentence scoping the promise. Level unchanged and re-derived, not inherited: `@objectstack/spec` `minor` plus the **BREAKING** banner. The disposition marker records HOW a break reaches the upgrade channels; it is not an input to the semver level. Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH Co-authored-by: Claude <noreply@anthropic.com> * docs(spec): point the stored-flow remedy at the warn line's `issues[].path`, not a CLI verb that does not exist (#15807) The stored-flow paragraph is written for the operator whose row exists only in `sys_metadata` — a Studio-authored flow with no config file. It told that reader to "Run `objectstack validate` on the exported stack". There is no export verb in the CLI: oclif discovers commands from `dist/commands/**`, which holds no `export`; `packages/cli/src/commands/meta/` offers `get|list|register|delete| resync`; and `validate` consumes a config file (`validate.ts` `loadConfig`), not a database. `git grep 'objectstack export|meta export|exported stack'` over `packages/cli/src` and `content/docs` returns only the module-export sense at `utils/config.ts:352`, against a lit control (`loadConfig` = 4 in `validate.ts`, `objectstack validate` = 8 doc files, `objectstack export` = 0). The route that does exist is the warn line the paragraph already names: all three boot sites spread `describeThrownForLog(e)` into the log meta, which reshapes the ZodError into `issues[].path` and renders it through `formatIssuePath` — numeric segments become `[i]`, so the offending edge reads `edges[N].condition`. `objectstack validate` is kept only where it is true: for a stack authored in config files. Folds in the path-spelling nit at the same time. Each spelling now matches the door it describes: the CLI prints `path.join('.')` over an `ObjectStackDefinitionSchema` parse, so its reading is `flows.N.edges.N.condition` (pinned, `flow-edge-condition-evaluated.test.ts`), while the warn renders `edges[N].condition`. Both carriers move — the changeset and the D3 entry's `acceptanceCriteria` — because leaving the false locator in the ledger keeps it in front of the reader who most needs it. `gen:migration-registry` re-run; the generated region is the entry's text modulo indent. No code, schema, door, pin or roster changes, and the not-interchangeable warning is untouched. Level re-derived and unchanged: `@objectstack/spec` `minor` + **BREAKING** + `adr-0087: registered flow-edge-condition-evaluated-slot-source-required`. Editing prose inside a `reason`/`acceptanceCriteria` string and a changeset paragraph moves nothing published and changes no break. Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 357f499 commit 53ec0b1

25 files changed

Lines changed: 832 additions & 70 deletions
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
feat(spec)!: `FlowEdgeSchema.condition` is an evaluated slot — it composes the new `EvaluatedExpressionInputSchema`, and `structuralConditionRefusal` no longer admits an `ast`-only envelope (#15807)
6+
7+
<!-- adr-0087: registered flow-edge-condition-evaluated-slot-source-required -->
8+
9+
**BREAKING** in the accept-set sense, landing in the launch window as `minor`
10+
(the lockstep convention: `major` is refused by `check-changeset-no-major`, and
11+
breaking-ness is carried by this banner plus the ADR-0087 disposition): the
12+
edge condition of a flow — `FlowEdgeSchema.condition`, the branch predicate
13+
`AutomationEngine.evaluateCondition` runs at every traversal — now refuses at
14+
authoring an envelope the engine cannot evaluate, where it used to parse,
15+
register, pass `objectstack validate`, and then answer a **silent `false`**: a
16+
branch that quietly never fired.
17+
18+
Two spellings of one seam, refused by ONE rule with one sentence
19+
(`EVALUATED_EXPRESSION_SOURCE_REQUIRED`, the rule #15430 introduced for the
20+
`assignment` value envelope):
21+
22+
```yaml
23+
edges:
24+
- { id: e1, source: check, target: approve, condition: { dialect: cel, ast: { kind: const, value: true } } } # `ast` only — the engine never reads it
25+
- { id: e2, source: check, target: reject, condition: { dialect: cel, source: ' ' } } # blank after trimming
26+
- { id: e3, source: check, target: escalate, condition: ' ' } # the shorthand for the same blank source
27+
```
28+
29+
> An expression in an evaluated slot needs a non-blank `source`: the expression
30+
> engine evaluates `source` (the canonical persisted form of phase M9.1) and
31+
> cannot evaluate `ast` alone, so an envelope carrying only `ast`, or a `source`
32+
> that is blank after trimming, would validate and register and then fault at
33+
> run time. Write `{ dialect: 'cel', source: '…' }`.
34+
35+
- **New export `EvaluatedExpressionInputSchema`** (type `EvaluatedExpressionInput`),
36+
the sibling of `ExpressionInputSchema` for an evaluated slot: the bare-string
37+
shorthand still normalizes to `{ dialect: 'cel', source }`, but the string
38+
must be non-blank after trimming, and the envelope arm composes
39+
`EvaluatedExpressionSchema` (`source` required and non-blank) instead of
40+
`ExpressionSchema`. `FlowEdgeSchema.condition` is the first slot to compose
41+
it. An `ast`-only envelope and a blank bare string surface as one
42+
`invalid_union` issue at the slot carrying the sentence above; a blank
43+
`source` inside an envelope surfaces as one `custom` issue at `source`.
44+
- **`ExpressionSchema` / `ExpressionInputSchema` are NOT narrowed.** They remain
45+
the persistence contract (`source` OR `ast`), whose docblock declares that
46+
`ast` becomes required in build output at phase M9.2. When AST-only
47+
evaluation lands, `EvaluatedExpressionSchema` is the one place to relax, and
48+
every evaluated slot follows.
49+
- **`structuralConditionRefusal` no longer admits an `ast`-only envelope** on
50+
either structural condition slot (`config.condition` on a node,
51+
`edge.condition`). #15662's refusal admitted it on purpose through a
52+
`rec.ast !== undefined` clause, because the spec still admitted the shape at
53+
`edge.condition` and refusing it from the consumer side would have decided
54+
#15430's question there; with the edge schema closed, that admission kept the
55+
refusal deliberately holed for a shape the engine cannot run on either slot.
56+
`STRUCTURAL_CONDITION_SHAPE_REFUSAL` now reads "an expression envelope
57+
carrying a string `source`" and says why. Consequence on `config.condition`
58+
(a start node's trigger gate, a decision node's predicate — an open record
59+
with no schema in front of it): an `ast`-only envelope there is refused at
60+
`registerFlow`, reported as a located `error` by `objectstack validate`, and
61+
refused by `evaluateCondition` with the same sentence, instead of answering a
62+
silent `false`. An `ast` BESIDE a string `source` is still admitted
63+
everywhere. The whitespace-only STRING ruling on `config.condition` (#15662:
64+
consistent `false` on both sides) is untouched.
65+
- **Three doors agree, through the spec.** `registerFlow` refuses the flow at
66+
`FlowSchema.parse` (edge) or at its structural pass (`config.condition`);
67+
`objectstack validate` refuses it at its `ObjectStackDefinitionSchema` parse
68+
(edge) or reports the structural refusal (`config.condition`);
69+
`evaluateCondition` refuses the shape a stored flow or a direct caller hands
70+
it. None of them grew a rule of its own.
71+
72+
**What an author does with a refused edge condition.** An edge condition that
73+
carried only `ast` has no evaluable form under M9.1: author its `source`. A
74+
whitespace-only condition — envelope or bare string — was never a predicate
75+
(the engine answered `false`, so that edge never fired): remove the
76+
`condition` key if the edge was meant to be unconditional, or write the
77+
expression if it was meant to branch. Every edge condition with a
78+
non-blank `source` is unchanged, and nothing is renamed, retired or rewritten —
79+
the refusal itself carries the prescription.
80+
81+
**A flow ALREADY STORED in `sys_metadata` stops running entirely — the whole
82+
flow, not just the edge.** The paragraph above is the author's remedy, at
83+
`objectstack validate` / `POST /flows`; a stored row has no author in front of
84+
it. Stored flows are deliberately NOT canonicalized by
85+
`applyConversionsToStoredItem` (`spec/src/conversions/stored.ts`, and the same
86+
skip in `metadata/src/loaders/database-loader.ts`'s `rowToData`) — flow-node
87+
conversions need the automation engine's live executor registry, so flows
88+
canonicalize at `registerFlow` instead, which parses through
89+
`canonicalizeStoredFlow` → `FlowSchema.parse`. Each of the three boot paths in
90+
`service-automation/src/plugin.ts` wraps that call in `try`/`catch`, logs one
91+
`warn` naming the flow, and continues. So an edge that used to answer a silent
92+
`false` while the rest of the flow ran now takes the flow down with it: it is
93+
never registered, its trigger is never armed, and the only announcement is that
94+
one warn line — `[Automation] failed to register flow` at boot,
95+
`[Automation] cold-boot flow bind: failed to register flow` at the kernel:ready
96+
bind, `[Automation] flow re-sync: failed to register flow` on a re-sync. That
97+
warn line is also the locator: its `issues[].path` names the offending edge —
98+
`edges[N].condition` — beside the sentence above, so nothing has to be exported
99+
to find it. Author the `source` — or remove the key, if the edge was meant to
100+
be unconditional — and republish. A stack authored in config files has a second
101+
door, `objectstack validate`, which locates the same edge at
102+
`flows.N.edges.N.condition`. Registered as the ADR-0087 D3 semantic entry
103+
`flow-edge-condition-evaluated-slot-source-required`, which carries the same
104+
judgment for a consumer replaying the chain.
105+
106+
Not touched here: `start.config.condition` has no Zod schema to narrow (the
107+
start node's `config` is an open record); its producer-side gate is the
108+
structural refusal above, which this change tightens but does not type.

‎content/docs/references/api/automation-api.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ const result = AutomationApiErrorCode.parse(data);
144144
| **id** | `string` | ✅ | Edge unique ID |
145145
| **source** | `string` | ✅ | Source Node ID |
146146
| **target** | `string` | ✅ | Target Node ID |
147-
| **condition** | `string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }` | optional | Predicate (CEL) returning boolean used for branching. |
147+
| **condition** | `string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Predicate (CEL) returning boolean used for branching. An evaluated slot: a bare non-blank CEL string, or an envelope carrying a non-blank `source` — an `ast`-only envelope, and a `source` that is blank after trimming, are refused at authoring because the engine evaluates `source` alone and would otherwise answer a silent `false`. |
148148
| **type** | `Enum<'default' \| 'fault' \| 'conditional' \| 'back'>` | optional (default: `"default"`) | Connection type: default (normal flow), fault (error path), conditional (expression-guarded), or back (ADR-0044 declared back-edge — traversed normally at run time, but excluded from DAG cycle validation so a revise/rework loop can re-enter an earlier node) |
149149
| **label** | `string` | optional | Label on the connector |
150150
| **isDefault** | `boolean` | optional (default: `false`) | BPMN default flow: traverse this edge only when no sibling conditional edge of the same source node matched. Mutually exclusive with `condition`; at most one per source node. |

‎content/docs/references/automation/control-flow.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ const result = FlowRegionSchema.parse(data);
129129
| **id** | `string` | ✅ | Edge unique ID |
130130
| **source** | `string` | ✅ | Source Node ID |
131131
| **target** | `string` | ✅ | Target Node ID |
132-
| **condition** | `string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }` | optional | Predicate (CEL) returning boolean used for branching. |
132+
| **condition** | `string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Predicate (CEL) returning boolean used for branching. An evaluated slot: a bare non-blank CEL string, or an envelope carrying a non-blank `source` — an `ast`-only envelope, and a `source` that is blank after trimming, are refused at authoring because the engine evaluates `source` alone and would otherwise answer a silent `false`. |
133133
| **type** | `Enum<'default' \| 'fault' \| 'conditional' \| 'back'>` | optional (default: `"default"`) | Connection type: default (normal flow), fault (error path), conditional (expression-guarded), or back (ADR-0044 declared back-edge — traversed normally at run time, but excluded from DAG cycle validation so a revise/rework loop can re-enter an earlier node) |
134134
| **label** | `string` | optional | Label on the connector |
135135
| **isDefault** | `boolean` | optional (default: `false`) | BPMN default flow: traverse this edge only when no sibling conditional edge of the same source node matched. Mutually exclusive with `condition`; at most one per source node. |
@@ -192,7 +192,7 @@ const result = FlowRegionSchema.parse(data);
192192
| **id** | `string` | ✅ | Edge unique ID |
193193
| **source** | `string` | ✅ | Source Node ID |
194194
| **target** | `string` | ✅ | Target Node ID |
195-
| **condition** | `string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }` | optional | Predicate (CEL) returning boolean used for branching. |
195+
| **condition** | `string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Predicate (CEL) returning boolean used for branching. An evaluated slot: a bare non-blank CEL string, or an envelope carrying a non-blank `source` — an `ast`-only envelope, and a `source` that is blank after trimming, are refused at authoring because the engine evaluates `source` alone and would otherwise answer a silent `false`. |
196196
| **type** | `Enum<'default' \| 'fault' \| 'conditional' \| 'back'>` | optional (default: `"default"`) | Connection type: default (normal flow), fault (error path), conditional (expression-guarded), or back (ADR-0044 declared back-edge — traversed normally at run time, but excluded from DAG cycle validation so a revise/rework loop can re-enter an earlier node) |
197197
| **label** | `string` | optional | Label on the connector |
198198
| **isDefault** | `boolean` | optional (default: `false`) | BPMN default flow: traverse this edge only when no sibling conditional edge of the same source node matched. Mutually exclusive with `condition`; at most one per source node. |

‎content/docs/references/automation/flow.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ const result = FlowSchema.parse(data);
8484
| **id** | `string` | ✅ | Edge unique ID |
8585
| **source** | `string` | ✅ | Source Node ID |
8686
| **target** | `string` | ✅ | Target Node ID |
87-
| **condition** | `string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }` | optional | Predicate (CEL) returning boolean used for branching. |
87+
| **condition** | `string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Predicate (CEL) returning boolean used for branching. An evaluated slot: a bare non-blank CEL string, or an envelope carrying a non-blank `source` — an `ast`-only envelope, and a `source` that is blank after trimming, are refused at authoring because the engine evaluates `source` alone and would otherwise answer a silent `false`. |
8888
| **type** | `Enum<'default' \| 'fault' \| 'conditional' \| 'back'>` | optional (default: `"default"`) | Connection type: default (normal flow), fault (error path), conditional (expression-guarded), or back (ADR-0044 declared back-edge — traversed normally at run time, but excluded from DAG cycle validation so a revise/rework loop can re-enter an earlier node) |
8989
| **label** | `string` | optional | Label on the connector |
9090
| **isDefault** | `boolean` | optional (default: `false`) | BPMN default flow: traverse this edge only when no sibling conditional edge of the same source node matched. Mutually exclusive with `condition`; at most one per source node. |
@@ -122,7 +122,7 @@ const result = FlowSchema.parse(data);
122122
| **id** | `string` | ✅ | Edge unique ID |
123123
| **source** | `string` | ✅ | Source Node ID |
124124
| **target** | `string` | ✅ | Target Node ID |
125-
| **condition** | `string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }` | optional | Predicate (CEL) returning boolean used for branching. |
125+
| **condition** | `string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source: string; ast?: any; meta?: object }` | optional | Predicate (CEL) returning boolean used for branching. An evaluated slot: a bare non-blank CEL string, or an envelope carrying a non-blank `source` — an `ast`-only envelope, and a `source` that is blank after trimming, are refused at authoring because the engine evaluates `source` alone and would otherwise answer a silent `false`. |
126126
| **type** | `Enum<'default' \| 'fault' \| 'conditional' \| 'back'>` | optional (default: `"default"`) | Connection type: default (normal flow), fault (error path), conditional (expression-guarded), or back (ADR-0044 declared back-edge — traversed normally at run time, but excluded from DAG cycle validation so a revise/rework loop can re-enter an earlier node) |
127127
| **label** | `string` | optional | Label on the connector |
128128
| **isDefault** | `boolean` | optional (default: `false`) | BPMN default flow: traverse this edge only when no sibling conditional edge of the same source node matched. Mutually exclusive with `condition`; at most one per source node. |

‎content/docs/references/index.mdx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Protocol Reference
3-
description: Every schema published by @objectstack/spec — 1582 schemas across 14 protocol modules
3+
description: Every schema published by @objectstack/spec — 1583 schemas across 14 protocol modules
44
---
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -29,11 +29,11 @@ counts are sums of the rows they head. Regenerate with
2929
| [Kernel Protocol](/docs/references/kernel) | 30 | 162 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. |
3030
| [QA Protocol](/docs/references/qa) | 1 | 8 | Declarative test suites — scenarios, steps, actions and assertions. |
3131
| [Security Protocol](/docs/references/security) | 5 | 30 | Permission sets, row-level security, sharing rules, tenancy posture. |
32-
| [Shared Protocol](/docs/references/shared) | 9 | 28 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. |
32+
| [Shared Protocol](/docs/references/shared) | 9 | 29 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. |
3333
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
3434
| [System Protocol](/docs/references/system) | 33 | 272 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
3535
| [UI Protocol](/docs/references/ui) | 16 | 153 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
36-
| **Total** | **198** | **1582** | 14 protocol modules |
36+
| **Total** | **198** | **1583** | 14 protocol modules |
3737

3838
---
3939

@@ -286,15 +286,15 @@ Permission sets, row-level security, sharing rules, tenancy posture.
286286

287287
## Shared Protocol
288288

289-
**Source:** `packages/spec/src/shared/` · **Import:** `@objectstack/spec/shared` · **9 pages, 28 schemas**
289+
**Source:** `packages/spec/src/shared/` · **Import:** `@objectstack/spec/shared` · **9 pages, 29 schemas**
290290

291291
Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums.
292292

293293
| File | Schemas |
294294
| :--- | :--- |
295295
| [`enums.zod.ts`](/docs/references/shared/enums) | `IsolationLevelEnum`, `MutationEventEnum`, `SortDirectionEnum`, `SortItem` |
296296
| [`epoch.zod.ts`](/docs/references/shared/epoch) | `EpochMs` |
297-
| [`expression.zod.ts`](/docs/references/shared/expression) | `CronExpressionInput`, `EvaluatedExpression`, `Expression`, `ExpressionDialect`, `ExpressionInput`, `ExpressionMeta`, `Predicate`, `PredicateInput`, `TemplateExpressionInput` |
297+
| [`expression.zod.ts`](/docs/references/shared/expression) | `CronExpressionInput`, `EvaluatedExpression`, `EvaluatedExpressionInput`, `Expression`, `ExpressionDialect`, `ExpressionInput`, `ExpressionMeta`, `Predicate`, `PredicateInput`, `TemplateExpressionInput` |
298298
| [`http.zod.ts`](/docs/references/shared/http) | `CorsConfig`, `HttpMethod`, `HttpMethodSubset`, `HttpRequest`, `RateLimitConfig`, `StaticMount` |
299299
| [`identifiers.zod.ts`](/docs/references/shared/identifiers) | `MetadataItemName`, `SnakeCaseIdentifier`, `SystemIdentifier` |
300300
| [`mapping.zod.ts`](/docs/references/shared/mapping) | `FieldMapping` |

‎content/docs/references/shared/expression.mdx‎

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ See also: content/docs/concepts/north-star.mdx §8 "No private expression DSL"
5757
## TypeScript Usage
5858

5959
```typescript
60-
import { CronExpressionInputSchema, EvaluatedExpressionSchema, ExpressionSchema, ExpressionDialect, ExpressionInputSchema, ExpressionMetaSchema, PredicateSchema, PredicateInputSchema, TemplateExpressionInputSchema } from '@objectstack/spec/shared';
61-
import type { CronExpressionInput, EvaluatedExpression, Expression, ExpressionDialect, ExpressionInput, ExpressionMeta, Predicate, PredicateInput, TemplateExpressionInput } from '@objectstack/spec/shared';
60+
import { CronExpressionInputSchema, EvaluatedExpressionSchema, EvaluatedExpressionInputSchema, ExpressionSchema, ExpressionDialect, ExpressionInputSchema, ExpressionMetaSchema, PredicateSchema, PredicateInputSchema, TemplateExpressionInputSchema } from '@objectstack/spec/shared';
61+
import type { CronExpressionInput, EvaluatedExpression, EvaluatedExpressionInput, Expression, ExpressionDialect, ExpressionInput, ExpressionMeta, Predicate, PredicateInput, TemplateExpressionInput } from '@objectstack/spec/shared';
6262

6363
// Validate data
6464
const result = CronExpressionInputSchema.parse(data);
@@ -106,6 +106,34 @@ Type: `string`
106106
| **meta** | `{ rationale?: string; generatedBy?: string }` | optional | |
107107

108108

109+
---
110+
111+
## EvaluatedExpressionInput
112+
113+
### Union Options
114+
115+
This schema accepts one of the following structures:
116+
117+
#### Option 1
118+
119+
Type: `string`
120+
121+
---
122+
123+
#### Option 2
124+
125+
### Properties
126+
127+
| Property | Type | Required | Description |
128+
| :--- | :--- | :--- | :--- |
129+
| **dialect** | `Enum<'cel' \| 'cron' \| 'template'>` | ✅ | |
130+
| **source** | `string` | ✅ | |
131+
| **ast** | `any` | optional | |
132+
| **meta** | `{ rationale?: string; generatedBy?: string }` | optional | |
133+
134+
---
135+
136+
109137
---
110138

111139
## Expression

0 commit comments

Comments
 (0)