Skip to content

Commit 0e7e975

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-21595-sqlite-week-bucket
2 parents 9328c3c + 045b946 commit 0e7e975

43 files changed

Lines changed: 2858 additions & 333 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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@objectstack/driver-mongodb': patch
3+
---
4+
5+
Provenance comments in `@objectstack/driver-mongodb` cite the commits that decided them, not tracker numbers that no longer resolve
6+
7+
Clause-②: no
8+
9+
Docblocks and comments across the package cited issue-tracker numbers that now answer 404 on GitHub.
10+
Each one now cites the commit in this repository's history that made the decision it describes. One of
11+
these docblocks sits on an exported member (`MongoDBDriver.update()`), so the reworded text appears in
12+
the published `index.d.ts` / `index.d.mts`; that docblock and one more comment esbuild keeps appear in
13+
the JavaScript output (`index.js` / `index.mjs`); the sourcemaps do not change.
14+
15+
Comment only: no export, type, error code, status, message text or runtime behaviour changes.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/platform-objects': patch
4+
---
5+
6+
feat(spec)!: `element:text` `variant` refuses `heading` / `subheading` by name — the vocabulary is the nine `ui:text` publishes, and `os migrate meta` rewrites them to `h2` / `h3` (#21015)
7+
8+
**BREAKING** — `heading` and `subheading` leave `ElementTextPropsSchema.variant` (an
9+
`element:text` page component's `properties.variant`). This is the second release of
10+
the ruled two-release convergence on the nine values `ui:text` publishes — `h1`-`h6`,
11+
`body`, `caption`, `overline`. 17.5.0 added the nine and refused nothing; 17.6.0 was
12+
the full release in which both vocabularies parsed; this release refuses the two old
13+
spellings. A heading is a document level, not a text style: `heading` and
14+
`subheading` named a style and left the renderer to pick the level.
15+
16+
### FROM → TO
17+
18+
| removed | what to write instead |
19+
| --- | --- |
20+
| `variant: 'heading'` | `variant: 'h2'` — the heading element `heading` always rendered — or the level the page outline means. |
21+
| `variant: 'subheading'` | `variant: 'h3'` — the heading element `subheading` always rendered — or the level the page outline means. |
22+
23+
**The one-line fix: `heading` → `h2`, `subheading` → `h3`.**
24+
`os migrate meta --from 17` lists the mechanical edits for existing sources.
25+
26+
The rewrite keeps the heading ELEMENT (so the document outline is unchanged) but not
27+
the size: `heading` drew in the `h3` style and `subheading` in a medium-weight small
28+
heading style, and `h2` / `h3` draw their own, larger styles. Where the old look
29+
mattered more than the level, pick the level whose style you want.
30+
31+
Each retired spelling is refused at parse with a prescription naming the level to
32+
write, and in `tsc` (the two members are gone from the input type). Any other unknown
33+
value keeps zod's own message. An `element:text` with no `variant` still parses to
34+
`body`.
35+
36+
### The retirement kit
37+
38+
- **Value-level retirement.** The enum is declared through `enumWithRetiredValues`
39+
(`shared/retired-key.ts`), with the two prescriptions module-private. No authorable
40+
KEY and no def changed, so nothing lands in `RETIRED_KEYS_BY_MAJOR` and the four
41+
surface ratchets (`api-surface`, `authorable-surface`, `json-schema.manifest`,
42+
`api-surface-signatures`) are byte-identical; the generated component reference
43+
page drops the two values.
44+
- **D2 conversion `element-text-variant-heading-levels`** (step 18, retired from the
45+
load path): `heading` → `h2` and `subheading` → `h3` on every `element:text` page
46+
component — regions, named slots and container nesting. Stored `sys_metadata` page
47+
rows replay it at rehydration; one notice per rewritten block.
48+
- **D3 entry `element-text-variant-heading-subheading-retired`** carries the judgement
49+
the conversion cannot make: whether the rewritten level is the one the page means.
50+
- **No further deprecation window**: 17.6.0 was the window the ruling asked for.
51+
52+
### Producers moved in this repository
53+
54+
- `@objectstack/platform-objects`: the four section headings on the `sys_user` record
55+
page's Security tab (`Password & Sign-in`, `Two-Factor Authentication`, `Email
56+
Verification`, `Danger Zone`) move from `subheading` to `h3`. They render the same
57+
h3 element, in the `h3` style.
58+
- `examples/app-showcase`: the `page-variables` detail heading moves to `h3`.
59+
60+
⚠️ **The out-of-repo author population is NOT MEASURED.** `@objectstack/spec` is
61+
published, and tenant-authored pages were not measured. In this repository the five
62+
writers above were the only ones outside `packages/spec`. objectui at `main` authors
63+
neither value; its `element:text` renderer, registry `inputs` enum, html tier and the
64+
published `sdui.manifest.json` still list the two, and drop them once this release is
65+
installable there (the objectui follow-up).
66+
67+
Clause-②: no (narrowing)
68+
69+
<!-- adr-0087: registered element-text-variant-heading-levels, element-text-variant-heading-subheading-retired -->
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@objectstack/metadata-protocol': minor
3+
'@objectstack/runtime': patch
4+
---
5+
6+
fix(metadata-protocol)!: the generic data door refuses a stored-metadata filter that reads the body or a content hash through a cross-field comparand or below its depth backstop, and exports its one filter-field collector and one search narrowing for the reader-context seam (#21544)
7+
8+
Clause-②: yes (narrowing)
9+
10+
<!-- adr-0087: not-required (no-migration-prescription) no metadata body, authorable key, spelling, export or stored shape moves; what changes is which read-query shapes the generic data door accepts over the two stored-metadata tables, and two module functions are added to the package surface, so `objectstack migrate meta` has nothing to rewrite. The other categories are closed on facts: both packages publish (not `unpublished`); no ADR-0087 id covers a refused query shape (not `registered` / `already-registered`); and the change is runtime behaviour plus additive exports, not a declaration (not `runtime-interface-only` / `type-surface-only`). -->
11+
12+
**BREAKING**: this narrows what the generic data door (`GET /api/v1/data/:object`, `POST /api/v1/data/:object/query` and the in-process `findData`) accepts when it reads `sys_metadata` or `sys_metadata_history`. Two filter shapes read the stored body column or a content-hash column (`checksum`, `previous_checksum`, or the history table's `change_note`) without the family's refusal ever seeing them, and both ran before this release:
13+
14+
- a cross-field comparand naming one of those columns — `{ "name": { "$ne": { "$field": "metadata" } } }`, in `where` or in an aggregation's `filter`, under `$not` included. The SQL drivers evaluate it row by row, so row presence disclosed the column's value;
15+
- a filter on one of those columns nested more than 32 combinators deep, which the door's field collector stopped reading at. A body `$contains` of a stored credential answered the row and a wrong guess answered none.
16+
17+
Both now answer the door's `400 INVALID_FIELD`, naming the column, before the query runs — the answer the same filter already gets when it names the column directly. The route: filter those tables by their scalar columns (the type, the name, the state and the like), compare scalar columns with each other, and read the bodies with a plain list, which is served projected. Every other column of the two tables, and every other object, is unchanged; a dotted key into one of those columns was, and stays, refused by the door's dotted-path rule. It ships as `minor` under the launch-window convention for accept-set narrowings.
18+
19+
- **`@objectstack/metadata-protocol`** exports two module functions the generic data door now calls itself:
20+
- `collectStoredMetadataFilterFields(object, query)` — the family's one filter-field collector: every column a read query's filters read (`where`, the engine's `filter` alias and each aggregation filter): each key's head and each cross-field `{ $field }` comparand, at any depth. `[]` outside the family.
21+
- `narrowStoredMetadataSearch(object, query, schema, wireSpelling?)` — the family's one default-search narrowing: an explicit search-field list naming the body or a hash column is refused, a default search is narrowed to the searchable set without them (returned for the caller to run as `searchFields`), and a set that narrows to nothing is refused. The `StoredMetadataSearchSchema` type it reads is exported beside it.
22+
- **`@objectstack/runtime`**: the stored-metadata reader-context seam (`ctx.api.object(...)` for action and hook bodies, a handler's `ctx.api`, and `ctx.engine.find`) calls those two functions instead of its own copy of the narrowing and `@objectstack/plugin-security`'s condition walk, so the seam and the door answer every family filter and search identically. A `count` through the seam now runs the query the guard returns. The seam's accept set is unchanged: every shape it refused before it still refuses, now through the door's collector.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@objectstack/metadata-protocol': minor
3+
---
4+
5+
The runtime save door refuses a view container saved under a name its own expansion produces
6+
7+
Clause-②: yes (narrowing)
8+
9+
<!-- adr-0087: not-required (no-migration-prescription) A validity narrowing at one runtime write door over existing keys: no key of `ViewSchema` or of any other metadata schema is removed, renamed or re-shaped, so there is no tombstone and nothing mechanical for `objectstack migrate meta` to rewrite. Whether such a container was meant as the object's container or as a view item of that name is authoring intent no conversion entry can decide. New saves are refused with the remedy; a row stored before this change keeps its bytes and is served as before, and no stored row is re-saved. The census found no such row and no writer that produces the shape by default: no seeded `sys_metadata` view rows in the example apps, no packaged container with a top-level `name` among the twelve `defineView` sites in `examples/`, and no Studio or in-repo AI writer that saves a container under an expanded name unless its author types that name into the container (Studio's generic metadata editor saves a body under its own `name`); hosted tenants were not measured. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id covers this rule and this diff adds none (not registered / already-registered); and the change narrows what a runtime write door accepts, not a runtime interface or a type surface alone (not runtime-interface-only / type-surface-only). -->
10+
11+
**BREAKING** accept-set narrowing at the runtime save door, shipped as `minor` under the repo's launch-window convention for breaking changes, the grade the same door's `name` refusals shipped with.
12+
13+
**What was accepted before.** `saveMetaItem`, which `PUT /api/v1/meta/view/:name` and the dispatcher's metadata save both call, accepted an aggregated view container (`list` / `form` / `listViews` / `formViews`) saved under one of the names its own expansion produces: for example `{ name: 'crm_lead.default', object: 'crm_lead', list: { … } }` saved as `crm_lead.default`, the name its bare `list` expands to. That row is the name's own stored row, and an expansion fills only names that have no row of their own (the object door adopts that rule in this same release), so the container's expansion never filled it. The object door (`GET /api/v1/meta/view?object=…`), which never lists a container, listed nothing under the name, and the by-name read answered the raw container. No door answered a view item for the name, and nothing said why.
14+
15+
**What is refused now.** That save, with `VALIDATION_ERROR` / 400, before anything is stored or registered, in draft and in publish mode. Whether a name is one the container's own expansion produces is decided by the same expansion the read doors run, so every member kind (a bare or named `list`, `listViews`, `form`, `formViews`) and the expander's de-duplicated names (`…_2`) are judged where the readers place them. A container with no `name` is judged under the save name the door stamps on it. A container on another package's object expands under its own name, which is never the name it is saved under, so it is not refused.
16+
17+
**What still saves.** A container under its object's name, which expands as before. A view item (a body carrying `viewKind`) under an expanded name, the sanctioned override for that name. The read doors are unchanged. A row stored in this shape before this change keeps its bytes and is served as before; `migrate meta --stored` and package duplication, which re-save stored rows through this door, now report such a row as failed with this refusal instead of re-saving it.
18+
19+
**The fix.** Save the container under its object's name (`crm_lead`), or save a view item (`name`, `object`, `viewKind`, `config`) under the expanded name (`crm_lead.default`).
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@objectstack/runtime': minor
3+
'@objectstack/cloud-connection': minor
4+
---
5+
6+
fix(runtime,cloud-connection)!: install-local refuses a hook with no `body` and a job `body` that does not bind, and withholds such a hook on rehydrate (#21585)
7+
8+
Clause-②: yes (narrowing)
9+
10+
<!-- adr-0087: not-required (no-migration-prescription) no authorable key, spelling, export of a published release or stored shape moves: `HookSchema` and `JobSchema` are unchanged, so `objectstack migrate meta` has nothing to rewrite. What changes is which packages one install door accepts, and which hooks it binds on a rehydrate. The other categories are closed on facts: the packages publish (not `unpublished`); no ADR-0087 id covers a refused install or a withheld hook (not `registered` / `already-registered`); and the change is runtime behaviour, not a declaration (not `runtime-interface-only` / `type-surface-only`). -->
11+
12+
**BREAKING**: `os package install` (the install-local door, `POST /api/v1/marketplace/install-local`) now refuses two more kinds of package it used to install with a 200:
13+
14+
- **A hook with no `body`.** A hook in the deprecated function-name `handler` form names code that travels only in an artifact's runtime module, never in the package JSON this door installs. Such a hook used to install and then either never fire or bind by name to a function the package does not ship. Every hook is judged, since a hook has no on/off switch. A hook that carries both a `body` and a `handler` installs as before: its `body` wins.
15+
- **An enabled job whose `body` does not bind.** The door used to judge only that a job `body` was present. It now judges that the body binds, by the declaration's own parse of `JobSchema.body`, the same parse the scheduler binds by. So a job whose `body` is an expression (L1) body, or carries `body.timeoutMs`, is refused instead of installed and never scheduled.
16+
17+
- **The refusal.** The install answers `422` with `VALIDATION_ERROR`, the answer the door already gives an enabled job with no `body`. One answer names everything the door cannot run: each hook and the function its `handler` names, each job and its handler, and each refused job `body` with the key the declaration refuses. Nothing is installed: nothing is registered, persisted, bound or scheduled. `os package install` exits non-zero and prints the code beside the status.
18+
- **Rehydrate.** A package installed by an earlier version keeps rehydrating after a restart. Its body hooks bind as before. A hook of it with no `body` is reported at `warn` by name and is **not bound**: this door carries no runtime module, so the hook's `handler` can never name the package's own code. Its job with no runnable `body` is reported and not run, as before.
19+
- **Runtime.** The binder exports the two judgements the door reads: `collectHooksWithoutBody`, and `collectJobsWithoutBody`, which also names a job whose `body` does not bind. `bindAppArtifactHandlers` takes `withholdHooksWithoutBody`, which a door that carries no runtime module sets, and reports the hooks it withheld as `withheldHooks`.
20+
- **Unchanged:** a boot that loads the artifact's runtime module (`os start --artifact`, a `defineStack` config) binds an app's handler hooks to its own functions exactly as before. Hooks authored through the metadata API are unchanged too. A package whose hooks carry a `body` and whose enabled jobs carry a valid `body` installs exactly as before.
21+
22+
The route for a refused package: give each hook a `body` (sandboxed JS, the form actions and jobs use), and correct each job `body` to the declared shape. That shape is a sandboxed JS body whose time limit is the job's own `timeoutMs`, and `os validate` reports the same refusal. Alternatively, boot the artifact with `os start --artifact`, which loads its runtime module. This ships as `minor`, under the launch-window convention for narrowings of an accept set.

‎content/docs/automation/jobs.mdx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,9 @@ export const CloseStaleTasksJob = defineJob({
170170
job's `body` through the same binder. A `handler` is code: it travels only in the
171171
artifact's runtime module, so it runs only on a boot that loads that module (a
172172
config, or `os start --artifact`). `os package install` therefore refuses a
173-
package whose enabled job has no `body`, with `422 VALIDATION_ERROR` and the
174-
remedy: give the job a `body`, or boot it with `os start --artifact`.
173+
package whose enabled job has no `body`, or a `body` that does not bind (an
174+
expression body, or one carrying `body.timeoutMs`), with `422 VALIDATION_ERROR`
175+
and the remedy: give the job a valid `body`, or boot it with `os start --artifact`.
175176
Uninstalling a package stops its scheduled jobs at once, and a reinstall whose
176177
new version drops a job stops that job.
177178
</Callout>

‎content/docs/references/ui/component.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ Sort field and direction pair
436436
| Property | Type | Required | Description |
437437
| :--- | :--- | :--- | :--- |
438438
| **content** | `string \| Record<string, string>` | ✅ | Text or Markdown content — a plain string, or an inline locale map |
439-
| **variant** | `Enum<'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6' \| 'body' \| 'caption' \| 'overline' \| 'heading' \| 'subheading'>` | optional (default: `"body"`) | Text style variant |
439+
| **variant** | `Enum<'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6' \| 'body' \| 'caption' \| 'overline'>` | optional (default: `"body"`) | Text style variant |
440440
| **align** | `Enum<'left' \| 'center' \| 'right'>` | optional (default: `"left"`) | Text alignment |
441441
| **aria** | `{ ariaLabel?: string \| Record<string, string>; ariaDescribedBy?: string; role?: string }` | optional | ARIA accessibility attributes |
442442

‎examples/app-showcase/src/ui/pages/page-variables.page.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export const PageVariablesPage = definePage({
8787
visibleWhen: "page.selectedProjectId != ''",
8888
properties: {
8989
content: '✓ Project selected',
90-
variant: 'subheading',
90+
variant: 'h3',
9191
},
9292
},
9393
{

0 commit comments

Comments
 (0)