@tier0/sdk-v2 0.9.9 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +102 -0
- package/LICENSE +21 -21
- package/README.md +165 -27
- package/dist/cjs/client.d.ts +79 -3
- package/dist/cjs/client.js +4 -6
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/cloud-server.d.ts +33 -26
- package/dist/cjs/cloud-server.js +102 -100
- package/dist/cjs/cloud-server.js.map +1 -1
- package/dist/cjs/cloud-transport.d.ts +15 -20
- package/dist/cjs/cloud-transport.js +87 -68
- package/dist/cjs/cloud-transport.js.map +1 -1
- package/dist/cjs/connection-diagnostics.d.ts +2 -0
- package/dist/cjs/connection-diagnostics.js +20 -0
- package/dist/cjs/connection-diagnostics.js.map +1 -0
- package/dist/cjs/data/bootstrap.js +3 -4
- package/dist/cjs/data/bootstrap.js.map +1 -1
- package/dist/cjs/data/call.d.ts +1 -3
- package/dist/cjs/data/call.js +8 -12
- package/dist/cjs/data/call.js.map +1 -1
- package/dist/cjs/data/client.d.ts +8 -7
- package/dist/cjs/data/client.js +24 -11
- package/dist/cjs/data/client.js.map +1 -1
- package/dist/cjs/data/collection.d.ts +3 -5
- package/dist/cjs/data/collection.js +3 -4
- package/dist/cjs/data/collection.js.map +1 -1
- package/dist/cjs/data/context.d.ts +5 -11
- package/dist/cjs/data/context.js +21 -36
- package/dist/cjs/data/context.js.map +1 -1
- package/dist/cjs/data/error.d.ts +1 -1
- package/dist/cjs/data/error.js +3 -3
- package/dist/cjs/data/error.js.map +1 -1
- package/dist/cjs/data/index.d.ts +5 -3
- package/dist/cjs/data/index.js +5 -5
- package/dist/cjs/data/index.js.map +1 -1
- package/dist/cjs/data/relay.d.ts +51 -0
- package/dist/cjs/data/relay.js +371 -0
- package/dist/cjs/data/relay.js.map +1 -0
- package/dist/cjs/data/subscribe.d.ts +22 -38
- package/dist/cjs/data/subscribe.js +22 -28
- package/dist/cjs/data/subscribe.js.map +1 -1
- package/dist/cjs/event-stream.d.ts +28 -0
- package/dist/cjs/event-stream.js +222 -0
- package/dist/cjs/event-stream.js.map +1 -0
- package/dist/cjs/mq/client.d.ts +32 -1
- package/dist/cjs/mq/client.js +404 -69
- package/dist/cjs/mq/client.js.map +1 -1
- package/dist/cjs/mq/index.d.ts +1 -0
- package/dist/cjs/mq/index.js +3 -1
- package/dist/cjs/mq/index.js.map +1 -1
- package/dist/cjs/mq/topic.d.ts +29 -0
- package/dist/cjs/mq/topic.js +48 -0
- package/dist/cjs/mq/topic.js.map +1 -0
- package/dist/cjs/mq/types.d.ts +21 -1
- package/dist/cjs/mq/types.js.map +1 -1
- package/dist/cjs/openapi/api.generated.d.ts +20 -0
- package/dist/cjs/openapi/api.generated.js +11 -0
- package/dist/cjs/openapi/api.generated.js.map +1 -1
- package/dist/cjs/openapi/client.d.ts +46 -0
- package/dist/cjs/openapi/client.js +161 -12
- package/dist/cjs/openapi/client.js.map +1 -1
- package/dist/cjs/openapi/codecs.generated.d.ts +74 -0
- package/dist/cjs/openapi/codecs.generated.js +1 -1
- package/dist/cjs/openapi/codecs.generated.js.map +1 -1
- package/dist/cjs/openapi/types.generated.d.ts +205 -1
- package/dist/cjs/openapi/types.generated.js.map +1 -1
- package/dist/cjs/page-runtime.d.ts +48 -0
- package/dist/cjs/page-runtime.js +74 -0
- package/dist/cjs/page-runtime.js.map +1 -0
- package/dist/cjs/platform-proxy.d.ts +17 -0
- package/dist/cjs/platform-proxy.js +52 -0
- package/dist/cjs/platform-proxy.js.map +1 -0
- package/dist/cjs/runtime-context.d.ts +2 -1
- package/dist/cjs/runtime-context.js +6 -2
- package/dist/cjs/runtime-context.js.map +1 -1
- package/dist/cjs/runtime-env.js.map +1 -1
- package/dist/cjs/source.d.ts +19 -1
- package/dist/cjs/source.js +9 -3
- package/dist/cjs/source.js.map +1 -1
- package/dist/cjs/stream.js +3 -2
- package/dist/cjs/stream.js.map +1 -1
- package/dist/cjs/streams/contract.generated.d.ts +7 -1
- package/dist/cjs/streams/contract.generated.js.map +1 -1
- package/dist/cjs/streams/messages.js +1 -10
- package/dist/cjs/streams/messages.js.map +1 -1
- package/dist/cjs/streams/requests.d.ts +3 -0
- package/dist/cjs/streams/requests.js +5 -2
- package/dist/cjs/streams/requests.js.map +1 -1
- package/dist/cjs/streams/transport.d.ts +1 -13
- package/dist/cjs/streams/transport.js +2 -18
- package/dist/cjs/streams/transport.js.map +1 -1
- package/dist/cjs/uns-v2/client.d.ts +1 -6
- package/dist/cjs/uns-v2/client.js +33 -8
- package/dist/cjs/uns-v2/client.js.map +1 -1
- package/dist/cjs/uns-v2/data-client.d.ts +5 -2
- package/dist/cjs/uns-v2/data-client.js +7 -4
- package/dist/cjs/uns-v2/data-client.js.map +1 -1
- package/dist/cjs/uns-v2/generated.d.ts +6783 -4655
- package/dist/cjs/uns-v2/generated.js +221 -65
- package/dist/cjs/uns-v2/generated.js.map +1 -1
- package/dist/cjs/uns-v2/sse.d.ts +7 -1
- package/dist/cjs/uns-v2/sse.js +12 -2
- package/dist/cjs/uns-v2/sse.js.map +1 -1
- package/dist/esm/client.d.ts +79 -3
- package/dist/esm/client.js +5 -7
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/cloud-server.d.ts +33 -26
- package/dist/esm/cloud-server.js +99 -97
- package/dist/esm/cloud-server.js.map +1 -1
- package/dist/esm/cloud-transport.d.ts +15 -20
- package/dist/esm/cloud-transport.js +86 -67
- package/dist/esm/cloud-transport.js.map +1 -1
- package/dist/esm/connection-diagnostics.d.ts +2 -0
- package/dist/esm/connection-diagnostics.js +17 -0
- package/dist/esm/connection-diagnostics.js.map +1 -0
- package/dist/esm/data/bootstrap.js +3 -4
- package/dist/esm/data/bootstrap.js.map +1 -1
- package/dist/esm/data/call.d.ts +1 -3
- package/dist/esm/data/call.js +9 -13
- package/dist/esm/data/call.js.map +1 -1
- package/dist/esm/data/client.d.ts +8 -7
- package/dist/esm/data/client.js +22 -9
- package/dist/esm/data/client.js.map +1 -1
- package/dist/esm/data/collection.d.ts +3 -5
- package/dist/esm/data/collection.js +3 -4
- package/dist/esm/data/collection.js.map +1 -1
- package/dist/esm/data/context.d.ts +5 -11
- package/dist/esm/data/context.js +19 -33
- package/dist/esm/data/context.js.map +1 -1
- package/dist/esm/data/error.d.ts +1 -1
- package/dist/esm/data/error.js +3 -3
- package/dist/esm/data/error.js.map +1 -1
- package/dist/esm/data/index.d.ts +5 -3
- package/dist/esm/data/index.js +3 -2
- package/dist/esm/data/index.js.map +1 -1
- package/dist/esm/data/relay.d.ts +51 -0
- package/dist/esm/data/relay.js +368 -0
- package/dist/esm/data/relay.js.map +1 -0
- package/dist/esm/data/subscribe.d.ts +22 -38
- package/dist/esm/data/subscribe.js +22 -27
- package/dist/esm/data/subscribe.js.map +1 -1
- package/dist/esm/event-stream.d.ts +28 -0
- package/dist/esm/event-stream.js +217 -0
- package/dist/esm/event-stream.js.map +1 -0
- package/dist/esm/mq/client.d.ts +32 -1
- package/dist/esm/mq/client.js +404 -69
- package/dist/esm/mq/client.js.map +1 -1
- package/dist/esm/mq/index.d.ts +1 -0
- package/dist/esm/mq/index.js +1 -0
- package/dist/esm/mq/index.js.map +1 -1
- package/dist/esm/mq/topic.d.ts +29 -0
- package/dist/esm/mq/topic.js +44 -0
- package/dist/esm/mq/topic.js.map +1 -0
- package/dist/esm/mq/types.d.ts +21 -1
- package/dist/esm/mq/types.js.map +1 -1
- package/dist/esm/openapi/api.generated.d.ts +20 -0
- package/dist/esm/openapi/api.generated.js +11 -0
- package/dist/esm/openapi/api.generated.js.map +1 -1
- package/dist/esm/openapi/client.d.ts +46 -0
- package/dist/esm/openapi/client.js +160 -12
- package/dist/esm/openapi/client.js.map +1 -1
- package/dist/esm/openapi/codecs.generated.d.ts +74 -0
- package/dist/esm/openapi/codecs.generated.js +1 -1
- package/dist/esm/openapi/codecs.generated.js.map +1 -1
- package/dist/esm/openapi/types.generated.d.ts +205 -1
- package/dist/esm/openapi/types.generated.js.map +1 -1
- package/dist/esm/page-runtime.d.ts +48 -0
- package/dist/esm/page-runtime.js +65 -0
- package/dist/esm/page-runtime.js.map +1 -0
- package/dist/esm/platform-proxy.d.ts +17 -0
- package/dist/esm/platform-proxy.js +47 -0
- package/dist/esm/platform-proxy.js.map +1 -0
- package/dist/esm/runtime-context.d.ts +2 -1
- package/dist/esm/runtime-context.js +6 -2
- package/dist/esm/runtime-context.js.map +1 -1
- package/dist/esm/runtime-env.js.map +1 -1
- package/dist/esm/source.d.ts +19 -1
- package/dist/esm/source.js +10 -4
- package/dist/esm/source.js.map +1 -1
- package/dist/esm/stream.js +4 -3
- package/dist/esm/stream.js.map +1 -1
- package/dist/esm/streams/contract.generated.d.ts +7 -1
- package/dist/esm/streams/contract.generated.js.map +1 -1
- package/dist/esm/streams/messages.js +1 -10
- package/dist/esm/streams/messages.js.map +1 -1
- package/dist/esm/streams/requests.d.ts +3 -0
- package/dist/esm/streams/requests.js +5 -2
- package/dist/esm/streams/requests.js.map +1 -1
- package/dist/esm/streams/transport.d.ts +1 -13
- package/dist/esm/streams/transport.js +1 -17
- package/dist/esm/streams/transport.js.map +1 -1
- package/dist/esm/uns-v2/client.d.ts +1 -6
- package/dist/esm/uns-v2/client.js +33 -8
- package/dist/esm/uns-v2/client.js.map +1 -1
- package/dist/esm/uns-v2/data-client.d.ts +5 -2
- package/dist/esm/uns-v2/data-client.js +8 -5
- package/dist/esm/uns-v2/data-client.js.map +1 -1
- package/dist/esm/uns-v2/generated.d.ts +6783 -4655
- package/dist/esm/uns-v2/generated.js +221 -65
- package/dist/esm/uns-v2/generated.js.map +1 -1
- package/dist/esm/uns-v2/sse.d.ts +7 -1
- package/dist/esm/uns-v2/sse.js +12 -2
- package/dist/esm/uns-v2/sse.js.map +1 -1
- package/package.json +25 -12
- package/skills/tier0-sdk-v2/SKILL.md +13 -5
- package/skills/tier0-sdk-v2/references/integration.md +14 -24
- package/skills/tier0-sdk-v2/references/local-connection.md +61 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/SKILL.md +11 -5
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/consistency.md +8 -12
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/errors.md +24 -5
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/modeling.md +3 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/read-write.md +46 -13
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/schema-evolution.md +2 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-members/SKILL.md +2 -2
- package/skills/tier0-sdk-v2/tier0-sdk-v2-members/references/app-members.md +5 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-mq/SKILL.md +25 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-mq/references/quickstart.md +1 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-notifications/SKILL.md +3 -3
- package/skills/tier0-sdk-v2/tier0-sdk-v2-notifications/references/send.md +6 -6
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/SKILL.md +4 -4
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/changes.md +2 -2
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/create.md +26 -36
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/delete.md +9 -23
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/protocols.md +12 -29
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/read.md +4 -5
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/update.md +5 -5
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/SKILL.md +3 -3
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/create.md +23 -9
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/deploy.md +14 -12
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/http-server.md +4 -4
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/read.md +4 -3
- package/skills/tier0-sdk-v2/tier0-sdk-v2-streams/SKILL.md +12 -12
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/SKILL.md +3 -2
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/create.md +2 -2
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/delete.md +47 -20
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/namespace.md +3 -3
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/read.md +11 -6
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/restore.md +12 -13
- package/dist/cjs/app-identity-contract.generated.d.ts +0 -22
- package/dist/cjs/app-identity-contract.generated.js +0 -51
- package/dist/cjs/app-identity-contract.generated.js.map +0 -1
- package/dist/cjs/runtime.d.ts +0 -13
- package/dist/cjs/runtime.js +0 -51
- package/dist/cjs/runtime.js.map +0 -1
- package/dist/esm/app-identity-contract.generated.d.ts +0 -22
- package/dist/esm/app-identity-contract.generated.js +0 -48
- package/dist/esm/app-identity-contract.generated.js.map +0 -1
- package/dist/esm/runtime.d.ts +0 -13
- package/dist/esm/runtime.js +0 -46
- package/dist/esm/runtime.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,107 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.0 (2026-11-10)
|
|
4
|
+
|
|
5
|
+
First stable npm release of the new-architecture migration, including the changes recorded below for the local aenv-next prereleases and the 0.9.11 candidate.
|
|
6
|
+
|
|
7
|
+
- Breaking: hosted Apps call the platform as their own App Key. Employee delegation, visitor-token verification and per-App platform policies are removed; use `parseVisitorHeader()` for trusted front-door visitor context, `runWithAbortSignal()` for cancellation and `createChangeRelay()` for server-mediated realtime data.
|
|
8
|
+
- App platform capabilities are fixed by the Backend contract. MQTT topics use the App namespace through `appTopic()`; external collection grants require a workspace Owner/Admin.
|
|
9
|
+
- Include explicit credential-source isolation, asynchronous per-request `apiKeyProvider`, single-header `X-API-Key` authentication, static Data-client identity and MQTT reconnect fixes.
|
|
10
|
+
- Include the native Source/Stream contract and diagnostic updates. Deploy against a Backend and App runtime that support these contracts; the prior local prerelease entries below describe the migration details.
|
|
11
|
+
|
|
12
|
+
- Added typed `client.uns.sampleDeleteExact` for bounded, explicit `{recordId,id,time}` Sample deletion with preview, missing counts and idempotent receipts. This Workspace management operation is unavailable to App keys; no App data-layer delete method or acquisition deduplication is added.
|
|
13
|
+
- Synchronized the committed Backend contract at `91b5f3dc4024f44df32ee7aafcd83a8d470b655d`, including Stream observations' optional `view:'summary'|'full'` and optional diagnostics for summary responses, plus the optional `charts`, `findings`, `metrics` and `severity` fields on `AgentResultCreateReq`. Generated methods continue to preserve exact request identities and server retry advice without automatically retrying writes.
|
|
14
|
+
|
|
15
|
+
## 0.10.0-aenv.11 (local aenv-next prerelease)
|
|
16
|
+
|
|
17
|
+
Breaking: an App's key is collections plus a fixed set of platform capabilities; there is no per-App platform policy any more (Backend `app-key-model/1`).
|
|
18
|
+
|
|
19
|
+
- Removed with the Backend contract, without replacement: `client.uns.appStreamPolicyGet` / `appStreamPolicyUpdate` (`/openapi/v2/uns/app-stream-policy/get|update`), their raw operations and codec schemas, and the types `CloudAppStreamPolicy`, `CloudAppStreamPolicyGetReq`, `CloudAppStreamPolicyGetResponse`, `CloudAppStreamPolicyUpdateReq`, `CloudAppStreamPolicyMutationResponse` and `CloudStreamMessageTopicPermission`. Code that calls them no longer compiles; at runtime Backend has no such route.
|
|
20
|
+
- `AppContext.platformPermissions` (`uns.dataAppContext`) is the fixed 11 (`openapi.base`, `platform.users.read`, `notifications.send`, `asset.file.upload` / `download` / `delete`, `stream.requests.read` / `invoke` / `write`, `stream.messages.publish` / `subscribe`) intersected with the key's stored permissions, key mode and workspace plan; the enum gains `stream.messages.publish` / `subscribe` and has at most 11 items.
|
|
21
|
+
- New `appTopic(suffix)` in `@tier0/sdk-v2/mq`: returns `apps/<TIER0_APP_ID>/<TIER0_RUNTIME>/<suffix>`, the only MQTT namespace Backend lets an App's hosted key use (the Preview key `preview`, the deployed key `deployed`). It reads both variables on every call and throws when `TIER0_APP_ID` is missing or not a valid topic level, when `TIER0_RUNTIME` is not `preview` / `deployed`, or when the suffix is empty or starts with `/`; it never guesses a segment. MQTT examples use it.
|
|
22
|
+
- Docs and skills follow the new model: the hosted App key route allowlist adds `stream-requests/discover`, `stream-requests/<code>/describe|invoke`, `stream-messages/connect` and `capabilities/discover`; `client.streams` works for an App's server code (bounded by the workspace plan, Stream request grants and, for messages, the topics published Stream manifests declare, with no per-App topic list); platform capabilities and MQTT topics are no longer described as managed or approved by workspace admins in App settings; external collections are granted only by a workspace Owner/Admin, the publish-time confirmation is gone, and `appDataPolicyUpdate` refuses other callers' new grants with `app_grant_admin_required` / `app_grant_agent_forbidden`.
|
|
23
|
+
- Docs correct two claims: an App's key reaches only `assets/files/**` under `assets/` (the exact file routes Backend allows; `assets/upload` answers 403), not `assets/**`; and `TIER0_RUNTIME` is set only by the platform, so local development must not set it (it also picks the `members.listApp()` scope and platform proxy mode). `appTopic` is for previews and deployments only and throws outside the platform; to debug MQTT locally, write the full topic.
|
|
24
|
+
- Every contract input is synchronized from Backend `1e6b2d7b6de7795a6509e4aafc8cd04699f5d00f` (`app-key-model/1`); the Source/Stream, Stream diagnostics, capability and Stream request pins move to the same commit. The external App registration operations (`appRegistration*`) leave the manifest (they were Core-only and never exported). Other contract changes: the `appDataPolicyUpdate` description (admin-only additions), the `AppContext.platformPermissions` description, and a description on `CloudAppStreamAccessItem.blockedReasons` (`app_stream_ceiling` means the workspace plan lacks the action). 90 public / 98 manifest operations, 45 platform operations.
|
|
25
|
+
|
|
26
|
+
This prerelease is built locally for the aenv-next scaffold and is not published to npm.
|
|
27
|
+
|
|
28
|
+
## 0.10.0-aenv.10 (local aenv-next prerelease)
|
|
29
|
+
|
|
30
|
+
Breaking: an App calls the platform as itself only (its App Key). Narrowing data by the visitor's role is the App's own job; the platform no longer does it for the App.
|
|
31
|
+
|
|
32
|
+
- Removed employee delegation: `runWithRequestContext`, `runWithRequiredEmployeeContext`, `runAsApp`, `currentDataSubject` and `currentDelegation` from `@tier0/sdk-v2/data`; the `unsV2AppDataSubject` and `delegation` client options; forwarding of `X-Tier0-Delegation` and `X-Tier0-App-Data-Subject`. An App built with an older SDK that calls `runWithRequiredEmployeeContext` throws once the front door stops sending the delegation header.
|
|
33
|
+
- The data layer still takes the request's abort signal, through a new API that carries only that: `runWithAbortSignal(request.signal, fn)`; `runDetached(fn)` runs work that must outlive the request. Ensure, seed writes and `data.subscribe` never take a request's signal.
|
|
34
|
+
- Removed `restricted` from `records:subscribe` change events, the `restricted` option of `data.subscribe`, `redactRestricted` and the collection-level `CollectionChange` it produced; `SubscriptionEvent` is a `RecordChange` or a `reset`. An older SDK drops every change event without `restricted`.
|
|
35
|
+
- Removed the ensure hint for the retired `DATA_RULE_FIELD` conflict.
|
|
36
|
+
- `MQTTConfig` docs name what the platform injects today: the broker in `TIER0_MQTT_HOST` / `TIER0_MQTT_PORT` and the App's key in `TIER0_API_KEY`.
|
|
37
|
+
- Visitor identity is the plain `X-Tier0-Visitor` header the front door sets: `parseVisitorHeader(value, { appID? })` in `@tier0/sdk-v2/server` decodes the canonical unpadded base64url JSON (at most 8192 bytes) strictly: `ws` is a positive decimal ID without sign or leading zero, an employee has a 1–128 byte `grant` and a machine visitor an empty one, `active_role` when present is 1–128 bytes, `app_roles` has no item limit (each 1–128 bytes, the whole JSON at most 8192 bytes) and duplicates are dropped, a byte order mark, a duplicate key (in any spelling) or a lone UTF-16 surrogate is refused, and `app` must equal `TIER0_APP_ID`, throwing `VisitorError` (`VISITOR_HEADER_INVALID` / `VISITOR_CONFIGURATION_INVALID`). Removed visitor token verification: `createVisitorTokenVerifier`, `VISITOR_TOKEN_HEADER`, `VisitorTokenError`, `VerifiedVisitor`, `parseVisitorJwks`, `frontdoorKeys`, `frontdoorKeysFromDir`, `frontdoorKeysFromUrl`, `FRONTDOOR_JWKS_REFRESH_MS`, `FRONTDOOR_JWKS_MISS_REFRESH_MS` and the `TIER0_JWKS_DIR` / `TIER0_JWKS_URL` variables. Apps built with an older SDK cannot recognize visitors once the front door sends the plain header, and must be rebuilt and republished.
|
|
38
|
+
- Browsers no longer connect to MQTT: removed `getMqttToken`, `reportMqttFailure` and `MqttTokenResponse` from the page runtime, and the browser token source of `Tier0MQClient` (its stop-after-refusals and sleep/resume handling). The server side through the node proxy is unchanged.
|
|
39
|
+
- New `createChangeRelay(data, options?)` in `@tier0/sdk-v2/data`: one `data.subscribe` per server process, fanned out to browsers as an SSE `Response` for an App route, with a per-browser `filter` the App decides by the visitor's roles. The relay numbers events itself (`<epoch>.<seq>`) and hands a browser a position only once the upstream can deliver every change after it: on a cold start browsers get `reset` (`upstream-started`) when the upstream first streams. It keeps the last UNS position and reopens a failed or idle-closed upstream from it in the same epoch, so browsers miss nothing and get no reset; only a UNS `reset` starts a new epoch. A reconnect's `Last-Event-ID` is replayed from the current epoch's buffer, anything else gets `reset`. While the upstream cannot connect (429 over the subscription limit, `APP_NOT_READY`, a failure that ends `data.subscribe`) the relay's `status` is `unavailable`, `onStatus` and `logger.warn` hear it, and browsers get `event: unavailable`. Heartbeats are id-only frames that move an idle browser's position. The relay sends no `retry:` field: `openEventStream` owns reconnect timing. Browser streams end after 10 minutes so the route authenticates the reconnect. `data.subscribe` reports connections, positions and retries to the relay through an internal observer.
|
|
40
|
+
- Removed the App request identity verifier (`X-Tier0-App-Identity`, issued by the retired `tier0-app-gateway`): `createCloudAppRequestVerifier`, `APP_REQUEST_IDENTITY_HEADER`, `AppRequestIdentityError` and their types. `@tier0/sdk-v2/server` now only parses the visitor header.
|
|
41
|
+
- `openEventStream` closes its connection while the page is hidden and reconnects from the last event id (`Last-Event-ID`) as soon as it is visible again, so a background tab no longer holds a long connection to the App; `pauseWhenHidden: false` keeps the old behavior.
|
|
42
|
+
- `Tier0MQClient` in an App runtime uses the hosted App key's clientId: with `TIER0_APP_ID` set, the key taken from the injected `TIER0_API_KEY`, and no explicit `clientId`, `username` or `password`, the generated clientId is `<ws>&h.<TIER0_APP_ID>.<16 random [a-z0-9]>` (username stays `<ws>&open`), the only form Backend (`share/hostedappkey`, `^([1-9][0-9]{0,18})&h\.([A-Za-z0-9_-]{1,64})\.[a-z0-9]{8,32}$`) accepts for a hosted App key. Otherwise, and when `TIER0_APP_ID` is not a valid app segment, it stays `<ws>&<8 random>`. One client keeps its clientId across reconnects.
|
|
43
|
+
- Docs, skills and messages describe the credential the platform actually provides: the App's key is `TIER0_API_KEY` in the App's process (Preview: the preview key, deployments: the deployed key) and the SDK uses it directly, sending one `X-API-Key`. Removed the claims that the VM holds no key, that the node proxy fills it in, and that hand-written requests must send `X-Tier0-SDK: 1`. Platform proxy mode (no `TIER0_API_KEY` with `TIER0_RUNTIME` set) is unchanged in code and documented as the mode of a keyless runtime, which the platform does not use today; the `sbx_proxy_*` error codes are documented under it.
|
|
44
|
+
- Removed the browser trusted runtime: `@tier0/sdk-v2/runtime` (`installAppRuntime`, `getAppRuntime`, `AppRuntime`, the `Symbol.for('tier0.sdk.app-runtime.v1')` host bridge) and the host branch of `client.streams`. Its only installer was the retired `tier0-app-gateway`. `client.streams` (requests, messages, capabilities) now always goes through the client's own HTTP configuration and key, on the server; browsers reach the platform only through the App's own server routes. The connect request's `appId` is `getCurrentAppId()` (`TIER0_APP_ID`). The artifact and native NATS acceptance scripts use a keyed server client.
|
|
45
|
+
- Docs name the route allowlist Backend now enforces for an App's key on every entry (decision 13: no more legacy / observe switch): `/openapi/v2/uns/**`, `assets/**`, `notifications/send`, `platform/getMembers`, `apps/access-members/list`, `info`; anything else answers 403 `hosted_app_key_route_forbidden`.
|
|
46
|
+
- Every contract input is synchronized from Backend `b6347c688615b0c277ebb696e851f3d1e3115652` (`app-data-rule-retirement/1`): `CloudAppDataGrant.dataRule`, `ruleContractVersion`, the `CloudAppDataRule*` schemas, `CloudAppCreateCapability` / `createCapability`, `CloudRecordChangeEvent.restricted` and the hosted App platform permission `notifications.read` are gone. The Source/Stream, capability and Stream request pins move to the same commit. Other Backend contract changes that come with it: `client.notifications.list` (`POST /openapi/v2/notifications/list`, not forwarded by the node proxy) and `recipientUserId` / `recipientName` on notification send/get responses; the raw operation `sourceOpcuaEndpoints` (OPC UA endpoint security discovery, builder-side, kept out of `client.source` like `edgeNodesList`); `sourceProbe` `mode` (`connection` / `data`) with `SourceCheckResult`, and `connectionCheck` / `dataCheck` on Source views; Stream run `delivery` / `diagnostics` and observation `diagnostics`; `reason` / `path` on `CloudStreamRunError`; `alias` / `resources` on `StreamSourceBinding`; reworded descriptions. 92 public / 106 manifest operations, 45 platform operations.
|
|
47
|
+
|
|
48
|
+
Also in this prerelease (merged from base before it was versioned):
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
- Merge the base 0.9.11 release candidate (base `593042c`) into the aenv-next line: per-request `apiKeyProvider`, explicit credential sources that never fall back to another source or `TIER0_API_KEY`, a single `X-API-Key` header (no `Authorization: Bearer`), Data clients bound to the host and static key resolved at creation, and MQTT `connect()` rejecting an explicitly undefined password.
|
|
52
|
+
- Platform proxy mode is unchanged in code: a client without an `apiKey` value, `getApiKey` or `apiKeyProvider` sends no credential, only `X-Tier0-SDK: 1`, and keeps the proxy retries. `createDataClient` fixes `apiKey` from the environment at creation, which is `undefined` in the platform, so it stays in proxy mode. Outside the platform the base credential rules apply as is.
|
|
53
|
+
- Source/Stream public operations and their schemas are synchronized from Backend `727c6395a` (as on base) over the aenv-next contract pin `8780ce2d6`; every other operation keeps the aenv-next pin. 91 public / 105 manifest operations. (Superseded in this prerelease by the full sync to `b6347c688` above.)
|
|
54
|
+
|
|
55
|
+
This prerelease is built locally for the aenv-next scaffold and is not published to npm.
|
|
56
|
+
|
|
57
|
+
## 0.10.0-aenv.9 (local aenv-next prerelease)
|
|
58
|
+
|
|
59
|
+
- Merge the base MQTT and data fixes of 2026-11-05 (base `d0276cf`) into the aenv-next line. MQTT: callbacks are bound to the connection generation, mqtt.js resubscription is off and every topic checks its SUBACK grant (MQTT 3.1.1 and 5), connection settings are preflighted and rolled back on failure, settings cannot change while a client exists, and a failed first connect drops the subscriptions it registered. aenv-next keeps its own reconnect model: the SDK reconnects itself, a browser fetches a fresh token on every reconnect, new subscriptions and publishes wait for the reconnect instead of failing, and `disconnect()` also rejects the waiting calls.
|
|
60
|
+
- `runWithRequiredEmployeeContext` accepts either `X-Tier0-App-Data-Subject` (base) or `X-Tier0-Delegation` (aenv-next employee identity); whichever is present must be well formed.
|
|
61
|
+
- Notifications are sent without `mode`; the platform derives test or live from the App Key's runtime (session sandboxes carry the preview key).
|
|
62
|
+
|
|
63
|
+
This prerelease is built locally for the aenv-next scaffold and is not published to npm.
|
|
64
|
+
|
|
65
|
+
## 0.10.0-aenv.8 (local aenv-next prerelease)
|
|
66
|
+
|
|
67
|
+
- Visitor tokens on AENV Apps: when `TIER0_JWKS_DIR` does not exist (published Apps have no guest agent writing it), `createVisitorTokenVerifier()` fetches the front door keys from `TIER0_JWKS_URL` (`https://<app>.<apps domain>/__tier0/jwks.json`, set by the platform) with the new `frontdoorKeysFromUrl()`: refreshed every 60 s, early when a token names an unknown `kid` (at most every 10 s), the last good set kept when a fetch fails, `dev-` kids never trusted. Until the first fetch lands every token is refused; `ready()` waits for it. `frontdoorKeys()` is the new default source; `frontdoorKeysFromDir()` is unchanged.
|
|
68
|
+
|
|
69
|
+
This prerelease is built locally for the aenv-next scaffold and is not published to npm.
|
|
70
|
+
|
|
71
|
+
## 0.10.0-aenv.7 (local aenv-next prerelease)
|
|
72
|
+
|
|
73
|
+
- Merge base 0.9.9 and 0.9.10 into the aenv-next line (`0.10.0-aenv.6` + base `0.9.10`): the Personal key local connection guide, non-JSON gateway error diagnostics, App versus workspace credential query guidance and the Namespace trash / delete / restore wording. The node-proxy credential model is unchanged: there is no `TIER0_API_KEY` in the VM, every platform request carries `X-Tier0-SDK: 1`, and the proxy error reference stays.
|
|
74
|
+
- Pin every contract input to Backend `8780ce2d6c07b6afee96409795c098e330c214dd` (the merge of base `2f79a4f87` into aenv-next).
|
|
75
|
+
- The Backend's new `sourceResourceCatalog`, `sourceResourceRead` and `sourceResourceScan` stay in the raw operation table only, like `edgeNodesList` and `sourceDocumentStateUpdate`; `client.source` does not expose them (base 0.9.10 does not either). The four contract baselines are re-pinned to the current contract: operation counts 87/100, the Namespace digest (its only reachable change is three more ensure schema-note reasons) and the facade membership test. `metadataPurge` now also accepts `directory` / `topic` (Backend `ae62b5e18`), so the type test no longer rejects a directory purge; the Skills still document collection purges only.
|
|
76
|
+
|
|
77
|
+
This prerelease is built locally for the aenv-next scaffold and is not published to npm.
|
|
78
|
+
|
|
79
|
+
## 0.10.0-aenv.6 (local aenv-next prerelease)
|
|
80
|
+
|
|
81
|
+
- Merge base 0.9.8 into the aenv-next line: `metadataPurge`, `directoryPath` App collection declarations and the shorter Source lifecycle, on top of aenv-next's node-proxy credential model (no `TIER0_API_KEY` in the VM).
|
|
82
|
+
- Pin every contract input to Backend `c63783eb485e99167fec3a1c6ffb034ada4170e1` (the merge of base into aenv-next). The App stream policy operations require `uns.write` there.
|
|
83
|
+
- `members.listApp` is available through the node proxy again (the proxy allowlists `/openapi/v2/apps/access-members/list`); the Backend authorizes a hosted App key by the App's `platform.users.read` like `platform/getMembers` and omits `email` from the response.
|
|
84
|
+
- The Backend's `edgeNodesList` and `sourceDocumentStateUpdate` stay in the raw operation table only; neither `client.source` nor `client.uns` exposes them. The four contract baseline tests (operation counts 84/97, the Namespace digest and the Source facade membership) are re-pinned to the current contract.
|
|
85
|
+
|
|
86
|
+
This prerelease is built locally for the aenv-next scaffold and is not published to npm.
|
|
87
|
+
|
|
88
|
+
## 0.9.11 (unreleased candidate)
|
|
89
|
+
|
|
90
|
+
- Send the single authentication header required by the Cloud Gateway; whoami uses `X-API-Key`.
|
|
91
|
+
- Support asynchronous per-request credential providers without sharing credentials or falling back after provider failure.
|
|
92
|
+
- Include the native Source/Stream contract updates from the Cloud migration target.
|
|
93
|
+
|
|
94
|
+
- Select the HTTP credential source by explicit configuration presence. Missing explicit credentials no longer fall back to another source or `TIER0_API_KEY`; omit all credential properties to retain the environment default.
|
|
95
|
+
- Preserve non-enumerable explicit credentials and delegation settings in client configuration snapshots.
|
|
96
|
+
- Bind each high-level Data client to the host and static credential resolved at creation, and reject dynamic credential callbacks so cached bootstrap and subscriptions cannot switch principals.
|
|
97
|
+
- Reject an explicitly undefined MQTT connect-time password instead of silently reusing a saved or environment credential.
|
|
98
|
+
|
|
99
|
+
## 0.9.10 (2026-11-04)
|
|
100
|
+
|
|
101
|
+
- Add a local connection guide for Personal keys, explicit OpenAPI gateway configuration and reading existing collections without a platform App.
|
|
102
|
+
- Explain non-JSON route, authentication, permission and service errors without exposing credentials or changing error types, execution/retry semantics or request destinations.
|
|
103
|
+
- Align Namespace trash, delete and restore guidance: retained data remains readable, archived targets reject writes, and the legacy record restoration limit does not apply to current collection archive / restore.
|
|
104
|
+
|
|
3
105
|
## 0.9.9 (2026-11-04)
|
|
4
106
|
|
|
5
107
|
- Clarify workspace Agent/Personal versus App credential query permissions. Keep `namespace()` on OpenAPI; document `dataAppContext` followed by `recordFind` for App keys, with a typed example and 403 troubleshooting. No runtime authorization changes.
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 FREEZONEX
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FREEZONEX
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -2,11 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
Tier0 云端统一 TypeScript / JavaScript SDK,数据管理与 App Stream 交互均使用 `@tier0/sdk-v2`。旧 SDK 不再作为依赖。
|
|
4
4
|
|
|
5
|
+
`0.10.0` 是新架构重构分支的正式版本,包含此前 aenv-next 预发布的全部变更、MQTT 修复、逐请求异步凭据和 Sample 精确删除接口。合同同步至 Backend `91b5f3dc4024f44df32ee7aafcd83a8d470b655d`;升级前需确认目标环境已部署相应合同及运行环境,详见下方「aenv-next 运行环境」和 CHANGELOG。
|
|
6
|
+
|
|
7
|
+
**`0.10.0` 包含破坏性变更**:App 的平台能力固定为 11 项(含 Stream 请求和消息),没有按 App 的平台策略了:`client.uns.appStreamPolicyGet` / `appStreamPolicyUpdate` 和 `CloudAppStreamPolicy*`、`CloudStreamMessageTopicPermission` 类型随 Backend 契约删除,没有替代。托管 App Key 的 MQTT 主题只能在 App 自己的命名空间 `apps/<TIER0_APP_ID>/<preview|deployed>/…` 里,新增 `appTopic()`(`@tier0/sdk-v2/mq`)拼主题,见「MQTT」。外部集合只由工作区 Owner/Admin 授权,发布时不再确认。
|
|
8
|
+
|
|
9
|
+
**`0.10.0-aenv.10` 是破坏性变更**:平台现在把 App 的 Key(`TIER0_API_KEY`)放在运行进程的环境里,SDK 直接用它(下文「aenv-next 运行环境」);早期预发布说明里的节点代理模式只在没有 Key 的运行时生效。App 调平台只有一种身份,App 自身(App Key);按用户角色收窄数据由 App 自己做,服务端用 `parseVisitorHeader()` 读前门注入的明文 `X-Tier0-Visitor` 拿到当前用户和角色。浏览器不再直连 MQTT,实时数据由 App 服务端订阅后转发(`createChangeRelay`);请求中止信号改由 `runWithAbortSignal` 传入。删除的接口见 CHANGELOG。用旧 SDK 打包的 App 在前门改为明文访问者头后无法识别访问者,必须升级重新发布。
|
|
10
|
+
|
|
11
|
+
**`0.10.0-aenv.2` 起是破坏性变更**:`TIER0_CREDENTIALS_FILE` 不再读取,SDK 在平台里不持有、不发送任何凭据,由节点代理注入 App 的长期 Key;必须和新的 AgentENV(`sbx-api-proxy`)一起上线,在旧 VM 上请求会被拒绝。`0.10.0-aenv.3` 起 `TIER0_RUNTIME` 只接受 `deployed` / `preview`,其他非空值(且没有 Key)直接报配置错误。`0.10.0-aenv.4` 起 MQTT 改用 `<ws>&open` / `<ws>&h.<app>.<随机>`,预览和正式同一行为(`appCredentialRuntime()` 已删除)。`0.10.0-aenv.5` 合入 base 的 `0.9.7`、`0.9.8`。`0.10.0-aenv.6` 起 `members.listApp` 经节点代理可用(托管 Key 按 `platform.users.read`,响应不带邮箱)。`0.10.0-aenv.7` 合入 base 的 `0.9.9`、`0.9.10`(个人 Key 本地接入指南、非 JSON 网关错误提示、Namespace 回收说明),节点代理凭据模型不变。`0.10.0-aenv.9` 合入 base 2026-11-05 的 MQTT 修复(按连接代次丢弃过期回调、逐个 topic 校验订阅确认、连接设置预检回滚),重连期间新订阅和发布仍等待重连。
|
|
12
|
+
|
|
13
|
+
`0.10.0-aenv.10` 合入了 base 的 `0.9.11` 待发布候选:隔离显式凭据来源、支持逐请求异步凭据(`apiKeyProvider`),并按 Gateway 合同只发送 `X-API-Key`;Data 客户端在创建时绑定静态主体;Source / Stream 公开合同同步到 Backend 的原生文档模型。平台代理模式(见「aenv-next 运行环境」)不变。
|
|
14
|
+
|
|
15
|
+
`0.9.10` 补齐个人 Key 本地接入指南与非 JSON 网关错误提示,并同步 Namespace 回收、删除和恢复说明;保留现有权限、请求地址和重试规则。
|
|
16
|
+
|
|
5
17
|
`0.9.9` 新增 `client.uns.metadataPurge`,通过明确的已归档 Collection ID 提交永久删除,返回 `accepted` / `jobId` 并通过现有 `jobs` 查询逐项完成结果;`dryRun` 可选。集合删除保留 Record/Sample,支持恢复或继续永久清理。
|
|
6
18
|
|
|
7
19
|
本版同时补齐 Agent / App 凭证的查询说明及 App 读取示例:`namespace()` 仍走 OpenAPI,App Key 使用 `dataAppContext` 和集合数据接口,服务端授权规则保持不变。
|
|
8
20
|
|
|
9
|
-
保留 `0.9.8` 的 App 集合业务目录声明 `directoryPath`,按已知业务范围创建或复用目录,已有集合保持位置。Source
|
|
21
|
+
保留 `0.9.8` 的 App 集合业务目录声明 `directoryPath`,按已知业务范围创建或复用目录,已有集合保持位置。Source 默认 enabled;原生状态入口为 `client.source.documents.state.update`,删除前必须先禁用 Source 本身,并按新 revision 评估删除影响。名称和描述修改无需影响确认,连接配置变更、禁用或删除存在引用时仍须显式确认。
|
|
10
22
|
|
|
11
23
|
`0.9.3` 同步集合授权合同:授予既有集合访问权限时无需提供别名,并更新生成类型与使用说明。集合声明的 `name` / `alias` 要求保持不变。
|
|
12
24
|
|
|
@@ -16,36 +28,133 @@ Collection 声明必须同时提供业务名称 `name`(例如「生产工单
|
|
|
16
28
|
|
|
17
29
|
`0.7.x` 已移除旧 Controls 入口、生成操作、DTO 和错误类,不保留别名。能力发现不再以可选交互合同阻断 Namespace,未知核心合同仍严格拒绝。V2 的 `execution` / `retry` 和结果未知语义保持不变。
|
|
18
30
|
|
|
19
|
-
|
|
31
|
+
Source / Stream 管理已统一为 Enterprise 参数:SourceDefinition 使用顶层 role、endpoint、http/sql 等;Stream 的 document 与 program 均使用 script: {code,timeoutMs}。旧 config/options/material、script 字符串和旧删除/凭据创建路由不再受支持。
|
|
20
32
|
|
|
21
|
-
`0.4.0` 版本变更:相对 0.3.x 移除已退役的 Stream 就绪探针且不保留别名,改用服务端现有统一权限,不再配置逐 Key Control scopes/codes。0.3.0 起统一使用 OpenAPI v2 并退役重复及旧接口;与 0.2.x 存在不兼容变更,迁移方式见下方“本次接口退役”。对应 UNS v2 契约 5.0.0,与 0.1.0 不兼容(新错误模型 `execution` / `retry`、封顶计数、删除 `recordChangesPoll` 等)。Collection 支持 int、float、double 和 datetime 声明;格式注解不新增数值范围或日期格式校验,Sample 外层 time 保持不变。使用时需确认目标环境已部署对应后端。要求 Node.js 20.19+。浏览器 HTTP、MQTT、React、Vue
|
|
33
|
+
`0.4.0` 版本变更:相对 0.3.x 移除已退役的 Stream 就绪探针且不保留别名,改用服务端现有统一权限,不再配置逐 Key Control scopes/codes。0.3.0 起统一使用 OpenAPI v2 并退役重复及旧接口;与 0.2.x 存在不兼容变更,迁移方式见下方“本次接口退役”。对应 UNS v2 契约 5.0.0,与 0.1.0 不兼容(新错误模型 `execution` / `retry`、封顶计数、删除 `recordChangesPoll` 等)。Collection 支持 int、float、double 和 datetime 声明;格式注解不新增数值范围或日期格式校验,Sample 外层 time 保持不变。使用时需确认目标环境已部署对应后端。要求 Node.js 20.19+。浏览器 HTTP、MQTT、React、Vue 和服务端分别提供独立入口。
|
|
22
34
|
|
|
23
35
|
合同快照的准确 Backend 提交和输入摘要见 contracts/upstream.json;使用前需确认目标环境已部署相应合同。请求统一权限、旧探针退役门禁、Source discovery 的副作用分类及不自动重试语义保持不变。
|
|
24
36
|
|
|
37
|
+
本分支新增 `client.uns.sampleDeleteExact`(Backend `sample-delete-exact/1`):管理调用方按 `{recordId,id,time}` 清理最多 200 条明确样本,支持预览和幂等回执。App Key 不支持此操作,App 数据层不增加删除样本方法;用法见[精确删除历史样本](skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/delete.md#精确删除历史样本)。
|
|
38
|
+
|
|
25
39
|
既有接口收敛:平台接口统一迁移到 `/openapi/v2`;移除 21 个重复 UNS 入口和旧 Agent nodes/relations 方法。完善 Source 操作、Stream 发布回执及 UNS 创建快照的使用说明,保留精确发布编号和完整文档访问规则。
|
|
26
40
|
|
|
27
41
|
## 安装与初始化
|
|
28
42
|
|
|
29
|
-
|
|
43
|
+
安装正式版:
|
|
30
44
|
|
|
31
45
|
```sh
|
|
32
|
-
npm install @tier0/sdk-v2@0.
|
|
46
|
+
npm install @tier0/sdk-v2@0.10.0
|
|
33
47
|
```
|
|
34
48
|
|
|
35
|
-
维护者发布前在本仓库运行 `npm ci`、`npm run check`,并验证生成的
|
|
49
|
+
维护者发布前在本仓库运行 `npm ci`、`npm run check`,并验证生成的 `tier0-sdk-v2-0.10.0.tgz`。
|
|
50
|
+
|
|
51
|
+
个人 Key 可直接读取已有集合,无需创建平台 App;网关配置、运行示例与排错见[本地连接指南](skills/tier0-sdk-v2/references/local-connection.md)。
|
|
36
52
|
|
|
37
53
|
```ts
|
|
38
54
|
import { createClient } from '@tier0/sdk-v2';
|
|
39
55
|
|
|
40
|
-
//
|
|
56
|
+
// 平台里只需 TIER0_API_HOST(平台已设置);本地开发另设 TIER0_API_KEY。
|
|
41
57
|
const tier0 = createClient();
|
|
42
|
-
// 首次接入时确认身份与 Workspace;不需要在每次业务操作前重复调用。
|
|
43
|
-
const user = await tier0.system.whoami();
|
|
44
58
|
```
|
|
45
59
|
|
|
46
60
|
也可显式配置 `{ apiHost, apiKey, transportTimeoutMs, fetch }`,或提供 `getApiHost` / `getApiKey` 回调。裸域名默认使用 HTTPS;本地 HTTP 测试请显式写 `http://127.0.0.1:端口`。不自动向浏览器注入或读取 `VITE_` API Key。
|
|
47
61
|
|
|
48
|
-
`
|
|
62
|
+
底层 HTTP 凭证来源按配置属性是否存在固定:`apiKeyProvider` 优先于 `apiKey`,再优先于 `getApiKey`;只有三者均未提供时才读取 `TIER0_API_KEY`。显式传入 `undefined`、空 Key,或同步 getter 返回 `undefined` 时不会回退到其他 Key,底层请求不带认证头;异步 provider 返回无效值会在发送前失败。Data 客户端对动态来源及缺失 Key 的处理见下文。此前依赖这些空值回退环境 Key 的调用方,需要去掉对应属性,或显式提供所需 Key。每个用户或请求应创建自己的客户端,不能用全局 `configureClient` 保存每请求身份。
|
|
63
|
+
|
|
64
|
+
SDK 访问平台 API 时仅发送 `X-API-Key`,不同时发送 `Authorization`;Cloud Gateway 的 enforce 模式会拒绝多来源凭据。`whoami` 同样使用单个 `X-API-Key`。API Key 应留在服务端,浏览器通过应用服务端调用平台 API;网关默认 CORS 配置可能不允许跨源 `X-API-Key`。
|
|
65
|
+
|
|
66
|
+
服务端底层 HTTP 客户端也可配置 `apiKeyProvider: async ({ signal }) => ...`,每次 HTTP 请求或 SSE 连接分别解析一次 API Key。provider 必须返回非空的可打印 ASCII 字符串;失败、取消或超时会终止该次请求,不回退到静态 Key 或环境变量。provider 应使用 `signal` 停止自己的异步工作。SDK 不缓存 provider 返回的 Key,也不会因此重放请求;调用者须确保重试期间身份和幂等作用域一致。`@tier0/sdk-v2/data` 的 `createDataClient` 不支持 `apiKeyProvider` 或 `getApiKey`,会直接拒绝;它在创建时固定静态 Key 和 Host,包括从环境变量取得的值。需要更换身份或环境配置时须新建 Data 客户端,不能复用原有 bootstrap、重试和订阅状态。该接口只提供现有 API Key 的动态获取。
|
|
67
|
+
|
|
68
|
+
服务端已有经授权的主体 Key 时,可以为每个可信主体创建独立 Data 客户端,并按实际权限访问已有集合:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { createDataClient } from '@tier0/sdk-v2/data';
|
|
72
|
+
|
|
73
|
+
export function dataForPrincipal(apiHost: string, apiKey: string) {
|
|
74
|
+
if (!apiHost.trim() || !apiKey.trim()) throw new Error('A host and principal key are required');
|
|
75
|
+
return createDataClient({ apiHost, apiKey });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 从可信服务端身份链取得该主体已获授权的 Key 和集合 ID 后:
|
|
79
|
+
// const count = await dataForPrincipal(apiHost, apiKey).collection({ collectionId }).countDocuments();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
不要从客户端自报的主体推导 Key,也不要跨主体复用客户端;Key 轮换后创建新实例。声明集合的 ensure/seed 是否允许由服务端凭证和权限决定,此例不声明集合。App 共用固定 App Key 时,平台不按访问者收窄数据;按用户角色决定谁能看什么是 App 自己的事(见「aenv-next 运行环境」的访问者头)。
|
|
83
|
+
|
|
84
|
+
Data 高层会跨请求复用 ensure 结果、重放带幂等键的 seed、恢复 stale 集合和续接 SSE。现有 whoami 不返回权威 App ID,同一工作空间和创建者的不同 App Key 可能具有相同的 workspace/user/type;名称、前缀或 provider 自报的身份不能证明不同 Key 属于同一主体。移除高层拒绝前,需要与实际请求凭据绑定、可验证且覆盖 App 及幂等作用域的服务端身份合同,并将这些跨请求状态隔离到已确认主体。`scripts/check-data-principal-boundary.mjs` 可在受控环境中用真实 Gateway 和两个不同 App 的临时 Key 验证此边界;脚本开头列出输入,Key 文件由调用方在验证后撤销和清理。
|
|
85
|
+
|
|
86
|
+
`createClient()` 实例之间不共享配置;推荐在一个稳定身份的服务生命周期内复用实例。App Key 只代表 App,不能替代当前用户身份,也不能下发到浏览器。浏览器页面应调用自己的服务端 API。
|
|
87
|
+
|
|
88
|
+
## aenv-next 运行环境
|
|
89
|
+
|
|
90
|
+
**平台把 App 的 Key 放在运行进程的环境里,SDK 直接用它。** Tier0 预览和部署里,平台给 App 的运行进程注入 `TIER0_API_KEY`(这个 App 的 Key,秘密;预览是 App 的 preview Key,部署是 deployed Key,平台据此把预览里发的通知判为 test)。它只在运行进程的环境里,不进构建、不写文件。SDK 照常读 `TIER0_API_HOST` / `TIER0_API_KEY`,每个请求只发一个 `X-API-Key`。App 代码不要自己读、复制、打印或下发这个 Key,也不要把它放进 `.env`、集合或页面。所有平台调用都是 App 自身,请求里不带访问者身份。
|
|
91
|
+
|
|
92
|
+
平台设置的环境变量:
|
|
93
|
+
|
|
94
|
+
| 变量 | 值 |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| `TIER0_API_HOST` | 平台 API 网关根地址,带 scheme。不带 scheme 的主机名按 `https://` 处理 |
|
|
97
|
+
| `TIER0_API_KEY` | App 的 Key(秘密,只在运行进程里) |
|
|
98
|
+
| `TIER0_MQTT_HOST`、`TIER0_MQTT_PORT` | App 服务端能连到的 MQTT broker 入口 |
|
|
99
|
+
| `TIER0_WORKSPACE_ID` | 工作区 ID |
|
|
100
|
+
| `TIER0_APP_ID` | App ID(`getCurrentAppId()` 先读它,再读旧的 `APP_ID`) |
|
|
101
|
+
| `TIER0_RUNTIME` | `deployed` 或 `preview`;只由平台设置,本地开发不要设(它还决定 `members.listApp()` 的口径和平台代理模式) |
|
|
102
|
+
|
|
103
|
+
App 的 Key(平台注入的这把)只能调 `/openapi/v2/uns/**`、`assets/files/**`(`client.files`;同在 `assets/` 下的 `assets/upload` 不在内,回 403)、`notifications/send`、`platform/getMembers`(`members.listWorkspace()`)、`apps/access-members/list`(`members.listApp()`)、`stream-requests/discover` 和 `stream-requests/<code>/describe|invoke`(`client.streams.requests`)、`stream-messages/connect`(`client.streams.messages`)、`capabilities/discover`、`info`,Backend 在每个入口都这样限制,其他接口(`system.whoami()`、`notifications.get`、`mqttAuth`、`agent`)回 403 `hosted_app_key_route_forbidden`;在 App 设置里另签给外部系统用的 App Key 不受这张表限制。App 的平台能力固定 11 项,每个 App 都有,不在 App 设置里配置:`openapi.base`、`platform.users.read`、`notifications.send`、`asset.file.upload` / `download` / `delete`、`stream.requests.read` / `invoke` / `write`、`stream.messages.publish` / `subscribe`。实际生效的是它们和 Key 自身权限、Key 模式、工作区套餐的交集(`uns.dataAppContext({})` 返回的 `platformPermissions`),例如套餐不含 Stream 时 Stream 请求和消息仍被拒绝。数据只能访问 App 自己建的集合,以及工作区 Owner/Admin 在 App 设置里授权的外部集合(Builder agent 加不了外部集合授权);授权删掉后下一个请求起生效,`records:subscribe` 在下一轮长轮询(最多约 15 秒)收到 `SCOPE_CHANGED`。MQTT 只能用 App 自己的命名空间,见 [MQTT](#mqtt)。
|
|
104
|
+
|
|
105
|
+
**平台代理模式**(保留给不带 Key 的运行时):`TIER0_RUNTIME` 为 `deployed` 或 `preview`、而进程里没有 `TIER0_API_KEY` 时,SDK 不读也不发任何凭据,每个请求带 `X-Tier0-SDK: 1`,只对节点代理自己产生的 `503`、`429`(带 `X-Tier0-Proxy: not-forwarded`,`code` 以 `sbx_proxy_` 开头)按 `retry_after_ms` 最多重试 3 次;MQTT 用 `TIER0_MQTT_URL`(`mqtt://`)且不发 password。传了有值的 `apiKey`,或给了 `getApiKey` / `apiKeyProvider` 的客户端始终按显式配置。没有 `TIER0_API_KEY` 而 `TIER0_RUNTIME` 是其他非空值时,SDK 抛出配置错误,不猜测模式。当前平台给绑定 App 的会话和已发布的 App 都注入 `TIER0_API_KEY`,不会进入这个模式;没绑 App 的会话没有 Key 也没有 `TIER0_API_HOST`,调平台会报 `DATA_NOT_CONFIGURED` 一类的配置错误。
|
|
106
|
+
|
|
107
|
+
本地开发照旧设 `TIER0_API_HOST`、`TIER0_API_KEY`,不要设 `TIER0_RUNTIME`(也不要写进 `.env` 或 `artifact.toml`)。
|
|
108
|
+
|
|
109
|
+
- `@tier0/sdk-v2/server`:`parseVisitorHeader(request.headers.get(VISITOR_HEADER))` 解析前门注入的 `X-Tier0-Visitor`,返回 `{ subType, subject, grant, workspaceID, appID, runtime, appRoles, activeRole? }`。头的值是 UTF-8 JSON 对象的无填充 base64url(编码前最多 8192 字节),不签名:前门转发前剥掉客户端带来的全部 `X-Tier0-*`,再注入这一个头。解析是严格的:只接受规范的无填充 base64url;BOM、重复键、孤立的 UTF-16 代理项(如 `"\ud800"`)、未知字段、缺必填字段、类型或长度不符、`ws` 不是正整数十进制串(不带符号和前导 0)、员工没有 grant 或机器访问者带 grant、`app` 不等于 `TIER0_APP_ID` 都抛 `VisitorError`(`VISITOR_HEADER_INVALID`);没有 `TIER0_APP_ID` 时是 `VISITOR_CONFIGURATION_INVALID`。App 用 `appRoles` 自己决定当前用户能看、能改哪些数据;平台对 App 的调用不带访问者身份,不会替 App 收窄。
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { parseVisitorHeader, VISITOR_HEADER, VisitorError } from '@tier0/sdk-v2/server';
|
|
113
|
+
|
|
114
|
+
function visitorOf(request: Request) {
|
|
115
|
+
try {
|
|
116
|
+
return parseVisitorHeader(request.headers.get(VISITOR_HEADER));
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (error instanceof VisitorError && error.code === 'VISITOR_HEADER_INVALID') return null; // 401
|
|
119
|
+
throw error; // 配置错误:503
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
| 字段 | 头里的字段 | 说明 |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| `workspaceID` | `ws` | 工作区 ID(十进制数字串) |
|
|
127
|
+
| `appID` | `app` | 等于 `TIER0_APP_ID` |
|
|
128
|
+
| `runtime` | `runtime` | `deployed` 或 `preview` |
|
|
129
|
+
| `subType` | `sub_type` | `employee` 或 `machine` |
|
|
130
|
+
| `subject` | `sub` | 员工用户 ID,或机器访问者的调用令牌 ID(1–128 字节) |
|
|
131
|
+
| `grant` | `grant` | 员工访问的 grant ID(1–128 字节);机器访问者必须是空串 |
|
|
132
|
+
| `appRoles` | `app_roles` | 访问者在 App 里的角色(不限项数,每项 1–128 字节,整个 JSON 不超过 8192 字节;重复的去掉);预览里是「以某角色查看」选中的角色 |
|
|
133
|
+
| `activeRole` | `active_role` | 可省略,只在预览的「以某角色查看」里出现 |
|
|
134
|
+
- `@tier0/sdk-v2/page-runtime`:页面运行时 v1 和平台错误(`x-sbx-error`)。
|
|
135
|
+
- `@tier0/sdk-v2/event-stream`:基于 fetch 的 SSE。App 休眠时停止,`resumed` 后重建,节点 agent 重启时透明重连。页面隐藏时关掉连接(后台标签页不占着到 App 的长连接),可见时带最后的事件 id(`Last-Event-ID`)立即续传,服务端补发或回 `reset`;`pauseWhenHidden: false` 关闭这一行为。`EventSource` 遇到 503 不重试,手写时要订阅 `resumed` 后重建。
|
|
136
|
+
|
|
137
|
+
**MQTT**:只在 App 服务端连接。平台里 `new Tier0MQClient()` 什么都不传:broker 取自 `TIER0_MQTT_HOST` / `TIER0_MQTT_PORT`,password 用 `TIER0_API_KEY`,username 为 `<ws>&open`(ws 从 Key 解析)。App 运行时(有 `TIER0_APP_ID`,Key 来自注入的 `TIER0_API_KEY`,没显式传 `clientId` / `username` / `password`)的 clientId 是托管格式 `<ws>&h.<app>.<16 位随机>`(app 即 `TIER0_APP_ID`),Backend 只接受这种 clientId 的托管 App Key;其他情况保持 `<ws>&<8 位随机>`,显式传的 `clientId` / `username` 优先。页面不直连 broker:浏览器要的实时消息由 App 服务端订阅,按访问者角色过滤后经 App 自己的接口(例如一个 SSE 路由)推给浏览器。`clean` 默认 `false`;服务端按指数退避一直重连,上限 5 分钟。持久会话、QoS 1 下重连不丢消息,但断开期间的积压超过 EMQX 离线队列(默认 `max_mqueue_len=1000`)仍会丢。
|
|
138
|
+
|
|
139
|
+
**记录变更推给浏览器**:`createChangeRelay(data)` 在每个服务端进程里只开一个 `data.subscribe`(App 身份,看得到 App 被授权集合的全部变更),转发给每个浏览器的 SSE 连接;`filter` 由 App 按访问者决定这个浏览器能知道哪些变更。浏览器不各开上游:Gateway 按工作区限制同时打开的订阅数。
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { createChangeRelay } from '@tier0/sdk-v2/data';
|
|
143
|
+
import type { Visitor } from '@tier0/sdk-v2/server';
|
|
144
|
+
|
|
145
|
+
const relay = createChangeRelay(data); // 每进程一个
|
|
146
|
+
|
|
147
|
+
// GET /api/changes,先按访问者头认证:
|
|
148
|
+
export function changes(request: Request, visitor: Visitor): Response {
|
|
149
|
+
return relay.stream({
|
|
150
|
+
filter: (change) => canRead(visitor.appRoles, change.collection),
|
|
151
|
+
lastEventId: request.headers.get('Last-Event-ID'),
|
|
152
|
+
signal: request.signal,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
帧:`event: change`(`{ eventId, collection, change, recordId, revision, occurredAt }`,不带记录数据)、`event: reset`(`{ reason }`:可能漏了变更,重新加载)、`event: unavailable`(`{ reason }`:上游连不上,仍在重试),每 15 秒一个只带 `id` 的帧推进位置(还没有位置时是 `: heartbeat` 注释)。`id` 是转发器自己的位置(`<epoch>.<seq>`),不是 UNS 游标,只在上游能送达其后所有变更时才给:冷启动时上游第一次连上之前浏览器拿不到位置,连上后每个浏览器先收到 `reset`(`upstream-started`)再拿位置,所以上游就绪前读过的数据会重读。转发器记住最后的 UNS 位置:上游失败、或最后一个浏览器离开 30 秒后关闭,都从这个位置重开,epoch 不变、不漏、不 reset;只有 UNS 回 `reset`(游标过期、访问范围变化、集合重建)才换 epoch,等上游重新连上后给连着的浏览器发 `reset`。重连带 `Last-Event-ID` 时,在当前 epoch 的最近 1000 条里找得到就补发之后的,找不到(换了 epoch、太旧、伪造)就只给这个浏览器一个 `reset`。上游连不上(例如超过订阅上限的 429、`APP_NOT_READY`)时状态变成 `unavailable`:`onStatus` 回调、`logger.warn` 记一次、所有浏览器收到 `event: unavailable`;`relay.status` 可查。每个浏览器连接 10 分钟(±10%)后结束,浏览器重连时 App 的路由重新认证;`filter` 抛错的变更不发。转发器不发 `retry:`:重连节奏由浏览器端 `@tier0/sdk-v2/event-stream` 自己决定(平台错误、App 休眠、页面隐藏都在那里处理);页面隐藏时断开,可见时带 `Last-Event-ID` 续传。
|
|
49
158
|
|
|
50
159
|
## 能力与入口
|
|
51
160
|
|
|
@@ -56,22 +165,21 @@ const user = await tier0.system.whoami();
|
|
|
56
165
|
| `tier0.streams` | App 请求、实时业务消息与独立交互能力发现 |
|
|
57
166
|
| `tier0.uns` | 目录、Topic、Collection、Record、Sample、事务与 App 数据上下文 |
|
|
58
167
|
| `tier0.system` | `whoami()`、`info()` |
|
|
59
|
-
| `tier0.members` | `listWorkspace()` 用于 Workspace 级成员选择;`listApp({ appId })` 查询能使用指定 App 的成员(Builder 预览按草稿角色,部署后按线上版本,`scope` 由 SDK
|
|
168
|
+
| `tier0.members` | `listWorkspace()` 用于 Workspace 级成员选择;`listApp({ appId })` 查询能使用指定 App 的成员(Builder 预览按草稿角色,部署后按线上版本,`scope` 由 SDK 自动带上)(Tier0 App 里可用:每个 App 固定有 `platform.users.read`;App 的 Key 查到的响应不带邮箱) |
|
|
60
169
|
| `tier0.notifications` | `send()`、`get()` |
|
|
61
170
|
| `tier0.files` | `upload()`、`uploadMultipart()`、`resumeMultipart()`、`url()`、`download()`、`delete()`、`multipart` 原子接口 |
|
|
62
171
|
| `tier0.mqttAuth` | 创建、删除 MQTT 凭证 |
|
|
63
172
|
| `tier0.agent` | `analyses`、`canvases`、`cards`、`jobs`、`jobRuns`、`library`、`results` |
|
|
64
173
|
| `tier0.capabilities` | 当前凭证可见的能力发现;就绪状态由服务端判断 |
|
|
65
|
-
| `tier0.api` |
|
|
174
|
+
| `tier0.api` | 45 个平台 OpenAPI v2 的完整生成接口;通常优先使用上面的领域入口 |
|
|
66
175
|
| `@tier0/sdk-v2/mq` | MQTT WebSocket 客户端与 broker 地址工具;不进入根入口依赖图 |
|
|
67
176
|
| `@tier0/sdk-v2/source` | `createSourceClient()`,独立 Source 客户端 |
|
|
68
177
|
| `@tier0/sdk-v2/stream` | `createStreamClient()`,独立 Stream 客户端 |
|
|
69
178
|
| `@tier0/sdk-v2/streams` | Stream 交互客户端、错误类与消息类型 |
|
|
70
|
-
| `@tier0/sdk-v2/runtime` | 受信 App 宿主 provider 安装与生命周期;业务页面无需调用 |
|
|
71
179
|
| `@tier0/sdk-v2/react` | `useTier0Query()`、`useTier0Mutation()`,需要 React 和 TanStack Query |
|
|
72
180
|
| `@tier0/sdk-v2/vue` | `useTier0Request()`,需要 Vue 3 |
|
|
73
|
-
| `@tier0/sdk-v2/server` | `
|
|
74
|
-
| `@tier0/sdk-v2/data` | App 数据层:`defineCollection()` 声明集合(含 TTL)、`createDataClient()` 首次访问时 ensure,按 collectionId 引用已有集合、Mongo 风格读写、带总数分页与封顶计数、原子批量与 `transact`、`subscribe`
|
|
181
|
+
| `@tier0/sdk-v2/server` | `parseVisitorHeader()` 解析访问者头;仅服务端 |
|
|
182
|
+
| `@tier0/sdk-v2/data` | App 数据层:`defineCollection()` 声明集合(含 TTL)、`createDataClient()` 首次访问时 ensure,按 collectionId 引用已有集合、Mongo 风格读写、带总数分页与封顶计数、原子批量与 `transact`、`subscribe` 变更订阅与 `createChangeRelay` 转发给浏览器、`runWithAbortSignal` 传请求中止信号;自动幂等键、并发上限与按 `retry.safe` 重试;仅服务端,需要 `zod` 4 |
|
|
75
183
|
| `@tier0/sdk-v2/data/define` | 集合声明与类型:`defineCollection()`、`Doc` / `NewDoc` 等、`compileCollection()`、`validateDocument()`;可进浏览器包,组件和 route 的客户端代码只从这里取声明和类型 |
|
|
76
184
|
|
|
77
185
|
集合声明必须分别提供稳定程序标识 `alias` 和业务名称 `name`,例如 `alias: 'production_orders'`、`name: '生产工单'`。目录、数据和引用直接用 name 展示,不提供 displayName 或名称回退;name 为 1–63 个 Unicode 字符,无首尾空白,不含 `/` 或空字符。ensure 按 alias 关联集合,仅首次创建设置 name,不覆盖用户改名。名称冲突时选择有区分度的业务名称,不拼随机后缀。
|
|
@@ -80,7 +188,7 @@ const user = await tier0.system.whoami();
|
|
|
80
188
|
|
|
81
189
|
## SDK 方法与凭证权限
|
|
82
190
|
|
|
83
|
-
同一 SDK 支持工作空间集成和 App 运行时,方法存在不代表当前凭证可调用。`client.uns.namespace()` 调用的是 `POST /openapi/v2/uns/namespace/query`,属于 OpenAPI;有相应权限且未绑定 App 的 Agent / Personal Key 可以使用,App Key 当前不能使用。App
|
|
191
|
+
同一 SDK 支持工作空间集成和 App 运行时,方法存在不代表当前凭证可调用。`client.uns.namespace()` 调用的是 `POST /openapi/v2/uns/namespace/query`,属于 OpenAPI;有相应权限且未绑定 App 的 Agent / Personal Key 可以使用,App Key 当前不能使用。App 已知集合 ID 时可直接用 `recordFind` 读取记录;需要发现可访问集合及结构时使用 `client.uns.dataAppContext({})`。`appCollectionsList` 也是管理接口,不能作为 App Key 的替代入口。
|
|
84
192
|
|
|
85
193
|
SDK 不从 Key 前缀推断权限,也不在 403 后换凭证或回退内部接口。Agent 程序、SDK、MCP 都不能扩大所持凭证的权限。身份区别和排错见[命名空间查询与凭证](skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/namespace.md),App 读取示例见[查询集合与记录](skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/read.md)。
|
|
86
194
|
|
|
@@ -88,7 +196,7 @@ SDK 不从 Key 前缀推断权限,也不在 403 后换凭证或回退内部接
|
|
|
88
196
|
|
|
89
197
|
HTTP 调用统一使用 `/openapi/v2`。21 个重复 UNS 公共入口及生成方法直接移除,没有旧路由回退;升级 SDK 时需同时部署对应后端。
|
|
90
198
|
|
|
91
|
-
- Source 的 list/get/create/update、connection.update 改用 documents.*;Stream 的 programs.get/create/update/validate/disable 改用 documents.*,停用同时携带 expectedRevision
|
|
199
|
+
- Source 的 list/get/create/update、connection.update 改用 documents.*;Stream 的 programs.get/create/update/validate/disable 改用 documents.*,停用同时携带 expectedRevision 与字符串 expectedActiveVersion。
|
|
92
200
|
- 低层 findOneAnd* 改用 updateOne/replaceOne/deleteOne 的 returnDocument;批量新增用 bulkWrite 的 insertOne 项;元数据单条读取用 metadataFind + limit:1,去重用 aggregate。
|
|
93
201
|
- App 数据层的 insertMany、findOneAnd*、distinct 等便捷方法保留,由 SDK 映射到当前入口;insertMany 仍是单次原子请求,最多 200 项。
|
|
94
202
|
- Agent 的旧 nodes.searchSemantic/indexSync 和 relations.list/teach 没有 v2 契约,不再导出。旧 Controls 便捷入口、操作、DTO 和错误类不再导出,也没有兼容别名。
|
|
@@ -106,12 +214,12 @@ const sources = await tier0.source.documents.list({ page: 1, size: 20 });
|
|
|
106
214
|
const programs = await tier0.stream.programs.list({ page: 1, size: 20 });
|
|
107
215
|
```
|
|
108
216
|
|
|
109
|
-
HTTP Source
|
|
217
|
+
HTTP Source 使用 protocol: 'http-api'、顶层 role: 'client' | 'server';OAuth 使用 protocol: 'oauth2'。配置字段与 Enterprise 相同,http 保存认证模式、OAuth 非秘密配置及命名参数。Server 不能主动请求,托管 endpoint 为 http://managed;路由和响应时机由 Stream 定义。普通 handler 在提交后回复,ctx.reply 可提前回复但不保证崩溃重放。通过 source.protocols.list({}) 核对目标能力;npm 管理 SDK 与执行器脚本 API 分开使用。见 [Source Skill](skills/tier0-sdk-v2/tier0-sdk-v2-source/SKILL.md) 和 [HTTPServer Stream](skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/http-server.md)。
|
|
110
218
|
|
|
111
219
|
Source/Stream 仍保留 `{ code, msg, requestId, data }` 响应以及 `UnsV2Error` 错误类型。Source 配置统一使用 `source.documents`;Stream 草稿统一通过 `stream.documents` 创建、读取、校验和修改,输入 `program` 与 `document` 二选一。发布和回滚仍走 `stream.programs`。保存、部署、运行成功分别判断。
|
|
112
220
|
|
|
113
221
|
- 已知 Source ID/revision 且没有 Stream 引用时,可 [直接 patch 或变更状态](skills/tier0-sdk-v2/tier0-sdk-v2-source/references/update.md);有引用时仍按精确意图确认影响。
|
|
114
|
-
- Stream
|
|
222
|
+
- Stream 发布和回滚可携带并发守卫 `expectedActiveVersion`,取已读取回执的权威版本 ID 字符串,`'0'` 表示无生效发布。Cloud 的 `expectedReleaseState: 'inactive'` 是另一种互斥守卫。[发布回执](skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/deploy.md) 的 activeRelease 是 Cloud 扩展的发布序号,只用于关联运行,不作为 activeVersion 使用。
|
|
115
223
|
- 单条 Record 新增可用 [returnDocument:'after'](skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/create.md) 同次取得创建快照;[App 数据层](skills/tier0-sdk-v2/tier0-sdk-v2-data/references/read-write.md) 支持相同选择及投影类型。
|
|
116
224
|
|
|
117
225
|
从最初提交迁移:`uns.sourceList` → `source.documents.list`;`uns.sourceDocumentGet` → `source.documents.get`;`uns.streamProgramDeploy` → `stream.programs.deploy`;`uns.streamDocumentValidate` → `stream.documents.validate`。这些管理方法已移出主入口的 `uns`。底层 `createUnsV2Client()` 仍提供完整生成契约供契约工具使用;数据专用客户端可用 `createUnsDataClient()`。
|
|
@@ -138,13 +246,13 @@ await tier0.uns.recordFind(
|
|
|
138
246
|
|
|
139
247
|
`client.capabilities` 只校验 Namespace/Source/Stream 管理合同,不要求全局交互标签;同包 `client.streams.capabilities` 独立校验 App 请求合同,不阻断 Namespace。各自消费的核心合同未知仍拒绝,不将方法存在当作授权或运行就绪。
|
|
140
248
|
|
|
141
|
-
实际集合 ID 和有效权限从平台配置/数据上下文取得。管理操作中,App 凭证只能调用本 App 集合的 `appCollectionsEnsure` / `appCollectionsSchemaReplace` / `appCollectionsArchive` / `appCollectionsRestore`;SDK 中存在 `metadataInsertOne` 等其他管理方法,不表示 App 凭证有对应权限。App 数据授权 `appDataPolicyGet` / `appDataPolicyUpdate` 的调用方必须是:持有 `uns.write` 的登录会话、持有 `uns.write` 且 full_access、不绑定 App 的 personal / agent Key
|
|
249
|
+
实际集合 ID 和有效权限从平台配置/数据上下文取得。管理操作中,App 凭证只能调用本 App 集合的 `appCollectionsEnsure` / `appCollectionsSchemaReplace` / `appCollectionsArchive` / `appCollectionsRestore`;SDK 中存在 `metadataInsertOne` 等其他管理方法,不表示 App 凭证有对应权限。App 数据授权 `appDataPolicyGet` / `appDataPolicyUpdate` 的调用方必须是:持有 `uns.write` 的登录会话、持有 `uns.write` 且 full_access、不绑定 App 的 personal / agent Key,或治理会话;其中**新增**授权(新集合、放宽权限、从 `disabled` 重新启用)只接受工作区 Owner/Admin 的人工会话或平台导入,其他调用方回 403(`data.reason` 为 `app_grant_admin_required` 或 `app_grant_agent_forbidden`),收窄和移除不受此限、下一个请求起生效;运行时 App 凭证不能管理授权。Core-only 后台接口不导出到公开 SDK。
|
|
142
250
|
|
|
143
251
|
UNS 保留 `{ code, msg, requestId, data }` 完整响应。平台 v2 包装接口返回已解包的 `data`,通知/能力发现返回直接对象。业务错误即使 HTTP 为 200 也会抛出 `ApiError`。
|
|
144
252
|
|
|
145
253
|
`int64` 和平台整数字段使用 `number | bigint`:安全范围内返回 number,超出范围返回 bigint。UNS 任意 JSON 中无法无损表示的数字用 `UnsV2RawNumber`;传回 SDK 可保持原值。对外输出 JSON 时自行确定 bigint/精确数字的表示,不要直接 `JSON.stringify` 包含这些值的对象。
|
|
146
254
|
|
|
147
|
-
`createClient()` 的 HTTP 操作不自动重试。UNS v2 错误带 `execution`(`notStarted` / `rolledBack` / `committed` / `unknown`)与 `retry: { safe, afterMs? }`:`safe` 表示可以用同一请求(同一幂等键)重发。`execution` 为 `unknown` 时抛出 `UnsV2OutcomeUnknownError`;它与 `ApiError.outcomeUnknown` 一样表示写操作可能已执行,先读回对账,按业务意图保留原幂等键,不自动生成新键重放。查询与写入均可受配置超时约束。App 数据层 `@tier0/sdk-v2/data` 另有自己的策略:每次调用从开始算最多 25 秒(含排队与重试)、每进程最多 6 个并发请求、写入自动生成幂等键、按 `retry.safe` 最多自动重试 2
|
|
255
|
+
`createClient()` 的 HTTP 操作不自动重试。UNS v2 错误带 `execution`(`notStarted` / `rolledBack` / `committed` / `unknown`)与 `retry: { safe, afterMs? }`:`safe` 表示可以用同一请求(同一幂等键)重发。`execution` 为 `unknown` 时抛出 `UnsV2OutcomeUnknownError`;它与 `ApiError.outcomeUnknown` 一样表示写操作可能已执行,先读回对账,按业务意图保留原幂等键,不自动生成新键重放。查询与写入均可受配置超时约束。App 数据层 `@tier0/sdk-v2/data` 另有自己的策略:每次调用从开始算最多 25 秒(含排队与重试)、每进程最多 6 个并发请求、写入自动生成幂等键、按 `retry.safe` 最多自动重试 2 次;请求入口用 `runWithAbortSignal(request.signal, …)` 传入请求的中止信号时,请求中止后排队中的调用不再发出,进行中的被取消。
|
|
148
256
|
|
|
149
257
|
## 通知
|
|
150
258
|
|
|
@@ -182,6 +290,23 @@ const bytes = await response.arrayBuffer();
|
|
|
182
290
|
|
|
183
291
|
## MQTT
|
|
184
292
|
|
|
293
|
+
**在 Tier0 App 里**直接 `new Tier0MQClient()`,什么都不传:broker、Key 和 clientId 都取平台注入的值(见上文「aenv-next 运行环境」)。App 的 Key 只能收发自己命名空间里的主题 `apps/<TIER0_APP_ID>/<preview|deployed>/…`:预览的 Key 只能用 `preview` 段,部署的 Key 只能用 `deployed` 段,可发布可订阅,命名空间内可以用 `+` / `#`;别的 App 的主题、另一个运行环境的段和其他任何主题都会被 broker 拒绝。主题不在 App 设置里配置,也不需要管理员授权。Backend 按 Key 自己的运行环境决定段名(不看 clientId 和环境变量),没有运行环境的旧 Key 按 `deployed`;平台注入的 `TIER0_RUNTIME` 和 Key 的运行环境同词。
|
|
294
|
+
|
|
295
|
+
用 `appTopic(suffix)` 拼主题:它读 `TIER0_APP_ID` 和 `TIER0_RUNTIME`,返回 `apps/<TIER0_APP_ID>/<TIER0_RUNTIME>/<suffix>`;缺一个、App ID 不能当主题的一层,或 `TIER0_RUNTIME` 不是 `preview` / `deployed` 时抛错,不猜段名。`appTopic` 只在平台里(预览 / 部署)用,平台外会抛错;本地开发不要为它设 `TIER0_RUNTIME`,需要本地调试 MQTT 时自己写完整主题。
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
import { Tier0MQClient, appTopic } from '@tier0/sdk-v2/mq';
|
|
299
|
+
|
|
300
|
+
const mq = new Tier0MQClient();
|
|
301
|
+
await mq.connect();
|
|
302
|
+
mq.subscribe(appTopic('alarms/#'), (topic, payload) => { /* 处理消息 */ });
|
|
303
|
+
await mq.publish(appTopic('alarms/line-1'), { level: 'high' }, { qos: 1 });
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
不用 helper 时写成 `` `apps/${process.env.TIER0_APP_ID}/${process.env.TIER0_RUNTIME}/alarms/#` ``。设备和 UNS 数据变更不走这里:服务端订阅 Record 变更用 `@tier0/sdk-v2/data` 的 `data.subscribe`。
|
|
307
|
+
|
|
308
|
+
**App 之外的集成客户端**用 `mqttAuth` 创建的凭证,主题按这份凭证的授权:
|
|
309
|
+
|
|
185
310
|
```ts
|
|
186
311
|
import { Tier0MQClient, toWebSocketUrl } from '@tier0/sdk-v2/mq';
|
|
187
312
|
|
|
@@ -199,6 +324,16 @@ mq.subscribe('平台授权的主题', (_topic, payload) => { /* 处理消息 */
|
|
|
199
324
|
|
|
200
325
|
完整凭证只在创建时返回一次。也支持从编码 Workspace 的平台 API Key 推导 MQTT 身份;无法解析的 Key 会拒绝连接,不回退到 Enterprise 身份。MQTT 自身的连接恢复/QoS 语义与 HTTP 的“不自动重试写入”是不同机制。
|
|
201
326
|
|
|
327
|
+
MQTT 未配置 `password` 时可使用进程环境的 `TIER0_API_KEY`;显式传入 `password: undefined` 不会选择环境或已保存的 Key,`connect` 会拒绝。省略该字段则保留已有设置。已连接或自动重连中的客户端拒绝变更凭据时保持原连接及其身份,调用方必须处理错误;需要切换身份时先断开并创建新客户端。
|
|
328
|
+
|
|
329
|
+
MQTT 连接建立后,凭据和连接设置由底层客户端持有,包括自动重连期间。调用 `connect` 修改这些设置会报错,不会替换正在使用的凭据。换证时应停止旧客户端的业务操作、调用 `disconnect()`,再使用新凭据创建一个新的 `Tier0MQClient` 并重新注册订阅。旧客户端的 `disconnect()` 会清除 SDK 的订阅列表;它不是等待 Broker 确认断开的异步屏障。换证期间的消息可能丢失或重复,在途发布的结果也可能未知,应用需按业务协议处理,不能假定 SDK 已重放或恰好执行一次。Broker 撤销已经建立连接的生效时机取决于服务端认证、ACL 和缓存策略;仅更换客户端不能证明旧连接已被撤权。
|
|
330
|
+
|
|
331
|
+
首次连接正在建立时,`subscribe` 会等待连接;首次连接失败时撤销该订阅并报告错误。SDK 自己负责重连(见上文「aenv-next 运行环境」):自动重连期间 `subscribe`、`connect` 等待重连完成,新订阅会登记,重连后与中断前已登记的订阅一起恢复,也可在恢复前取消;`publish` 交给同一底层客户端排队,重连后发出。客户端停止重连(浏览器授权失败)或调用 `disconnect()` 时,等待中的调用报错,等待中的新订阅撤销。
|
|
332
|
+
|
|
333
|
+
同一 topic 的多个 handler 共用一次 Broker 订阅和 SUBACK。SDK 同时检查报文及 mqtt.js 的授权结果;首次订阅或重连恢复若被 Broker 拒绝或确认不合法,该 topic 的 handler 会被移除,`error` 事件包含 topic,调用方可在处理原因后重新注册;其他 topic 不受影响。`connect` 事件仅表示连接建立,不表示所有订阅已获 Broker 确认。
|
|
334
|
+
|
|
335
|
+
`connect` 在启动底层客户端前检查地址和凭据;本地预检失败不会保存本次传入的设置。底层客户端已创建后若连接失败,其连接设置仍由该客户端持有,自动重连会继续使用这些设置(浏览器每次重连取新令牌,平台代理模式由节点代理填入 App 的 Key);要改变凭据,需先断开并按上述方式重建。
|
|
336
|
+
|
|
202
337
|
## React / Vue
|
|
203
338
|
|
|
204
339
|
```ts
|
|
@@ -265,27 +400,30 @@ node scripts/check-live-stream-runtime.mjs <consumer> <workspaceId> .tmp/live-st
|
|
|
265
400
|
|
|
266
401
|
平台接口与能力发现的本地联调可分别运行 `node scripts/sync-contracts.mjs <backend> --worktree --platform-only` 和 `node scripts/sync-capability-contract.mjs <backend> --worktree`。预览来源明确记录基线和内容摘要,不能直接发布。Backend 提交后,将两条命令的 `--worktree` 替换为正式提交 SHA,再运行 `npm run generate && npm run check`;UNS 数据模型仍使用其独立固定输入。
|
|
267
402
|
|
|
268
|
-
|
|
403
|
+
Source 创建/更新与 Stream 创建/更新/部署的 dryRun 默认 false。凭据统一为 basic/oauth2/bearer/custom 等原生 kind,使用 username/secret/values,不使用 material/method。SQL 使用 edge-sql 与顶层 sql 固定查询。删除使用 source.documents.delete,换证使用 source.documents.credentials.update;远端 Edge 秘密在 Edge 本地预配。Stream 远端绑定传 target: {fleetNodeId,sourceId,configRevision},服务端自动生成受控引用;不得与本地 sourceId/configRevision 同时传入。
|
|
269
404
|
|
|
270
405
|
## App Stream 交互与旧包迁移
|
|
271
406
|
|
|
272
|
-
|
|
407
|
+
`client.streams` 只在服务端用带 Key 的客户端(`createClient()`,读 `TIER0_API_HOST` / `TIER0_API_KEY`,或显式传 `apiHost` / `apiKey`):`client.streams.requests.use({code}).invoke(input, {idempotencyKey})`、`client.streams.messages.use({topic}).subscribe(handler)`。浏览器不直接调平台,经 App 自己的服务端接口。Tier0 App 的 Key 可以用:Stream 请求和消息是 App 的固定能力,仍受工作区套餐约束;请求还要 Stream 请求授权给这个 App 授过对应的 request code;消息主题以已发布 Stream 的 manifest 为准(App 能订阅 Stream 声明发布的主题,能发布到有 Stream 订阅的主题),没有按 App 的主题配置(见 [Stream 交互](skills/tier0-sdk-v2/tier0-sdk-v2-streams/SKILL.md))。订阅不再使用时 dispose,客户端退出时 `await client.dispose()`;请求与消息不会自动重放业务写入。
|
|
273
408
|
|
|
274
|
-
旧 `/cloud` 改用根入口,`/cloud/server` 改用 `/server`,`/openapi/react` 和 `/openapi/vue` 改用 `/react` 和 `/vue`。`/streams
|
|
409
|
+
旧 `/cloud` 改用根入口,`/cloud/server` 改用 `/server`,`/openapi/react` 和 `/openapi/vue` 改用 `/react` 和 `/vue`。`/streams` 的现役能力已原生迁入本包;浏览器受信宿主(`/runtime`、`installAppRuntime`、`Symbol.for('tier0.sdk.app-runtime.v1')`)已删除,没有替代。Source/Stream 管理、文件、MQTT、通知、成员和集合继续使用本包现有入口;旧文件单例函数改为 `client.files` 对应方法。旧 `getCurrentProjectId`、Flow、Control 及项目接口已退役,不迁回。
|
|
275
410
|
|
|
276
411
|
| 旧包现役能力 | 统一后的入口与迁移方式 |
|
|
277
412
|
| --- | --- |
|
|
278
413
|
| 根入口、`/cloud` 的 `client` / `createClient` | `@tier0/sdk-v2` 根入口;同一实例提供数据管理和 App 交互 |
|
|
279
414
|
| Stream `describe` / `discover` / `invoke` | `client.streams.requests`;保留类型、幂等键和结果未知语义 |
|
|
280
|
-
| 消息 `publish` / `subscribe` / `onError` / `dispose` | `client.streams.messages`;原生 NATS WSS
|
|
415
|
+
| 消息 `publish` / `subscribe` / `onError` / `dispose` | `client.streams.messages`;原生 NATS WSS,消息需要 Node.js 22+;HTTP 仍支持 Node.js 20.19+ |
|
|
281
416
|
| 请求/invoke 能力发现 | `client.streams.capabilities.discover()`;`client.capabilities.discover()` 独立保留 Namespace/Source/Stream 作用域 |
|
|
282
|
-
|
|
|
417
|
+
| 访问者头 | `/server`(`parseVisitorHeader`) |
|
|
418
|
+
| Trusted runtime provider(`/runtime`) | 已删除;浏览器经 App 自己的服务端接口 |
|
|
283
419
|
| `unsV2`、平台操作、MQTT | `client.uns` / `client.source` / `client.stream`、现有平台入口、`/mq`;旧包全部现役公开操作已有对应 v2 操作 |
|
|
284
420
|
| `uploadFile` / `uploadFileMultipart` / `resumeMultipartUpload` | `client.files.upload` / `uploadMultipart` / `resumeMultipart`;上传选项与返回类型从 `/files` 导入 |
|
|
285
421
|
| `getFileUrl` / `downloadFile` / `deleteFile` | `client.files.url` / `download` / `delete`;download 返回原生 `Response` |
|
|
286
422
|
| 旧 React / Vue 每接口 hook | `/react` 的通用 query/mutation、`/vue` 的 `useTier0Request`;使用统一 client 和对应方法 |
|
|
287
423
|
| Project、Control、Flow 及旧配置版本入口 | 已退役,不提供别名或重新导出 |
|
|
288
424
|
|
|
289
|
-
真实消息本地验收用 `npm run test:streams:artifact`(Go、Node.js 22+、`nats-server`,通过 `TIER0_STREAM_MESSAGES_BACKEND_DIR` 指向 Backend checkout
|
|
425
|
+
真实消息本地验收用 `npm run test:streams:artifact`(Go、Node.js 22+、`nats-server`,通过 `TIER0_STREAM_MESSAGES_BACKEND_DIR` 指向 Backend checkout)。脚本用带 Key 的服务端客户端验证生产 Go issuer、TLS NATS、已安装 tarball 的 ESM/CJS,以及根入口的浏览器打包依赖闭包;固定夹具身份不代表线上 SYS/Gateway/App 权限链路。
|
|
290
426
|
|
|
291
427
|
代码存在不代表已发布。先构建并安装这次统一实现的不可变制品,更新 App 模板、依赖锁与存量 App,再移除旧包;不能用旧 npm 0.7.2 替代本分支构建。完整交互说明见 [Stream 交互](skills/tier0-sdk-v2/tier0-sdk-v2-streams/SKILL.md)。
|
|
428
|
+
|
|
429
|
+
Source/Stream 定向契约同步使用 `node scripts/sync-source-stream-contract.mjs <backend> <commit>`,然后 `npm run generate && npm run check`。该生成流程同步相关公开操作、路由、协议目录及依赖 schema,保留其余数据模型与平台输入的固定版本;来源和摘要记录在 contracts/uns-v2/provenance.json 的 source-stream-public 条目。`--worktree` 仅供预览,发布前必须重新固定已提交 SHA。
|