Skip to content

Commit fa9836b

Browse files
committed
Merge origin/main into claude/project-thread-yg03y1
2 parents 2582296 + 39da1bd commit fa9836b

45 files changed

Lines changed: 2163 additions & 362 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+
'@object-ui/app-shell': patch
3+
---
4+
5+
fix(setup): metadata lists under Setup take their title from the sidebar, lead with the label, and keep rows short
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
A metadata list under Setup (Positions, Permission Sets and the other catalog pages) titled itself with the type's singular label ("Position"), led each row with the API name, and let a long description wrap a row to several lines on a phone. Under Setup the page now takes its title from the app's own navigation entry that opens it, so it reads as the sidebar row that opened it ("Positions", in the served language), and falls back to the type's label when no entry opens exactly that page. Each row leads with the item's label, with its API name beneath it in small monospace type, in place of a separate Label column; an item with no label keeps its API name as the link. A description shows two lines at most, with the whole text on hover. The same page outside Setup is unchanged.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/cli': minor
4+
'@objectstack/lint': minor
5+
---
6+
7+
feat(spec): a form view's thank-you panel is translated through `objects.OBJECT._views.VIEW.submitBehavior.title` / `.message`
8+
9+
Clause-②: yes
10+
Metadata-protocol: added
11+
12+
A form whose `submitBehavior` is `{ kind: 'thank-you', title, message }` used to confirm a submit in the authoring language only: the strict `_views` translation entry declared no key for the panel, and `translateView` never read `config.submitBehavior`. A public form read in another language showed a translated heading and field labels, then a source-language confirmation.
13+
14+
- **The key.** `objects.OBJECT._views.VIEW.submitBehavior` takes `title` and `message`, nested the way the authored copy is nested, so the panel's heading never shares a key with the form's own `title`. A `thankYou` key is refused naming `submitBehavior`, and `url` / `kind` are refused with a prescription, since neither is display copy.
15+
- **The overlay.** `translateView` overlays the two strings onto the served form's `config.submitBehavior`, on the same locale chain and under the same rule as the form's `title`: only a `kind: 'thank-you'` panel is translated, only copy the form authors (a key cannot invent a `message` the form left to the console's default), a missing key keeps the authored string, and a string edited on top of the packaged view keeps the edit (ADR-0029 D9.2a, judged per string). The anonymous `GET /api/v1/forms/SLUG` already serves the translated view config under `Accept-Language`, so a public form's visitor now reads the panel in their language with no client change.
16+
- **Extraction and coverage.** `os i18n extract` scaffolds `_views.VIEW.submitBehavior.title` / `.message` for every form a view container serves and for a `viewKind: 'form'` item, keyed by its runtime identity, and the coverage report counts an authored panel a supported locale leaves untranslated.
17+
- **Lint.** `os lint` accepts a `submitBehavior` key on exactly the views a `title` key is legal on (a named form view, the default form, a form item) and refuses one over a list view.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/types': patch
4+
'@objectstack/service-automation': patch
5+
---
6+
7+
feat(spec)!: `oauth2` and `app-credential` join declarative connector-instance auth, and the runtime `oauth2` variant is retired (ADR-0143 S0, #28137)
8+
9+
Clause-②: yes (narrowing)
10+
Metadata-protocol: narrowed
11+
12+
<!-- adr-0087: registered connector-auth-config-oauth2-retired -->
13+
14+
An upstream that takes no static credential (an OAuth 2.0 API, a Feishu, DingTalk or WeCom app) had no open-tier way to be declared: the instance `auth` stopped at four static kinds, and the runtime `authentication` shape had an `oauth2` variant that held a client secret and a refresh token inline and that nothing ran. ADR-0143 opens the grant-bearing kinds on the instance, with the secret as a reference.
15+
16+
- **`@objectstack/spec`**: `ConnectorInstanceAuthSchema` gains `oauth2` (D2.1), one strict shape per `grant` (`authorization_code` with PKCE always on, `client_credentials` for a confidential client), and `app-credential` (D2.2, `vendor` `feishu` / `dingtalk` / `wecom`, `appId`, a required `credentialRef`). The publish door refuses a missing `tokenUrl`, `clientId` or `authorizationUrl` at its key under `auth`, except on `provider: 'mcp'` over `http`, where the MCP authorization flow discovers them. An inline `clientSecret`, `refreshToken`, `accessToken` or `appSecret` is refused by name with the `credentialRef` prescription. `signed` is not a member (#26710). `ConnectorOAuth2Schema` is removed from `ConnectorAuthConfigSchema` (D7), and `ResolvedConnectorAuth` stays the four static kinds. The per-dispatch handle types (`ConnectorCredentialHandle`, `BorrowedConnectorCredential`, `BorrowedConnectorAuth`, `ConnectorCredentialRequest`) move into `@objectstack/spec/integration`, and `ConnectorProviderContext` gains an optional `credentialHandle` (D4). The D3 entry `connector-auth-config-oauth2-retired` names the removal for `os migrate meta`.
17+
- **`@objectstack/types`**: re-exports the four handle types from `@objectstack/spec/integration`; the same names, structurally unchanged.
18+
- **`@objectstack/service-automation`**: this server does not run the per-dispatch token resolution yet (ADR-0143 S1). A grant-bearing instance is held **degraded** with a reason that names its kind, and its upstream is never called without the credential. A datasource that borrows it through `external.connector` is refused with `422`, naming the kind.
19+
20+
**BREAKING** for a connector that writes `authentication: { type: 'oauth2', ... }`.
21+
22+
- **Before.** `authentication: { type: 'oauth2', authorizationUrl, tokenUrl, clientId, clientSecret, refreshToken }` was accepted, with the secret and the refresh token inline, and no runtime obtained or refreshed a token from it.
23+
- **Now.** `ConnectorAuthConfigSchema` refuses it at `type`, with a prescription naming ADR-0143 D7 and the replacement. No first-party source declares it.
24+
- **Fix.** Declare the grant on a declarative instance's `auth`, with the secret as a reference: `auth: { type: 'oauth2', grant: 'authorization_code', authorizationUrl, tokenUrl, scopes, clientId, credentialRef: 'VENDOR_CLIENT_SECRET' }`, or `grant: 'client_credentials'` without `authorizationUrl`. Code that registers a connector with a token it already holds passes `{ type: 'bearer', token }`.

‎content/docs/automation/connectors.mdx‎

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -394,22 +394,67 @@ auth: { type: 'bearer', credentialRef: 'BILLING_API_TOKEN' },
394394
— fails validation, and `auth: { type: 'bearer', token: … }` is not even a
395395
legal shape (the declarative union has no `token` field to put a secret in).
396396

397-
### Where is `oauth2`?
398-
399-
Deliberately absent from declarative instances — **not** an oversight to work
400-
around. The runtime auth shape has five variants (`none`, `bearer`,
401-
`api-key`, `basic`, `oauth2`); the declarative shape stops at four. Static
402-
credentials resolved from env/config are the open tier; the OAuth2
403-
authorization-code/refresh **lifecycle** — token acquisition, rotation,
404-
per-tenant connections — is the enterprise tier (ADR-0015). If your upstream
405-
needs OAuth2, use an enterprise credential resolver or register the connector
406-
from a plugin that manages its own tokens; don't try to emulate it by stuffing
407-
a short-lived access token behind a `bearer` ref.
397+
### Grant-bearing kinds: `oauth2` and `app-credential`
398+
399+
Some upstreams take no static credential: the token is obtained from a token
400+
endpoint and refreshed as it expires. Two more `auth` kinds declare that, in
401+
the open tier (ADR-0143). They are **grant-bearing**: the metadata carries the
402+
endpoints, the client id and a `credentialRef` for the client or app secret,
403+
never a token. Access and refresh tokens are runtime state and have no key here.
404+
405+
```ts
406+
// OAuth 2.0 — a user grants access once (`authorization_code`, PKCE S256 always on):
407+
auth: {
408+
type: 'oauth2',
409+
grant: 'authorization_code',
410+
authorizationUrl: 'https://vendor.example.com/oauth/authorize',
411+
tokenUrl: 'https://vendor.example.com/oauth/token',
412+
scopes: ['crm.read'],
413+
clientId: 'objectstack-prod',
414+
credentialRef: 'VENDOR_CLIENT_SECRET', // omit for a public client
415+
}
416+
417+
// OAuth 2.0 — server to server (`client_credentials`, a confidential client):
418+
auth: {
419+
type: 'oauth2',
420+
grant: 'client_credentials',
421+
tokenUrl: 'https://vendor.example.com/oauth/token',
422+
resource: 'https://api.vendor.example.com',
423+
clientId: 'objectstack-prod',
424+
credentialRef: 'VENDOR_CLIENT_SECRET', // required for this grant
425+
}
426+
427+
// A vendor app credential (Feishu, DingTalk, WeCom): app id + app secret → tenant token
428+
auth: { type: 'app-credential', vendor: 'feishu', appId: 'cli_a1b2', credentialRef: 'FEISHU_APP_SECRET' }
429+
```
430+
431+
What the publish door checks, each refusal located at its key under `auth`:
432+
433+
- **`oauth2`** needs `tokenUrl` and `clientId`, plus `authorizationUrl` on
434+
`authorization_code`. The one exception is `provider: 'mcp'` over an `http`
435+
transport, which may omit the endpoints and the client id: the MCP
436+
authorization flow discovers the server's metadata and can register the
437+
client. `client_credentials` has no authorization step, so
438+
`authorizationUrl` is refused there, and it requires `credentialRef`.
439+
A `credentialRef` without its `clientId` is refused. An inline
440+
`clientSecret`, `refreshToken` or `accessToken` is refused by name, pointing
441+
at `credentialRef`. There is no PKCE switch.
442+
- **`app-credential`** takes `vendor` (`feishu`, `dingtalk` or `wecom`), `appId`
443+
and a required `credentialRef`. An inline `appSecret` is refused by name.
444+
445+
A server that does not yet run the per-dispatch token resolution holds such an
446+
instance **degraded**, with a reason that names the grant-bearing kind, and
447+
never calls the upstream without its credential. A datasource that borrows
448+
from it (`external.connector`) is refused with `422`. Until the server serves
449+
them, declare a static kind (`bearer`, `api-key` or `basic`) to dispatch now;
450+
don't emulate a grant by putting a short-lived access token behind a `bearer`
451+
ref.
408452

409453
### The runtime shape: `authentication`
410454

411-
The `authentication` field is the **runtime** auth config — it carries the
412-
resolved secret *inline* (`{ type: 'bearer', token }`), because it is supplied
455+
The `authentication` field is the **runtime** auth config, with four static
456+
variants (`none`, `bearer`, `api-key`, `basic`). It carries the resolved
457+
secret *inline* (`{ type: 'bearer', token }`), because it is supplied
413458
by **host code** (a plugin calling `engine.registerConnector`, or the
414459
executors' hand-wired instance options reading `process.env`). That is a
415460
different trust anchor than metadata: code review guards what enters it, and
@@ -418,6 +463,11 @@ leave `authentication` alone — provider-bound instances must use `auth`, and
418463
catalog descriptors documenting a planned integration must still never carry a
419464
live secret value.
420465

466+
The runtime shape's `oauth2` variant was removed in `@objectstack/spec` 18
467+
(ADR-0143 D7): it held a client secret and a refresh token inline and nothing
468+
ran its lifecycle. `authentication: { type: 'oauth2' }` is refused with that
469+
prescription; declare the grant on an instance's `auth` instead, as above.
470+
421471
## Dispatching from a flow
422472

423473
A materialized instance is dispatched exactly like any other connector — by
@@ -449,6 +499,7 @@ retry):
449499
|:---|:---|
450500
| `provider` names no installed factory | **Hard boot error** naming the entry, the provider, and the plugin that supplies it — add the executor to `plugins:` |
451501
| Invalid `providerConfig` (missing `baseUrl` / `spec` / `transport`, wrong shape) | **Hard boot error** from the provider factory |
502+
| `auth` is a grant-bearing kind (`oauth2`, `app-credential`) on a server that does not yet run its per-dispatch resolution | **Not fatal**: the instance registers **degraded**, naming the kind, and the upstream is never called without its credential; a datasource borrowing it is refused with `422` |
452503
| `credentialRef` resolves to nothing | **Not fatal**: the instance registers **degraded**, naming the reference — set it (`PUT …/credential-refs/:ref`), and it materializes without a restart |
453504
| The upstream refuses the credential (`401` / `403`) | **Not fatal**: the instance registers **degraded**. A stored credential is marked `rejected` and waits until it is set again (or a restart); an environment or vault credential is retried on the backoff |
454505
| Instance `name` collides with a plugin-registered connector | **Hard boot error** — there is no silent precedence between the two worlds |

‎content/docs/references/index.mdx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Protocol reference — every schema by module
33
navTitle: Protocol Reference
4-
description: Every schema published by @objectstack/spec — 1549 schemas across 14 protocol modules
4+
description: Every schema published by @objectstack/spec — 1555 schemas across 14 protocol modules
55
---
66

77
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -25,7 +25,7 @@ counts are sums of the rows they head. Regenerate with
2525
| [Automation Protocol](/docs/references/automation) | 15 | 77 | Flows and their nodes, approvals, ETL pipelines, webhooks, state machines, execution records. |
2626
| [Data Protocol](/docs/references/data) | 31 | 174 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. |
2727
| [Identity Protocol](/docs/references/identity) | 5 | 27 | Users and accounts, organizations, positions, SCIM provisioning. |
28-
| [Integration Protocol](/docs/references/integration) | 1 | 12 | The single connector protocol (ADR-0097) — catalog descriptors and provider-bound instances. |
28+
| [Integration Protocol](/docs/references/integration) | 1 | 18 | The single connector protocol (ADR-0097) — catalog descriptors and provider-bound instances. |
2929
| [Kernel Protocol](/docs/references/kernel) | 29 | 151 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. |
3030
| [Marketplace Protocol](/docs/references/marketplace) | 4 | 30 | The package & marketplace format — package identity and versions, listing, publish, review, search, install, template manifests. |
3131
| [QA Protocol](/docs/references/qa) | 1 | 8 | Declarative test suites — scenarios, steps, actions and assertions. |
@@ -34,7 +34,7 @@ counts are sums of the rows they head. Regenerate with
3434
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
3535
| [System Protocol](/docs/references/system) | 37 | 287 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
3636
| [UI Protocol](/docs/references/ui) | 16 | 172 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
37-
| **Total** | **202** | **1549** | 14 protocol modules |
37+
| **Total** | **202** | **1555** | 14 protocol modules |
3838

3939
---
4040

@@ -190,13 +190,13 @@ Users and accounts, organizations, positions, SCIM provisioning.
190190

191191
## Integration Protocol
192192

193-
**Source:** `packages/spec/src/integration/` · **Import:** `@objectstack/spec/integration` · **1 page, 12 schemas**
193+
**Source:** `packages/spec/src/integration/` · **Import:** `@objectstack/spec/integration` · **1 page, 18 schemas**
194194

195195
The single connector protocol (ADR-0097) — catalog descriptors and provider-bound instances.
196196

197197
| File | Schemas |
198198
| :--- | :--- |
199-
| [`connector.zod.ts`](/docs/references/integration/connector) | `Connector`, `ConnectorAction`, `ConnectorActionEffect`, `ConnectorInstanceAPIKeyAuth`, `ConnectorInstanceAuth`, `ConnectorInstanceBasicAuth`, `ConnectorInstanceBearerAuth`, `ConnectorInstanceNoAuth`, `ConnectorRetryStrategy`, `ConnectorType`, `DeclarativeConnectorEntry`, `RetryConfig` |
199+
| [`connector.zod.ts`](/docs/references/integration/connector) | `Connector`, `ConnectorAction`, `ConnectorActionEffect`, `ConnectorAppCredentialVendor`, `ConnectorInstanceAPIKeyAuth`, `ConnectorInstanceAppCredentialAuth`, `ConnectorInstanceAuth`, `ConnectorInstanceBasicAuth`, `ConnectorInstanceBearerAuth`, `ConnectorInstanceNoAuth`, `ConnectorInstanceOAuth2Auth`, `ConnectorInstanceOAuth2AuthorizationCodeAuth`, `ConnectorInstanceOAuth2ClientCredentialsAuth`, `ConnectorOAuth2Grant`, `ConnectorRetryStrategy`, `ConnectorType`, `DeclarativeConnectorEntry`, `RetryConfig` |
200200

201201
---
202202

0 commit comments

Comments
 (0)