Skip to content
Open
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
10 changes: 10 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,16 @@ jobs:
# shared CI-boot paths trigger it.
- *shared
- 'packages/bxl/**'
# The card-authoring skill's claims are pinned by a BXL suite
# that reads the skill file, so an edit to the skill has to run
# that suite too.
- 'packages/boxel-cli/plugin/skills/bxl-authoring/**'
Comment thread
habdelra marked this conversation as resolved.
# That suite also asserts the host suites the skill cites still
# exist. `bxl-test` runs unconditionally on main, so without
# these a host-only rename would merge green and redden main on
# a commit whose own CI never ran the failing suite.
- 'packages/host/tests/integration/bxl-*'
- 'packages/host/tests/helpers/cards/bxl-*'
bench-amd:
# The AMD transpiler is a runtime-common module with no
# cross-workspace deps that affect its wall-time, so the
Expand Down
13 changes: 9 additions & 4 deletions packages/bxl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,14 +331,19 @@ Same string language everywhere. Each slot in the object is a plain string; the

### Using BXL inside Boxel

In Boxel realms, import the compute factory and syntax tags from the uploaded
bundle, then assign the returned function to `computeVia`:
In Boxel realms, import the compute factory and syntax tags from the
platform module — the host serves `@cardstack/bxl` to card code, and a realm
that carries its own uploaded bundle imports that bundle by relative path
instead — then assign the returned function to `computeVia`:

```ts
import { expression, fx, jq } from '../bxl';
import { expression, fx, jq } from '@cardstack/bxl';

// An aggregate's iterating argument has to be collected: function arguments
// are jq streams, so the uncollected `SUM(LineItems[].Amount)` would run once
// per line item and hand the field one value per element.
@field subtotal = contains(NumberField, {
computeVia: expression(fx`SUM("Line Item".Amount)`),
computeVia: expression(fx`SUM([LineItems[].Amount])`),
});

@field slug = contains(StringField, {
Expand Down
25 changes: 20 additions & 5 deletions packages/bxl/docs/syntax-modes.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,26 @@ locked down by a case in [`../tests/boxel/`](../tests/boxel/).

## Using BXL Inside Boxel

In a Boxel realm, import from the uploaded realm bundle using the
relative path to that bundle:
In a Boxel realm, import the platform module by its bare specifier —
the host serves it to card code, and the package root is the
card-facing entry:

```ts
import { expression, fx, jq } from '../bxl';
import { expression, fx, jq } from '@cardstack/bxl';
Comment thread
habdelra marked this conversation as resolved.
```

A realm that carries its own uploaded bundle imports that bundle by
relative path instead (`from '../bxl'`); everything below applies the
same either way.

`expression` is the same compute factory as `bxl` / `expr`; it returns
a function shaped for `computeVia`. The factory validates the source against
the `derive` execution profile when it is constructed, so non-deterministic
or request-scoped expressions are rejected before Boxel runs the field.

```ts
@field subtotal = contains(NumberField, {
computeVia: expression(fx`SUM("Line Item".Amount)`),
computeVia: expression(fx`SUM([LineItems[].Amount])`),
});

@field slug = contains(StringField, {
Expand Down Expand Up @@ -158,7 +163,7 @@ Watch for:
```ts
expression(fx`ROUND(Salary / 2080, 2)`);
expression(fx`PatientId & " — " & FirstName & " " & LastName`);
expression(fx`SUM(Patients[].Billing.RoomCharge)`);
expression(fx`SUM([Patients[].Billing.RoomCharge])`);
```

The compiler treats `` fx`…` `` exactly like a plain string —
Expand Down Expand Up @@ -187,6 +192,16 @@ These apply regardless of the tag:
- **Null-tolerant arithmetic.** `null - 5`, `5 / 0`, `null * x`,
`null | startswith("a")` all return `null` / `false` instead of
throwing.
- **A single-argument aggregate takes a collected array.** Function
arguments are jq streams, so `SUM(Claims[].Paid)` calls `SUM` once per
claim and yields one output per claim rather than a total. Collect the
iterating argument — `SUM([Claims[].Paid])` — or supply a `schema`,
which makes implicit iteration collect on its own
(`SUM("Line Item"."Line Total")` compiles to
`SUM([.lineItems[].lineTotal])`). A comma list is already collected by
the compiler (`SUM(Paid, Reserve)` → `SUM([.paid, .reserve])`), and a
scalar parameter must not be wrapped: `ROUND([1.234], 2)` hands an
array to a function that wants a number.

## Mixed-syntax expressions

Expand Down
1 change: 1 addition & 0 deletions packages/bxl/tests/boxel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ rules, so a failing case name points straight at the one that broke.
| `fielddef-threading.ts` | Multi-stage `{ as: Cls }` threading — the insurance pipeline pattern |
| `card-source-mutation.ts` | The card-source mutation adapter: schema derivation, computed-field skips, relationship serialization, stale-plan safety |
| `update-via-bxl.ts` | The `updateViaBxl` adapter |
| `authoring-skill-claims.ts` | Drift guard for the `bxl-authoring` agent skill: every behavior it teaches, plus the repo paths it cites |

## Running

Expand Down
Loading
Loading