Skip to content

Commit 29af940

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-ik6sn2
2 parents a3fcb30 + c88467d commit 29af940

113 files changed

Lines changed: 6364 additions & 827 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/metadata-protocol': patch
3+
---
4+
5+
refactor(metadata-protocol): one `ObjectStackProtocolImplementation` member body moves out of `protocol.ts` through a host object, the shape the rest of the class follows
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
No behaviour change. `tenantAuthoredWriteRefusal` keeps its docblock and signature on the class; its body now lives in `protocol-tenant-authored-write.ts`, reached through `protocol-host.ts`, a per-instance object that forwards to the class's own members (built in the class's static block, so the class gains no member). The moved body differs from the method body only by `this.` reading `host.`. The published `index.d.ts` (ESM and CJS) is byte-identical to the one `main` builds before this change. Nothing to migrate.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): the objectui testing guide stops teaching msw and shows a fetch double over the real adapter
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** `skills/objectui/guides/testing.md` taught "MSW for API mocking in integration tests" with `import … from 'msw'` and `msw/node`. No package in this stack declares `msw` any more, so a test written from the guide cannot resolve its imports.
11+
- **Now.** The subsection shows the pattern this repository's own integration tests use: a `vi.fn` passed as the `fetch` of a real `ObjectStackAdapter`, so the adapter and client run and only the wire is faked. It names one real test as the worked example. The guide stays within its token ceiling (3101 of 3101).

‎.changeset/28576-packageless-overlay-write-closed.md‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,23 @@
55
'@object-ui/app-shell': patch
66
---
77

8-
fix(metadata-protocol)!: a write naming no package onto an installed package's view, dashboard, report, translation or email template is refused where `os migrate packageless-overlays` can move its row, naming the base's `.custom` pair and the command
8+
fix(metadata-protocol)!: a write naming no package onto an installed package's view, dashboard, report, translation or email template is refused where `os migrate packageless-overlays` can move its row as a declared override, naming the base's `.custom` pair and the command
99

10-
Clause-②: yes (narrowing) — the metadata save, publish, delete and rollback doors refuse a write that names no package onto an item of the five overlay types that one installed read-only package ships, where the ceremony would classify the row (or the first write's body) `movable` and its move can succeed; they accepted it before as a package-unbound Setup row. Every row the ceremony lists and does not move keeps that write. One write is widened: a rollback of the `.custom` pair's own declared row of an item its base ships (#28644 item 1) is judged as the pair's declared override, where the store sealed it as an artifact override before, on every type the pair can override (a page or flow pair row included).
10+
Clause-②: yes (narrowing) — the metadata save, publish, delete and rollback doors refuse a write that names no package onto an item of the five overlay types that one installed read-only package ships, where the ceremony would classify the row (or the first write's body) `movable` and its move as a declared override can succeed; they accepted it before as a package-unbound Setup row. Every row the ceremony lists and does not move keeps that write. One write is widened: a rollback of the `.custom` pair's own declared row of an item its base ships (#28644 item 1) is judged as the pair's declared override, where the store sealed it as an artifact override before, on every type the pair can override (a page or flow pair row included).
1111

12-
Metadata-protocol: narrowed (the `allowEnvironmentOverlay` flag of `view`, `dashboard`, `report`, `translation` and `email_template` no longer opens a package-less write onto a packaged item the ceremony can move; it stays the predicate the migration ceremony, the read of an unmigrated row and every row the ceremony leaves use)
12+
Metadata-protocol: narrowed (the `allowEnvironmentOverlay` flag of `view`, `dashboard`, `report`, `translation` and `email_template` no longer opens a package-less write onto a packaged item the ceremony can move as a declared override; it stays the predicate the migration ceremony, the read of an unmigrated row and every row the ceremony leaves use)
1313

1414
Skills-docs: updated here — `skills/objectstack-platform/SKILL.md`'s ADR-0146 paragraph said "a package-unbound Setup row still wins over the base's until D11"; it now says such a row from before D11 wins over the base's until `os migrate packageless-overlays` moves it, and that its package-less save, reset and rollback are refused once the command can move it. To stay under the bundle's token ceiling, the same paragraph drops "the base's is unchanged" after the export sentence (which "replaces the base's document whole" already implies) and the aside that the other overridable types wait for their doors (the sentence already names the six types the read honours today).
1515

1616
<!-- adr-0087: registered packageless-overlay-write-closed -->
1717

1818
**BREAKING** write narrowing at the metadata doors (`@objectstack/metadata-protocol`), shipped as `minor` during the launch window.
1919

20-
**Upgrade, then run the ceremony.** The command ships in this release. On each deployment with Setup customizations of a package's items, upgrade, run `os migrate packageless-overlays` (preview) with the flags and environment the deployment boots with (`--preset` and `--dev` mean what they mean to `os serve`), then `os migrate packageless-overlays --apply`. Nothing breaks at the upgrade: between the upgrade and `--apply`, every customization is served as before, and the package-less save, reset, publish and rollback of one the command can move are refused, naming the command. Done when the preview lists no movable row.
20+
**Upgrade, then run the ceremony.** The command ships in this release. On each deployment with Setup customizations of a package's items, upgrade, run `os migrate packageless-overlays` (preview) with the flags and environment the deployment boots with (`--preset` and `--dev` mean what they mean to `os serve`), then `os migrate packageless-overlays --apply`. Nothing breaks at the upgrade: between the upgrade and `--apply`, every customization is served as before, and the package-less save, reset, publish and rollback of one the command moves as a declared override are refused, naming the command (one it moves as the pair's own member keeps that write, see `movable` below). Done when the preview lists no movable row.
2121

2222
The preview lists five dispositions; only `movable` rows are moved, and every other row keeps its package-less write:
2323

24-
- `movable`: `--apply` moves it into the pair. A move that ends `failed` keeps the row as it was; fix what the error names and re-run. When the error names the pair (it cannot be ensured, or it refuses the row) or the base's missing version, the row also keeps its package-less write and may be left.
24+
- `movable`: `--apply` moves it into the pair, as the base's declared override (outcome `moved` with its `basedOn`). A row of an `email_template` locale the base does not ship (the preview lists it as the pair's own member; `--json` carries it as `addedMember`) moves instead as the pair's own member at that locale, declaring nothing (outcome `moved` with that `addedMember` in place of a `basedOn`), since a declared override of a locale the base does not ship is refused; its package-less write stays open, before and after the move. A move that ends `failed` keeps the row as it was; fix what the error names and re-run. When the error names the pair (it cannot be ensured, or it refuses the row) or, for a declared override, the base's missing version, the row also keeps its package-less write and may be left.
2525
- `multi_shipped` (several packages ship the name): delete it naming no package, then save it into the pair of the package it customizes if it is still wanted (the pair's override is then served at that base's address, `?package=<base>`; a read naming no package is not pinned to it while several packages ship the name).
2626
- `no_pair` (the base's id forms no pair): nothing to do.
2727
- `foreign_declaration` (its `overrides` names another package): remove the key with a save naming no package, then re-run: the move declares the right override. (A package-less save that keeps a corrected `overrides` is refused by the override write gate.)
@@ -43,10 +43,10 @@ PUT /api/v1/meta/view/account_list?package=com.acme.crm.custom
4343
{ "name": "account_list", "label": "My accounts", "overrides": { "package": "com.acme.crm", "basedOn": "1.4.0" } }
4444
```
4545

46-
To restore the package original, FROM `DELETE /api/v1/meta/view/account_list` TO `DELETE /api/v1/meta/view/account_list?package=com.acme.crm.custom`. `basedOn` is the base's installed `manifest.version`. The pair is created by `os migrate packageless-overlays --apply`, or by Studio's Customize.
46+
To restore the package original, FROM `DELETE /api/v1/meta/view/account_list` TO `DELETE /api/v1/meta/view/account_list?package=com.acme.crm.custom`. `basedOn` is the base's installed `manifest.version`. The pair is created by `os migrate packageless-overlays --apply`, or by Studio's Customize. A locale of an email template the base does not ship is the pair's own member, not an override: write it into the pair with no `overrides` (one declaring the base is refused), and `DELETE …?package=<base>.custom&member=<locale>` removes it (with no `member`, the delete addresses the canonical locale).
4747

48-
- **Now refused.** A save (draft or publish mode), a publish, a delete of the active item, or a rollback whose restored row is package-less, with no `?package=` (or an empty one), of a `view`, `dashboard`, `report`, `translation` or `email_template` that exactly one installed read-only package ships, where the item's stored package-less row in that state (or, with none, the saved body) is one the ceremony classifies `movable`, the pair can be ensured (the base has a namespace and a SemVer version), and the override write gate admits that row into the pair. This covers an unmigrated `movable` row, and the first package-less write of an item the pair already holds (a moved one). Each answers `403 NOT_OVERRIDABLE`, writes nothing, and its message names the item, the shipping package, the pair (`?package=<base>.custom`), the `overrides` declaration and `os migrate packageless-overlays`; a rollback's message says to save a version from before the move into the pair, declaring it. The metadata item page shows that message as sent when its URL holds no `?package=`. The refusal comes before every gate that reads a stored row's lock, so it is the same with or without `overrides` in the body.
49-
- **Still open.** Every write naming `?package=<base>.custom` (the pair's save, publish, delete and rollback, judged by the override write gate as before); a rollback of the pair's own declared row, which is judged as the pair's override; a write of a name no installed package ships (environment metadata); and every package-less write the ceremony would not move, as before this release: a name several read-only packages ship (`multi_shipped`); a base whose id forms no pair (`no_pair`); a base whose pair cannot be ensured (no namespace, or a version that is not SemVer); a stored package-less row whose `overrides` names another package (`foreign_declaration`) or beside the pair's row of the item (`shadowed`; its package-less writes are addressed to the pair's row as before, so its package-less delete restores the package original); and an `email_template` locale the base does not ship (the pair refuses it, #28777; the ceremony's move of it is #28904). Discarding a draft stays open.
48+
- **Now refused.** A save (draft or publish mode), a publish, a delete of the active item, or a rollback whose restored row is package-less, with no `?package=` (or an empty one), of a `view`, `dashboard`, `report`, `translation` or `email_template` that exactly one installed read-only package ships, where the item's stored package-less row in that state (or, with none, the saved body) is one the ceremony classifies `movable`, the pair can be ensured (the base has a namespace and a SemVer version), and the override write gate admits that row, declared as the base's override, into the pair (so not an `email_template` locale the base does not ship: see **Still open**). This covers an unmigrated `movable` row, and the first package-less write of an item the pair already holds (a moved one). Each answers `403 NOT_OVERRIDABLE`, writes nothing, and its message names the item, the shipping package, the pair (`?package=<base>.custom`), the `overrides` declaration and `os migrate packageless-overlays`; a rollback's message says to save a version from before the move into the pair, declaring it. The metadata item page shows that message as sent when its URL holds no `?package=`. The refusal comes before every gate that reads a stored row's lock, so it is the same with or without `overrides` in the body.
49+
- **Still open.** Every write naming `?package=<base>.custom` (the pair's save, publish, delete and rollback, judged by the override write gate as before); a rollback of the pair's own declared row, which is judged as the pair's override; a write of a name no installed package ships (environment metadata); and every package-less write the ceremony would not move, as before this release: a name several read-only packages ship (`multi_shipped`); a base whose id forms no pair (`no_pair`); a base whose pair cannot be ensured (no namespace, or a version that is not SemVer); a stored package-less row whose `overrides` names another package (`foreign_declaration`) or beside the pair's row of the item (`shadowed`; its package-less writes are addressed to the pair's row as before, so its package-less delete restores the package original). Every package-less write of an `email_template` locale the base does not ship stays open too, before and after the ceremony moves its row as the pair's own member (#28904): the close judges only the declared-override move, and the override write gate refuses a declared override of that locale (#28777). Discarding a draft stays open.
5050
- **Unchanged.** A package-less row stored before this release is still served exactly as before, by every read. The ceremony still moves it: it writes into the pair, never package-less.
5151
- **Studio.** The read-only item notice no longer links to the package-less Setup overlay (`@object-ui/app-shell`).
5252
- **Removed with it.** #28413's interim allowance, by which a package-less save that landed on a moved row kept that row's `overrides` block, is gone: such a save is refused like any other.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/i18n': patch
4+
---
5+
6+
feat(console): Setup → Integrations → Connector Connections asks for the installer's OAuth client when an instance declares `clientSource: 'installer'`
7+
8+
Clause-②: yes
9+
Metadata-protocol: none
10+
11+
An `oauth2` `authorization_code` connector instance whose declaration says `clientSource: 'installer'` takes the OAuth client the administrator registered with the vendor. Connect and Reconnect on such an instance now open a form for its client ID and client secret before the sign-in window opens; Continue begins the connect with `{ clientId, clientSecret }` (the secret omitted for a public client). The secret input is masked, starts empty on every opening and is cleared the moment the connect is sent; the stored secret is never read back. A connected instance shows which client it keeps (`OAuth client ID, secret stored`), and Reconnect prefills that client ID and sends the client again, because the server takes a client on every connect of such an instance. Every other instance connects with no form, as before.
12+
13+
Twelve new keys under `connectorConnections` in all ten `@object-ui/i18n` locales: `clientKeptWithSecret`, `clientKept`, `clientTitle`, `clientIntro`, `clientStored`, `clientStoredSecret`, `clientStoredNoSecret`, `clientResend`, `clientId`, `clientSecret`, `clientSecretHint`, `clientContinue`.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/lint': minor
3+
---
4+
5+
feat(lint): the publish-time null-guard gate judges a record-triggered flow's start, edge and decision-branch conditions
6+
7+
Clause-②: no (narrowing)
8+
Metadata-protocol: narrowed
9+
10+
<!-- adr-0087: not-required (no-migration-prescription) No key of any schema is added, removed, renamed or re-shaped, and no export or type of the package's public surface is retired: the publish-time null-guard gate now judges three predicate slots of a record-triggered flow that it already judges on validation rules and hook conditions. A flow it now refuses is fixed in the flow's own expression (add the `!= null` test the message names), so there is nothing for `objectstack migrate meta` to rewrite and no tombstone. -->
11+
12+
`os build`, `os validate`, `os lint` and the runtime publish gate now refuse a record-triggered flow (start `triggerType: 'record-…'`) whose start condition, edge `condition` or `decision` branch `expression` applies an ordering (`< <= > >=`) or arithmetic (`+ - * / %`) operator to a nullable declared field of the trigger object with no `!= null` / `== null` / `!isBlank()` test in front of it. The run's `record` and `previous` carry every declared field, nulls included, so such a comparison faults with `no such overload` on the first record where the field is empty, and the run fails there (the write that fired it still lands).
13+
14+
- **Who moves.** A flow condition such as `budget > 200000` on an optional `budget`. Fix: `budget != null && budget > 200000`, or put the `!= null` test in the flow's start condition, which gates every run, so every edge and branch after it may compare freely. `has(record.budget)` is not a guard: the key is always present.
15+
- **Bare names count.** The run spreads the record's fields to top level, so a bare `budget` is read as `record.budget`, unless the flow declares a variable or a node id of that name, or has a node that can bind names nobody declared (a `script`, `connector_action`, `wait`, `subflow` or `map`); those bare names are not judged. At the runtime publish gate bare names are not judged.
16+
- **Not judged.** Flows with no record trigger (an `api` flow's request body, an action's or `map` item's record are not made total), a `screen` field's `visibleWhen`, and `config.condition` on any node but `start`.
17+
- **Fewer findings elsewhere.** A `count` or `sum` roll-up summary is now always valued for this gate (zero children is zero), on validation rules and hook conditions too; `min` / `max` / `avg` roll-ups stay nullable.

0 commit comments

Comments
 (0)