Skip to content

Commit c0c2549

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-20602-export-year-pad
Brings in PR #20746 (core builds a UTC instant from parts with wallClockToUtcMs), so the export -> import round trip for datetime years 0001..0099 can be measured against a base that reads the padded year right. Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB Co-authored-by: Claude <noreply@anthropic.com>
2 parents e739a50 + 9509ea1 commit c0c2549

1,076 files changed

Lines changed: 44678 additions & 6455 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: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
'@objectstack/rest': minor
3+
---
4+
5+
feat(rest): `GET /api/v1/data/:object/export?template=true` answers an xlsx import template for the object (#18386)
6+
7+
Clause-②: yes (widening)
8+
9+
The export door takes one more query parameter, `template`. `template=true`
10+
answers an `.xlsx` workbook with no data rows; `template=false` answers the export.
11+
Without a `template` parameter the export is exactly as before, byte for byte.
12+
13+
- **Columns.** Every field of the object except
14+
those marked `system` or `readonly`, and `formula`, `summary` and
15+
`autonumber` fields, in the order the object declares them. A `hidden` field
16+
that can be written is a column. The seven columns the platform adds to every
17+
object (`organization_id`, `created_at`, `created_by`, `updated_at`,
18+
`updated_by`, `owner_id`, `owning_business_unit_id`) are never template
19+
columns. A field the caller's field-level security does not let them edit is
20+
left out. If the security service cannot say which fields the caller can edit,
21+
the columns are narrowed by the fields the caller can read instead. The
22+
instructions sheet then says so, and the `X-Export-Template-Projection`
23+
response header reads `readable` instead of `writable` (`none` when no
24+
field-level security applies). An explicit `?fields=` list is used as sent.
25+
- **First sheet.** The header row, with ` *` after each field that is required
26+
and has no default value, and one example row to replace or delete. Select,
27+
radio and boolean columns carry a dropdown.
28+
- **Second sheet.** One row per column: the field's API name, its type, whether
29+
it is required, and the values the import accepts for it.
30+
- **Language.** The sheets are in Chinese for a `zh` request locale
31+
(`?locale=` or `Accept-Language`) and in English otherwise.
32+
33+
The same two permission checks as the export apply: an object that does not
34+
expose export answers `405`, and a caller without the export permission answers
35+
`403`. `template` with a value other than `true` or `false`, a `format` other
36+
than `xlsx`, or any of `limit`, `page`, `filter`, `search`, `searchFields`,
37+
`orderby` or `header` beside `template=true`, answers `400 VALIDATION_ERROR`.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@objectstack/plugin-security': minor
3+
---
4+
5+
The `security` service implements `getWritableFields(object, context)` (#18386). It uses the same permission sets, field grants, `requiredPermissions` check and on-behalf-of delegator intersection as the write gate. A field is in the answer exactly when a write naming it passes the field-level-security check. `getReadableFields` now shares that derivation, and its answers are unchanged.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
`ISecurityService` (`@objectstack/spec/contracts`) gains an optional `getWritableFields(object, context)`: the field names field-level security lets the caller write on the object, the write-side twin of `getReadableFields` (#18386).
6+
7+
Clause-②: yes (widening)
8+
9+
- It is the exact complement of the fields the write path's field-level-security gate refuses when a payload names them. Neither the object permission nor a field's own rules (`readonly`, `system`, a `formula`, `summary` or `autonumber` type) are part of the answer.
10+
- It fails soft like `getReadableFields`: `undefined` means no answer, `[]` means no field is writable. A system context gets every field.
11+
- It is optional. A consumer checks `typeof svc.getWritableFields === 'function'`. When the method is missing, the consumer may narrow by `getReadableFields` instead, and must say in its response that it did.

‎.changeset/19518-picklist-kind.md‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/platform-objects': patch
4+
'@objectstack/cli': patch
5+
'@objectstack/driver-sql': patch
6+
---
7+
8+
feat(spec): the `picklist` metadata kind — a shared option list that select fields reference by name (#19518)
9+
10+
Clause-②: yes (widening)
11+
12+
- **The kind.** `PicklistSchema` — `{ name, label, description?, options }`, where `options` is the field option shape (`SelectOptionSchema`) reused as is. Authored in a package as `*.picklist.ts` (`definePicklist`) or `defineStack({ picklists })`. It is a registered kind (`MetadataTypeSchema`, `DEFAULT_METADATA_TYPE_REGISTRY`, `getMetadataTypeSchema('picklist')`) that loads before `object`. It is package-owned, so a runtime create or a per-organization overlay is refused.
13+
- **The reference.** `Field.select({ picklist: 'industry' })` adds a `picklist` key to `FieldSchema`. It is valid on the option types only (select, radio, multiselect, checkboxes, tags). A field that declares both `picklist` and `options` is refused at `options`, with a prescription. The functional-completeness predicate counts a `picklist` reference as the field's option source.
14+
- **The served shape.** `PicklistServedFieldSchema` declares what a client reads for a picklist-bound field: the resolved `options` next to the `picklist` that names the list. This release does not resolve the reference. Until the runtime does, a picklist-bound field is served without options, and the liveness ledger grades the key `planned` and warns an author who writes it.
15+
- **Extensions.** `defineStack({ picklistExtensions: [{ extend, options }] })` adds options to a picklist that another package owns. It can only add; removing or renaming a value stays with the owning package.
16+
- **Translation.** `TranslationData` gains `picklists.<name>.{ label?, options: { value: label } }`. `translatePicklist` translates a served picklist item. `translateObject` gives a picklist-bound field the list's option labels, and a field-level `options` entry still wins over them.
17+
- **Studio type label.** `@objectstack/platform-objects` carries the `picklist` type's label and description in its metadata-forms translation bundles (en, zh-CN, ja-JP, es-ES).
18+
- **Extraction.** `os i18n extract` walks `picklists.NAME.{label, options.VALUE}`, including an extension's options under the list it extends, and `os lint` reports an untranslated option under its own rule, `i18n/missing-picklist`.
19+
- **SQL driver.** The SQL driver classifies the `picklist` field key as presentation, so it adds no column.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
docs(spec): the `picklist` field key's description, two doc comments and the refusal of `picklist` with `options` no longer say that the reference is resolved and its options served (#19518)
6+
7+
Clause-②: no
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
'@objectstack/metadata-protocol': minor
3+
'@objectstack/spec': minor
4+
---
5+
6+
fix(metadata-protocol,spec)!: a saved view stores the parsed value of every key its body carried, and a ViewItem record's top-level `options` bag is refused by name (#20051)
7+
8+
**BREAKING** — two narrowings on the `view` write door (`PUT /api/v1/meta/view/:name`, the Studio and MCP save). They ship as `minor` under the repo's launch-window convention for breaking changes. This is stage (iv), the last stage, of ruling 甲 on #20051. The storage half follows the maintainer's ruling on its Q2, letter B (「同意 批次 #256」).
9+
10+
Clause-②: no (narrowing)
11+
12+
## What changes
13+
14+
**1. What a saved view stores.** `saveMetaItem` used to validate a `view` body and then store the request body as sent, with two normalizations grafted back (filter operator spellings, and form `groups` → `sections`). It now stores the parsed value of every key the body carried, and nothing else:
15+
16+
- **An undeclared key is dropped.** The members' top-level `.strip()` used to drop it from the parse only, so it lived in the store and nowhere in the contract. Every key the console reads back off a stored row is declared (`VIEW_CONSOLE_ROUND_TRIP_KEYS`), so nothing the console relies on is lost. The keys the console writes that are dropped are the ones mapped rather than declared in that record: a sort row's `id` (the builders mint a fresh one), a top-level `id` (read only when a row has no `name`), and `objectName` (every reader falls back to `object`, which both writers stamp).
17+
- **A declared key keeps its normalized value.** Examples: `notEquals` → `not_equals`, `exportOptions: ['csv']` → `{ formats: ['csv'] }`, and a CEL string → its expression object.
18+
- **A moved key is stored under its canonical spelling.** Examples: `groups` → `sections`, and `visibleOn` → `visibleWhen`. The second was never grafted before, so a form stored with `visibleOn` kept the legacy spelling.
19+
- **A schema default the author did not write is NOT stored.** This is ADR-0087's `storable` rule, the one flows already follow: a stored row never pins the day's default. A console toolbar save (sort, density, hidden fields, column widths, inline edit) stores no `type: 'grid'`. A form stores no `sharing.enabled` and no section `collapsible` / `collapsed` / `columns` it did not write. Storing the whole parse output instead would also have minted rows that fail their own re-save: a column-less toolbar patch carrying the `grid` default is refused as "sets `type` but lists no `columns`".
20+
21+
The stored row re-parses to exactly what the save accepted, so a GET → PUT of it is judged the same. Every other metadata type keeps its request body, with the two grafts, as before.
22+
23+
**2. A ViewItem record's top-level `options` bag is refused.** On a record (`{ name, object, viewKind, config }`), the member's top-level strip used to drop `options` from the parse unread while the save stored it. The interface page then rendered it and the object page did not. It is now refused by name with `422 INVALID_METADATA` at `options`, and the message prescribes `config.KIND`. No console write puts the bag on a record. The flattened list overlay's legacy `options` bag is unchanged: it is judged key by key, and objectui pins it.
24+
25+
## FROM → TO
26+
27+
| you wrote | the stored row / the door now |
28+
|:--|:--|
29+
| a view body with a key its member does not declare (`objectName`, a form-only `layout` on a list view, `isPinned` on a form) | the key is not stored: write only declared keys (`object`, not `objectName`) |
30+
| a view body relying on a schema default being written into the row | the row carries only what you wrote; the parse applies the default on every read |
31+
| `sort: [{ id, field, order }]` | stored as `sort: [{ field, order }]` |
32+
| `groups: [...]` / `visibleOn: '…'` on a form | stored as `sections: [...]` / `visibleWhen: { dialect: 'cel', source: '…' }` |
33+
| a ViewItem record with `options: { kanban: {...} }` | `422` at `options`: move it to `config: { kanban: {...} }` (`config.KIND`), or remove it |
34+
35+
**The one-line fix:** write the declared spelling. A stored view you read back is what the contract accepts, and a record's per-kind blocks live under `config`.
36+
37+
## Existing rows
38+
39+
Stored rows are not migrated and not re-read differently. The maintainer's word on this card is 「20051 不考虑现有的数据」. A row keeps its bytes until its next save, and that save stores it as described above. A record carrying a top-level `options` is refused on its next save and served as stored until then.
40+
41+
<!-- adr-0087: registered view-item-options-bag-refused -->
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@objectstack/platform-objects': patch
3+
'@objectstack/plugin-audit': patch
4+
---
5+
6+
fix(platform-objects,plugin-audit): Setup and Studio navigation entries for the console's Audit Log and Integrations & APIs pages (#20142)
7+
8+
The console retired its System Hub card wall and its Developer Hub, which had been the only in-app links to several pages, and registered each page under a component-registry key instead. Framework navigation reaches a console page only through a `type: 'component'` item that names such a key, and no item named them, so each page was reachable only by a typed URL. Two entries now name the pages whose capability ships in the open framework:
9+
10+
| Entry | App / group | `componentRef` | Contributed by | Gate |
11+
| --- | --- | --- | --- | --- |
12+
| `nav_audit_log_browser` ("Audit Log Browser") | Setup / Diagnostics, directly under Audit Logs | `audit:log` | `@objectstack/plugin-audit` | none: it lives and dies with the plugin that owns `sys_audit_log` |
13+
| `nav_integrations` ("Integrations & APIs") | Studio / Developer, after Public Forms | `developer:integrations` | `@objectstack/platform-objects` | none beyond Studio's own `studio.access` |
14+
15+
**Two audit entries, on purpose.** The existing Audit Logs entry (the `sys_audit_log` object view) stays. It carries the named list views, search, and the actor and tenant rendered as resolved lookups. The new page adds one filterable table whose detail drawer pretty-prints a change's before and after JSON, where the record page shows `old_value` / `new_value` as raw text. Neither surface replaces the other.
16+
17+
**No entry for the console's AI Approvals page (`ai:approvals`) here.** Under ADR-0029 D7, each capability plugin contributes its own navigation entries into a Setup slot, and the Setup shell does not enumerate capability objects. The AI pending-action queue belongs to the AI capability, whose provider (`@objectstack/service-ai`) ships in Cloud/Enterprise, not in the open framework. Its entry is therefore that capability's to contribute.
18+
19+
The keys are the ones the console registers at the objectui commit this release's console is built from. Labels ship in all four locales (en, zh-CN, ja-JP, es-ES), with their source hashes recorded. Nothing is removed or renamed, and there is nothing to migrate.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
Notes in seven more liveness ledgers cite the commit that decided them, or say the decision in words, instead of a tracker number that no longer resolves
6+
7+
Clause-②: no
8+
9+
Notes in the `datasource`, `api`, `query`, `object`, `email_template`, `mapping` and
10+
`webhook` ledgers named GitHub issues that no longer exist, so a reader could not tell why
11+
a row carries its verdict. Each such note now either names the commit that made the
12+
decision or, where the number alone carried the meaning, says what was decided. The
13+
`manifest` ledger's `permissions` note also names the commit that recorded its structured
14+
arm's zero apart. The `liveness/` ledgers ship in this package's tarball, which is why
15+
this is a release note at all. Note text only: no row's status, evidence, proof or date
16+
changes, and no schema, export or runtime behaviour changes.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
Notes in twenty-one more liveness ledgers, and one `datasource` evidence string, cite the commit that decided them, or say the decision in words, instead of a tracker number that no longer resolves
6+
7+
Clause-②: no
8+
9+
Notes in the `book`, `doc`, `job`, `validation`, `translation`, `hook`, `seed`, `flow`,
10+
`capability`, `qa`, `dashboard`, `action`, `agent`, `skill`, `tool`, `rest_api`,
11+
`route_generation`, `crud_endpoints`, `metadata_endpoints`, `batch_endpoints` and
12+
`analytics_cube` ledgers cited tracker numbers that no longer resolve on GitHub, so a reader
13+
could not tell why a row carries its verdict. Each such note now either names the commit that
14+
made the decision or, where the number alone carried the meaning, says what was decided. The
15+
`datasource` ledger's `ssl.rejectUnauthorized` evidence string cited one such number in its
16+
prose; it now names the commit that made the fix, and its code anchors are unchanged. The
17+
`liveness/` ledgers ship in this package's tarball, which is why this is a release note at all.
18+
Note text and that one evidence parenthesis only: no row's status, proof or date changes, and
19+
no schema, export or runtime behaviour changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
Notes in the `app` and `view` liveness ledgers that cited a tracker number which no longer resolves now either cite the commit that decided them or say the decision in words
6+
7+
Clause-②: no
8+
9+
Sixteen notes in the `app` and `view` ledgers cited a GitHub issue that no longer exists, so a
10+
reader could not tell why a row carries its verdict. Each such note now either names the commit
11+
that made the decision or, where the number alone carried the meaning, says what was decided.
12+
The `liveness/` ledgers ship in this package's tarball, which is why this is a release note at
13+
all. Note text only: no row's status, evidence, proof, producer or date changes, and no schema,
14+
export or runtime behaviour changes.

0 commit comments

Comments
 (0)