@collective-intelligence/external-apps-client-sdk-react 0.1.37 → 0.1.38

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
@@ -66,6 +66,54 @@ The old public TanStack option factories and native-result resource hooks are no
66
66
  Use the deliberate entity hooks above. Existing generic slotted `Resource*` presentation remains
67
67
  separate from common entity CRUD.
68
68
 
69
+ ## Optimistic Writes
70
+
71
+ Entity writes are Optimistic Writes. The value is in the QueryClient cache, so every view that
72
+ reads the record changes at once:
73
+
74
+ - Before CI answers an update or a delete, the record view and each cached list page show the
75
+ new value. A deleted record has `deletedAt` in its record view and leaves the list pages.
76
+ - A create shows a pending row at the start of each cached first list page. Its `id` is the
77
+ idempotency key and its `value` is the create input. CI's record replaces it.
78
+ - To change the cache only when CI answers, pass `optimistic: false` in the target:
79
+ `useEntityUpdate(contract, { id, optimistic: false })`, also for `useEntityDelete`.
80
+ - Writes to one record run one at a time, in call order. An entity read of a record waits until
81
+ the pending write of that record settles, and a list read waits for the pending writes of its
82
+ resource. Thus a refetch after a realtime change or a Resync does not replace the shown value.
83
+ - On success, the views show CI's record, and the write ends. Then each view of the resource
84
+ reads CI once more; the write does not wait for these reads.
85
+ - On a failure or an interruption, a view goes back to its value from before the write, but only
86
+ when it still shows the write's value. In a list page, only the write's own row changes, so
87
+ the rows of pending writes to other records stay. A read outside the entity hooks, for example
88
+ an app `fetchQuery` on an entity key, does not wait for a write; the view then keeps that
89
+ newer value. The read after the write shows CI's value in each case.
90
+
91
+ `onWriteFailed` on `CollectiveIntelligenceProvider` or `CollectiveIntelligenceLoginProvider`
92
+ gets a `WriteFailed` for each entity write that CI refuses or that does not reach CI, after the
93
+ views went back. `error` is a `CiError` (CI refused the write) or a `CiTransportError` (the
94
+ request did not reach CI, or its answer was lost); `operation` is `create`, `update` or
95
+ `delete`; `label` is the resource id (`contract.id`). It is not called for an interruption, or
96
+ when the entity source changed during the write. The provider calls the `onWriteFailed` of the
97
+ latest render. `.` and `./effect` export the `WriteFailed` type. This package shows no toast:
98
+ `ciWriteFailedToast(messages?)` of `@collective-intelligence/ui-react/ui` returns a handler that
99
+ shows one in the app's `Toaster`.
100
+
101
+ ```tsx
102
+ import {
103
+ Toaster,
104
+ ciWriteFailedToast
105
+ } from "@collective-intelligence/ui-react/ui"
106
+
107
+ ;<CollectiveIntelligenceProvider
108
+ client={appSdk}
109
+ entitySource={entitySource}
110
+ onWriteFailed={ciWriteFailedToast()}
111
+ >
112
+ <Orders />
113
+ <Toaster />
114
+ </CollectiveIntelligenceProvider>
115
+ ```
116
+
69
117
  ## Declared entity views
70
118
 
71
119
  Import `defineEntity`, `EntityTable`, `EntityList`, and `EntityForm` from
@@ -184,6 +232,7 @@ const LeadBoard = () => {
184
232
  `./effect` connects XState machines to Effect and to the application's
185
233
  QueryClient. It has no Effect Atom bindings.
186
234
 
235
+ - `WriteFailed` is the type of the [`onWriteFailed`](#optimistic-writes) event.
187
236
  - `fromEffect(run)` makes actor logic from an Effect. The actor runs the effect
188
237
  on `input.runtime`, so annotate `input` with a `runtime: EffectRunner<R>`
189
238
  that provides every service of the effect. The actor's output is the
package/dist/effect.d.ts CHANGED
@@ -56,5 +56,13 @@ export declare const EffectRuntimeProvider: ({ children }: Readonly<{
56
56
  }>) => import("react").JSX.Element;
57
57
  /** The runtime of the nearest `EffectRuntimeProvider`. */
58
58
  export declare const useEffectRuntime: () => EffectRunner<QueryCache>;
59
+ /** A typed failure of an entity write, for the app's `onWriteFailed`. */
60
+ export type WriteFailed = Readonly<{
61
+ /** CI refused the write (`CiError`), or the write did not reach CI or its answer was lost (`CiTransportError`). */
62
+ error: CiError | CiTransportError;
63
+ operation: "create" | "update" | "delete";
64
+ /** The resource id of the write (`contract.id`). */
65
+ label: string;
66
+ }>;
59
67
 
60
68
  export {};
@@ -0,0 +1,2 @@
1
+ import{createEntityHttpTransport,createResourceClient}from"@collective-intelligence/external-apps-client-sdk";import{useQueryClient}from"@tanstack/react-query";import*as ManagedRuntime from"effect/ManagedRuntime";import{createContext,useContext,useLayoutEffect,useMemo,useSyncExternalStore}from"react";import{queryOptions}from"@tanstack/react-query";var rootKey=["ci-resources"];var resourceKeys={all:rootKey,list:(organizationId,resourceId,version,query)=>[...resourceKeys.lists(organizationId,resourceId,version),query],lists:(organizationId,resourceId,version)=>[...resourceKeys.resource(organizationId,resourceId,version),"lists"],organization:(organizationId)=>[...rootKey,"organizations",organizationId],record:(organizationId,resourceId,version,recordId,includeDeleted=false)=>[...resourceKeys.records(organizationId,resourceId,version),recordId,includeDeleted],records:(organizationId,resourceId,version)=>[...resourceKeys.resource(organizationId,resourceId,version),"records"],resource:(organizationId,resourceId,version)=>[...resourceKeys.organization(organizationId),"resources",resourceId,"versions",version]};class ResourceQueryError2 extends Error{correlationId;resourceError;constructor(correlationId,error){super(error.message);this.name="ResourceQueryError";this.correlationId=correlationId;this.resourceError=error}}var unwrapResourceResult2=(result)=>{if(!result.ok){throw new ResourceQueryError2(result.correlationId,result.error)}return result.value};var createResourceListQueryOptions=(options)=>queryOptions({queryFn:async({signal})=>unwrapResourceResult2(await options.client.list(options.query,{signal})),queryKey:resourceKeys.list(options.organizationId,options.client.definition.id,options.client.definition.version,options.query)});var mapResourceAuthState2=(source)=>{if("ok"in source){if(source.ok){return{status:"authenticated"}}if(source.error.code==="forbidden"){return{status:"forbidden"}}if(source.error.code==="unauthenticated"){return{status:"unauthenticated"}}return{status:"authenticated"}}if(source.status==="authenticated"){return{status:"authenticated"}}if(source.status==="expired"){return{status:"expired"}}if(source.status==="error"&&source.error.code==="reauthorization_required"){return{status:"reauthentication_required"}}return{status:"unauthenticated"}};var hasResourceCapability2=(capabilities,capability)=>capabilities[capability]===true;var clearProtectedResourceQueries=async(queryClient,organizationId)=>{const queryKey=organizationId===undefined?resourceKeys.all:resourceKeys.organization(organizationId);await queryClient.cancelQueries({queryKey});queryClient.removeQueries({queryKey})};import{hashKey}from"@tanstack/react-query";import*as Context from"effect/Context";import*as Effect from"effect/Effect";import*as Layer from"effect/Layer";import*as RcMap from"effect/RcMap";import*as Semaphore from"effect/Semaphore";class WriteLock extends Context.Service()("@collective-intelligence/external-apps-client-sdk-react/WriteLock"){}var idleTimeToLive="30 seconds";var layer=Layer.effect(WriteLock,Effect.gen(function*makeWriteLock(){const locks=yield*RcMap.make({idleTimeToLive,lookup:(_hash)=>Semaphore.make(1)});const holding=(hash)=>(self)=>Effect.scoped(Effect.flatMap(RcMap.get(locks,hash),(lock)=>lock.withPermits(1)(self)));return WriteLock.of({settled:(queryKey)=>Effect.flatMap(RcMap.keys(locks),(hashes)=>{const hash=hashKey(queryKey);const below=`${hash.slice(0,-1)},`;return Effect.forEach([...hashes].filter((held)=>held===hash||held.startsWith(below)),(held)=>holding(held)(Effect.void),{discard:true})}),withRecord:(recordKey)=>holding(hashKey(recordKey))})}));var writesSettled=(queryKey)=>Effect.gen(function*awaitWrites(){const lock=yield*WriteLock;yield*lock.settled(queryKey)});import{jsx}from"react/jsx-runtime";var entityScopeError=(outcome="not_dispatched")=>new ResourceQueryError2("client-scope",{code:"unauthenticated",message:"The authenticated entity source has changed",outcome,retryable:false});var identityKey=(state)=>state.status==="authenticated"?[state.identity.sourceId,state.identity.applicationId,state.identity.userId,state.identity.organizationId]:[state.status];var EntityContext=createContext(null);var createRuntime=(source,state)=>{let active=true;let onWriteFailed;return{activate:()=>{active=true},current:()=>active&&source.getSnapshot()===state&&state.status==="authenticated",deactivate:()=>{active=false},formScope:globalThis.crypto.randomUUID(),key:identityKey(state),reportWriteFailures:(listener)=>{onWriteFailed=listener},source,state,writeFailed:(event)=>{onWriteFailed?.(event)},writeLock:ManagedRuntime.make(layer)}};var EntityRuntimeProvider=({source,onWriteFailed,children})=>{const queryClient=useQueryClient();const state=useSyncExternalStore(source.subscribe,source.getSnapshot,source.getSnapshot);const runtime=useMemo(()=>createRuntime(source,state),[source,state]);useLayoutEffect(()=>{runtime.reportWriteFailures(onWriteFailed)},[runtime,onWriteFailed]);useLayoutEffect(()=>{runtime.activate();return()=>{runtime.deactivate();queryClient.cancelQueries({queryKey:["ci-entities",...runtime.key]});queryClient.removeQueries({queryKey:["ci-entities",...runtime.key]})}},[queryClient,runtime]);return jsx(EntityContext.Provider,{value:runtime,children})};var useEntityRuntime=()=>{const context=useContext(EntityContext);if(context===null){throw new Error("Configure an entity source on CollectiveIntelligenceProvider")}return context};var defaultPathFor=(contract)=>`/api/resources/${contract.id}`;var createDelegatedEntitySource=(options)=>{const fetch=options.fetch??globalThis.fetch;const pathFor=options.pathFor??defaultPathFor;const sourceId=options.sourceId??`${options.origin}/api/resources`;let cached;const snapshot=()=>{const identity=options.getIdentity();const status=options.getStatus();const next=status==="authenticated"&&identity!==null?{identity:{applicationId:options.applicationId,organizationId:identity.organizationId,sourceId,userId:identity.userId},status:"authenticated"}:{status:status==="expired"?"expired":"unauthenticated"};if(cached!==undefined&&identityKey(cached).join("\x00")===identityKey(next).join("\x00")){return cached}cached=next;return next};return{bind:(contract)=>createResourceClient({definition:contract,protocol:"entity",transport:createEntityHttpTransport({authentication:{credentials:"same-origin",mode:"server_delegated"},destination:{endpoint:new URL(pathFor(contract),options.origin).href,kind:"gateway"},fetch})}),getSnapshot:snapshot,subscribe:options.subscribe}};var assertConfiguredFields=(config)=>{const declared=new Set(Object.keys(config.fields));const referenced=[...config.table.columns,...Object.values(config.table.variants??{}).flatMap((variant)=>variant.columns),config.list.title,...config.list.description===undefined?[]:[config.list.description],...config.forms.create.fields,...config.forms.update.fields];const unknown=referenced.find((field)=>!declared.has(field));if(unknown!==undefined){throw new Error(`Entity view references unconfigured field: ${unknown}`)}};var defineEntity2=(declaration)=>{assertConfiguredFields(declaration.config);const schemaFields=new Set(Object.keys(declaration.contract.schemas.entity.properties??{}));const unknown=Object.keys(declaration.config.fields).find((field)=>!schemaFields.has(field));if(unknown!==undefined){throw new Error(`Configured field is absent from the entity schema: ${unknown}`)}return Object.freeze(declaration)};import{ResourceFilterPrimitive,ResourceFiltersBar,ResourceFormPrimitive,ResourceListPrimitive,ResourcePaginationPrimitive,ResourceTablePrimitive}from"@collective-intelligence/ui-react/resources";import{useMemo as useMemo5,useState as useState2}from"react";import{useIsMutating,useMutation,useQueryClient as useQueryClient2}from"@tanstack/react-query";import{useMemo as useMemo2,useRef}from"react";import{CiError,CiErrorCode,CiTransportError}from"@collective-intelligence/external-apps-client-sdk/effect";import*as Effect2 from"effect/Effect";import*as Exit from"effect/Exit";import*as Schema from"effect/Schema";var heldWrites=new WeakMap;var fetchedOrder=new WeakMap;var orderOf=(page)=>fetchedOrder.get(page)??page.items.map((row)=>row.id);var withOrder=(page,order)=>{fetchedOrder.set(page,order);return page};var store=(queryClient,key,data,next,write,holds)=>{const writes=new Set(heldWrites.get(data));if(holds){writes.add(write)}else{writes.delete(write)}const stored=next===data?data:queryClient.setQueryData(key,next);if(stored!==undefined){heldWrites.set(stored,writes);const order=fetchedOrder.get(next)??fetchedOrder.get(data);if(order!==undefined){fetchedOrder.set(stored,order)}}};var touch=(queryClient,key,snapshot,edit,write,optimistic)=>{store(queryClient,key,snapshot,optimistic?edit.pending(snapshot):snapshot,write,true);return(confirmed)=>{const data=queryClient.getQueryData(key);if(data!==undefined&&heldWrites.get(data)?.has(write)===true){store(queryClient,key,data,edit.settled(data,snapshot,confirmed),write,false)}}};var predicted=(change,record)=>change.operation==="update"?{...record,value:{...record.value,...change.input.value}}:{...record,deletedAt:new Date().toISOString()};var pendingRow=(input)=>({id:input.idempotencyKey,revision:"",value:input.value});var replaceItem=(page,id,replace)=>page.items.some((item)=>item.id===id)?{...page,items:page.items.map((item)=>item.id===id?replace(item):item)}:page;var removeItem=(page,id)=>page.items.some((item)=>item.id===id)?{...page,items:page.items.filter((item)=>item.id!==id)}:page;var idsAfter=(ids,id)=>{const index=ids.indexOf(id);return index===-1?[]:ids.slice(index+1)};var restoreItem=(page,snapshot,id)=>{const item=snapshot.items.find((row)=>row.id===id);if(item===undefined||page.items.some((row)=>row.id===id)){return page}const later=new Set(idsAfter(orderOf(page),id));const at=page.items.findIndex((row)=>later.has(row.id));const items=[...page.items];items.splice(at===-1?items.length:at,0,item);return{...page,items}};var pageEdit=(change)=>{if(change.operation==="update"){const{id}=change.input;return{pending:(page)=>replaceItem(page,id,(item)=>predicted(change,item)),settled:(page,snapshot,confirmed)=>replaceItem(page,id,(item)=>confirmed??snapshot.items.find((row)=>row.id===id)??item)}}if(change.operation==="delete"){const{id}=change.input;return{pending:(page)=>removeItem(page,id),settled:(page,snapshot,confirmed)=>confirmed===undefined?restoreItem(page,snapshot,id):removeItem(page,id)}}const key=change.input.idempotencyKey;const first=(page,row)=>page.page===1?withOrder({...page,items:[row,...page.items]},[row.id,...orderOf(page)]):page;return{pending:(page)=>first(page,pendingRow(change.input)),settled:(page,_snapshot,confirmed)=>{if(!page.items.some((item)=>item.id===key)){return confirmed===undefined?page:first(page,confirmed)}const order=orderOf(page);return confirmed===undefined?withOrder(removeItem(page,key),order.filter((id)=>id!==key)):withOrder(replaceItem(page,key,()=>confirmed),order.map((id)=>id===key?confirmed.id:id))}}};var recordKeyOf=(resourceKey,change)=>[...resourceKey,"record",change.operation==="create"?change.input.idempotencyKey:change.input.id];var apply=(options,recordKey,write)=>{const{change,optimistic,queryClient,resourceKey}=options;const settles=[];if(change.operation!=="create"){const record=queryClient.getQueryData(recordKey);if(record!==undefined){settles.push(touch(queryClient,recordKey,record,{pending:(data)=>predicted(change,data),settled:(_data,snapshot,confirmed)=>confirmed??snapshot},write,optimistic))}}for(const[key,page]of queryClient.getQueriesData({queryKey:[...resourceKey,"list"]})){if(page!==undefined){fetchedOrder.set(page,orderOf(page));settles.push(touch(queryClient,key,page,pageEdit(change),write,optimistic))}}return settles};var isCiErrorCode=Schema.is(CiErrorCode);var failure=(error)=>error.code==="transport_error"?new CiTransportError({cause:error}):new CiError({code:isCiErrorCode(error.code)?error.code:"invalid_request",details:error.outcome===undefined?{...error.details,resourceCode:error.code}:{...error.details,outcome:error.outcome,resourceCode:error.code},message:error.message,retryable:error.retryable});var inScope=(current)=>Effect2.suspend(()=>current()?Effect2.void:Effect2.fail(entityScopeError()));var write=(options)=>Effect2.gen(function*optimisticWrite(){const lock=yield*WriteLock;const{queryClient,resourceKey}=options;const recordKey=recordKeyOf(resourceKey,options.change);const token=Symbol("optimistic write");return yield*lock.withRecord(recordKey)(Effect2.acquireUseRelease(Effect2.gen(function*acquire(){yield*inScope(options.current);yield*Effect2.promise(()=>Promise.all([queryClient.cancelQueries({exact:true,queryKey:recordKey}),queryClient.cancelQueries({queryKey:[...resourceKey,"list"]})]));yield*inScope(options.current);return apply(options,recordKey,token)}),()=>Effect2.promise(options.execute),(settles,exit)=>Effect2.sync(()=>{const result=Exit.isSuccess(exit)?exit.value:undefined;const confirmed=result?.ok===true?result.value:undefined;for(const settle of settles){settle(confirmed)}if(result?.ok===false&&options.current()){options.onWriteFailed({error:failure(result.error),label:options.label,operation:options.change.operation})}})))}).pipe(Effect2.ensuring(Effect2.sync(()=>{if(options.current()){options.queryClient.invalidateQueries({queryKey:options.resourceKey})}})));var immutable=(value)=>{if(value!==null&&typeof value==="object"){for(const child of Object.values(value)){immutable(child)}Object.freeze(value)}return value};var useEntityMutation=(contract,options)=>{const runtime=useEntityRuntime();const queryClient=useQueryClient2();const client=useMemo2(()=>runtime.source.bind(contract),[runtime,contract]);const resourceKey=["ci-entities",...runtime.key,contract.id,contract.version];const mutationKey=[...resourceKey,"write",options.id??"create"];const lastAction=useRef(null);const scopeOptions={};if(options.id!==undefined){scopeOptions.scope={id:JSON.stringify(mutationKey)}}const mutation=useMutation({mutationKey,...scopeOptions,mutationFn:async(action)=>{if(!action.current()){throw entityScopeError()}const result=await action.execute();if(!action.current()){throw entityScopeError("uncertain")}if(!result.ok){throw new ResourceQueryError2(result.correlationId,result.error)}return result},networkMode:"always",retry:false});const pending=useIsMutating({mutationKey})>0;const send=async(action)=>{try{const result=await mutation.mutateAsync(action);if(!action.current()){const error=entityScopeError("uncertain");return{correlationId:error.correlationId,error:error.resourceError,ok:false}}return result}catch(error){if(error instanceof ResourceQueryError2){return{correlationId:error.correlationId,error:error.resourceError,ok:false}}throw error}};const submit=(input)=>{const captured=immutable(structuredClone(options.prepare(input)));const action={current:runtime.current,execute:()=>runtime.writeLock.runPromise(write({change:options.change(captured),current:runtime.current,execute:()=>options.execute(client,captured),label:contract.id,onWriteFailed:runtime.writeFailed,optimistic:options.optimistic??true,queryClient,resourceKey})),input:captured,runtime,target:options.id};lastAction.current=action;return send(action)};const resubmit=()=>{const action=lastAction.current;if(action===null){throw new Error("No submitted entity action to resubmit")}if(action.target!==options.id){throw new Error("The submitted action belongs to a different record")}return send(action)};const current=mutation.variables===undefined||mutation.variables.runtime===runtime&&mutation.variables.target===options.id&&mutation.variables.current();const error=current&&mutation.error instanceof ResourceQueryError2?mutation.error:null;const failureStatus=()=>{if(error?.resourceError.outcome==="uncertain"){return"uncertain"}if(error?.resourceError.code==="conflict"){return"conflict"}return"rejected"};const status=mutation.isError?failureStatus():mutation.status;return{available:runtime.state.status==="authenticated"&&client.definition.capabilities[options.operation]===true,data:current?mutation.data?.value:undefined,error,pending,resubmit,status:current?status:"idle",submit,submitted:current?mutation.variables?.input:undefined}};var useEntityUpdate2=(contract,target)=>useEntityMutation(contract,{change:(input)=>({input,operation:"update"}),execute:(client,input)=>client.update(input),id:target.id,operation:"update",optimistic:target.optimistic,prepare:(input)=>({...input,id:target.id,idempotencyKey:globalThis.crypto.randomUUID()})});var useEntityCreate2=(contract)=>useEntityMutation(contract,{change:(input)=>({input,operation:"create"}),execute:(client,input)=>client.create(input),operation:"create",prepare:(input)=>({...input,idempotencyKey:globalThis.crypto.randomUUID()})});var useEntityDelete2=(contract,target)=>useEntityMutation(contract,{change:(input)=>({input,operation:"delete"}),execute:(client,input)=>client.delete(input),id:target.id,operation:"delete",optimistic:target.optimistic,prepare:(input)=>({...input,id:target.id,idempotencyKey:globalThis.crypto.randomUUID()})});import{useQuery}from"@tanstack/react-query";import{useMemo as useMemo3}from"react";var authStateForError=(error,state)=>{if(error instanceof ResourceQueryError2){if(error.resourceError.code==="forbidden"){return{status:"forbidden"}}if(error.resourceError.code==="unauthenticated"){return{status:"unauthenticated"}}}return state};var useEntityList2=(contract,query)=>{const runtime=useEntityRuntime();const client=useMemo3(()=>runtime.source.bind(contract),[runtime,contract]);const result=useQuery({enabled:runtime.state.status==="authenticated"&&client.definition.capabilities.search,queryFn:async({queryKey,signal})=>{if(!runtime.current()){throw entityScopeError()}await runtime.writeLock.runPromise(writesSettled(queryKey.slice(0,-2)),{signal});const data=unwrapResourceResult2(await client.list(query,{signal}));if(!runtime.current()){throw entityScopeError()}return data},queryKey:["ci-entities",...runtime.key,contract.id,contract.version,"list",query],retry:false});return{authState:authStateForError(result.error,runtime.state),capabilities:client.definition.capabilities,data:result.data,empty:result.data?.items.length===0,error:result.error,loading:result.isLoading,refresh:async()=>{await result.refetch()},refreshing:result.isFetching}};var useEntityRecord2=(contract,input)=>{const runtime=useEntityRuntime();const client=useMemo3(()=>runtime.source.bind(contract),[runtime,contract]);const result=useQuery({enabled:runtime.state.status==="authenticated"&&client.definition.capabilities.get===true,queryFn:async({queryKey,signal})=>{if(!runtime.current()){throw entityScopeError()}await runtime.writeLock.runPromise(writesSettled(queryKey),{signal});const data=unwrapResourceResult2(await client.get(input,{signal}));if(!runtime.current()){throw entityScopeError()}return data},queryKey:["ci-entities",...runtime.key,contract.id,contract.version,"record",input.id],retry:false});return{authState:authStateForError(result.error,runtime.state),capabilities:client.definition.capabilities,data:result.data,error:result.error,loading:result.isLoading,refresh:async()=>{await result.refetch()},refreshing:result.isFetching}};import{createEntityProtocol}from"@collective-intelligence/external-apps-client-sdk/resources";import{createParser,parseAsJson,useQueryStates}from"nuqs";import{useMemo as useMemo4,useState}from"react";var DEFAULT_PAGE_SIZE=25;var namespacePattern=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;var isObject=(value)=>typeof value==="object"&&value!==null&&!Array.isArray(value);var isFilter=(value)=>{if(!isObject(value)){return false}if((value.operator==="and"||value.operator==="or")&&"filters"in value){return Array.isArray(value.filters)&&value.filters.every(isFilter)}if(value.operator==="not"&&"filter"in value){return isFilter(value.filter)}return typeof value.field==="string"&&typeof value.operator==="string"&&(value.operator==="is_null"||value.operator==="is_not_null"||value.operator==="is_empty"||("value"in value))};var isSorts=(value)=>Array.isArray(value)&&value.every((sort)=>isObject(sort)&&typeof sort.field==="string"&&(sort.direction==="asc"||sort.direction==="desc"));var positiveInteger=(maximum=Number.MAX_SAFE_INTEGER)=>createParser({parse:(value)=>{const number=Number(value);return/^[1-9]\d*$/u.test(value)&&Number.isSafeInteger(number)&&number<=maximum?number:null},serialize:String});var pageSizeFor=(value=DEFAULT_PAGE_SIZE)=>Number.isSafeInteger(value)&&value>0&&value<=100?value:DEFAULT_PAGE_SIZE;var parsersFor=(pageSize,contract)=>{const protocol=contract===undefined?undefined:createEntityProtocol(contract);const accepts=(query)=>protocol===undefined||protocol.decodeRequest({operation:"list",query:{pagination:{mode:"page",page:1,pageSize},...query},requestId:"view-state",resourceId:contract?.id}).ok;return{filter:parseAsJson((value)=>isFilter(value)&&accepts({filter:value})?value:null),page:positiveInteger().withDefault(1),pageSize:positiveInteger(100).withDefault(pageSize),sort:parseAsJson((value)=>isSorts(value)&&accepts({sort:value})?value:null).withDefault([]),textSearch:createParser({parse:(value)=>accepts({textSearch:value})?value:null,serialize:(value)=>value}).withDefault("")}};var queryFor=(state)=>{const query={pagination:{mode:"page",page:state.page,pageSize:state.pageSize}};if(state.filter!==null){query.filter=state.filter}if(state.sort.length>0){query.sort=state.sort}if(state.textSearch.trim()!==""){query.textSearch=state.textSearch.trim()}return query};var controller=(state,update)=>({query:queryFor(state),setFilter:(filter)=>update({filter,page:1}),setPage:(page)=>update({page:Math.max(1,page)}),setPageSize:(pageSize)=>update({page:1,pageSize:Math.max(1,Math.min(100,pageSize))}),setSort:(sort)=>update({page:1,sort:[...sort]}),setTextSearch:(textSearch)=>update({page:1,textSearch}),state});var useEntityViewState2=(namespace,input={})=>{if(!namespacePattern.test(namespace)){throw new Error("URL-backed entity views require a stable alphanumeric namespace")}const parsers=useMemo4(()=>parsersFor(pageSizeFor(input.pageSize),input.contract),[input.pageSize,input.contract]);const[state,setState]=useQueryStates(parsers,{clearOnDefault:true,history:"replace",shallow:false,urlKeys:{filter:`${namespace}.filter`,page:`${namespace}.page`,pageSize:`${namespace}.pageSize`,sort:`${namespace}.sort`,textSearch:`${namespace}.textSearch`}});const normalized=useMemo4(()=>({filter:state.filter,page:state.page>0?state.page:1,pageSize:state.pageSize>0&&state.pageSize<=100?state.pageSize:input.pageSize??DEFAULT_PAGE_SIZE,sort:state.sort,textSearch:state.textSearch}),[input.pageSize,state]);return controller(normalized,(next)=>{const{sort,...values}=next;const update=values;if(sort!==undefined){update.sort=[...sort]}setState(update)})};var useMemoryEntityViewState2=(input={})=>{const[state,setState]=useState({filter:null,page:1,pageSize:pageSizeFor(input.pageSize),sort:[],textSearch:""});return controller(state,(next)=>{setState((current)=>({...current,...next}))})};import{jsx as jsx2,jsxs,Fragment}from"react/jsx-runtime";var isObject2=(value)=>value!==null&&typeof value==="object"&&!Array.isArray(value);var schemaProperties=(entity,mode="entity")=>{const properties=entity.contract.schemas[mode]?.properties;return isObject2(properties)?properties:{}};var requiredFields=(entity,mode)=>{const required=entity.contract.schemas[mode]?.required;return new Set(Array.isArray(required)?required.filter((value)=>typeof value==="string"):[])};var scalarTypes=(schema)=>{const{type}=schema;if(typeof type==="string"){return[type]}return Array.isArray(type)?type.filter((value)=>typeof value==="string"):[]};var presentationSchema=(schema)=>{const union=schema.anyOf??schema.oneOf;if(!Array.isArray(union)){return schema}const members=union.filter(isObject2);const values=members.filter((member)=>member.type!=="null");const[value]=values;if(members.length===2&&values.length===1&&value!==undefined){return{...schema,...value,type:[...scalarTypes(value),"null"]}}return schema};var defaultControl=(schema)=>{if(schema.$ref!==undefined||(schema.anyOf!==undefined||schema.oneOf!==undefined)&&schema.type===undefined){return"custom"}if(Array.isArray(schema.enum)){return"select"}const types=scalarTypes(schema);if(types.includes("boolean")){return"checkbox"}if(types.includes("number")||types.includes("integer")){return"number"}if(schema.format==="email"){return"email"}if(schema.format==="date"){return"date"}if(schema.format==="date-time"){return"datetime-local"}return types.some((type)=>type==="object"||type==="array")?"custom":"text"};var configuredControl=(config,schema)=>{switch(config.control){case"text":case"email":case"number":case"checkbox":case"date":case"datetime-local":case"select":case"custom":{return config.control}case undefined:{return defaultControl(schema)}default:{return"custom"}}};var formFields=(entity,mode)=>{const properties=schemaProperties(entity,mode);const entityProperties=schemaProperties(entity);const required=requiredFields(entity,mode);return entity.config.forms[mode].fields.map((name)=>{const config=entity.config.fields[name];if(config===undefined){throw new Error(`Missing entity field configuration: ${name}`)}const candidate=properties[name]??entityProperties[name];const schema=isObject2(candidate)?presentationSchema(candidate):{};const entityField=entityProperties[name];const enums=Array.isArray(schema.enum)?schema.enum.filter((value)=>typeof value==="string"||typeof value==="number"||typeof value==="boolean"):[];return{control:configuredControl(config,schema),label:config.label,name,nullable:scalarTypes(schema).includes("null")||Array.isArray(schema.enum)&&schema.enum.includes(null),options:enums.map((value)=>({label:String(value),value})),readOnly:config.readOnly===true||schema.readOnly===true||isObject2(entityField)&&entityField.readOnly===true,required:required.has(name)}})};var displayValue=(value)=>{if(value===null||value===undefined){return"—"}if(typeof value==="boolean"){return value?"Yes":"No"}return typeof value==="object"?JSON.stringify(value):String(value)};var renderField=(entity,record,name,renderers)=>{const renderer=entity.config.fields[name]?.renderer;return renderer===undefined||renderers?.[renderer]===undefined?displayValue(record.value[name]):renderers[renderer]({field:name,record})};var sortFor=(state,field)=>{const[current]=state.state.sort;return[{direction:current?.field===field&&current.direction==="asc"?"desc":"asc",field}]};var QueryControls=({view})=>jsx2(ResourceFiltersBar,{controls:[{control:jsx2(ResourceFilterPrimitive,{label:"Search",onChange:(value)=>view.setTextSearch(value),value:view.state.textSearch}),fullWidth:true,key:"search"}]});var EntityStatus=({state})=>{if(state.authState.status!=="authenticated"){return jsx2("output",{children:state.authState.status})}if(state.loading){return jsx2("output",{children:"Loading"})}if(state.error!==null){return jsx2("output",{role:"alert",children:state.error.message})}return null};var EntityPagination=({result,view,pending})=>{if(result===undefined||result.pagination!=="page"){return null}return jsx2(ResourcePaginationPrimitive,{currentPage:result.page,totalPages:result.totalPages,hasPreviousPage:result.page>1,hasNextPage:result.page<result.totalPages,isPending:pending,onPageChange:(page)=>view.setPage(page),onPrevious:()=>view.setPage(result.page-1),onNext:()=>view.setPage(result.page+1)})};var ControlledEntityTable=({entity,empty,renderers,variant,view})=>{const result=useEntityList2(entity.contract,view.query);const names=variant===undefined?entity.config.table.columns:entity.config.table.variants?.[variant]?.columns??entity.config.table.columns;const columns=names.map((name)=>({id:name,label:entity.config.fields[name]?.label??name}));const hasStatus=result.authState.status!=="authenticated"||result.loading||result.error!==null;return jsxs("section",{children:[jsx2(QueryControls,{view}),hasStatus?jsx2(EntityStatus,{state:result}):jsxs(Fragment,{children:[jsx2(ResourceTablePrimitive,{columns:columns.map((column)=>({...column,label:entity.contract.query.sortableFields.includes(column.id)?jsx2("button",{type:"button",onClick:()=>view.setSort(sortFor(view,column.id)),children:column.label}):column.label})),empty,getRowKey:(record)=>record.id,renderCell:(record,column)=>{const name=names.find((field)=>field===column.id);return name===undefined?null:renderField(entity,record,name,renderers)},rows:result.data?.items??[]}),jsx2(EntityPagination,{pending:result.refreshing,result:result.data,view})]})]})};var UrlEntityTable=(props)=>jsx2(ControlledEntityTable,{...props,view:useEntityViewState2(props.namespace,{contract:props.entity.contract,pageSize:props.pageSize??25})});var MemoryEntityTable=(props)=>jsx2(ControlledEntityTable,{...props,view:useMemoryEntityViewState2(props.pageSize===undefined?{}:{pageSize:props.pageSize})});var EntityTable2=(props)=>props.viewState==="memory"?jsx2(MemoryEntityTable,{...props}):jsx2(UrlEntityTable,{...props,namespace:props.namespace});var ControlledEntityList=({entity,empty,renderers,view})=>{const result=useEntityList2(entity.contract,view.query);const hasStatus=result.authState.status!=="authenticated"||result.loading||result.error!==null;return jsxs("section",{children:[jsx2(QueryControls,{view}),hasStatus?jsx2(EntityStatus,{state:result}):jsxs(Fragment,{children:[jsx2(ResourceListPrimitive,{empty,getItemKey:(record)=>record.id,items:result.data?.items??[],renderItem:(record)=>jsxs("article",{children:[jsx2("strong",{children:renderField(entity,record,entity.config.list.title,renderers)}),entity.config.list.description===undefined?null:jsx2("p",{children:renderField(entity,record,entity.config.list.description,renderers)})]})}),jsx2(EntityPagination,{pending:result.refreshing,result:result.data,view})]})]})};var UrlEntityList=(props)=>jsx2(ControlledEntityList,{...props,view:useEntityViewState2(props.namespace,{contract:props.entity.contract,pageSize:props.pageSize??25})});var MemoryEntityList=(props)=>jsx2(ControlledEntityList,{...props,view:useMemoryEntityViewState2(props.pageSize===undefined?{}:{pageSize:props.pageSize})});var EntityList2=(props)=>props.viewState==="memory"?jsx2(MemoryEntityList,{...props}):jsx2(UrlEntityList,{...props,namespace:props.namespace});var initialFormValues=(entity,mode,supplied,record)=>{const values={};const properties=schemaProperties(entity,mode);const required=requiredFields(entity,mode);for(const name of entity.config.forms[mode].fields){const schema=properties[name];if(required.has(name)&&isObject2(schema)&&scalarTypes(presentationSchema(schema)).includes("boolean")){values[name]=false}if(isObject2(schema)&&schema.default!==undefined){values[name]=schema.default}if(record?.value[name]!==undefined){values[name]=record.value[name]}if(supplied?.[name]!==undefined){values[name]=supplied[name]}}return values};var controlRenderer=(entity,controls)=>function renderEntityControl(props){const fields=entity.config.fields;const key=fields[props.field.name]?.control;return key===undefined||controls?.[key]===undefined?jsxs("p",{role:"alert",children:["A custom control is required for ",props.field.label,"."]}):controls[key](props)};var mutationMessage=(mutation)=>{if(mutation.status==="conflict"){return"This entity changed. Your draft is preserved."}if(mutation.status==="uncertain"){return"The write outcome is unknown. Resubmit the same action to resolve it."}return mutation.error?.resourceError.message};var writableValues=(fields,values)=>{const writable=new Set(fields.filter((field)=>!field.readOnly).map((field)=>field.name));return Object.fromEntries(Object.entries(values).filter(([name])=>writable.has(name)))};var displayedValues=(fields,values)=>Object.fromEntries(Object.entries(values).map(([name,value])=>{if(typeof value!=="string"||fields.find((field)=>field.name===name)?.control!=="datetime-local"){return[name,value]}const date=new Date(value);if(Number.isNaN(date.getTime())){return[name,value]}return[name,new Date(date.getTime()-date.getTimezoneOffset()*60000).toISOString().slice(0,23)]}));var wireValue=(fields,name,value)=>{if(typeof value!=="string"||value===""||fields.find((field)=>field.name===name)?.control!=="datetime-local"){return value}const date=new Date(value);return Number.isNaN(date.getTime())?value:date.toISOString()};var CreateEntityForm=(props)=>{const mutation=useEntityCreate2(props.entity.contract);const[values,setValues]=useState2(()=>initialFormValues(props.entity,"create",props.initialValues));const fields=useMemo5(()=>formFields(props.entity,"create"),[props.entity]);return jsx2(ResourceFormPrimitive,{label:"Create entity",fields,values:displayedValues(fields,values),disabled:!mutation.available,pending:mutation.pending,error:mutationMessage(mutation),...props.onCancel===undefined?{}:{onCancel:props.onCancel},onChange:(name,value)=>setValues((current)=>({...current,[name]:wireValue(fields,name,value)})),onSubmit:async()=>{const value=writableValues(fields,values);const action=mutation.status==="uncertain"?mutation.resubmit():mutation.submit({value});const result=await action;if(result.ok){props.onSuccess?.(result.value)}},renderControl:controlRenderer(props.entity,props.controls),submitLabel:mutation.status==="uncertain"?"Resubmit":"Create"})};var UpdateEntityForm=(props)=>{const mutation=useEntityUpdate2(props.entity.contract,{id:props.record.id});const[draft,setDraft]=useState2(()=>({expectedRevision:props.record.revision,values:initialFormValues(props.entity,"update",props.initialValues,props.record)}));const{expectedRevision,values}=draft;const fields=useMemo5(()=>formFields(props.entity,"update"),[props.entity]);return jsx2(ResourceFormPrimitive,{label:"Update entity",fields,values:displayedValues(fields,values),disabled:!mutation.available,pending:mutation.pending,error:mutationMessage(mutation),...props.onCancel===undefined?{}:{onCancel:props.onCancel},onChange:(name,value)=>setDraft((current)=>({...current,values:{...current.values,[name]:wireValue(fields,name,value)}})),onSubmit:async()=>{const value=writableValues(fields,values);const result=await(mutation.status==="uncertain"?mutation.resubmit():mutation.submit({expectedRevision,value}));if(result.ok){props.onSuccess?.(result.value)}},renderControl:controlRenderer(props.entity,props.controls),submitLabel:mutation.status==="uncertain"?"Resubmit":"Save"})};var EntityForm2=(props)=>{const runtime=useEntityRuntime();if(runtime.state.status!=="authenticated"){return jsx2("output",{children:runtime.state.status})}return props.mode==="create"?jsx2(CreateEntityForm,{...props},runtime.formScope):jsx2(UpdateEntityForm,{...props,record:props.record},JSON.stringify([runtime.formScope,props.record.id]))};
2
+ export{resourceKeys,ResourceQueryError2,unwrapResourceResult2,createResourceListQueryOptions,mapResourceAuthState2,hasResourceCapability2,clearProtectedResourceQueries,EntityRuntimeProvider,createDelegatedEntitySource,defineEntity2,useEntityUpdate2,useEntityCreate2,useEntityDelete2,useEntityList2,useEntityRecord2,useEntityViewState2,useMemoryEntityViewState2,EntityTable2,EntityList2,EntityForm2};
package/dist/index.d.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
3
  import { CiError as CiErrorEnvelope, CiErrorCode, CiResult, CiStreamPage, ClientDomains, ClientFetch, ClientOperationDefinition, ClientOperationId, ClientOperationInput, ClientOperationMap, ClientOperationOutput, ClientSdkClient, FederatedAuth, FederatedAuthError, FederatedAuthErrorCode, FederatedAuthState, FederatedSessionIdentity, FollowRunOptions, JsonValue, KnowledgeQuery, OperationExecutionOptions, OrganizationClient, QueryFilter, RealtimeChange, RealtimeClient, RealtimeClientOptions, RealtimeClientState, RealtimeClientStatus, RealtimeTopic, RealtimeTopicFilter, SearchQuery, TypedOperationExecutor, WorkflowInvokeTarget } from '@collective-intelligence/external-apps-client-sdk';
4
+ import { CiError, CiTransportError } from '@collective-intelligence/external-apps-client-sdk/effect';
4
5
  import { EntityClient, EntityCreateInput, EntityDeleteInput, EntityListQuery, EntityMemberValue, EntityPage, EntityUpdateInput, EntityUploadInput, EntityUploadSlot, IdempotencyKey, PortableResourceSchema, ResourceCapabilities, ResourceCreateInput, ResourceCursor, ResourceDefinition, ResourceError, ResourceErrorCode, ResourceFilter, ResourceId, ResourceIdentityField, ResourceJsonValue, ResourceLifecycleInput, ResourceListQuery, ResourceListResult, ResourceMutationInput, ResourcePagination, ResourcePaginationMode, ResourceRecord, ResourceRecordId, ResourceRequestOptions, ResourceResult, ResourceRevision, ResourceSort } from '@collective-intelligence/external-apps-client-sdk/resources';
5
6
  import { ResourceField, ResourceFieldControlProps, ResourceObject, ResourceValue } from '@collective-intelligence/ui-react/resources';
6
7
  import { QueryClient } from '@tanstack/react-query';
8
+ import * as Context from 'effect/Context';
9
+ import * as Effect from 'effect/Effect';
10
+ import * as Layer from 'effect/Layer';
7
11
  import { CSSProperties, ElementType, ReactNode } from 'react';
8
12
 
9
13
  export type PromptKeyEvent = Readonly<{
@@ -73,6 +77,14 @@ export type AgentPromptProps = Readonly<{
73
77
  submitLabel?: ReactNode;
74
78
  }>;
75
79
  export declare const AgentPrompt: ({ agentId, autoFocus, className, components, createIdempotencyKey, label, onResult, organizationId, style, submitLabel }: AgentPromptProps) => import("react").JSX.Element;
80
+ /** A typed failure of an entity write, for the app's `onWriteFailed`. */
81
+ export type WriteFailed = Readonly<{
82
+ /** CI refused the write (`CiError`), or the write did not reach CI or its answer was lost (`CiTransportError`). */
83
+ error: CiError | CiTransportError;
84
+ operation: "create" | "update" | "delete";
85
+ /** The resource id of the write (`contract.id`). */
86
+ label: string;
87
+ }>;
76
88
  export type EntityIdentity = Readonly<{
77
89
  sourceId: string;
78
90
  applicationId: string;
@@ -115,6 +127,12 @@ export type CollectiveIntelligenceProviderProps = Readonly<{
115
127
  entitySource?: EntitySource | undefined;
116
128
  /** The app's QueryClient; without it the provider owns one for its lifetime. */
117
129
  queryClient?: QueryClient | undefined;
130
+ /**
131
+ * Gets each entity write that CI refuses or that does not reach CI, after
132
+ * the views show the record before the write again. Not called for an
133
+ * interruption.
134
+ */
135
+ onWriteFailed?: ((event: WriteFailed) => void) | undefined;
118
136
  } & ({
119
137
  children: ReactNode;
120
138
  client: Client;
@@ -314,9 +332,10 @@ export declare const clientSdkReactEntrypoint: "react";
314
332
  * The envelope of an Expected Error. The client SDK also exports a `CiError`
315
333
  * class; this entrypoint exports no class at run time, so only the type.
316
334
  */
317
- export type CiError = CiErrorEnvelope;
335
+ type CiError$1 = CiErrorEnvelope;
318
336
 
319
337
  export {
338
+ CiError$1 as CiError,
320
339
  CiErrorCode,
321
340
  CiResult,
322
341
  CiStreamPage,
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{EntityRuntimeProvider,createDelegatedEntitySource,defineEntity2,useEntityViewState2,useMemoryEntityViewState2,EntityTable2,EntityList2,EntityForm2}from"./index-bjd4xpqf.js";import{__export}from"./index-6ra894v2.js";var exports_src={};__export(exports_src,{AgentPrompt:()=>AgentPrompt,AgentPromptPrimitive:()=>AgentPromptPrimitive,CollectiveIntelligenceLoginProvider:()=>CollectiveIntelligenceLoginProvider,CollectiveIntelligenceProvider:()=>CollectiveIntelligenceProvider,EntityForm:()=>EntityForm2,EntityList:()=>EntityList2,EntityTable:()=>EntityTable2,LoginButton:()=>LoginButton,LoginCallback:()=>LoginCallback,LoginCallbackPrimitive:()=>LoginCallbackPrimitive,RealtimeProvider:()=>RealtimeProvider,SessionGate:()=>SessionGate,clientSdkReactEntrypoint:()=>clientSdkReactEntrypoint,createDelegatedEntitySource:()=>createDelegatedEntitySource,createPromptKeyHandler:()=>createPromptKeyHandler,defineEntity:()=>defineEntity2,useAgentInvocation:()=>useAgentInvocation,useAgents:()=>useAgents,useAttachments:()=>useAttachments,useClientContext:()=>useClientContext,useCollectiveIntelligenceAuth:()=>useCollectiveIntelligenceAuth,useCollectiveIntelligenceClient:()=>useCollectiveIntelligenceClient,useEntityViewState:()=>useEntityViewState2,useKnowledge:()=>useKnowledge,useMemoryEntityViewState:()=>useMemoryEntityViewState2,useOrganizationClient:()=>useOrganizationClient,useRealtimeClient:()=>useRealtimeClient,useRealtimeTopic:()=>useRealtimeTopic,useRuns:()=>useRuns,useSession:()=>useSession,useSkills:()=>useSkills,useWorkflows:()=>useWorkflows});import{useCallback,useId,useState as useState2}from"react";import{QueryClient,QueryClientProvider}from"@tanstack/react-query";import{createContext,useContext,useMemo,useState}from"react";import{jsx}from"react/jsx-runtime";var ClientContext=createContext(null);var CollectiveIntelligenceProvider=(props)=>{const{clientFactory}=props;const[ownedQueryClient]=useState(()=>new QueryClient({defaultOptions:{mutations:{retry:false},queries:{retry:false}}}));const ownedClient=useMemo(()=>clientFactory===undefined?undefined:clientFactory(),[clientFactory]);const client=props.client??ownedClient;const queryClient=props.queryClient??ownedQueryClient;if(client===undefined){throw new TypeError("A client or clientFactory is required")}return jsx(ClientContext.Provider,{value:client,children:jsx(QueryClientProvider,{client:queryClient,children:props.entitySource===undefined?props.children:jsx(EntityRuntimeProvider,{source:props.entitySource,children:props.children})})})};var useCollectiveIntelligenceClient=()=>{const client=useContext(ClientContext);if(client===null){throw new Error("CollectiveIntelligenceProvider must wrap Collective Intelligence hooks")}return client};var useOrganizationClient=(organizationId)=>{const client=useCollectiveIntelligenceClient();return useMemo(()=>client.forOrganization(organizationId),[client,organizationId])};var useKnowledge=(organizationId)=>useOrganizationClient(organizationId).knowledge;var useAgents=(organizationId)=>useOrganizationClient(organizationId).agents;var useSkills=(organizationId)=>useOrganizationClient(organizationId).skills;var useWorkflows=(organizationId)=>useOrganizationClient(organizationId).workflows;var useRuns=(organizationId)=>useOrganizationClient(organizationId).runs;var useAttachments=(organizationId)=>useOrganizationClient(organizationId).attachments;var useClientContext=(organizationId)=>useOrganizationClient(organizationId).context;import{jsx as jsx2,jsxs}from"react/jsx-runtime";var ignoreSubmit=()=>false;var createPromptKeyHandler=(onSubmit)=>(event)=>{if(event.key==="Enter"&&!event.shiftKey&&!event.isComposing){event.preventDefault();onSubmit()}};var AgentPromptPrimitive=({autoFocus=false,className,components,id,label,onChange,onSubmit,pending,status,style,submitLabel="Send",value})=>{const Form=components?.Form??"form";const Label=components?.Label??"label";const Textarea=components?.Textarea??"textarea";const Button=components?.Button??"button";const Status=components?.Status??"div";const statusId=`${id}-status`;const submit=(event)=>{event.preventDefault();if(!pending){onSubmit()}};const onKeyDown=(event)=>{createPromptKeyHandler(pending?ignoreSubmit:onSubmit)({isComposing:event.nativeEvent.isComposing,key:event.key,preventDefault:()=>{event.preventDefault()},shiftKey:event.shiftKey})};return jsxs(Form,{className:["ci-agent-prompt",className].filter(Boolean).join(" "),onSubmit:submit,style,children:[jsx2(Label,{className:"ci-agent-prompt__label",htmlFor:id,children:label}),jsx2(Textarea,{"aria-describedby":statusId,autoFocus,className:"ci-agent-prompt__input",disabled:pending,id,onChange:(event)=>{onChange(event.currentTarget.value)},onKeyDown,value}),jsx2(Button,{className:"ci-agent-prompt__submit",disabled:pending||value.trim().length===0,type:"submit",children:submitLabel}),jsx2(Status,{"aria-live":"polite",className:"ci-agent-prompt__status",id:statusId,children:status})]})};var useAgentInvocation=(organizationId)=>{const{agents}=useOrganizationClient(organizationId);const[state,setState]=useState2({status:"idle"});const invoke=useCallback(async(input)=>{setState({status:"submitting"});try{const result=await agents.invoke(input);setState(result.ok?{result,status:"success"}:{result,status:"failure"});return result}catch(error){setState({error,status:"error"});throw error}},[agents]);return{invoke,reset:()=>{setState({status:"idle"})},state}};var invocationStatus=(state)=>{switch(state.status){case"submitting":{return"Sending"}case"success":{return"Request started"}case"failure":{return state.result.error.message}case"error":{return"Request failed"}case"idle":{return null}default:{return null}}};var EMPTY_AGENT_PROMPT_COMPONENTS={};var EMPTY_AGENT_PROMPT_STYLE={};var createDefaultIdempotencyKey=()=>globalThis.crypto.randomUUID();var AgentPrompt=({agentId,autoFocus=false,className="",components=EMPTY_AGENT_PROMPT_COMPONENTS,createIdempotencyKey=createDefaultIdempotencyKey,label="Message",onResult,organizationId,style=EMPTY_AGENT_PROMPT_STYLE,submitLabel="Send"})=>{const id=`ci-agent-prompt-${useId().replaceAll(":","")}`;const[value,setValue]=useState2("");const invocation=useAgentInvocation(organizationId);const submit=()=>{const prompt=value.trim();if(prompt.length===0||invocation.state.status==="submitting"){return}const submitInvocation=async()=>{try{const result=await invocation.invoke({agentId,idempotencyKey:createIdempotencyKey(),input:{prompt}});if(result.ok){setValue("")}onResult?.(result)}catch{}};submitInvocation()};return jsx2(AgentPromptPrimitive,{autoFocus,className,components,id,label,onChange:setValue,onSubmit:submit,pending:invocation.state.status==="submitting",status:invocationStatus(invocation.state),style,submitLabel,value})};import{FederatedAuthError}from"@collective-intelligence/external-apps-client-sdk";import{createContext as createContext2,useCallback as useCallback2,useContext as useContext2,useEffect,useMemo as useMemo2,useSyncExternalStore}from"react";import{jsx as jsx3}from"react/jsx-runtime";var AuthContext=createContext2(null);var toFederatedAuthError=(error)=>new FederatedAuthError("authorization_failed",error.message);var unknownAuthError=()=>new FederatedAuthError("authorization_failed","Sign in failed");var CollectiveIntelligenceLoginProvider=(props)=>{const state=useSyncExternalStore(props.auth.subscribe,props.auth.getState,props.auth.getState);const contextValue=useMemo2(()=>({auth:props.auth,state}),[props.auth,state]);return jsx3(AuthContext.Provider,{value:contextValue,children:props.clientFactory===undefined?jsx3(CollectiveIntelligenceProvider,{client:props.client,entitySource:props.entitySource,queryClient:props.queryClient,children:props.children}):jsx3(CollectiveIntelligenceProvider,{clientFactory:props.clientFactory,entitySource:props.entitySource,queryClient:props.queryClient,children:props.children})})};var useCollectiveIntelligenceAuth=()=>{const value=useContext2(AuthContext);if(value===null){throw new Error("CollectiveIntelligenceLoginProvider must wrap authentication bindings")}return{...value,beginLogin:value.auth.beginLogin,completeLogin:value.auth.completeLogin,logout:value.auth.logout,reauthorize:value.auth.reauthorize}};var LoginButton=({children="Sign in with Collective Intelligence",className,components,disabled=false,onError,returnTo,style})=>{const{auth,state}=useCollectiveIntelligenceAuth();const Button=components?.Button??"button";const redirecting=state.status==="redirecting";const onClick=useCallback2((event)=>{event.preventDefault();const target=returnTo??globalThis.location.href;const begin=async()=>{try{await auth.beginLogin({returnTo:target})}catch(error){onError?.(error instanceof Error?toFederatedAuthError(error):unknownAuthError())}};begin()},[auth,onError,returnTo]);return jsx3(Button,{"aria-busy":redirecting,className:["ci-login-button",className].filter(Boolean).join(" "),disabled:disabled||redirecting,onClick,style,type:"button",children})};var callbackMessage=(state)=>{switch(state.status){case"authenticated":{return"Sign in complete"}case"error":{return state.error.message}case"expired":{return"Sign in expired"}case"anonymous":case"redirecting":{return"Completing sign in"}default:{return"Completing sign in"}}};var LoginCallbackPrimitive=({className,components,state,style})=>{const Status=components?.Status??"div";return jsx3(Status,{"aria-live":"polite",className:["ci-login-callback",className].filter(Boolean).join(" "),style,children:callbackMessage(state)})};var LoginCallback=({callbackUrl,onComplete,onError,...primitiveProps})=>{const{auth,state}=useCollectiveIntelligenceAuth();useEffect(()=>{const url=callbackUrl??globalThis.location.href;const complete=async()=>{try{const{returnTo}=await auth.completeLogin(url);onComplete?.(returnTo)}catch(error){onError?.(error instanceof Error?toFederatedAuthError(error):unknownAuthError())}};complete()},[auth,callbackUrl,onComplete,onError]);return jsx3(LoginCallbackPrimitive,{...primitiveProps,state})};var useSession=()=>{const{state}=useCollectiveIntelligenceAuth();return state};var SessionGate=({children,fallback=null})=>{const session=useSession();if(session.status!=="authenticated"){return fallback}return children};import{createRealtimeClient}from"@collective-intelligence/external-apps-client-sdk";import{createContext as createContext3,useContext as useContext3,useEffect as useEffect2,useState as useState3}from"react";import{jsx as jsx4}from"react/jsx-runtime";var idle={connections:0,lastEventId:undefined,status:"idle",topics:[]};var hasOnReset=(options)=>options.onReset!==undefined;var shareClient=(initial)=>{let options=initial;let client;let holders=0;const subscriptions=new Set;const create=()=>createRealtimeClient({document:globalThis.document,window:globalThis.window,...options,onAccessChanged:()=>options.onAccessChanged?.(),...hasOnReset(options)&&{onReset:()=>options.onReset?.()}});const acquire=()=>{holders+=1;client??=create();return client};const release=()=>{holders-=1;queueMicrotask(()=>{if(holders===0){client?.close();client=undefined}})};const subscribe=(topic,onChanges)=>{const subscription={onChanges,topic,unsubscribe:acquire().subscribe(topic,onChanges)};subscriptions.add(subscription);return()=>{if(subscriptions.delete(subscription)){subscription.unsubscribe();release()}}};return{acquire,getState:()=>client?.getState()??idle,release,subscribe,update:(next)=>{const previous=options;options=next;if(client===undefined||hasOnReset(next)===hasOnReset(previous)){return}for(const subscription of subscriptions){subscription.unsubscribe()}client.close();client=create();for(const subscription of subscriptions){subscription.unsubscribe=client.subscribe(subscription.topic,subscription.onChanges)}}}};var RealtimeContext=createContext3(null);var RealtimeProvider=({children,...options})=>{const[shared]=useState3(()=>shareClient(options));useEffect2(()=>{shared.update(options)});useEffect2(()=>{shared.acquire();return shared.release},[shared]);return jsx4(RealtimeContext.Provider,{value:shared,children})};var useRealtimeClient=()=>{const client=useContext3(RealtimeContext);if(client===null){throw new Error("useRealtimeClient requires a RealtimeProvider")}return client};import{realtimeTopicKey}from"@collective-intelligence/external-apps-client-sdk";import{useEffect as useEffect3,useRef,useState as useState4}from"react";var useRealtimeTopic=(topic,onChanges)=>{const client=useRealtimeClient();const latest=useRef(onChanges);const key=topic===null?null:realtimeTopicKey(topic);const[subscribed,setSubscribed]=useState4({key,topic});if(subscribed.key!==key){setSubscribed({key,topic})}useEffect3(()=>{latest.current=onChanges});useEffect3(()=>subscribed.topic===null?undefined:client.subscribe(subscribed.topic,(changes)=>latest.current(changes)),[client,subscribed])};"use client";var clientSdkReactEntrypoint="react";"use client";var{AgentPrompt:AgentPrompt2,AgentPromptPrimitive:AgentPromptPrimitive2,CollectiveIntelligenceLoginProvider:CollectiveIntelligenceLoginProvider2,CollectiveIntelligenceProvider:CollectiveIntelligenceProvider2,LoginButton:LoginButton2,LoginCallback:LoginCallback2,LoginCallbackPrimitive:LoginCallbackPrimitive2,RealtimeProvider:RealtimeProvider2,SessionGate:SessionGate2,clientSdkReactEntrypoint:clientSdkReactEntrypoint2,createPromptKeyHandler:createPromptKeyHandler2,useAgents:useAgents2,useAttachments:useAttachments2,useClientContext:useClientContext2,useCollectiveIntelligenceAuth:useCollectiveIntelligenceAuth2,useCollectiveIntelligenceClient:useCollectiveIntelligenceClient2,useKnowledge:useKnowledge2,useOrganizationClient:useOrganizationClient2,useRealtimeClient:useRealtimeClient2,useRealtimeTopic:useRealtimeTopic2,useRuns:useRuns2,useSession:useSession2,useSkills:useSkills2,useWorkflows:useWorkflows2,useAgentInvocation:useAgentInvocation2}=exports_src;export{AgentPrompt2 as AgentPrompt,AgentPromptPrimitive2 as AgentPromptPrimitive,CollectiveIntelligenceLoginProvider2 as CollectiveIntelligenceLoginProvider,CollectiveIntelligenceProvider2 as CollectiveIntelligenceProvider,LoginButton2 as LoginButton,LoginCallback2 as LoginCallback,LoginCallbackPrimitive2 as LoginCallbackPrimitive,RealtimeProvider2 as RealtimeProvider,SessionGate2 as SessionGate,clientSdkReactEntrypoint2 as clientSdkReactEntrypoint,createPromptKeyHandler2 as createPromptKeyHandler,useAgentInvocation2 as useAgentInvocation,useAgents2 as useAgents,useAttachments2 as useAttachments,useClientContext2 as useClientContext,useCollectiveIntelligenceAuth2 as useCollectiveIntelligenceAuth,useCollectiveIntelligenceClient2 as useCollectiveIntelligenceClient,useKnowledge2 as useKnowledge,useOrganizationClient2 as useOrganizationClient,useRealtimeClient2 as useRealtimeClient,useRealtimeTopic2 as useRealtimeTopic,useRuns2 as useRuns,useSession2 as useSession,useSkills2 as useSkills,useWorkflows2 as useWorkflows};
2
+ import{EntityRuntimeProvider,createDelegatedEntitySource,defineEntity2,useEntityViewState2,useMemoryEntityViewState2,EntityTable2,EntityList2,EntityForm2}from"./index-wb56c1cj.js";import{__export}from"./index-6ra894v2.js";var exports_src={};__export(exports_src,{AgentPrompt:()=>AgentPrompt,AgentPromptPrimitive:()=>AgentPromptPrimitive,CollectiveIntelligenceLoginProvider:()=>CollectiveIntelligenceLoginProvider,CollectiveIntelligenceProvider:()=>CollectiveIntelligenceProvider,EntityForm:()=>EntityForm2,EntityList:()=>EntityList2,EntityTable:()=>EntityTable2,LoginButton:()=>LoginButton,LoginCallback:()=>LoginCallback,LoginCallbackPrimitive:()=>LoginCallbackPrimitive,RealtimeProvider:()=>RealtimeProvider,SessionGate:()=>SessionGate,clientSdkReactEntrypoint:()=>clientSdkReactEntrypoint,createDelegatedEntitySource:()=>createDelegatedEntitySource,createPromptKeyHandler:()=>createPromptKeyHandler,defineEntity:()=>defineEntity2,useAgentInvocation:()=>useAgentInvocation,useAgents:()=>useAgents,useAttachments:()=>useAttachments,useClientContext:()=>useClientContext,useCollectiveIntelligenceAuth:()=>useCollectiveIntelligenceAuth,useCollectiveIntelligenceClient:()=>useCollectiveIntelligenceClient,useEntityViewState:()=>useEntityViewState2,useKnowledge:()=>useKnowledge,useMemoryEntityViewState:()=>useMemoryEntityViewState2,useOrganizationClient:()=>useOrganizationClient,useRealtimeClient:()=>useRealtimeClient,useRealtimeTopic:()=>useRealtimeTopic,useRuns:()=>useRuns,useSession:()=>useSession,useSkills:()=>useSkills,useWorkflows:()=>useWorkflows});import{useCallback,useId,useState as useState2}from"react";import{QueryClient,QueryClientProvider}from"@tanstack/react-query";import{createContext,useContext,useMemo,useState}from"react";import{jsx}from"react/jsx-runtime";var ClientContext=createContext(null);var CollectiveIntelligenceProvider=(props)=>{const{clientFactory}=props;const[ownedQueryClient]=useState(()=>new QueryClient({defaultOptions:{mutations:{retry:false},queries:{retry:false}}}));const ownedClient=useMemo(()=>clientFactory===undefined?undefined:clientFactory(),[clientFactory]);const client=props.client??ownedClient;const queryClient=props.queryClient??ownedQueryClient;if(client===undefined){throw new TypeError("A client or clientFactory is required")}return jsx(ClientContext.Provider,{value:client,children:jsx(QueryClientProvider,{client:queryClient,children:props.entitySource===undefined?props.children:jsx(EntityRuntimeProvider,{onWriteFailed:props.onWriteFailed,source:props.entitySource,children:props.children})})})};var useCollectiveIntelligenceClient=()=>{const client=useContext(ClientContext);if(client===null){throw new Error("CollectiveIntelligenceProvider must wrap Collective Intelligence hooks")}return client};var useOrganizationClient=(organizationId)=>{const client=useCollectiveIntelligenceClient();return useMemo(()=>client.forOrganization(organizationId),[client,organizationId])};var useKnowledge=(organizationId)=>useOrganizationClient(organizationId).knowledge;var useAgents=(organizationId)=>useOrganizationClient(organizationId).agents;var useSkills=(organizationId)=>useOrganizationClient(organizationId).skills;var useWorkflows=(organizationId)=>useOrganizationClient(organizationId).workflows;var useRuns=(organizationId)=>useOrganizationClient(organizationId).runs;var useAttachments=(organizationId)=>useOrganizationClient(organizationId).attachments;var useClientContext=(organizationId)=>useOrganizationClient(organizationId).context;import{jsx as jsx2,jsxs}from"react/jsx-runtime";var ignoreSubmit=()=>false;var createPromptKeyHandler=(onSubmit)=>(event)=>{if(event.key==="Enter"&&!event.shiftKey&&!event.isComposing){event.preventDefault();onSubmit()}};var AgentPromptPrimitive=({autoFocus=false,className,components,id,label,onChange,onSubmit,pending,status,style,submitLabel="Send",value})=>{const Form=components?.Form??"form";const Label=components?.Label??"label";const Textarea=components?.Textarea??"textarea";const Button=components?.Button??"button";const Status=components?.Status??"div";const statusId=`${id}-status`;const submit=(event)=>{event.preventDefault();if(!pending){onSubmit()}};const onKeyDown=(event)=>{createPromptKeyHandler(pending?ignoreSubmit:onSubmit)({isComposing:event.nativeEvent.isComposing,key:event.key,preventDefault:()=>{event.preventDefault()},shiftKey:event.shiftKey})};return jsxs(Form,{className:["ci-agent-prompt",className].filter(Boolean).join(" "),onSubmit:submit,style,children:[jsx2(Label,{className:"ci-agent-prompt__label",htmlFor:id,children:label}),jsx2(Textarea,{"aria-describedby":statusId,autoFocus,className:"ci-agent-prompt__input",disabled:pending,id,onChange:(event)=>{onChange(event.currentTarget.value)},onKeyDown,value}),jsx2(Button,{className:"ci-agent-prompt__submit",disabled:pending||value.trim().length===0,type:"submit",children:submitLabel}),jsx2(Status,{"aria-live":"polite",className:"ci-agent-prompt__status",id:statusId,children:status})]})};var useAgentInvocation=(organizationId)=>{const{agents}=useOrganizationClient(organizationId);const[state,setState]=useState2({status:"idle"});const invoke=useCallback(async(input)=>{setState({status:"submitting"});try{const result=await agents.invoke(input);setState(result.ok?{result,status:"success"}:{result,status:"failure"});return result}catch(error){setState({error,status:"error"});throw error}},[agents]);return{invoke,reset:()=>{setState({status:"idle"})},state}};var invocationStatus=(state)=>{switch(state.status){case"submitting":{return"Sending"}case"success":{return"Request started"}case"failure":{return state.result.error.message}case"error":{return"Request failed"}case"idle":{return null}default:{return null}}};var EMPTY_AGENT_PROMPT_COMPONENTS={};var EMPTY_AGENT_PROMPT_STYLE={};var createDefaultIdempotencyKey=()=>globalThis.crypto.randomUUID();var AgentPrompt=({agentId,autoFocus=false,className="",components=EMPTY_AGENT_PROMPT_COMPONENTS,createIdempotencyKey=createDefaultIdempotencyKey,label="Message",onResult,organizationId,style=EMPTY_AGENT_PROMPT_STYLE,submitLabel="Send"})=>{const id=`ci-agent-prompt-${useId().replaceAll(":","")}`;const[value,setValue]=useState2("");const invocation=useAgentInvocation(organizationId);const submit=()=>{const prompt=value.trim();if(prompt.length===0||invocation.state.status==="submitting"){return}const submitInvocation=async()=>{try{const result=await invocation.invoke({agentId,idempotencyKey:createIdempotencyKey(),input:{prompt}});if(result.ok){setValue("")}onResult?.(result)}catch{}};submitInvocation()};return jsx2(AgentPromptPrimitive,{autoFocus,className,components,id,label,onChange:setValue,onSubmit:submit,pending:invocation.state.status==="submitting",status:invocationStatus(invocation.state),style,submitLabel,value})};import{FederatedAuthError}from"@collective-intelligence/external-apps-client-sdk";import{createContext as createContext2,useCallback as useCallback2,useContext as useContext2,useEffect,useMemo as useMemo2,useSyncExternalStore}from"react";import{jsx as jsx3}from"react/jsx-runtime";var AuthContext=createContext2(null);var toFederatedAuthError=(error)=>new FederatedAuthError("authorization_failed",error.message);var unknownAuthError=()=>new FederatedAuthError("authorization_failed","Sign in failed");var CollectiveIntelligenceLoginProvider=(props)=>{const state=useSyncExternalStore(props.auth.subscribe,props.auth.getState,props.auth.getState);const contextValue=useMemo2(()=>({auth:props.auth,state}),[props.auth,state]);return jsx3(AuthContext.Provider,{value:contextValue,children:props.clientFactory===undefined?jsx3(CollectiveIntelligenceProvider,{client:props.client,entitySource:props.entitySource,onWriteFailed:props.onWriteFailed,queryClient:props.queryClient,children:props.children}):jsx3(CollectiveIntelligenceProvider,{clientFactory:props.clientFactory,entitySource:props.entitySource,onWriteFailed:props.onWriteFailed,queryClient:props.queryClient,children:props.children})})};var useCollectiveIntelligenceAuth=()=>{const value=useContext2(AuthContext);if(value===null){throw new Error("CollectiveIntelligenceLoginProvider must wrap authentication bindings")}return{...value,beginLogin:value.auth.beginLogin,completeLogin:value.auth.completeLogin,logout:value.auth.logout,reauthorize:value.auth.reauthorize}};var LoginButton=({children="Sign in with Collective Intelligence",className,components,disabled=false,onError,returnTo,style})=>{const{auth,state}=useCollectiveIntelligenceAuth();const Button=components?.Button??"button";const redirecting=state.status==="redirecting";const onClick=useCallback2((event)=>{event.preventDefault();const target=returnTo??globalThis.location.href;const begin=async()=>{try{await auth.beginLogin({returnTo:target})}catch(error){onError?.(error instanceof Error?toFederatedAuthError(error):unknownAuthError())}};begin()},[auth,onError,returnTo]);return jsx3(Button,{"aria-busy":redirecting,className:["ci-login-button",className].filter(Boolean).join(" "),disabled:disabled||redirecting,onClick,style,type:"button",children})};var callbackMessage=(state)=>{switch(state.status){case"authenticated":{return"Sign in complete"}case"error":{return state.error.message}case"expired":{return"Sign in expired"}case"anonymous":case"redirecting":{return"Completing sign in"}default:{return"Completing sign in"}}};var LoginCallbackPrimitive=({className,components,state,style})=>{const Status=components?.Status??"div";return jsx3(Status,{"aria-live":"polite",className:["ci-login-callback",className].filter(Boolean).join(" "),style,children:callbackMessage(state)})};var LoginCallback=({callbackUrl,onComplete,onError,...primitiveProps})=>{const{auth,state}=useCollectiveIntelligenceAuth();useEffect(()=>{const url=callbackUrl??globalThis.location.href;const complete=async()=>{try{const{returnTo}=await auth.completeLogin(url);onComplete?.(returnTo)}catch(error){onError?.(error instanceof Error?toFederatedAuthError(error):unknownAuthError())}};complete()},[auth,callbackUrl,onComplete,onError]);return jsx3(LoginCallbackPrimitive,{...primitiveProps,state})};var useSession=()=>{const{state}=useCollectiveIntelligenceAuth();return state};var SessionGate=({children,fallback=null})=>{const session=useSession();if(session.status!=="authenticated"){return fallback}return children};import{createRealtimeClient}from"@collective-intelligence/external-apps-client-sdk";import{createContext as createContext3,useContext as useContext3,useEffect as useEffect2,useState as useState3}from"react";import{jsx as jsx4}from"react/jsx-runtime";var idle={connections:0,lastEventId:undefined,status:"idle",topics:[]};var hasOnReset=(options)=>options.onReset!==undefined;var shareClient=(initial)=>{let options=initial;let client;let holders=0;const subscriptions=new Set;const create=()=>createRealtimeClient({document:globalThis.document,window:globalThis.window,...options,onAccessChanged:()=>options.onAccessChanged?.(),...hasOnReset(options)&&{onReset:()=>options.onReset?.()}});const acquire=()=>{holders+=1;client??=create();return client};const release=()=>{holders-=1;queueMicrotask(()=>{if(holders===0){client?.close();client=undefined}})};const subscribe=(topic,onChanges)=>{const subscription={onChanges,topic,unsubscribe:acquire().subscribe(topic,onChanges)};subscriptions.add(subscription);return()=>{if(subscriptions.delete(subscription)){subscription.unsubscribe();release()}}};return{acquire,getState:()=>client?.getState()??idle,release,subscribe,update:(next)=>{const previous=options;options=next;if(client===undefined||hasOnReset(next)===hasOnReset(previous)){return}for(const subscription of subscriptions){subscription.unsubscribe()}client.close();client=create();for(const subscription of subscriptions){subscription.unsubscribe=client.subscribe(subscription.topic,subscription.onChanges)}}}};var RealtimeContext=createContext3(null);var RealtimeProvider=({children,...options})=>{const[shared]=useState3(()=>shareClient(options));useEffect2(()=>{shared.update(options)});useEffect2(()=>{shared.acquire();return shared.release},[shared]);return jsx4(RealtimeContext.Provider,{value:shared,children})};var useRealtimeClient=()=>{const client=useContext3(RealtimeContext);if(client===null){throw new Error("useRealtimeClient requires a RealtimeProvider")}return client};import{realtimeTopicKey}from"@collective-intelligence/external-apps-client-sdk";import{useEffect as useEffect3,useRef,useState as useState4}from"react";var useRealtimeTopic=(topic,onChanges)=>{const client=useRealtimeClient();const latest=useRef(onChanges);const key=topic===null?null:realtimeTopicKey(topic);const[subscribed,setSubscribed]=useState4({key,topic});if(subscribed.key!==key){setSubscribed({key,topic})}useEffect3(()=>{latest.current=onChanges});useEffect3(()=>subscribed.topic===null?undefined:client.subscribe(subscribed.topic,(changes)=>latest.current(changes)),[client,subscribed])};"use client";var clientSdkReactEntrypoint="react";"use client";var{AgentPrompt:AgentPrompt2,AgentPromptPrimitive:AgentPromptPrimitive2,CollectiveIntelligenceLoginProvider:CollectiveIntelligenceLoginProvider2,CollectiveIntelligenceProvider:CollectiveIntelligenceProvider2,LoginButton:LoginButton2,LoginCallback:LoginCallback2,LoginCallbackPrimitive:LoginCallbackPrimitive2,RealtimeProvider:RealtimeProvider2,SessionGate:SessionGate2,clientSdkReactEntrypoint:clientSdkReactEntrypoint2,createPromptKeyHandler:createPromptKeyHandler2,useAgents:useAgents2,useAttachments:useAttachments2,useClientContext:useClientContext2,useCollectiveIntelligenceAuth:useCollectiveIntelligenceAuth2,useCollectiveIntelligenceClient:useCollectiveIntelligenceClient2,useKnowledge:useKnowledge2,useOrganizationClient:useOrganizationClient2,useRealtimeClient:useRealtimeClient2,useRealtimeTopic:useRealtimeTopic2,useRuns:useRuns2,useSession:useSession2,useSkills:useSkills2,useWorkflows:useWorkflows2,useAgentInvocation:useAgentInvocation2}=exports_src;export{AgentPrompt2 as AgentPrompt,AgentPromptPrimitive2 as AgentPromptPrimitive,CollectiveIntelligenceLoginProvider2 as CollectiveIntelligenceLoginProvider,CollectiveIntelligenceProvider2 as CollectiveIntelligenceProvider,LoginButton2 as LoginButton,LoginCallback2 as LoginCallback,LoginCallbackPrimitive2 as LoginCallbackPrimitive,RealtimeProvider2 as RealtimeProvider,SessionGate2 as SessionGate,clientSdkReactEntrypoint2 as clientSdkReactEntrypoint,createPromptKeyHandler2 as createPromptKeyHandler,useAgentInvocation2 as useAgentInvocation,useAgents2 as useAgents,useAttachments2 as useAttachments,useClientContext2 as useClientContext,useCollectiveIntelligenceAuth2 as useCollectiveIntelligenceAuth,useCollectiveIntelligenceClient2 as useCollectiveIntelligenceClient,useKnowledge2 as useKnowledge,useOrganizationClient2 as useOrganizationClient,useRealtimeClient2 as useRealtimeClient,useRealtimeTopic2 as useRealtimeTopic,useRuns2 as useRuns,useSession2 as useSession,useSkills2 as useSkills,useWorkflows2 as useWorkflows};
@@ -3,6 +3,9 @@
3
3
  import { ClientFetch, FederatedAuthErrorCode, FederatedAuthState, FederatedSessionIdentity } from '@collective-intelligence/external-apps-client-sdk';
4
4
  import { EntityClient, EntityCreateInput, EntityDeleteInput, EntityListQuery, EntityMemberValue, EntityPage, EntityUpdateInput, EntityUploadInput, EntityUploadSlot, IdempotencyKey, PortableResourceSchema, ResourceCapabilities, ResourceClient, ResourceCreateInput, ResourceCursor, ResourceDefinition, ResourceError, ResourceErrorCode, ResourceFilter, ResourceId, ResourceIdentityField, ResourceJsonValue, ResourceLifecycleInput, ResourceListQuery, ResourceListResult, ResourceMutationInput, ResourcePaginationMode, ResourceRecord, ResourceRecordId, ResourceRequestOptions, ResourceResult, ResourceRevision, ResourceSort } from '@collective-intelligence/external-apps-client-sdk/resources';
5
5
  import { ResourceField, ResourceFieldControlProps, ResourceObject, ResourceValue } from '@collective-intelligence/ui-react/resources';
6
+ import * as Context from 'effect/Context';
7
+ import * as Effect from 'effect/Effect';
8
+ import * as Layer from 'effect/Layer';
6
9
  import { ComponentType, ReactNode } from 'react';
7
10
 
8
11
  export declare class ResourceQueryError extends Error {
@@ -236,6 +239,7 @@ export declare const useEntityRecord: <Value, Create, Patch>(contract: ResourceD
236
239
  };
237
240
  export declare const useEntityUpdate: <Value, Create, Patch>(contract: ResourceDefinition<Value, Create, Patch>, target: Readonly<{
238
241
  id: string;
242
+ optimistic?: boolean;
239
243
  }>) => {
240
244
  available: boolean;
241
245
  data: Readonly<{
@@ -256,7 +260,7 @@ export declare const useEntityUpdate: <Value, Create, Patch>(contract: ResourceD
256
260
  truncated?: readonly string[];
257
261
  value: Value;
258
262
  }>>>;
259
- status: "pending" | "success" | "idle" | "conflict" | "uncertain" | "rejected";
263
+ status: "pending" | "success" | "idle" | "conflict" | "rejected" | "uncertain";
260
264
  submit: (input: Omit<EntityUpdateInput<Patch>, "id" | "idempotencyKey">) => Promise<ResourceResult<Readonly<{
261
265
  deletedAt?: string;
262
266
  id: import("@collective-intelligence/external-apps-client-sdk").ResourceRecordId;
@@ -287,7 +291,7 @@ export declare const useEntityCreate: <Value, Create, Patch>(contract: ResourceD
287
291
  truncated?: readonly string[];
288
292
  value: Value;
289
293
  }>>>;
290
- status: "pending" | "success" | "idle" | "conflict" | "uncertain" | "rejected";
294
+ status: "pending" | "success" | "idle" | "conflict" | "rejected" | "uncertain";
291
295
  submit: (input: Omit<Readonly<{
292
296
  idempotencyKey: import("@collective-intelligence/external-apps-client-sdk").IdempotencyKey;
293
297
  value: Create;
@@ -306,6 +310,7 @@ export declare const useEntityCreate: <Value, Create, Patch>(contract: ResourceD
306
310
  };
307
311
  export declare const useEntityDelete: <Value, Create, Patch>(contract: ResourceDefinition<Value, Create, Patch>, target: Readonly<{
308
312
  id: string;
313
+ optimistic?: boolean;
309
314
  }>) => {
310
315
  available: boolean;
311
316
  data: Readonly<{
@@ -326,7 +331,7 @@ export declare const useEntityDelete: <Value, Create, Patch>(contract: ResourceD
326
331
  truncated?: readonly string[];
327
332
  value: Value;
328
333
  }>>>;
329
- status: "pending" | "success" | "idle" | "conflict" | "uncertain" | "rejected";
334
+ status: "pending" | "success" | "idle" | "conflict" | "rejected" | "uncertain";
330
335
  submit: (input: Omit<EntityDeleteInput, "id" | "idempotencyKey">) => Promise<ResourceResult<Readonly<{
331
336
  deletedAt?: string;
332
337
  id: import("@collective-intelligence/external-apps-client-sdk").ResourceRecordId;
package/dist/resources.js CHANGED
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{resourceKeys,ResourceQueryError2,unwrapResourceResult2,createResourceListQueryOptions,mapResourceAuthState2,hasResourceCapability2,clearProtectedResourceQueries,createDelegatedEntitySource,defineEntity2,useEntityUpdate2,useEntityCreate2,useEntityDelete2,useEntityList2,useEntityRecord2,useEntityViewState2,useMemoryEntityViewState2,EntityTable2,EntityList2,EntityForm2}from"./index-bjd4xpqf.js";import{__export}from"./index-6ra894v2.js";var exports_resources={};__export(exports_resources,{EntityForm:()=>EntityForm2,EntityList:()=>EntityList2,EntityTable:()=>EntityTable2,ResourceCapabilityAction:()=>ResourceCapabilityAction,ResourceFilter:()=>ResourceFilter,ResourceLifecycleAction:()=>ResourceLifecycleAction,ResourceList:()=>ResourceList,ResourcePagination:()=>ResourcePagination,ResourceProvider:()=>ResourceProvider,ResourceQueryError:()=>ResourceQueryError2,ResourceTable:()=>ResourceTable,createDelegatedEntitySource:()=>createDelegatedEntitySource,defineEntity:()=>defineEntity2,hasResourceCapability:()=>hasResourceCapability2,mapResourceAuthState:()=>mapResourceAuthState2,unwrapResourceResult:()=>unwrapResourceResult2,useEntityCreate:()=>useEntityCreate2,useEntityDelete:()=>useEntityDelete2,useEntityList:()=>useEntityList2,useEntityRecord:()=>useEntityRecord2,useEntityUpdate:()=>useEntityUpdate2,useEntityViewState:()=>useEntityViewState2,useMemoryEntityViewState:()=>useMemoryEntityViewState2});import{useMutation,useQuery,useQueryClient as useQueryClient2}from"@tanstack/react-query";import{mutationOptions}from"@tanstack/react-query";var invalidateResource=async(options)=>{await options.queryClient.invalidateQueries({queryKey:resourceKeys.resource(options.organizationId,options.client.definition.id,options.client.definition.version)})};var createResourceMutationOptions=(options)=>mutationOptions({mutationFn:async(input)=>{let result;switch(options.operation){case"hard_delete":{unwrapResourceResult2(await options.client.hardDelete(input));break}case"restore":{result=unwrapResourceResult2(await options.client.restore(input));break}case"soft_delete":{result=unwrapResourceResult2(await options.client.softDelete(input));break}default:{throw new TypeError(`Unknown resource operation: ${String(options.operation)}`)}}return result},mutationKey:[...resourceKeys.resource(options.organizationId,options.client.definition.id,options.client.definition.version),"mutations",options.operation],onSuccess:async()=>{await invalidateResource(options)}});import{useQueryClient}from"@tanstack/react-query";import{createContext,useContext,useEffect,useMemo,useRef}from"react";import{jsx}from"react/jsx-runtime";var useResourceQueryCleanup=(queryClient,authStatus,organizationId)=>{const previousOrganizationId=useRef(null);const input=useMemo(()=>({authStatus,organizationId}),[authStatus,organizationId]);useEffect(()=>{const previous=previousOrganizationId.current;previousOrganizationId.current=input.organizationId;if(input.authStatus!=="authenticated"){clearProtectedResourceQueries(queryClient);return}if(previous!==null&&previous!==input.organizationId){clearProtectedResourceQueries(queryClient,previous)}},[input,queryClient])};var ResourceContext=createContext(null);var ResourceProvider=({authState,capabilities,children,organizationId})=>{const queryClient=useQueryClient();useResourceQueryCleanup(queryClient,authState.status,organizationId);const value=useMemo(()=>({authState,capabilities,organizationId}),[authState,capabilities,organizationId]);return jsx(ResourceContext.Provider,{value,children})};var useResourceContext=()=>{const value=useContext(ResourceContext);if(value===null){throw new Error("ResourceProvider must wrap resource bindings")}return value};var authStateForError=(error,fallback)=>error instanceof ResourceQueryError2?mapResourceAuthState2({correlationId:error.correlationId,error:error.resourceError,ok:false}):fallback;var useResourceList=(client,query)=>{const context=useResourceContext();const result=useQuery({...createResourceListQueryOptions({client,organizationId:context.organizationId,query}),enabled:context.authState.status==="authenticated"});return{...result,authState:authStateForError(result.error,context.authState)}};var useResourceLifecycleMutation=(client,operation)=>{const{capabilities,organizationId}=useResourceContext();const queryClient=useQueryClient2();const mutation=useMutation(createResourceMutationOptions({client,operation,organizationId,queryClient}));let capability;if(operation==="hard_delete"){capability="hardDelete"}else if(operation==="soft_delete"){capability="softDelete"}else{capability=operation}return{...mutation,available:capabilities[capability]===true}};import{jsx as jsx2,jsxs,Fragment}from"react/jsx-runtime";var nextQuery=(query,result)=>{if(result.pagination==="cursor"){return result.nextCursor===undefined?undefined:{...query,pagination:{cursor:result.nextCursor,limit:query.pagination.mode==="cursor"?query.pagination.limit:25,mode:"cursor"}}}return result.page>=result.totalPages?undefined:{...query,pagination:{mode:"page",page:result.page+1,pageSize:result.pageSize}}};var previousQuery=(query,result)=>result.pagination==="page"&&result.page>1?{...query,pagination:{mode:"page",page:result.page-1,pageSize:result.pageSize}}:undefined;var DefaultStatus=({authState,error,pending})=>{let message=authState.status;if(authState.status==="authenticated"){message=pending?"Loading resources":error?.message??""}return jsx2("output",{"aria-live":"polite",children:message})};var ResourceList=({client,components,onQueryChange,query,renderItem})=>{const resourceQuery=useResourceList(client,query);const Status=components.Status??DefaultStatus;if(resourceQuery.authState.status!=="authenticated"||resourceQuery.isPending||resourceQuery.error!==null){return jsx2(Status,{authState:resourceQuery.authState,pending:resourceQuery.isPending,...resourceQuery.error===null?{}:{error:resourceQuery.error}})}const{List}=components;const{Pagination}=components;const result=resourceQuery.data;const next=nextQuery(query,result);const previous=previousQuery(query,result);return jsxs(Fragment,{children:[jsx2(List,{getItemKey:(item)=>item.id,items:result.items,renderItem}),Pagination===undefined?null:jsx2(Pagination,{hasNextPage:next!==undefined,hasPreviousPage:previous!==undefined,isPending:resourceQuery.isFetching,onNext:()=>{if(next!==undefined){onQueryChange?.(next)}},onPrevious:()=>{if(previous!==undefined){onQueryChange?.(previous)}},...result.pagination==="page"?{currentPage:result.page}:{}})]})};var ResourceCapabilityAction=({capability,children,components,unsupported="disable"})=>{const{capabilities}=useResourceContext();const supported=capabilities[capability]===true;if(!supported&&unsupported==="hide"){return null}const{Action}=components;return jsx2(Action,{disabled:!supported,children})};var ResourceFilter=({components,label="Search resources",onQueryChange,query})=>{const{Filter}=components;const{textSearch:_textSearch,...queryWithoutTextSearch}=query;return jsx2(Filter,{label,onChange:(value)=>{onQueryChange(value===""?queryWithoutTextSearch:{...query,textSearch:value})},value:query.textSearch??""})};var ResourcePagination=({components,isPending=false,onQueryChange,query,result})=>{const{Pagination}=components;const next=nextQuery(query,result);const previous=previousQuery(query,result);return jsx2(Pagination,{hasNextPage:next!==undefined,hasPreviousPage:previous!==undefined,isPending,onNext:()=>{if(next!==undefined){onQueryChange(next)}},onPrevious:()=>{if(previous!==undefined){onQueryChange(previous)}},...result.pagination==="page"?{currentPage:result.page}:{}})};var ResourceTable=({caption,client,columns,components,query})=>{const resourceQuery=useResourceList(client,query);const Status=components.Status??DefaultStatus;if(resourceQuery.authState.status!=="authenticated"||resourceQuery.isPending||resourceQuery.error!==null){return jsx2(Status,{authState:resourceQuery.authState,pending:resourceQuery.isPending,...resourceQuery.error===null?{}:{error:resourceQuery.error}})}const{Table}=components;return jsx2(Table,{caption,columns,getRowKey:(row)=>row.id,renderCell:(row,column)=>column.render(row),rows:resourceQuery.data.items})};var ResourceLifecycleAction=({children,client,components,input,operation,unsupported="disable"})=>{const mutation=useResourceLifecycleMutation(client,operation);if(!mutation.available&&unsupported==="hide"){return null}const{Action}=components;return jsx2(Action,{disabled:!mutation.available,onClick:()=>{mutation.mutate(input)},pending:mutation.isPending,children})};"use client";var{defineEntity,EntityForm,EntityList,EntityTable,ResourceCapabilityAction:ResourceCapabilityAction2,ResourceFilter:ResourceFilter2,ResourceLifecycleAction:ResourceLifecycleAction2,ResourceList:ResourceList2,ResourcePagination:ResourcePagination2,ResourceProvider:ResourceProvider2,ResourceQueryError,ResourceTable:ResourceTable2,hasResourceCapability,mapResourceAuthState,unwrapResourceResult,useEntityCreate,useEntityDelete,useEntityList,useEntityRecord,useEntityUpdate,useEntityViewState,useMemoryEntityViewState}=exports_resources;export{EntityForm,EntityList,EntityTable,ResourceCapabilityAction2 as ResourceCapabilityAction,ResourceFilter2 as ResourceFilter,ResourceLifecycleAction2 as ResourceLifecycleAction,ResourceList2 as ResourceList,ResourcePagination2 as ResourcePagination,ResourceProvider2 as ResourceProvider,ResourceQueryError,ResourceTable2 as ResourceTable,defineEntity,hasResourceCapability,mapResourceAuthState,unwrapResourceResult,useEntityCreate,useEntityDelete,useEntityList,useEntityRecord,useEntityUpdate,useEntityViewState,useMemoryEntityViewState};
2
+ import{resourceKeys,ResourceQueryError2,unwrapResourceResult2,createResourceListQueryOptions,mapResourceAuthState2,hasResourceCapability2,clearProtectedResourceQueries,createDelegatedEntitySource,defineEntity2,useEntityUpdate2,useEntityCreate2,useEntityDelete2,useEntityList2,useEntityRecord2,useEntityViewState2,useMemoryEntityViewState2,EntityTable2,EntityList2,EntityForm2}from"./index-wb56c1cj.js";import{__export}from"./index-6ra894v2.js";var exports_resources={};__export(exports_resources,{EntityForm:()=>EntityForm2,EntityList:()=>EntityList2,EntityTable:()=>EntityTable2,ResourceCapabilityAction:()=>ResourceCapabilityAction,ResourceFilter:()=>ResourceFilter,ResourceLifecycleAction:()=>ResourceLifecycleAction,ResourceList:()=>ResourceList,ResourcePagination:()=>ResourcePagination,ResourceProvider:()=>ResourceProvider,ResourceQueryError:()=>ResourceQueryError2,ResourceTable:()=>ResourceTable,createDelegatedEntitySource:()=>createDelegatedEntitySource,defineEntity:()=>defineEntity2,hasResourceCapability:()=>hasResourceCapability2,mapResourceAuthState:()=>mapResourceAuthState2,unwrapResourceResult:()=>unwrapResourceResult2,useEntityCreate:()=>useEntityCreate2,useEntityDelete:()=>useEntityDelete2,useEntityList:()=>useEntityList2,useEntityRecord:()=>useEntityRecord2,useEntityUpdate:()=>useEntityUpdate2,useEntityViewState:()=>useEntityViewState2,useMemoryEntityViewState:()=>useMemoryEntityViewState2});import{useMutation,useQuery,useQueryClient as useQueryClient2}from"@tanstack/react-query";import{mutationOptions}from"@tanstack/react-query";var invalidateResource=async(options)=>{await options.queryClient.invalidateQueries({queryKey:resourceKeys.resource(options.organizationId,options.client.definition.id,options.client.definition.version)})};var createResourceMutationOptions=(options)=>mutationOptions({mutationFn:async(input)=>{let result;switch(options.operation){case"hard_delete":{unwrapResourceResult2(await options.client.hardDelete(input));break}case"restore":{result=unwrapResourceResult2(await options.client.restore(input));break}case"soft_delete":{result=unwrapResourceResult2(await options.client.softDelete(input));break}default:{throw new TypeError(`Unknown resource operation: ${String(options.operation)}`)}}return result},mutationKey:[...resourceKeys.resource(options.organizationId,options.client.definition.id,options.client.definition.version),"mutations",options.operation],onSuccess:async()=>{await invalidateResource(options)}});import{useQueryClient}from"@tanstack/react-query";import{createContext,useContext,useEffect,useMemo,useRef}from"react";import{jsx}from"react/jsx-runtime";var useResourceQueryCleanup=(queryClient,authStatus,organizationId)=>{const previousOrganizationId=useRef(null);const input=useMemo(()=>({authStatus,organizationId}),[authStatus,organizationId]);useEffect(()=>{const previous=previousOrganizationId.current;previousOrganizationId.current=input.organizationId;if(input.authStatus!=="authenticated"){clearProtectedResourceQueries(queryClient);return}if(previous!==null&&previous!==input.organizationId){clearProtectedResourceQueries(queryClient,previous)}},[input,queryClient])};var ResourceContext=createContext(null);var ResourceProvider=({authState,capabilities,children,organizationId})=>{const queryClient=useQueryClient();useResourceQueryCleanup(queryClient,authState.status,organizationId);const value=useMemo(()=>({authState,capabilities,organizationId}),[authState,capabilities,organizationId]);return jsx(ResourceContext.Provider,{value,children})};var useResourceContext=()=>{const value=useContext(ResourceContext);if(value===null){throw new Error("ResourceProvider must wrap resource bindings")}return value};var authStateForError=(error,fallback)=>error instanceof ResourceQueryError2?mapResourceAuthState2({correlationId:error.correlationId,error:error.resourceError,ok:false}):fallback;var useResourceList=(client,query)=>{const context=useResourceContext();const result=useQuery({...createResourceListQueryOptions({client,organizationId:context.organizationId,query}),enabled:context.authState.status==="authenticated"});return{...result,authState:authStateForError(result.error,context.authState)}};var useResourceLifecycleMutation=(client,operation)=>{const{capabilities,organizationId}=useResourceContext();const queryClient=useQueryClient2();const mutation=useMutation(createResourceMutationOptions({client,operation,organizationId,queryClient}));let capability;if(operation==="hard_delete"){capability="hardDelete"}else if(operation==="soft_delete"){capability="softDelete"}else{capability=operation}return{...mutation,available:capabilities[capability]===true}};import{jsx as jsx2,jsxs,Fragment}from"react/jsx-runtime";var nextQuery=(query,result)=>{if(result.pagination==="cursor"){return result.nextCursor===undefined?undefined:{...query,pagination:{cursor:result.nextCursor,limit:query.pagination.mode==="cursor"?query.pagination.limit:25,mode:"cursor"}}}return result.page>=result.totalPages?undefined:{...query,pagination:{mode:"page",page:result.page+1,pageSize:result.pageSize}}};var previousQuery=(query,result)=>result.pagination==="page"&&result.page>1?{...query,pagination:{mode:"page",page:result.page-1,pageSize:result.pageSize}}:undefined;var DefaultStatus=({authState,error,pending})=>{let message=authState.status;if(authState.status==="authenticated"){message=pending?"Loading resources":error?.message??""}return jsx2("output",{"aria-live":"polite",children:message})};var ResourceList=({client,components,onQueryChange,query,renderItem})=>{const resourceQuery=useResourceList(client,query);const Status=components.Status??DefaultStatus;if(resourceQuery.authState.status!=="authenticated"||resourceQuery.isPending||resourceQuery.error!==null){return jsx2(Status,{authState:resourceQuery.authState,pending:resourceQuery.isPending,...resourceQuery.error===null?{}:{error:resourceQuery.error}})}const{List}=components;const{Pagination}=components;const result=resourceQuery.data;const next=nextQuery(query,result);const previous=previousQuery(query,result);return jsxs(Fragment,{children:[jsx2(List,{getItemKey:(item)=>item.id,items:result.items,renderItem}),Pagination===undefined?null:jsx2(Pagination,{hasNextPage:next!==undefined,hasPreviousPage:previous!==undefined,isPending:resourceQuery.isFetching,onNext:()=>{if(next!==undefined){onQueryChange?.(next)}},onPrevious:()=>{if(previous!==undefined){onQueryChange?.(previous)}},...result.pagination==="page"?{currentPage:result.page}:{}})]})};var ResourceCapabilityAction=({capability,children,components,unsupported="disable"})=>{const{capabilities}=useResourceContext();const supported=capabilities[capability]===true;if(!supported&&unsupported==="hide"){return null}const{Action}=components;return jsx2(Action,{disabled:!supported,children})};var ResourceFilter=({components,label="Search resources",onQueryChange,query})=>{const{Filter}=components;const{textSearch:_textSearch,...queryWithoutTextSearch}=query;return jsx2(Filter,{label,onChange:(value)=>{onQueryChange(value===""?queryWithoutTextSearch:{...query,textSearch:value})},value:query.textSearch??""})};var ResourcePagination=({components,isPending=false,onQueryChange,query,result})=>{const{Pagination}=components;const next=nextQuery(query,result);const previous=previousQuery(query,result);return jsx2(Pagination,{hasNextPage:next!==undefined,hasPreviousPage:previous!==undefined,isPending,onNext:()=>{if(next!==undefined){onQueryChange(next)}},onPrevious:()=>{if(previous!==undefined){onQueryChange(previous)}},...result.pagination==="page"?{currentPage:result.page}:{}})};var ResourceTable=({caption,client,columns,components,query})=>{const resourceQuery=useResourceList(client,query);const Status=components.Status??DefaultStatus;if(resourceQuery.authState.status!=="authenticated"||resourceQuery.isPending||resourceQuery.error!==null){return jsx2(Status,{authState:resourceQuery.authState,pending:resourceQuery.isPending,...resourceQuery.error===null?{}:{error:resourceQuery.error}})}const{Table}=components;return jsx2(Table,{caption,columns,getRowKey:(row)=>row.id,renderCell:(row,column)=>column.render(row),rows:resourceQuery.data.items})};var ResourceLifecycleAction=({children,client,components,input,operation,unsupported="disable"})=>{const mutation=useResourceLifecycleMutation(client,operation);if(!mutation.available&&unsupported==="hide"){return null}const{Action}=components;return jsx2(Action,{disabled:!mutation.available,onClick:()=>{mutation.mutate(input)},pending:mutation.isPending,children})};"use client";var{defineEntity,EntityForm,EntityList,EntityTable,ResourceCapabilityAction:ResourceCapabilityAction2,ResourceFilter:ResourceFilter2,ResourceLifecycleAction:ResourceLifecycleAction2,ResourceList:ResourceList2,ResourcePagination:ResourcePagination2,ResourceProvider:ResourceProvider2,ResourceQueryError,ResourceTable:ResourceTable2,hasResourceCapability,mapResourceAuthState,unwrapResourceResult,useEntityCreate,useEntityDelete,useEntityList,useEntityRecord,useEntityUpdate,useEntityViewState,useMemoryEntityViewState}=exports_resources;export{EntityForm,EntityList,EntityTable,ResourceCapabilityAction2 as ResourceCapabilityAction,ResourceFilter2 as ResourceFilter,ResourceLifecycleAction2 as ResourceLifecycleAction,ResourceList2 as ResourceList,ResourcePagination2 as ResourcePagination,ResourceProvider2 as ResourceProvider,ResourceQueryError,ResourceTable2 as ResourceTable,defineEntity,hasResourceCapability,mapResourceAuthState,unwrapResourceResult,useEntityCreate,useEntityDelete,useEntityList,useEntityRecord,useEntityUpdate,useEntityViewState,useMemoryEntityViewState};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@collective-intelligence/external-apps-client-sdk-react",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "description": "React bindings for Collective Intelligence external applications.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -32,6 +32,7 @@
32
32
  },
33
33
  "scripts": {
34
34
  "build": "bun tools/build.ts",
35
+ "check:models": "tsprover",
35
36
  "test": "bun tools/test-pack.ts",
36
37
  "test:pack": "bun tools/test-pack.ts",
37
38
  "test:unit": "vitest run src",
@@ -48,7 +49,7 @@
48
49
  "nuqs": "2.10.1"
49
50
  },
50
51
  "peerDependencies": {
51
- "@collective-intelligence/ui-react": "0.1.26",
52
+ "@collective-intelligence/ui-react": "0.1.27",
52
53
  "@tanstack/react-query": "^5.101.4",
53
54
  "effect": "^4.1.0",
54
55
  "react": ">=18.2 <20",
@@ -59,7 +60,7 @@
59
60
  "access": "public"
60
61
  },
61
62
  "devDependencies": {
62
- "@collective-intelligence/ui-react": "0.1.26",
63
+ "@collective-intelligence/ui-react": "0.1.27",
63
64
  "@oxlint/plugins": "1.90.0",
64
65
  "@tanstack/react-query": "5.101.4",
65
66
  "@tanstack/react-router": "1.170.30",
@@ -77,11 +78,12 @@
77
78
  "oxlint": "1.90.0",
78
79
  "react": "^19.2.8",
79
80
  "react-dom": "^19.2.8",
81
+ "tsprover": "0.0.2",
80
82
  "typescript": "5.9.3",
81
83
  "ultracite": "7.10.7",
82
84
  "vite": "8.2.1",
83
85
  "vitest": "4.1.10",
84
86
  "xstate": "5.33.3"
85
87
  },
86
- "gitHead": "b46c00d8ce76db49b810e7a3f25933ace4f2252f"
88
+ "gitHead": "bfe1f5e741d70b6e5a5072e411d84b00e737670d"
87
89
  }
@@ -1,2 +0,0 @@
1
- import{createEntityHttpTransport,createResourceClient}from"@collective-intelligence/external-apps-client-sdk";import{useQueryClient}from"@tanstack/react-query";import{createContext,useContext,useLayoutEffect,useMemo,useSyncExternalStore}from"react";import{queryOptions}from"@tanstack/react-query";var rootKey=["ci-resources"];var resourceKeys={all:rootKey,list:(organizationId,resourceId,version,query)=>[...resourceKeys.lists(organizationId,resourceId,version),query],lists:(organizationId,resourceId,version)=>[...resourceKeys.resource(organizationId,resourceId,version),"lists"],organization:(organizationId)=>[...rootKey,"organizations",organizationId],record:(organizationId,resourceId,version,recordId,includeDeleted=false)=>[...resourceKeys.records(organizationId,resourceId,version),recordId,includeDeleted],records:(organizationId,resourceId,version)=>[...resourceKeys.resource(organizationId,resourceId,version),"records"],resource:(organizationId,resourceId,version)=>[...resourceKeys.organization(organizationId),"resources",resourceId,"versions",version]};class ResourceQueryError2 extends Error{correlationId;resourceError;constructor(correlationId,error){super(error.message);this.name="ResourceQueryError";this.correlationId=correlationId;this.resourceError=error}}var unwrapResourceResult2=(result)=>{if(!result.ok){throw new ResourceQueryError2(result.correlationId,result.error)}return result.value};var createResourceListQueryOptions=(options)=>queryOptions({queryFn:async({signal})=>unwrapResourceResult2(await options.client.list(options.query,{signal})),queryKey:resourceKeys.list(options.organizationId,options.client.definition.id,options.client.definition.version,options.query)});var mapResourceAuthState2=(source)=>{if("ok"in source){if(source.ok){return{status:"authenticated"}}if(source.error.code==="forbidden"){return{status:"forbidden"}}if(source.error.code==="unauthenticated"){return{status:"unauthenticated"}}return{status:"authenticated"}}if(source.status==="authenticated"){return{status:"authenticated"}}if(source.status==="expired"){return{status:"expired"}}if(source.status==="error"&&source.error.code==="reauthorization_required"){return{status:"reauthentication_required"}}return{status:"unauthenticated"}};var hasResourceCapability2=(capabilities,capability)=>capabilities[capability]===true;var clearProtectedResourceQueries=async(queryClient,organizationId)=>{const queryKey=organizationId===undefined?resourceKeys.all:resourceKeys.organization(organizationId);await queryClient.cancelQueries({queryKey});queryClient.removeQueries({queryKey})};import{jsx}from"react/jsx-runtime";var entityScopeError=(outcome="not_dispatched")=>new ResourceQueryError2("client-scope",{code:"unauthenticated",message:"The authenticated entity source has changed",outcome,retryable:false});var identityKey=(state)=>state.status==="authenticated"?[state.identity.sourceId,state.identity.applicationId,state.identity.userId,state.identity.organizationId]:[state.status];var EntityContext=createContext(null);var createRuntime=(source,state)=>{let active=true;return{activate:()=>{active=true},current:()=>active&&source.getSnapshot()===state&&state.status==="authenticated",deactivate:()=>{active=false},formScope:globalThis.crypto.randomUUID(),key:identityKey(state),source,state}};var EntityRuntimeProvider=({source,children})=>{const queryClient=useQueryClient();const state=useSyncExternalStore(source.subscribe,source.getSnapshot,source.getSnapshot);const runtime=useMemo(()=>createRuntime(source,state),[source,state]);useLayoutEffect(()=>{runtime.activate();return()=>{runtime.deactivate();queryClient.cancelQueries({queryKey:["ci-entities",...runtime.key]});queryClient.removeQueries({queryKey:["ci-entities",...runtime.key]})}},[queryClient,runtime]);return jsx(EntityContext.Provider,{value:runtime,children})};var useEntityRuntime=()=>{const context=useContext(EntityContext);if(context===null){throw new Error("Configure an entity source on CollectiveIntelligenceProvider")}return context};var defaultPathFor=(contract)=>`/api/resources/${contract.id}`;var createDelegatedEntitySource=(options)=>{const fetch=options.fetch??globalThis.fetch;const pathFor=options.pathFor??defaultPathFor;const sourceId=options.sourceId??`${options.origin}/api/resources`;let cached;const snapshot=()=>{const identity=options.getIdentity();const status=options.getStatus();const next=status==="authenticated"&&identity!==null?{identity:{applicationId:options.applicationId,organizationId:identity.organizationId,sourceId,userId:identity.userId},status:"authenticated"}:{status:status==="expired"?"expired":"unauthenticated"};if(cached!==undefined&&identityKey(cached).join("\x00")===identityKey(next).join("\x00")){return cached}cached=next;return next};return{bind:(contract)=>createResourceClient({definition:contract,protocol:"entity",transport:createEntityHttpTransport({authentication:{credentials:"same-origin",mode:"server_delegated"},destination:{endpoint:new URL(pathFor(contract),options.origin).href,kind:"gateway"},fetch})}),getSnapshot:snapshot,subscribe:options.subscribe}};var assertConfiguredFields=(config)=>{const declared=new Set(Object.keys(config.fields));const referenced=[...config.table.columns,...Object.values(config.table.variants??{}).flatMap((variant)=>variant.columns),config.list.title,...config.list.description===undefined?[]:[config.list.description],...config.forms.create.fields,...config.forms.update.fields];const unknown=referenced.find((field)=>!declared.has(field));if(unknown!==undefined){throw new Error(`Entity view references unconfigured field: ${unknown}`)}};var defineEntity2=(declaration)=>{assertConfiguredFields(declaration.config);const schemaFields=new Set(Object.keys(declaration.contract.schemas.entity.properties??{}));const unknown=Object.keys(declaration.config.fields).find((field)=>!schemaFields.has(field));if(unknown!==undefined){throw new Error(`Configured field is absent from the entity schema: ${unknown}`)}return Object.freeze(declaration)};import{ResourceFilterPrimitive,ResourceFiltersBar,ResourceFormPrimitive,ResourceListPrimitive,ResourcePaginationPrimitive,ResourceTablePrimitive}from"@collective-intelligence/ui-react/resources";import{useMemo as useMemo5,useState as useState2}from"react";import{useIsMutating,useMutation,useQueryClient as useQueryClient2}from"@tanstack/react-query";import{useMemo as useMemo2,useRef}from"react";var immutable=(value)=>{if(value!==null&&typeof value==="object"){for(const child of Object.values(value)){immutable(child)}Object.freeze(value)}return value};var useEntityMutation=(contract,options)=>{const runtime=useEntityRuntime();const queryClient=useQueryClient2();const client=useMemo2(()=>runtime.source.bind(contract),[runtime,contract]);const resourceKey=["ci-entities",...runtime.key,contract.id,contract.version];const mutationKey=[...resourceKey,"write",options.id??"create"];const lastAction=useRef(null);const scopeOptions={};if(options.id!==undefined){scopeOptions.scope={id:JSON.stringify(mutationKey)}}const mutation=useMutation({mutationKey,...scopeOptions,mutationFn:async(action)=>{if(!action.current()){throw entityScopeError()}const result=await action.execute();if(!action.current()){throw entityScopeError("uncertain")}if(!result.ok){throw new ResourceQueryError2(result.correlationId,result.error)}return result},networkMode:"always",onSuccess:async(_data,action)=>{if(action.current()){await queryClient.invalidateQueries({queryKey:resourceKey})}},retry:false});const pending=useIsMutating({mutationKey})>0;const send=async(action)=>{try{const result=await mutation.mutateAsync(action);if(!action.current()){const error=entityScopeError("uncertain");return{correlationId:error.correlationId,error:error.resourceError,ok:false}}return result}catch(error){if(error instanceof ResourceQueryError2){return{correlationId:error.correlationId,error:error.resourceError,ok:false}}throw error}};const submit=(input)=>{const captured=immutable(structuredClone(options.prepare(input)));const action={current:runtime.current,execute:()=>options.execute(client,captured),input:captured,runtime,target:options.id};lastAction.current=action;return send(action)};const resubmit=()=>{const action=lastAction.current;if(action===null){throw new Error("No submitted entity action to resubmit")}if(action.target!==options.id){throw new Error("The submitted action belongs to a different record")}return send(action)};const current=mutation.variables===undefined||mutation.variables.runtime===runtime&&mutation.variables.target===options.id&&mutation.variables.current();const error=current&&mutation.error instanceof ResourceQueryError2?mutation.error:null;const failureStatus=()=>{if(error?.resourceError.outcome==="uncertain"){return"uncertain"}if(error?.resourceError.code==="conflict"){return"conflict"}return"rejected"};const status=mutation.isError?failureStatus():mutation.status;return{available:runtime.state.status==="authenticated"&&client.definition.capabilities[options.operation]===true,data:current?mutation.data?.value:undefined,error,pending,resubmit,status:current?status:"idle",submit,submitted:current?mutation.variables?.input:undefined}};var useEntityUpdate2=(contract,target)=>useEntityMutation(contract,{execute:(client,input)=>client.update(input),id:target.id,operation:"update",prepare:(input)=>({...input,id:target.id,idempotencyKey:globalThis.crypto.randomUUID()})});var useEntityCreate2=(contract)=>useEntityMutation(contract,{execute:(client,input)=>client.create(input),operation:"create",prepare:(input)=>({...input,idempotencyKey:globalThis.crypto.randomUUID()})});var useEntityDelete2=(contract,target)=>useEntityMutation(contract,{execute:(client,input)=>client.delete(input),id:target.id,operation:"delete",prepare:(input)=>({...input,id:target.id,idempotencyKey:globalThis.crypto.randomUUID()})});import{useQuery}from"@tanstack/react-query";import{useMemo as useMemo3}from"react";var authStateForError=(error,state)=>{if(error instanceof ResourceQueryError2){if(error.resourceError.code==="forbidden"){return{status:"forbidden"}}if(error.resourceError.code==="unauthenticated"){return{status:"unauthenticated"}}}return state};var useEntityList2=(contract,query)=>{const runtime=useEntityRuntime();const client=useMemo3(()=>runtime.source.bind(contract),[runtime,contract]);const result=useQuery({enabled:runtime.state.status==="authenticated"&&client.definition.capabilities.search,queryFn:async({signal})=>{if(!runtime.current()){throw entityScopeError()}const data=unwrapResourceResult2(await client.list(query,{signal}));if(!runtime.current()){throw entityScopeError()}return data},queryKey:["ci-entities",...runtime.key,contract.id,contract.version,"list",query],retry:false});return{authState:authStateForError(result.error,runtime.state),capabilities:client.definition.capabilities,data:result.data,empty:result.data?.items.length===0,error:result.error,loading:result.isLoading,refresh:async()=>{await result.refetch()},refreshing:result.isFetching}};var useEntityRecord2=(contract,input)=>{const runtime=useEntityRuntime();const client=useMemo3(()=>runtime.source.bind(contract),[runtime,contract]);const result=useQuery({enabled:runtime.state.status==="authenticated"&&client.definition.capabilities.get===true,queryFn:async({signal})=>{if(!runtime.current()){throw entityScopeError()}const data=unwrapResourceResult2(await client.get(input,{signal}));if(!runtime.current()){throw entityScopeError()}return data},queryKey:["ci-entities",...runtime.key,contract.id,contract.version,"record",input.id],retry:false});return{authState:authStateForError(result.error,runtime.state),capabilities:client.definition.capabilities,data:result.data,error:result.error,loading:result.isLoading,refresh:async()=>{await result.refetch()},refreshing:result.isFetching}};import{createEntityProtocol}from"@collective-intelligence/external-apps-client-sdk/resources";import{createParser,parseAsJson,useQueryStates}from"nuqs";import{useMemo as useMemo4,useState}from"react";var DEFAULT_PAGE_SIZE=25;var namespacePattern=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;var isObject=(value)=>typeof value==="object"&&value!==null&&!Array.isArray(value);var isFilter=(value)=>{if(!isObject(value)){return false}if((value.operator==="and"||value.operator==="or")&&"filters"in value){return Array.isArray(value.filters)&&value.filters.every(isFilter)}if(value.operator==="not"&&"filter"in value){return isFilter(value.filter)}return typeof value.field==="string"&&typeof value.operator==="string"&&(value.operator==="is_null"||value.operator==="is_not_null"||value.operator==="is_empty"||("value"in value))};var isSorts=(value)=>Array.isArray(value)&&value.every((sort)=>isObject(sort)&&typeof sort.field==="string"&&(sort.direction==="asc"||sort.direction==="desc"));var positiveInteger=(maximum=Number.MAX_SAFE_INTEGER)=>createParser({parse:(value)=>{const number=Number(value);return/^[1-9]\d*$/u.test(value)&&Number.isSafeInteger(number)&&number<=maximum?number:null},serialize:String});var pageSizeFor=(value=DEFAULT_PAGE_SIZE)=>Number.isSafeInteger(value)&&value>0&&value<=100?value:DEFAULT_PAGE_SIZE;var parsersFor=(pageSize,contract)=>{const protocol=contract===undefined?undefined:createEntityProtocol(contract);const accepts=(query)=>protocol===undefined||protocol.decodeRequest({operation:"list",query:{pagination:{mode:"page",page:1,pageSize},...query},requestId:"view-state",resourceId:contract?.id}).ok;return{filter:parseAsJson((value)=>isFilter(value)&&accepts({filter:value})?value:null),page:positiveInteger().withDefault(1),pageSize:positiveInteger(100).withDefault(pageSize),sort:parseAsJson((value)=>isSorts(value)&&accepts({sort:value})?value:null).withDefault([]),textSearch:createParser({parse:(value)=>accepts({textSearch:value})?value:null,serialize:(value)=>value}).withDefault("")}};var queryFor=(state)=>{const query={pagination:{mode:"page",page:state.page,pageSize:state.pageSize}};if(state.filter!==null){query.filter=state.filter}if(state.sort.length>0){query.sort=state.sort}if(state.textSearch.trim()!==""){query.textSearch=state.textSearch.trim()}return query};var controller=(state,update)=>({query:queryFor(state),setFilter:(filter)=>update({filter,page:1}),setPage:(page)=>update({page:Math.max(1,page)}),setPageSize:(pageSize)=>update({page:1,pageSize:Math.max(1,Math.min(100,pageSize))}),setSort:(sort)=>update({page:1,sort:[...sort]}),setTextSearch:(textSearch)=>update({page:1,textSearch}),state});var useEntityViewState2=(namespace,input={})=>{if(!namespacePattern.test(namespace)){throw new Error("URL-backed entity views require a stable alphanumeric namespace")}const parsers=useMemo4(()=>parsersFor(pageSizeFor(input.pageSize),input.contract),[input.pageSize,input.contract]);const[state,setState]=useQueryStates(parsers,{clearOnDefault:true,history:"replace",shallow:false,urlKeys:{filter:`${namespace}.filter`,page:`${namespace}.page`,pageSize:`${namespace}.pageSize`,sort:`${namespace}.sort`,textSearch:`${namespace}.textSearch`}});const normalized=useMemo4(()=>({filter:state.filter,page:state.page>0?state.page:1,pageSize:state.pageSize>0&&state.pageSize<=100?state.pageSize:input.pageSize??DEFAULT_PAGE_SIZE,sort:state.sort,textSearch:state.textSearch}),[input.pageSize,state]);return controller(normalized,(next)=>{const{sort,...values}=next;const update=values;if(sort!==undefined){update.sort=[...sort]}setState(update)})};var useMemoryEntityViewState2=(input={})=>{const[state,setState]=useState({filter:null,page:1,pageSize:pageSizeFor(input.pageSize),sort:[],textSearch:""});return controller(state,(next)=>{setState((current)=>({...current,...next}))})};import{jsx as jsx2,jsxs,Fragment}from"react/jsx-runtime";var isObject2=(value)=>value!==null&&typeof value==="object"&&!Array.isArray(value);var schemaProperties=(entity,mode="entity")=>{const properties=entity.contract.schemas[mode]?.properties;return isObject2(properties)?properties:{}};var requiredFields=(entity,mode)=>{const required=entity.contract.schemas[mode]?.required;return new Set(Array.isArray(required)?required.filter((value)=>typeof value==="string"):[])};var scalarTypes=(schema)=>{const{type}=schema;if(typeof type==="string"){return[type]}return Array.isArray(type)?type.filter((value)=>typeof value==="string"):[]};var presentationSchema=(schema)=>{const union=schema.anyOf??schema.oneOf;if(!Array.isArray(union)){return schema}const members=union.filter(isObject2);const values=members.filter((member)=>member.type!=="null");const[value]=values;if(members.length===2&&values.length===1&&value!==undefined){return{...schema,...value,type:[...scalarTypes(value),"null"]}}return schema};var defaultControl=(schema)=>{if(schema.$ref!==undefined||(schema.anyOf!==undefined||schema.oneOf!==undefined)&&schema.type===undefined){return"custom"}if(Array.isArray(schema.enum)){return"select"}const types=scalarTypes(schema);if(types.includes("boolean")){return"checkbox"}if(types.includes("number")||types.includes("integer")){return"number"}if(schema.format==="email"){return"email"}if(schema.format==="date"){return"date"}if(schema.format==="date-time"){return"datetime-local"}return types.some((type)=>type==="object"||type==="array")?"custom":"text"};var configuredControl=(config,schema)=>{switch(config.control){case"text":case"email":case"number":case"checkbox":case"date":case"datetime-local":case"select":case"custom":{return config.control}case undefined:{return defaultControl(schema)}default:{return"custom"}}};var formFields=(entity,mode)=>{const properties=schemaProperties(entity,mode);const entityProperties=schemaProperties(entity);const required=requiredFields(entity,mode);return entity.config.forms[mode].fields.map((name)=>{const config=entity.config.fields[name];if(config===undefined){throw new Error(`Missing entity field configuration: ${name}`)}const candidate=properties[name]??entityProperties[name];const schema=isObject2(candidate)?presentationSchema(candidate):{};const entityField=entityProperties[name];const enums=Array.isArray(schema.enum)?schema.enum.filter((value)=>typeof value==="string"||typeof value==="number"||typeof value==="boolean"):[];return{control:configuredControl(config,schema),label:config.label,name,nullable:scalarTypes(schema).includes("null")||Array.isArray(schema.enum)&&schema.enum.includes(null),options:enums.map((value)=>({label:String(value),value})),readOnly:config.readOnly===true||schema.readOnly===true||isObject2(entityField)&&entityField.readOnly===true,required:required.has(name)}})};var displayValue=(value)=>{if(value===null||value===undefined){return"—"}if(typeof value==="boolean"){return value?"Yes":"No"}return typeof value==="object"?JSON.stringify(value):String(value)};var renderField=(entity,record,name,renderers)=>{const renderer=entity.config.fields[name]?.renderer;return renderer===undefined||renderers?.[renderer]===undefined?displayValue(record.value[name]):renderers[renderer]({field:name,record})};var sortFor=(state,field)=>{const[current]=state.state.sort;return[{direction:current?.field===field&&current.direction==="asc"?"desc":"asc",field}]};var QueryControls=({view})=>jsx2(ResourceFiltersBar,{controls:[{control:jsx2(ResourceFilterPrimitive,{label:"Search",onChange:(value)=>view.setTextSearch(value),value:view.state.textSearch}),fullWidth:true,key:"search"}]});var EntityStatus=({state})=>{if(state.authState.status!=="authenticated"){return jsx2("output",{children:state.authState.status})}if(state.loading){return jsx2("output",{children:"Loading"})}if(state.error!==null){return jsx2("output",{role:"alert",children:state.error.message})}return null};var EntityPagination=({result,view,pending})=>{if(result===undefined||result.pagination!=="page"){return null}return jsx2(ResourcePaginationPrimitive,{currentPage:result.page,totalPages:result.totalPages,hasPreviousPage:result.page>1,hasNextPage:result.page<result.totalPages,isPending:pending,onPageChange:(page)=>view.setPage(page),onPrevious:()=>view.setPage(result.page-1),onNext:()=>view.setPage(result.page+1)})};var ControlledEntityTable=({entity,empty,renderers,variant,view})=>{const result=useEntityList2(entity.contract,view.query);const names=variant===undefined?entity.config.table.columns:entity.config.table.variants?.[variant]?.columns??entity.config.table.columns;const columns=names.map((name)=>({id:name,label:entity.config.fields[name]?.label??name}));const hasStatus=result.authState.status!=="authenticated"||result.loading||result.error!==null;return jsxs("section",{children:[jsx2(QueryControls,{view}),hasStatus?jsx2(EntityStatus,{state:result}):jsxs(Fragment,{children:[jsx2(ResourceTablePrimitive,{columns:columns.map((column)=>({...column,label:entity.contract.query.sortableFields.includes(column.id)?jsx2("button",{type:"button",onClick:()=>view.setSort(sortFor(view,column.id)),children:column.label}):column.label})),empty,getRowKey:(record)=>record.id,renderCell:(record,column)=>{const name=names.find((field)=>field===column.id);return name===undefined?null:renderField(entity,record,name,renderers)},rows:result.data?.items??[]}),jsx2(EntityPagination,{pending:result.refreshing,result:result.data,view})]})]})};var UrlEntityTable=(props)=>jsx2(ControlledEntityTable,{...props,view:useEntityViewState2(props.namespace,{contract:props.entity.contract,pageSize:props.pageSize??25})});var MemoryEntityTable=(props)=>jsx2(ControlledEntityTable,{...props,view:useMemoryEntityViewState2(props.pageSize===undefined?{}:{pageSize:props.pageSize})});var EntityTable2=(props)=>props.viewState==="memory"?jsx2(MemoryEntityTable,{...props}):jsx2(UrlEntityTable,{...props,namespace:props.namespace});var ControlledEntityList=({entity,empty,renderers,view})=>{const result=useEntityList2(entity.contract,view.query);const hasStatus=result.authState.status!=="authenticated"||result.loading||result.error!==null;return jsxs("section",{children:[jsx2(QueryControls,{view}),hasStatus?jsx2(EntityStatus,{state:result}):jsxs(Fragment,{children:[jsx2(ResourceListPrimitive,{empty,getItemKey:(record)=>record.id,items:result.data?.items??[],renderItem:(record)=>jsxs("article",{children:[jsx2("strong",{children:renderField(entity,record,entity.config.list.title,renderers)}),entity.config.list.description===undefined?null:jsx2("p",{children:renderField(entity,record,entity.config.list.description,renderers)})]})}),jsx2(EntityPagination,{pending:result.refreshing,result:result.data,view})]})]})};var UrlEntityList=(props)=>jsx2(ControlledEntityList,{...props,view:useEntityViewState2(props.namespace,{contract:props.entity.contract,pageSize:props.pageSize??25})});var MemoryEntityList=(props)=>jsx2(ControlledEntityList,{...props,view:useMemoryEntityViewState2(props.pageSize===undefined?{}:{pageSize:props.pageSize})});var EntityList2=(props)=>props.viewState==="memory"?jsx2(MemoryEntityList,{...props}):jsx2(UrlEntityList,{...props,namespace:props.namespace});var initialFormValues=(entity,mode,supplied,record)=>{const values={};const properties=schemaProperties(entity,mode);const required=requiredFields(entity,mode);for(const name of entity.config.forms[mode].fields){const schema=properties[name];if(required.has(name)&&isObject2(schema)&&scalarTypes(presentationSchema(schema)).includes("boolean")){values[name]=false}if(isObject2(schema)&&schema.default!==undefined){values[name]=schema.default}if(record?.value[name]!==undefined){values[name]=record.value[name]}if(supplied?.[name]!==undefined){values[name]=supplied[name]}}return values};var controlRenderer=(entity,controls)=>function renderEntityControl(props){const fields=entity.config.fields;const key=fields[props.field.name]?.control;return key===undefined||controls?.[key]===undefined?jsxs("p",{role:"alert",children:["A custom control is required for ",props.field.label,"."]}):controls[key](props)};var mutationMessage=(mutation)=>{if(mutation.status==="conflict"){return"This entity changed. Your draft is preserved."}if(mutation.status==="uncertain"){return"The write outcome is unknown. Resubmit the same action to resolve it."}return mutation.error?.resourceError.message};var writableValues=(fields,values)=>{const writable=new Set(fields.filter((field)=>!field.readOnly).map((field)=>field.name));return Object.fromEntries(Object.entries(values).filter(([name])=>writable.has(name)))};var displayedValues=(fields,values)=>Object.fromEntries(Object.entries(values).map(([name,value])=>{if(typeof value!=="string"||fields.find((field)=>field.name===name)?.control!=="datetime-local"){return[name,value]}const date=new Date(value);if(Number.isNaN(date.getTime())){return[name,value]}return[name,new Date(date.getTime()-date.getTimezoneOffset()*60000).toISOString().slice(0,23)]}));var wireValue=(fields,name,value)=>{if(typeof value!=="string"||value===""||fields.find((field)=>field.name===name)?.control!=="datetime-local"){return value}const date=new Date(value);return Number.isNaN(date.getTime())?value:date.toISOString()};var CreateEntityForm=(props)=>{const mutation=useEntityCreate2(props.entity.contract);const[values,setValues]=useState2(()=>initialFormValues(props.entity,"create",props.initialValues));const fields=useMemo5(()=>formFields(props.entity,"create"),[props.entity]);return jsx2(ResourceFormPrimitive,{label:"Create entity",fields,values:displayedValues(fields,values),disabled:!mutation.available,pending:mutation.pending,error:mutationMessage(mutation),...props.onCancel===undefined?{}:{onCancel:props.onCancel},onChange:(name,value)=>setValues((current)=>({...current,[name]:wireValue(fields,name,value)})),onSubmit:async()=>{const value=writableValues(fields,values);const action=mutation.status==="uncertain"?mutation.resubmit():mutation.submit({value});const result=await action;if(result.ok){props.onSuccess?.(result.value)}},renderControl:controlRenderer(props.entity,props.controls),submitLabel:mutation.status==="uncertain"?"Resubmit":"Create"})};var UpdateEntityForm=(props)=>{const mutation=useEntityUpdate2(props.entity.contract,{id:props.record.id});const[draft,setDraft]=useState2(()=>({expectedRevision:props.record.revision,values:initialFormValues(props.entity,"update",props.initialValues,props.record)}));const{expectedRevision,values}=draft;const fields=useMemo5(()=>formFields(props.entity,"update"),[props.entity]);return jsx2(ResourceFormPrimitive,{label:"Update entity",fields,values:displayedValues(fields,values),disabled:!mutation.available,pending:mutation.pending,error:mutationMessage(mutation),...props.onCancel===undefined?{}:{onCancel:props.onCancel},onChange:(name,value)=>setDraft((current)=>({...current,values:{...current.values,[name]:wireValue(fields,name,value)}})),onSubmit:async()=>{const value=writableValues(fields,values);const result=await(mutation.status==="uncertain"?mutation.resubmit():mutation.submit({expectedRevision,value}));if(result.ok){props.onSuccess?.(result.value)}},renderControl:controlRenderer(props.entity,props.controls),submitLabel:mutation.status==="uncertain"?"Resubmit":"Save"})};var EntityForm2=(props)=>{const runtime=useEntityRuntime();if(runtime.state.status!=="authenticated"){return jsx2("output",{children:runtime.state.status})}return props.mode==="create"?jsx2(CreateEntityForm,{...props},runtime.formScope):jsx2(UpdateEntityForm,{...props,record:props.record},JSON.stringify([runtime.formScope,props.record.id]))};
2
- export{resourceKeys,ResourceQueryError2,unwrapResourceResult2,createResourceListQueryOptions,mapResourceAuthState2,hasResourceCapability2,clearProtectedResourceQueries,EntityRuntimeProvider,createDelegatedEntitySource,defineEntity2,useEntityUpdate2,useEntityCreate2,useEntityDelete2,useEntityList2,useEntityRecord2,useEntityViewState2,useMemoryEntityViewState2,EntityTable2,EntityList2,EntityForm2};