@collective-intelligence/extensions-sdk-frontend-react 0.1.31 → 0.1.33

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 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 is reserved
12
- for the Effect API of this package and exports nothing in this release.
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};
@@ -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.31",
3
+ "version": "0.1.33",
4
4
  "description": "Public hosted React contribution contracts for Collective Intelligence extensions.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -64,23 +64,24 @@
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.30",
80
- "@collective-intelligence/extensions-sdk-frontend": "0.1.30"
80
+ "@collective-intelligence/extensions-sdk": "0.1.31",
81
+ "@collective-intelligence/extensions-sdk-frontend": "0.1.31"
81
82
  },
82
83
  "peerDependencies": {
83
- "@collective-intelligence/ui-react": "0.1.26",
84
+ "@collective-intelligence/ui-react": "0.1.27",
84
85
  "@tanstack/react-query": ">=5 <6",
85
86
  "@xyflow/react": ">=12 <13",
86
87
  "effect": "^4.1.0",
@@ -96,7 +97,8 @@
96
97
  "access": "public"
97
98
  },
98
99
  "devDependencies": {
99
- "@collective-intelligence/ui-react": "0.1.26",
100
+ "@collective-intelligence/ui-react": "0.1.27",
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": "^3.2.7",
119
+ "vitest": "5.0.2",
118
120
  "es-module-lexer": "2.3.2"
119
121
  },
120
- "gitHead": "24d1d077eeb604df774f04db008630b8c10d130c"
122
+ "gitHead": "bfe1f5e741d70b6e5a5072e411d84b00e737670d"
121
123
  }