@collective-intelligence/extensions-sdk-frontend-react 0.1.31 → 0.1.32
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/README.md +66 -2
- package/dist/effect/studio-api/service.d.ts +112 -0
- package/dist/effect.d.ts +3 -1
- package/dist/effect.js +1 -0
- package/dist/errors.d.ts +20 -0
- package/package.json +10 -8
package/README.md
CHANGED
|
@@ -8,8 +8,8 @@ React and React DOM are host-provided peers. The optional
|
|
|
8
8
|
`/workflow` or `/ui-react` subpaths; declaration-only consumers do not need it.
|
|
9
9
|
|
|
10
10
|
`effect` (`^4.1.0`) is a required peer: the app installs one copy of `effect`,
|
|
11
|
-
and the package build keeps it external. The `/effect` entrypoint
|
|
12
|
-
|
|
11
|
+
and the package build keeps it external. The `/effect` entrypoint exports the
|
|
12
|
+
`StudioApi` service; see [Studio API on Effect](#studio-api-on-effect).
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
import { declareFrontendCatalogContribution } from "@collective-intelligence/extensions-sdk-frontend"
|
|
@@ -217,6 +217,70 @@ export const CustomerWidget = ({ context }: StudioViewComponentProps) =>
|
|
|
217
217
|
marked operation, run `bun run --filter @repo/backend api:openapi`, then
|
|
218
218
|
`bun run api:codegen:studio-api` in this package. CI runs
|
|
219
219
|
`api:codegen:studio-api:check` and fails when the committed client is stale.
|
|
220
|
+
A new operation also needs its method in `StudioApi`; a test fails until it
|
|
221
|
+
has one.
|
|
222
|
+
|
|
223
|
+
### Studio API on Effect
|
|
224
|
+
|
|
225
|
+
The `/effect` entrypoint exports the `StudioApi` service. It has one method per
|
|
226
|
+
operation of the `studio-api` client, with the same name, request options and
|
|
227
|
+
success type. `StudioApi` calls the shared `studio-api` client: it imports the
|
|
228
|
+
client through `./studio-api`, so a Studio Widget calls the client that the
|
|
229
|
+
Studio Host binds. A Studio Extension release bundles `/effect` and keeps
|
|
230
|
+
`/studio-api` external; the release build needs a `ci` CLI version that
|
|
231
|
+
bundles an SDK `./effect` subpath (see the CLI changelog). Only the Studio
|
|
232
|
+
Host provides `/studio-api`. A Dashboard release bundles its own copy of the
|
|
233
|
+
client, and no Host binds that copy.
|
|
234
|
+
|
|
235
|
+
`/effect` imports each operation of `/studio-api` by name. In a Studio
|
|
236
|
+
Extension, the Studio Host provides that module. If the `/studio-api` of the
|
|
237
|
+
Host does not export one of these operations, `/effect` does not load, also
|
|
238
|
+
for a Widget that does not call that operation. Use `/effect` only with a
|
|
239
|
+
Studio Host that ships this SDK version or a later one that keeps each
|
|
240
|
+
operation.
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import type { StudioViewContext } from "@collective-intelligence/extensions-sdk-frontend-react/studio"
|
|
244
|
+
|
|
245
|
+
import {
|
|
246
|
+
layerStudioApi,
|
|
247
|
+
StudioApi
|
|
248
|
+
} from "@collective-intelligence/extensions-sdk-frontend-react/effect"
|
|
249
|
+
import { studioPublishedViewScope } from "@collective-intelligence/extensions-sdk-frontend-react/studio"
|
|
250
|
+
import * as Effect from "effect/Effect"
|
|
251
|
+
|
|
252
|
+
const runCount = (context: StudioViewContext) =>
|
|
253
|
+
Effect.gen(function* () {
|
|
254
|
+
const api = yield* StudioApi
|
|
255
|
+
const page = yield* api.studioWorkflowRunsList(
|
|
256
|
+
studioPublishedViewScope(context)
|
|
257
|
+
)
|
|
258
|
+
return page.data.length
|
|
259
|
+
}).pipe(
|
|
260
|
+
Effect.catchTag("StudioApiError", (error) => Effect.fail(error.status)),
|
|
261
|
+
Effect.provide(layerStudioApi())
|
|
262
|
+
)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
- An error answer fails with `StudioApiError`. It holds the `status` and the
|
|
266
|
+
`body` that the client read (JSON, else text). The client does not check
|
|
267
|
+
`body`, and a status outside the generated error map, such as a 502 of a
|
|
268
|
+
proxy, holds any body. So `body` is `unknown`: decode it with a Schema
|
|
269
|
+
before use.
|
|
270
|
+
- A request that does not reach the Backend, or an OK answer that is not
|
|
271
|
+
readable, fails with `StudioApiTransportError`, which holds the `cause`.
|
|
272
|
+
- Bad input, such as a body that JSON cannot write, is a `TypeError` Defect.
|
|
273
|
+
The request does not leave.
|
|
274
|
+
- An interruption of the fiber aborts the request.
|
|
275
|
+
- The service sets `client`, `fetch`, `baseUrl`, `signal`, `responseStyle` and
|
|
276
|
+
`throwOnError`. Set headers and the other request options per call.
|
|
277
|
+
|
|
278
|
+
`layerStudioApi({ baseUrl })` sends each request to `baseUrl`. A Studio Widget
|
|
279
|
+
gives no base URL: the Host binds the client to its Backend and refuses other
|
|
280
|
+
URLs. A host that does not bind the client, such as a test or a preview, gives
|
|
281
|
+
the URL of its Backend. That URL is deployment configuration, so
|
|
282
|
+
`layerStudioApiConfig({ baseUrl: Config.String("STUDIO_API_BASE_URL") })`
|
|
283
|
+
reads it with Effect `Config`.
|
|
220
284
|
|
|
221
285
|
## Building in the workspace
|
|
222
286
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Studio API on the Effect Core: one method per operation of the
|
|
3
|
+
* generated `./studio-api` client. The service imports that client through
|
|
4
|
+
* the package's own `./studio-api` subpath, which the Studio Host provides
|
|
5
|
+
* and binds to its Backend transport, so a Studio Extension calls the client
|
|
6
|
+
* that the Host binds.
|
|
7
|
+
*
|
|
8
|
+
* An error answer fails with `StudioApiError`. A request that does not reach
|
|
9
|
+
* the Backend, or an OK answer that is not readable, fails with
|
|
10
|
+
* `StudioApiTransportError`.
|
|
11
|
+
*/
|
|
12
|
+
import type { Options, StudioAgentRunsGetData, StudioAgentRunsGetErrors, StudioAgentRunsGetResponses, StudioAgentRunsListData, StudioAgentRunsListErrors, StudioAgentRunsListResponses, StudioViewDataGetItemData, StudioViewDataGetItemErrors, StudioViewDataGetItemResponses, StudioViewDataSearchData, StudioViewDataSearchErrors, StudioViewDataSearchMembersData, StudioViewDataSearchMembersErrors, StudioViewDataSearchMembersResponses, StudioViewDataSearchResponses, StudioViewDataUpdateItemData, StudioViewDataUpdateItemErrors, StudioViewDataUpdateItemResponses, StudioWorkflowRunsGetData, StudioWorkflowRunsGetErrors, StudioWorkflowRunsGetResponses, StudioWorkflowRunsListData, StudioWorkflowRunsListErrors, StudioWorkflowRunsListResponses } from "@collective-intelligence/extensions-sdk-frontend-react/studio-api";
|
|
13
|
+
import * as Config from "effect/Config";
|
|
14
|
+
import * as Context from "effect/Context";
|
|
15
|
+
import * as Effect from "effect/Effect";
|
|
16
|
+
import * as Layer from "effect/Layer";
|
|
17
|
+
import { StudioApiError, StudioApiTransportError } from "../../errors";
|
|
18
|
+
/** The request data, success map and error map of each generated operation. */
|
|
19
|
+
type Generated = Readonly<{
|
|
20
|
+
studioAgentRunsGet: [
|
|
21
|
+
StudioAgentRunsGetData,
|
|
22
|
+
StudioAgentRunsGetResponses,
|
|
23
|
+
StudioAgentRunsGetErrors
|
|
24
|
+
];
|
|
25
|
+
studioAgentRunsList: [
|
|
26
|
+
StudioAgentRunsListData,
|
|
27
|
+
StudioAgentRunsListResponses,
|
|
28
|
+
StudioAgentRunsListErrors
|
|
29
|
+
];
|
|
30
|
+
studioViewDataGetItem: [
|
|
31
|
+
StudioViewDataGetItemData,
|
|
32
|
+
StudioViewDataGetItemResponses,
|
|
33
|
+
StudioViewDataGetItemErrors
|
|
34
|
+
];
|
|
35
|
+
studioViewDataSearch: [
|
|
36
|
+
StudioViewDataSearchData,
|
|
37
|
+
StudioViewDataSearchResponses,
|
|
38
|
+
StudioViewDataSearchErrors
|
|
39
|
+
];
|
|
40
|
+
studioViewDataSearchMembers: [
|
|
41
|
+
StudioViewDataSearchMembersData,
|
|
42
|
+
StudioViewDataSearchMembersResponses,
|
|
43
|
+
StudioViewDataSearchMembersErrors
|
|
44
|
+
];
|
|
45
|
+
studioViewDataUpdateItem: [
|
|
46
|
+
StudioViewDataUpdateItemData,
|
|
47
|
+
StudioViewDataUpdateItemResponses,
|
|
48
|
+
StudioViewDataUpdateItemErrors
|
|
49
|
+
];
|
|
50
|
+
studioWorkflowRunsGet: [
|
|
51
|
+
StudioWorkflowRunsGetData,
|
|
52
|
+
StudioWorkflowRunsGetResponses,
|
|
53
|
+
StudioWorkflowRunsGetErrors
|
|
54
|
+
];
|
|
55
|
+
studioWorkflowRunsList: [
|
|
56
|
+
StudioWorkflowRunsListData,
|
|
57
|
+
StudioWorkflowRunsListResponses,
|
|
58
|
+
StudioWorkflowRunsListErrors
|
|
59
|
+
];
|
|
60
|
+
}>;
|
|
61
|
+
/** The name of one generated operation. */
|
|
62
|
+
export type StudioApiOperationName = keyof Generated;
|
|
63
|
+
/** The options that the service sets on each call of the generated client. */
|
|
64
|
+
type CallOptions = Readonly<{
|
|
65
|
+
baseUrl?: string;
|
|
66
|
+
fetch: typeof globalThis.fetch;
|
|
67
|
+
responseStyle: "fields";
|
|
68
|
+
signal: AbortSignal;
|
|
69
|
+
throwOnError: false;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* The request options of one operation, less those that the service sets;
|
|
73
|
+
* the service always calls the shared client.
|
|
74
|
+
*/
|
|
75
|
+
export type StudioApiRequest<Name extends StudioApiOperationName> = Omit<Options<Generated[Name][0], false>, keyof CallOptions | "client">;
|
|
76
|
+
/** The one value type of a generated status map, such as `{ 200: Page }`. */
|
|
77
|
+
type ValueOf<Map> = Map[keyof Map];
|
|
78
|
+
/** The success value of one operation: the generated success type. */
|
|
79
|
+
export type StudioApiSuccess<Name extends StudioApiOperationName> = ValueOf<Generated[Name][1]>;
|
|
80
|
+
/** One operation of `StudioApi`. */
|
|
81
|
+
export type StudioApiOperation<Name extends StudioApiOperationName> = (request: StudioApiRequest<Name>) => Effect.Effect<StudioApiSuccess<Name>, StudioApiError | StudioApiTransportError>;
|
|
82
|
+
export type StudioApiValue = Readonly<{
|
|
83
|
+
[Name in StudioApiOperationName]: StudioApiOperation<Name>;
|
|
84
|
+
}>;
|
|
85
|
+
declare const StudioApi_base: Context.ServiceClass<StudioApi, "@collective-intelligence/extensions-sdk-frontend-react/StudioApi", Readonly<{
|
|
86
|
+
studioAgentRunsGet: StudioApiOperation<"studioAgentRunsGet">;
|
|
87
|
+
studioAgentRunsList: StudioApiOperation<"studioAgentRunsList">;
|
|
88
|
+
studioViewDataGetItem: StudioApiOperation<"studioViewDataGetItem">;
|
|
89
|
+
studioViewDataSearch: StudioApiOperation<"studioViewDataSearch">;
|
|
90
|
+
studioViewDataSearchMembers: StudioApiOperation<"studioViewDataSearchMembers">;
|
|
91
|
+
studioViewDataUpdateItem: StudioApiOperation<"studioViewDataUpdateItem">;
|
|
92
|
+
studioWorkflowRunsGet: StudioApiOperation<"studioWorkflowRunsGet">;
|
|
93
|
+
studioWorkflowRunsList: StudioApiOperation<"studioWorkflowRunsList">;
|
|
94
|
+
}>>;
|
|
95
|
+
export declare class StudioApi extends StudioApi_base {
|
|
96
|
+
}
|
|
97
|
+
export type StudioApiOptions = Readonly<{
|
|
98
|
+
/**
|
|
99
|
+
* The base URL of each request. A Studio Extension leaves it out: the
|
|
100
|
+
* Studio Host binds the client to its Backend.
|
|
101
|
+
*/
|
|
102
|
+
baseUrl?: string;
|
|
103
|
+
}>;
|
|
104
|
+
export declare const make: (options?: StudioApiOptions) => StudioApiValue;
|
|
105
|
+
/** `StudioApi` over the `./studio-api` client. */
|
|
106
|
+
export declare const layer: (options?: StudioApiOptions) => Layer.Layer<StudioApi, never, never>;
|
|
107
|
+
/**
|
|
108
|
+
* `layer` with options that Effect `Config` reads: the base URL is
|
|
109
|
+
* deployment configuration of a host that does not bind the client.
|
|
110
|
+
*/
|
|
111
|
+
export declare const layerConfig: (config: Config.Wrap<StudioApiOptions>) => Layer.Layer<StudioApi, Config.ConfigError, never>;
|
|
112
|
+
export {};
|
package/dist/effect.d.ts
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export { StudioApiError, StudioApiTransportError } from "./errors.js";
|
|
2
|
+
export { StudioApi, layer as layerStudioApi, layerConfig as layerStudioApiConfig } from "./effect/studio-api/service.js";
|
|
3
|
+
export type { StudioApiOperation, StudioApiOperationName, StudioApiOptions, StudioApiRequest, StudioApiSuccess, StudioApiValue } from "./effect/studio-api/service.js";
|
package/dist/effect.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as o from"effect/Schema";class d extends o.TaggedError()("StudioApiError",{body:o.Unknown,status:o.Int}){}class n extends o.TaggedError()("StudioApiTransportError",{cause:o.Defect()}){}import{client as g,studioAgentRunsGet as y,studioAgentRunsList as h,studioViewDataGetItem as E,studioViewDataSearch as G,studioViewDataSearchMembers as k,studioViewDataUpdateItem as O,studioWorkflowRunsGet as x,studioWorkflowRunsList as L}from"@collective-intelligence/extensions-sdk-frontend-react/studio-api";import*as f from"effect/Config";import*as c from"effect/Context";import*as u from"effect/Effect";import*as p from"effect/Layer";class S extends c.Service()("@collective-intelligence/extensions-sdk-frontend-react/StudioApi"){}var N=(s={})=>{let w=s.baseUrl===void 0?{}:{baseUrl:s.baseUrl},i=(t,e)=>u.fn(`StudioApi.${t}`)(function*(A){let l=!1,m=g.getConfig().fetch??globalThis.fetch,D=Object.assign(async(r,V)=>(l=!0,await m(r,V)),m),a=yield*u.tryPromise({catch:(r)=>{if(l)return new n({cause:r});throw r},try:(r)=>e(A,{...w,fetch:D,responseStyle:"fields",signal:r,throwOnError:!1})});if(a.response===void 0)return yield*new n({cause:a.error});if(!a.response.ok)return yield*new d({body:a.error,status:a.response.status});return a.data});return S.of({studioAgentRunsGet:i("studioAgentRunsGet",(t,e)=>y({...t,...e})),studioAgentRunsList:i("studioAgentRunsList",(t,e)=>h({...t,...e})),studioViewDataGetItem:i("studioViewDataGetItem",(t,e)=>E({...t,...e})),studioViewDataSearch:i("studioViewDataSearch",(t,e)=>G({...t,...e})),studioViewDataSearchMembers:i("studioViewDataSearchMembers",(t,e)=>k({...t,...e})),studioViewDataUpdateItem:i("studioViewDataUpdateItem",(t,e)=>O({...t,...e})),studioWorkflowRunsGet:i("studioWorkflowRunsGet",(t,e)=>x({...t,...e})),studioWorkflowRunsList:i("studioWorkflowRunsList",(t,e)=>L({...t,...e}))})},R=(s={})=>p.sync(S,()=>N(s)),b=(s)=>p.unwrap(u.map(f.unwrap(s),R));export{S as StudioApi,d as StudioApiError,n as StudioApiTransportError,R as layerStudioApi,b as layerStudioApiConfig};
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
declare const StudioApiError_base: Schema.Class<StudioApiError, Schema.TaggedStruct<"StudioApiError", {
|
|
3
|
+
/**
|
|
4
|
+
* The error answer as the generated client reads it: JSON, else text.
|
|
5
|
+
* The client does not check it, and a status outside the generated error
|
|
6
|
+
* map, such as a 502 of a proxy, holds any body: decode it before use.
|
|
7
|
+
*/
|
|
8
|
+
readonly body: Schema.Unknown;
|
|
9
|
+
readonly status: Schema.Int;
|
|
10
|
+
}>, import("effect/Cause").YieldableError>;
|
|
11
|
+
/** A Studio API operation got an answer with an error status. */
|
|
12
|
+
export declare class StudioApiError extends StudioApiError_base {
|
|
13
|
+
}
|
|
14
|
+
declare const StudioApiTransportError_base: Schema.Class<StudioApiTransportError, Schema.TaggedStruct<"StudioApiTransportError", {
|
|
15
|
+
readonly cause: Schema.Defect;
|
|
16
|
+
}>, import("effect/Cause").YieldableError>;
|
|
17
|
+
/** The request did not reach the Backend, or its OK answer is not readable. */
|
|
18
|
+
export declare class StudioApiTransportError extends StudioApiTransportError_base {
|
|
19
|
+
}
|
|
20
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@collective-intelligence/extensions-sdk-frontend-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"description": "Public hosted React contribution contracts for Collective Intelligence extensions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -64,20 +64,21 @@
|
|
|
64
64
|
"api:codegen:studio-api": "bun tools/codegen/generate-studio-api.ts",
|
|
65
65
|
"api:codegen:studio-api:check": "bun tools/codegen/generate-studio-api.ts --check",
|
|
66
66
|
"build": "bun tools/build.ts",
|
|
67
|
-
"test": "bun test src tools && bun run test:pack && vitest run test/frontend-runtime.test.tsx test/built-runtime.test.ts test/workflow-field-help.test.tsx",
|
|
68
|
-
"test:unit": "bun test src",
|
|
67
|
+
"test": "bun --conditions=ci-source test src tools && bun run test:pack && vitest run test/frontend-runtime.test.tsx test/built-runtime.test.ts test/workflow-field-help.test.tsx",
|
|
68
|
+
"test:unit": "bun --conditions=ci-source test src",
|
|
69
69
|
"test:pack": "bun tools/test-pack.ts",
|
|
70
|
+
"test:properties": "vitest run test/properties",
|
|
70
71
|
"format": "oxfmt --write .",
|
|
71
72
|
"format:check": "oxfmt --check .",
|
|
72
73
|
"format:check:staged": "oxfmt --check",
|
|
73
74
|
"lint": "node \"../../../.agents/scripts/managed-lint-runner.mjs\" --scope \"packages/public/extensions-sdk-frontend-react\"",
|
|
74
75
|
"lint:staged": "oxlint --no-error-on-unmatched-pattern",
|
|
75
76
|
"prepack": "bun run build",
|
|
76
|
-
"type-check": "bun check"
|
|
77
|
+
"type-check": "bun check && bun check -p tsconfig.type-tests.json"
|
|
77
78
|
},
|
|
78
79
|
"dependencies": {
|
|
79
|
-
"@collective-intelligence/extensions-sdk": "0.1.
|
|
80
|
-
"@collective-intelligence/extensions-sdk-frontend": "0.1.
|
|
80
|
+
"@collective-intelligence/extensions-sdk": "0.1.31",
|
|
81
|
+
"@collective-intelligence/extensions-sdk-frontend": "0.1.31"
|
|
81
82
|
},
|
|
82
83
|
"peerDependencies": {
|
|
83
84
|
"@collective-intelligence/ui-react": "0.1.26",
|
|
@@ -97,6 +98,7 @@
|
|
|
97
98
|
},
|
|
98
99
|
"devDependencies": {
|
|
99
100
|
"@collective-intelligence/ui-react": "0.1.26",
|
|
101
|
+
"@effect/vitest": "4.1.0",
|
|
100
102
|
"@hey-api/openapi-ts": "^0.96.0",
|
|
101
103
|
"@oxlint/plugins": "1.90.0",
|
|
102
104
|
"@tanstack/react-query": "5.101.4",
|
|
@@ -114,8 +116,8 @@
|
|
|
114
116
|
"react-dom": "^19.2.8",
|
|
115
117
|
"typescript": "7.0.2",
|
|
116
118
|
"ultracite": "7.10.7",
|
|
117
|
-
"vitest": "
|
|
119
|
+
"vitest": "5.0.2",
|
|
118
120
|
"es-module-lexer": "2.3.2"
|
|
119
121
|
},
|
|
120
|
-
"gitHead": "
|
|
122
|
+
"gitHead": "b46c00d8ce76db49b810e7a3f25933ace4f2252f"
|
|
121
123
|
}
|